Terraform using count.index in a resource name - terraform

using terraform i'm trying to include the count in the name of my resource using count.index, but unable to get the count to display. I'm basically looking to add the count to the resource name so that the resource can be found, otherwise the resource is unknown.
count = 3
autoscaling_group_name = "${aws_autoscaling_group.exampleautoscaling-("count.index")-example.name}"
ERROR
resource variables must be three parts: TYPE.NAME.ATTR in:
expected is : exampleautoscaling-1-example.name,exampleautoscaling-2-example.name,exampleautoscaling-3-example.name

My suggestion will be to add tags and use name_prefix arguments. But specific to your question
Here are some snippets from the documentation what you can try
"${var.hostnames[count.index]}"
OR
resource "aws_instance" "web" {
# ...
count = "${var.count}"
# Tag the instance with a counter starting at 1, ie. web-001
tags {
Name = "${format("web-%03d", count.index + 1)}"
}
}
Providing the link here. Look under the Math section.

Your syntax is incorrect. You are trying to insert count into the middle of a resource name. You need to change it to be the following:
count = 3
autoscaling_group_name = "${aws_autoscaling_group.exampleautoscaling.name}-${count.index}"

Related

Terraform Outputs: How do I create a (map?) and how do I use the map? Is a map even the right tool?

I am struggling with a few terraform concepts.
I am successfully using the aztfmod/azurecaf provider to name my resourcegroup, but this means I need to get that name as an output for the companynet.resource_group module, so that I can use that name again when calling the companynet.key_vault module.
# terraform.tfvars
resource_groups = {
rg1 = {
name = "resourcegroup1"
location = "eastus"
}
rg2 = {
name = "resourcegroup2"
location = "eastus"
}
}
# root main.tf
provider "azurerm" {
features {}
}
module "companynet" {
source = "./modules/companynet"
tenant_id = var.tenant_id
environment = var.environment
resource_groups = var.resource_groups
key_vaults = var.key_vaults
storage_accounts = var.storage_accounts
app_service_plans = var.app_service_plans
}
# modules/companynet/main.tf
module "resource_group" {
source = "../companynet.resource_group"
environment = var.environment
resource_groups = var.resource_groups
}
module "key_vault" {
source = "../companynet.key_vault"
tenant_id = var.tenant_id
environment = var.environment
resource_groups = "${module.resource_group.resource_groups.companynet}"
key_vaults = var.key_vaults
}
The module resource_group has the following main.tf:
# modules/companynet.resource_group/main.tf
resource "azurecaf_name" "resource_group" {
for_each = var.resource_groups
name = each.value.name
resource_type = "azurerm_resource_group"
suffixes = ["${var.environment}", "001"]
}
resource "azurerm_resource_group" "resource_group" {
for_each = var.resource_groups
name = azurecaf_name.resource_group[each.key].result
location = each.value.location
}
but I don't know how to get the output of that resource_group name.
I have tried a few different things that do not work
# modules/companynet.resource_group/outputs.tf
output "resource_groups" {
value = azurerm_resource_group.resource_group[*].name
}
value = azurerm_resource_group.resource_group.name
value = azurerm_resource_group.resource_group.companynet.name
value = azurerm_resource_group.resource_group[companynet].name
Each of these results in one error or another, all indicating a problem with modules/companynet.resource_group/outputs.tf
Ideally I would get an object that I can then iterate through in another module. I expect to be able to call something like to get access to those resource group names in other modules such as:
# modules/companynet.key_vault/main.tf
resource "azurerm_key_vault" "key_vault" {
for_each = var.key_vaults
name = azurecaf_name.key_vault[each.key].result
location = var.resource_groups.location
resource_groups = "${module.resource_group.resource_groups.[companynet]}"
sku_name = "standard"
tenant_id = var.tenant_id
}
azurerm_resource_group.resource_group is declared with for_each, and so that expression refers to a map of objects where the keys match the keys of the for_each expression and the values are the corresponding declared resource instances.
In References to Resource Attributes there are various examples of referring to resource attributes in different situations, including the following about resources using for_each:
When a resource has the for_each argument set, the resource itself becomes a map of instance objects rather than a single object, and attributes of instances must be specified by key, or can be accessed using a for expression.
aws_instance.example["a"].id returns the id of the "a"-keyed resource.
[for value in aws_instance.example: value.id] returns a list of all of the ids of each of the instances.
That second item shows how to use a for expression to produce a list of the ids of aws_instance.example, but it doesn't show exactly how to produce a map and instead expects you to refer to the linked documentation about for expressions to learn about that:
The type of brackets around the for expression decide what type of result it produces.
The above example uses [ and ], which produces a tuple. If you use { and } instead, the result is an object and you must provide two result expressions that are separated by the => symbol:
{for s in var.list : s => upper(s)}
This expression produces an object whose attributes are the original elements from var.list and their corresponding values are the uppercase versions. For example, the resulting value might be as follows:
{
foo = "FOO"
bar = "BAR"
baz = "BAZ"
}
A for expression alone can only produce either an object value or a tuple value, but Terraform's automatic type conversion rules mean that you can typically use the results in locations where lists, maps, and sets are expected.
This section describes how to produce an object and then notes that you can use the result in a location where a map is expected. In practice it's often possible to use object-typed values and mapped-type values interchangeably in Terraform, because they both have in common that they have elements identified by string keys. The difference is that an object type can have a separate type for each of its attributes, whereas a map must have the same type for all attributes.
Given all of this information, we can produce an object value describing the names for each resource group like this:
output "resource_groups" {
value = { for k, g in azurerm_resource_group.resource_group : k => g.name }
}
For most purposes it doesn't really matter that this is an object-typed result rather than specifically a map, but since we know that .name is always a string we can infer that all of the attributes of this object have string-typed values, and so it would also be valid to explicitly convert to a map of strings using the tomap function (which is a "location where [...] maps [...] are expected", per the above documentation):
output "resource_groups" {
value = tomap({
for k, g in azurerm_resource_group.resource_group : k => g.name
})
}

Creating a record list from a terraform resource

In Terraform, I'm trying to create a DNS SRV record from created DNS A records. I would like to populate the records with the names from the aws_route53_record.etcd names, but running into errors when referencing the resource names.
Is there an easy way to achieve this?
# This resource works without errors
resource "aws_route53_record" "etcd" {
count = length(var.control_plane_private_ips)
zone_id = data.aws_route53_zone.test.zone_id
name = "etcd-${count.index}.${data.aws_route53_zone.test.name}"
type = "A"
ttl = 60
records = var.control_plane_private_ips
}
resource "aws_route53_record" "etcd_ssl_tcp" {
zone_id = data.aws_route53_zone.test.zone_id
name = "_etcd-server-ssl._tcp.${data.aws_route53_zone.test.name}"
type = "SRV"
ttl = 60
# code is producing an error here. Would like to add the names to the records
for_each = [for n in aws_route53_record.etcd : { name = n.name }]
records = [
"0 10 2380 ${each.value.name}.${data.aws_route53_zone.test.name}"
]
}
When running a terraform plan, I get the following error.
Error: Invalid for_each argument
on main.tf line 55, in resource "aws_route53_record" "etcd_ssl_tcp":
55: for_each = [for n in aws_route53_record.etcd : { name = n.name }]
The given "for_each" argument value is unsuitable: the "for_each" argument
must be a map, or set of strings, and you have provided a value of type tuple.
you use for_each and for in the same line. Both are describing loops and this makes it really hard to fallow. Try to split the line in 2 different lines and assign the for to a local variable. Splitting the for and for_each will help us check this.
I think the issue is [for n in aws_route53_record.etcd : { name = n.name }]
the starting bracket [for ... defines a list and the
{ name .. defines a map . So a list of maps. Perhaps to remove the { ?
Figured it out based on the feedback. Thanks for the help!
resource "aws_route53_record" "etcd_ssl_tcp" {
zone_id = data.aws_route53_zone.kubic.zone_id
name = "_etcd-server-ssl._tcp.${data.aws_route53_zone.test.name}"
type = "SRV"
ttl = 60
records = [
for n in aws_route53_record.etcd :
"0 10 2380 ${n.name}"
]
}

How do I assign unique "Name" tag to the EC2 instance(s)?

I am using Terraform 0.12. I am trying to build EC2 in bulk for a project and instead of sequentially naming the ec2's I will to name the instances by providing unique names.
I think of using dynamic tags, however, not quite sure how to incorporate in the code.
resource "aws_instance" "tf_server" {
count = var.instance_count
instance_type = var.instance_type
ami = data.aws_ami.server_ami.id
associate_public_ip_address = var.associate_public_ip_address
##This provides sequential name.
tags = {
Name = "tf_server-${count.index +1}"
}
key_name = "${aws_key_pair.tf_auth.id}"
vpc_security_group_ids = ["${var.security_group}"]
subnet_id = "${element(var.subnets, count.index)}"
}
If I understand your requirement correctly, you can pass the list of VM names as a terraform variable and use count.index to get the name from a specific position in the list based on the count.
# variables.tf
# Length of list should be the same as the count of instances being created
variable "instance_names" {
default = ["apple", "banana", "carrot"]
}
#main.tf
resource "aws_instance" "tf_server" {
count = var.instance_count
instance_type = var.instance_type
ami = data.aws_ami.server_ami.id
associate_public_ip_address = var.associate_public_ip_address
##This provides names as per requirement from the list.
tags = {
Name = "${element(var.instance_names, count.index)}"
}
key_name = "${aws_key_pair.tf_auth.id}"
vpc_security_group_ids = ["${var.security_group}"]
subnet_id = "${element(var.subnets, count.index)}"
}
Would the following be similar to what you are after?
Define a list of name prefixes as a variable and then cycle through the naming prefixes using the element function.
variable "name_prefixes" {
default = ["App", "Db", "Web"]
}
...
##This provides sequential name.
tags = {
Name = "${element(var.name_prefixes, count.index)}${count.index + 1}"
}
...
The result would be App1, Db2, Web3, App4, Db5... The numbering is not ideal, but at least you would have a distinct name per instance.
The only way I can think of naming them sequentially (e.g. App1, App2, Db1, Db2 etc.) would require an individual resource for each type of instance and then just use count.index on the name like your original code.

terraform use count index in module [duplicate]

This question already has answers here:
Variance in attributes based on count.index in terraform
(3 answers)
Closed 3 years ago.
I want to use the count.index in the terraform module for my aws ec2 instance to name the instance in increment order
file: ec2/main.tf
resource "aws_instance" "instance"{
ami = "ami-xxx"
tags {
Name = "var.instance"
}
count = "var.count"
}
file: ec2instance.tf
module "ec2"{
source = "./ec2"
count = 3
instance_name = "firsttypeinstance-${count.index+1}"
}
module "ec20"{
source = "./ec2"
count = 2
instance_name = "secondtype-${count.index+1}"
}
I want the instance name to be populated as
firsttypeinstance-1
firsttypeinstance-2
firsttypeinstance-3
secondtype-1
secondtype-2
but i get the error that i cannot use count index in the module
From terraform doc:
In addition to the above, the argument names count, for_each and lifecycle are not currently used by Terraform but are reserved for planned future features.
however, you could create the my_count variable in your module and use it on resources inside your module
module ec2
resource "aws_instance" "instance"{
ami = "ami-xxx"
tags {
Name = "var.instance-${count.index}"
}
count = "var.my_count"
}
module main
module "ec2"{
source = "./ec2"
my_count = 3
instance_name = "firsttypeinstance" ## actually instance prefix
}

Terraform - reference instance value when calling module throwing errors

I have the block of code below that creates a bunch of subnets based on a list of names and a list of address_prefixes.
resource "azurerm_subnet" "subnet" {
count = "${length(var.subnet_names)}"
name = "${element(var.subnet_names, count.index)}"
resource_group_name = "${var.vnet_rg_name}"
virtual_network_name = "${data.azurerm_virtual_network.vnet.name}"
address_prefix = "${element(var.subnet_prefixes, count.index)}"
service_endpoints = ["Microsoft.Sql","Microsoft.Storage","Microsoft.AzureCosmosDB"]
network_security_group_id = "${data.azurerm_network_security_group.required_nsg.id}"
route_table_id = "${element(azurerm_route_table.routetable.*.id, count.index)}"
depends_on = ["azurerm_route_table.routetable"]
}
I am then trying to create some routes using a module but when I try to pass in values for variables using properties from a specific instance of the azurerm_subnet.subnet resource, it throws the error:
"module.insidedmzroutes.var.subnet_name: Resource 'azurerm_subnet.subnet' not found for variable 'azurerm_subnet.subnet.5.name'"
module "insidedmzroutes" {
source = "./modules/dmzroutes"
subnet_name = "${azurerm_subnet.subnet.5.name}"
vnet_rg = "${data.azurerm_resource_group.vnet_rg.name}"
route_table_name = "${azurerm_route_table.routetable.5.name}"
next_hop_ip = "${cidrhost(azurerm_subnet.subnet.5.address_prefix, 4)}"
subnet_names = ["${var.subnet_names}"]
subnet_prefixes = ["${var.subnet_prefixes}"]
}
Does this not work or do I have the reference constructed incorrectly?
Please have a look at the Terraform interpolation syntax documentation, look for interpolation syntax.
The following would work (as indicated by Adil B):
subnet_name = "${azurerm_subnet.subnet.*.name[5]}"
As with the splat syntax * you select all of the elements created using a count variable, which will then return a list, which you can select the correct element from [5].
However, why are you also passing along the entire list of subnets? Which subnets are these? It's not very clear from your code if these are the 5 subnets you created earlier or different ones. Are you creating an insidedmzroutes for every subnet? If so, I'd get rid of the subnet_name var and instead implement something like this in the resource inside the module:
count = "${length(var.subnet_names)}"
subnet_name = "${element(var.subnet_names, count.index)}"

Resources