How to validate acm certificate created via terraform - terraform

I'm trying to create 3 certificates for 3 services named test,valid,jsc and using an exiting hosted zone.But terraform is failing in cert validation stating that certificate is in pending_validation state
variable "service-names" {
type = list(any)
default = ["valid", "jsc", "test"]
}
resource "aws_acm_certificate" "cert" {
for_each = toset(var.service-names)
domain_name = "www.us.${each.value}.xxxxx.cloud"
validation_method = "DNS"
tags = {
Name = "www.us.${each.value}.xxxxx.cloud"
}
lifecycle {
create_before_destroy = true
}
}
data "aws_route53_zone" "ecomzoneinfo" {
name = "xxxxx.cloud"
tags = {
"Name" = "www.us.${each.value}.xxxxx.cloud"
}
}
locals {
dns-record-list = flatten([
for svc in var.service-names : [
for dvo in aws_acm_certificate.cert[svc].domain_validation_options : {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
service = svc
}
]
])
}
resource "aws_route53_record" "ecom-route" {
for_each = { for idx,dns in local.dns-record-list : "${dns.service}" => dns }
allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = data.aws_route53_zone.ecomzoneinfo.zone_id
}
resource "aws_acm_certificate_validation" "ecom" {
for_each = toset(var.service-names)
certificate_arn = aws_acm_certificate.cert[each.value].arn
validation_record_fqdns = [aws_route53_record.ecom-route[each.value].fqdn]
}
Getting Error stating as certificate is in pending validation and yes i'm able to the certs created by terraform aws_acm_certification resource are in pending status.Even i'm able to see the cname records are added under the zone xxxxx.cloud hosted zone.So i'm not sure what is wrong here
Error: Error describing created certificate: Expected certificate to be issued but was in state PENDING_VALIDATION
│
│ with aws_acm_certificate_validation.ecom["jsc"],
│ on main.tf line 449, in resource "aws_acm_certificate_validation" "ecom":
│ 449: resource "aws_acm_certificate_validation" "ecom" {
│
╵
╷
│ Error: Error describing created certificate: Expected certificate to be issued but was in state PENDING_VALIDATION
│
│ with aws_acm_certificate_validation.ecom["test"],
│ on main.tf line 449, in resource "aws_acm_certificate_validation" "ecom":
│ 449: resource "aws_acm_certificate_validation" "ecom" {
│
╵
╷
│ Error: Error describing created certificate: Expected certificate to be issued but was in state PENDING_VALIDATION
│
│ with aws_acm_certificate_validation.ecom["valid"],
│ on main.tf line 449, in resource "aws_acm_certificate_validation" "ecom":
│ 449: resource "aws_acm_certificate_validation" "ecom" {

As per the documentation provided.
resource "aws_acm_certificate" "certificate" {
domain_name = "mydomain.com"
subject_alternative_names = [
"*.mydomain.com"
]
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
resource "aws_route53_record" "acm-records" {
for_each = {
for dvo in aws_acm_certificate.certificate.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
allow_overwrite = true
name = each.value.name
records = [
each.value.record
]
ttl = 60
type = each.value.type
zone_id = aws_route53_zone.mydomain.zone_id
}
resource "aws_acm_certificate_validation" "acm-validation" {
certificate_arn = aws_acm_certificate.certificate.arn
validation_record_fqdns = [for record in aws_route53_record.acm-records : record.fqdn]
}

Related

Problem referencing aws_nat_gateway within route table

The nat_gateway_id argument in the last resource below is busted. I've been staring at the sun for too long, though, and the issue isn't jumping out at me.
resource "aws_eip" "nat_gateway_ip" {
for_each = aws_subnet.public
vpc = true
}
}
# Create NAT gateways in public subnets
resource "aws_nat_gateway" "private" {
for_each = aws_subnet.public
allocation_id = aws_eip.nat_gateway_ip[each.key].id
subnet_id = each.value.id
}
# Create Route tables and default routes
resource "aws_route_table" "private" {
for_each = toset([ "test", "prod"])
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.private[each.key].id
}
}
The resulting error is:
nat_gateway_id = aws_nat_gateway.private[each.key].id
│ ├────────────────
│ │ aws_nat_gateway.private is object with 2 attributes
│ │ each.key is "test"
│
│ The given key does not identify an element in this collection value.

Use count instead of for_each for terraform resource

When deploying resources, the template terraform gave uses for_each. This poses as a problem as it will give
Error: Invalid for_each argument
│
│ on /home/baiyuc/workspaces/billow/src/GoAmzn-LambdaStackTools/configurations/terraform/sync.tf line 410, in resource "aws_route53_record" "subdomain_cert_validation":
│ 410: for_each = {
│ 411: for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
│ 412: name = dvo.resource_record_name
│ 413: record = dvo.resource_record_value
│ 414: type = dvo.resource_record_type
│ 415: }
│ 416: }
│ ├────────────────
│ │ aws_acm_certificate.cert.domain_validation_options is a set of object, known only after apply
The "for_each" map includes keys derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.
When working with unknown values in for_each, it's better to define the map keys statically in your configuration and place apply-time results only in the map values.
Alternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully converge.
error when using terraform import.
I found a potential solution that suggests using count in this type of scenarios, but it didn't go into details. Anyone can give any details on how to do so?
The code of interest is for resource "aws_route53_record" "subdomain_cert_validation":
data "aws_route53_zone" "root_domain" {
name = "${var.root_domain}."
private_zone = false
}
resource "aws_acm_certificate" "cert" {
depends_on = [aws_route53_record.sub-zone]
domain_name = var.domain
validation_method = "DNS"
}
resource "aws_route53_zone" "core-domain" {
name = var.domain
count = var.root_domain == var.domain ? 0 : 1 # If the two are the same, do not create this resource.
tags = {
Environment = var.stack_tag
}
}
resource "aws_route53_record" "sub-zone" {
depends_on = [aws_route53_zone.core-domain]
zone_id = data.aws_route53_zone.root_domain.zone_id
name = var.domain
type = "NS"
ttl = "30"
count = var.root_domain == var.domain ? 0 : 1 # If the two are the same, do not create this resource.
records = var.root_domain == var.domain ? [] : [
aws_route53_zone.core-domain[0].name_servers[0],
aws_route53_zone.core-domain[0].name_servers[1],
aws_route53_zone.core-domain[0].name_servers[2],
aws_route53_zone.core-domain[0].name_servers[3],
]
}
resource "aws_route53_record" "subdomain_cert_validation" {
depends_on = [aws_acm_certificate.cert]
for_each = {
for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
allow_overwrite = true
name = each.value.name
records = [each.value.record]
type = each.value.type
ttl = 600
zone_id = var.root_domain == var.domain ? data.aws_route53_zone.root_domain.zone_id : aws_route53_zone.core-domain[0].zone_id
}
resource "aws_acm_certificate_validation" "core" {
certificate_arn = aws_acm_certificate.cert.arn
validation_record_fqdns = [for record in aws_route53_record.subdomain_cert_validation : record.fqdn]
}
this issue is pretty common when using iteration and is caused by trying to use keys that will be dynamically generated at apply time. You need to make sure that your keys are statically defined so they're known at apply time, the value of the map can then be dynamic. Some good reference, for this issue and solutions are here
for_each example and solution
for_each example and solution

What does the error "GroupsClient.BaseClient.Post() An invalid operation was included in the following modified references: 'members'" mean?

I am trying to add an existing Azure Registered Application to an existing Azure Active Directory Group via Terraform. I used the following sequence to complete the task:
// References the existing AAD group
data "azuread_group" "existing_aad_group" {
display_name = "<display name of the aad group>"
security_enabled = true
}
// References the existing registered application
data "azuread_application" "existing_registered_application" {
display_name = "<display name of the registered application>"
}
// --> Adds the application as a member of the AAD group.
resource "azuread_group_member" "registered_app_member" {
group_object_id = data.azuread_group.existing_aad_group.object_id
member_object_id = data.azuread_application.existing_registered_application.object_id
}
The above code fails with the following error:
╷
│ Error: Adding group member "ceb93cb8XXXXX" to group "2f16446cXXXX"
│
│ with module.service.azuread_group_member.function_app,
│ on ../../resources/aad_group.tf line 6, in resource "azuread_group_member" "function_app":
│ 6: resource "azuread_group_member" "function_app" {
│
│ GroupsClient.BaseClient.Post(): unexpected status 400 with OData error:
│ Request_BadRequest: An invalid operation was included in the following
│ modified references: 'members'.
╵
Question
What does this error mean and how can I fix it?
I tried to reproduce the same in my environment :
Code used:
resource "azuread_group" "example" {
display_name = "kavyaMyGroup"
owners = [data.azuread_client_config.current.object_id]
security_enabled = true
members = [
azuread_user.example.object_id,
# more users
]
}
resource "azuread_group_member" "registered_app_member" {
group_object_id = azuread_group.example.object_id
member_object_id = azuread_application.example.object_id
}
resource "azuread_application" "example" {
display_name = "example"
owners = [data.azuread_client_config.current.object_id]
sign_in_audience = "AzureADMultipleOrgs"
required_resource_access {
resource_app_id = "00000003-0000-0000-c000-000000000000" # Microsoft Graph
resource_access {
id = "df021288-bdef-4463-88db-98f22de89214" # User.Read.All
type = "Role"
}
resource_access {
id = "b4e74841-8e56-480b-be8b-910348b18b4c" # User.ReadWrite
type = "Scope"
}
}
web {
homepage_url = "https://app.example.net"
logout_url = "https://app.example.net/logout"
redirect_uris = ["https://app.example.net/account"]
implicit_grant {
access_token_issuance_enabled = true
id_token_issuance_enabled = true
}
}
}
Received same Error:
azuread_group_member.registered_app_member: Creating... │ Error:
Adding group member "xxx" to group "xxxx"
│ with azuread_group_member.registered_app_member, │ on main.tf
line 84, in resource "azuread_group_member" "registered_app_member": │
84: resource "azuread_group_member" "registered_app_member" {
│ GroupsClient.BaseClient.Post(): unexpected status 400 with OData
error: Request_BadRequest: An invalid operation │ was included in
the following modified references: 'members'.
As it could not add the application , directly , i tried creating service principal of the existing application and then assigned to the group using its object ID:
Code:
resource "azuread_application" "example" {
display_name = "example"
owners = [data.azuread_client_config.current.object_id]
sign_in_audience = "AzureADMultipleOrgs"
required_resource_access {
resource_app_id = "00000003-0000-0000-c000-000000000000" # Microsoft Graph
resource_access {
id = "df021288-bdef-4463-88db-98f22de89214" # User.Read.All
type = "Role"
}
resource_access {
id = "b4e74841-8e56-480b-be8b-910348b18b4c" # User.ReadWrite
type = "Scope"
}
}
web {
homepage_url = "https://app.example.net"
logout_url = "https://app.example.net/logout"
redirect_uris = ["https://app.example.net/account"]
implicit_grant {
access_token_issuance_enabled = true
id_token_issuance_enabled = true
}
}
}
resource "azuread_service_principal" "example" {
application_id = azuread_application.example.application_id
app_role_assignment_required = false
owners = [data.azuread_client_config.current.object_id]
}
#below code adds Enterprise app to required group
resource "azuread_service_principal" "example" {
application_id = azuread_application.example.application_id
app_role_assignment_required = false
owners = [data.azuread_client_config.current.object_id]
}
Terraform code is successfully run with terraform apply
Could see the app added to the group in the form of enterprise app as we are using service principal of app:
App:
Reference:
azuread_service_principal | Resources | hashicorp/azuread | Terraform Registry

Using the resource ID from a for_each resource block

I've created a Terraform template that creates 2 route tables and 2 subnets using the for_each command. I am trying to associate the route tables to the two subnets, however I am struggling to do so because I don't know how to obtain the ID for the route tables and subnets as the details are not in a variable, and I'm not sure how to get that information and use it. Please may someone provide assistance?
Thank you
Main Template
# SUBNETS DEPLOYMENT
resource "azurerm_subnet" "subnets" {
depends_on = [azurerm_virtual_network.vnet]
for_each = var.subnets
resource_group_name = var.rg.name
virtual_network_name = var.vnet.config.name
name = each.value.subnet_name
address_prefixes = each.value.address_prefixes
}
# ROUTE TABLE DEPLOYMENT
resource "azurerm_route_table" "rt" {
depends_on = [azurerm_virtual_network.vnet]
for_each = var.rt
name = each.value.route_table_name
resource_group_name = var.rg.name
location = var.rg.location
disable_bgp_route_propagation = true
route = [ {
address_prefix = each.value.address_prefix
name = each.value.route_name
next_hop_in_ip_address = each.value.next_hop_ip
next_hop_type = each.value.next_hop_type
} ]
}
# ROUTE TABLE ASSOICATION
resource "azurerm_subnet_route_table_association" "rt_assoication" {
subnet_id = azurerm_subnet.subnets.id
route_table_id = azurerm_route_table.rt.id
}
Variables
# SUBNET VARIBALES
variable "subnets" {
description = "subnet names and address prefixes"
type = map(any)
default = {
subnet1 = {
subnet_name = "snet-001"
address_prefixes = ["172.17.208.0/28"]
}
subnet2 = {
subnet_name = "snet-002"
address_prefixes = ["172.17.208.32/27"]
}
}
}
# ROUTE TABLES VARIABLES
variable "rt" {
description = "variable for route tables."
type = map(any)
default = {
rt1 = {
route_table_name = "rt1"
address_prefix = "0.0.0.0/0"
route_name = "udr-azure-firewall"
next_hop_ip = "10.0.0.0"
next_hop_type = "VirtualAppliance"
}
rt2 = {
route_table_name = "rt2"
address_prefix = "0.0.0.0/0"
route_name = "udr-azure-firewall"
next_hop_ip = "10.0.0.0"
next_hop_type = "VirtualAppliance"
}
}
}
The error I get when I run terraform plan is:
│ Error: Missing resource instance key
│
│ on modules\vnet\main.tf line 74, in resource "azurerm_subnet_route_table_association" "rt_assoication":
│ 74: subnet_id = azurerm_subnet.subnets.id
│
│ Because azurerm_subnet.subnets has "for_each" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ azurerm_subnet.subnets[each.key]
╵
╷
│ Error: Missing resource instance key
│
│ on modules\vnet\main.tf line 75, in resource "azurerm_subnet_route_table_association" "rt_assoication":
│ 75: route_table_id = azurerm_route_table.rt.id
│
│ Because azurerm_route_table.rt has "for_each" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ azurerm_route_table.rt[each.key]
Looks like you are almost there, update the following in the subnet-route table association block, it should work:
# ROUTE TABLE ASSOICATION
resource "azurerm_subnet_route_table_association" "rt_assoication" {
subnet_id = azurerm_subnet.subnets[each.key].id
route_table_id = azurerm_route_table.rt[each.key].id
}

How to add security group rule to multiple security groups

I'm creating one security group rule and want to attach it to multiple security groups. How can I do it? For example:
resource "aws_security_group" "test-sg-1" {
name = "Test SG 1"
description = "Test Security Group one"
vpc_id = aws_vpc.test_vpc.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group" "test-sg-2" {
name = "Test SG 2"
description = "Test Security Group two"
vpc_id = aws_vpc.test_vpc.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group_rule" "egress_all" {
from_port = 0
protocol = "-1"
security_group_id = [aws_security_group.test-sg-1.id, aws_security_group.test-sg-2.id]
to_port = 0
type = "egress"
cidr_blocks = ["0.0.0.0/0"]
}
I'm getting error if I try this above way of using a list.
│ Error: Incorrect attribute value type
│
│ on main.tf line 76, in resource "aws_security_group_rule" "egress_all":
│ 76: security_group_id = [aws_security_group.test-sg-1.id, aws_security_group.test-sg-2.id]
│ ├────────────────
│ │ aws_security_group.test-sg-1.id will be known only after apply
│ │ aws_security_group.test-sg-2.id will be known only after apply
│
│ Inappropriate value for attribute "security_group_id": string required.
In this case using the for_each meta-argument [1] might be a good idea to avoid code repetition. So this is what I would do:
locals {
sg_names = ["Test SG 1", "Test SG 2"]
}
resource "aws_security_group" "test_sg" {
for_each = toset(local.sg_names)
name = each.value
description = each.value
vpc_id = aws_vpc.test_vpc.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group_rule" "egress_all" {
for_each = aws_security_group.test_sg
from_port = 0
protocol = "-1"
security_group_id = each.value.id
to_port = 0
type = "egress"
cidr_blocks = ["0.0.0.0/0"]
}
Here the resource chaining is used. You can read more in [2].
[1] https://www.terraform.io/language/meta-arguments/for_each#basic-syntax
[2] https://www.terraform.io/language/meta-arguments/for_each#chaining-for_each-between-resources

Resources