I would like to skip adding a vpc to lambda in certain env. The current terraform code to update vpc is like below
data "aws_subnet" "lambda-private-subnet_1" {
availability_zone = var.environment_type_tag != "prd" ? "us-east-1a" : null
dynamic "filter" {
for_each = var.environment_type_tag == "prd" ? [] : [1]
content {
name = "tag:Name"
values = [var.subnet_value]
}
}
}
resource "aws_lambda_function" "tests" {
dynamic "vpc_config" {
for_each = var.environment_type_tag == "prd" ? [] : [1]
content {
subnet_ids = [data.aws_subnet.lambda-private-subnet_1.id]
security_group_ids = [var.security_group]
}
}
}
During 'terraform plan', the output is like below
##[error][1m[31mError: [0m[0m[1mmultiple EC2 Subnets matched; use additional constraints to reduce matches to a single EC2 Subnet[0m
I would like to skip the 'data "aws_subnet"' block if its 'prd' environment type.
So there are four different questions in this question. We can attempt to answer each one:
dynamic block to skip vpc config to lambda
This is already occurring with the given code. The dynamic blocks are "skipped" in prd with the current code.
I would like to skip adding a vpc to lambda in certain env.
If you mean "subnet" instead of "vpc", then this is also already occurring with the given code. Otherwise, please update with the vpc config.
##[error][1m[31mError: [0m[0m[1mmultiple EC2 Subnets matched; use additional constraints to reduce matches to a single EC2 Subnet[0m
The error message is due to the fact that your filters match multiple subnets outside of prd, and therefore you need to constrain the filter conditions.
I would like to skip the 'data "aws_subnet"' block if its 'prd' environment type.
You just need to extend your current code to make the data optional:
data "aws_subnet" "lambda-private-subnet_1" {
for_each = var.environment_type_tag == "prd" ? [] : toset(["this"])
...
}
You can then remove the for_each from the dynamic block in the resource as it is redundant, and update the attribute references with elements accordingly:
subnet_ids = [data.aws_subnet.lambda-private-subnet_1["this"].id]
Related
So I want do use terraform and configure my storage account so that each account that is not a file storage gets soft container deletion enabled, and if i provide a boolean flag "cors_enabled" in my account variables, add some cors rules. Since both of these are in the blob_properties block, I have to decide if I add a blob_properties block and then decide again if I have to add the re
The only way I could think of is to have a for_each loop over the variables, and then use nested dynamic blocks like this.
my vars.account looks somewhat like this:
{
"account1": {
"name": "account1",
"account_kind": "BlobStorage",
"account_replication_type": "LRS",
"cors_enabled": "true",
# other stuff that's not relevant
},
"account2": {
"name": "account1",
"account_kind": "FileStorage",
"account_replication_type": "LRS",
"cors_enabled": "true",
# other stuff that's not relevant
}
}
My hcl looks like this:
resource "azurerm_storage_account" "account" {
for_each = var.accounts
name = each.value.name
# [...] do some further configuation, set some required variables
dynamic blob_properties {
for_each = lookup(each.value, "account_kind", "StorageV2") != "FileStorage" || lookup(each.value, "cors_enabled", "false") ? [""] : []
content {
dynamic container_delete_retention_policy {
for_each = lookup(each.value, "account_kind", "StorageV2") != "FileStorage" ? [""] : []
content {
days = 30
}
}
dynamic cors_rule {
for_each = lookup(each.value, "cors_enabled", "false") ? [""] : []
content {
# some cors rule configuration
}
}
}
}
But this of course doesn't work, because the "each.value" in the container_delete_retention_policy refers to the each in the INNER for_each loop (in the dynamic blob_properties blob) and the same goes for the each.value in the cors_rule, when I want it to refer to the "each" infor_each = var.accounts
I tried using count instead of the outer for_each loop (and then do some magic with local maps so I can access the correct key/value by the count's index), but count seems to produce something different than for_each; when I use for_each, I can later use
myvariable = azurerm_storage_account.account["mykey"].name
so I guess it produces some kind of map. When I use count instead of for_each, I then get an error
azurerm_storage_account.acount is tuple with 8 elements The given key does not identify an element in this collection value: a number is required.
So I guess it produces a list?
Is there a way in terraform to access the "each" of an outer for_each loop in an inner for_each loop? If not, is there a different way to achieve what I want?
I got it wrong and got confused by an unrelated error I got - the code above behaves exactly like I want it to, i.e. always referring to the outermost "each".
I am creating an AWS VPC with a single public subnet in a brand-new Terraform project, consisting only of a main.tf file. In that file I am using two resource blocks, aws_vpc and aws_subnet. The second resource must be attached to the first using the vpc_id attribute. The value of this attribute is created only upon apply, so it cannot be hard-coded. How do I get the ID of the resource I just created, so I can use it in the subsequent block?
resource "aws_vpc" "my_vpc" {
cidr_block = "102.0.0.0/16"
tags = {
Name = "My-VPC"
}
}
resource "aws_subnet" "my_subnet" {
vpc_id = # what goes here?
cidr_block = "102.0.0.0/24"
tags = {
Name = "My-Subnet"
}
}
The docs give the example of data.aws_subnet.selected.vpc_id for vpc_id. The value of this appears to depend on two other blocks, variable and data. I am having a hard time seeing how to wire these up to my VPC. I tried copying them directly from the docs. Upon running terraform plan I get the prompt:
var.subnet_id
Enter a value:
This is no good; I want to pull the value from the VPC I just created, not enter it at the command prompt. Where do I specify that the data source is the resource that I just created in the previous code block?
I have heard that people create a separate file to hold Terraform variables. Is that what I should to do here? It seems like it should be so basic to get an ID from one resource and use it in the next. Is there a one-liner to pass along this information?
You can just call the VPC in the subnet block by referencing Terraform's pointer. Also, doing this tells Terraform that the VPC needs to be created first and destroyed second.
resource "aws_vpc" "my_vpc" {
cidr_block = "102.0.0.0/16"
tags = {
Name = "My-VPC"
}
}
resource "aws_subnet" "my_subnet" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "102.0.0.0/24"
tags = {
Name = "My-Subnet"
}
}
I want to pull the value from the VPC I just created,
You can't do this. You can't dynamically populate variables from data sources. But you could use local instead:
locals {
subnet_id = data.aws_subnet.selected.id
}
and refer to it as local.subnet_id.
So I start to explore Terraform, and so far Im able to get my network and VMs up in Azure.
Since I have seven VM`s that are created, I would like to get the IP-adress and the Hostname of this.
In my main.tf i have this:
provider "azurerm" {
features {}
# Configuration options
}
module "splunk_architect" {
source = "./modules/architect"
}
And just an example from my main.tf in /modules/architect
resource "azurerm_network_interface" "main" {
for_each = toset(var.vm_names)
name = "${each.value}-nic"
location = azurerm_resource_group.rsg.location
resource_group_name = azurerm_resource_group.rsg.name
ip_configuration {
name = "testconfiguration1"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.pubip[each.key].id
}
And my outputs.tf in /modules/architect
output "ip" {
value = azurerm_network_interface.main[each.key].private_ip_address
}
So when i run this, i get this error message:
Error: Reference to "each" in context without for_each
on modules\architect\outputs.tf line 6, in output "ip": 6: value = azurerm_network_interface.main[each.key].private_ip_address
The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set.
So I have tried to set the for_each in the module, but did not get that to work.
I have also been through the documentations without any success.
Any tips to what I can try to get the IP of each VM printed out?
Since you have multiple virtual machines (declared with for_each), you will also have multiple private IP addresses to return. You'll need to decide for your module what is the best way to return those IP addresses to the caller.
One common answer is to return a map whose keys are the elements from var.vm_names, so the caller can easily correlate the VM names it passed in to the IP addresses you're returning, like this:
output "ip" {
value = tomap({
for name, vm in azurerm_network_interface.main : name => vm.private_ip_address
})
}
This is a for expression, which constructs a new data structure from an existing data structure by evaluating expressions against each element. In this case, it's taking the keys from azurerm_network_interface.main -- which will match the values given in for_each -- and mapping them to the private_ip_address attribute for each object.
The result will therefore appear as a map from VM names to IP addresses, perhaps like this:
{
"example1" = "10.1.2.1"
"example2" = "10.1.2.45"
}
Not directly related to your question, but note also that if your module only ever uses var.vm_names as a set then it can be better to declare it as a set in the first place, rather than converting it at each use, because then it'll be clearer to users of your module that the order of the strings inside doesn't matter and that there can't be two elements with the same string:
variable "vm_names" {
type = set(string)
}
With that declaration, var.vm_names will already be a set of strings and so you don't need to explicitly convert it in for_each:
for_each = var.vm_names
For Azure Terraform:
If a variable is declared in a tf file will this value be applied to same variable in other tf files processed together? Why is there a default value associated with a variable statement?
If I made a tfvars file:
cidrs = [ "10.0.0.0/16", "10.1.0.0/16" ]
Can cidr be used as below for subnet id? Not really understanding usage syntax?
subnet_id = "${azurerm_subnet.subnet.id}"
subnet id = cidr
What exactly is the "Default" function when used with variables? See below:
variable "prefix" {
type = "string"
default = "my"
}
variable "tags" {
type = "map"
default = {
Environment = "Terraform GS"
Dept = "Engineering"
}
}
variable "sku" {
default = {
westus = "16.04-LTS"
eastus = "18.04-LTS"
}
}
There are several questions there. the easy one: default:
The variable declaration can also include a default argument. If
present, the variable is considered to be optional and the default
value will be used if no value is set when calling the module or
running Terraform. The default argument requires a literal value and
cannot reference other objects in the configuration.
For the other question, refer to this example: https://www.terraform.io/docs/providers/azurerm/r/subnet.html#attributes-reference
So in short, to use the existing subnet cidr, you need to refer to it like this:
azurerm_subnet.%subnetname%.address_prefix
subnet name, however, cannot be equals to a cidr, because it doesnt allow for / inside of the name. you can use something like this though: 10.0.0.0-24
In file1.tf (generated by kops) I have a resource like this:
resource "aws_vpc" "my-vpc-tf-id" {
...
}
The resource ID was dynamically generated by kops and also added to terraform.tfvars (so it can be used in other places in the .tf files):
my_var = "my-vpc-tf-id"
Now I would like to reference the VPC resource from file2.tf without hardcoding its name:
resource "aws_security_group" "db" {
...
vpc_id = "${aws_vpc.${var.my_var}.id}"
...
}
but Terraform complains that the ${var.my_var} is not allowed. So instead I define this in file2.tf:
resource "aws_security_group" "db" {
...
vpc_id = "${aws_vpc.{{MY_VAR_VAL}}.id}"
...
}
and I use sed to replace the placeholder with the value. This works well but complicates certain other tasks so I was wondering if there were other ways of achieving this without using sed or hardcoding the my_var value (just Terraform's HCL).
The normal way to do this is to use data sources to look up the thing you want to refer to.
The VPC data source allows you to filter based on a number of different things but a typical one is to use the Name tag:
data "aws_vpc" "selected" {
tags {
Name = "${var.vpc}"
}
}
And then you can refer to this VPC with:
resource "aws_security_group" "db" {
...
vpc_id = "${data.aws_vpc.selected.id}"
...
}
The two cases are
(i) if they are both vpc and security group are in same run
we can directly refer vpc id in security group without any data sources
resource "aws_security_group" "db" {
...
vpc_id = "${aws_vpc.my-vpc-tf-id.id}"
...
}
(ii) if they are run differently (using same remote state files or importing config)
use data source as mentioned above by ydaetskcoR