I'm trying to use a dynamic block in a kubernetes_ingress resource. The dynamic block is for the spec.rule.http.path block. Unfortunately, I am trying to dynamically create a path block which causes issues as path appears to be a reserved word.
Is it possible to rename the loop variable within the dynamic block, or to otherwise circumvent this issue?
This is my current code:
resource "kubernetes_ingress" "ingress" {
metadata { ... }
spec {
tls { ... }
rule {
http {
dynamic "path" {
for_each = var.services
content {
path = path.value.path
backend {
service_name = path.value.name
service_port = path.value.port
}
}
}
}
}
}
}
The services variable has the following structure:
[
{
name: "foo",
port: 3000,
path: "/foo",
}
]
Dynamic blocks take an argument called iterator that lets you rename the symbol it assigns values to.
dynamic "path" {
for_each = var.services
iterator = "service"
content {
path = service.value.path
backend {
service_name = service.value.name
service_port = service.value.port
}
}
}
Related
We're currently migrating our terraform kubernetes_ingress resource to a kubernetes_ingress_v1 resource. Previously, we had these annotations on the ingress:
annotations = {
"kubernetes.io/ingress.class" = "alb"
"alb.ingress.kubernetes.io/scheme" = "internet-facing"
"alb.ingress.kubernetes.io/certificate-arn" = var.create_acm_certificate ? aws_acm_certificate.eks_domain_cert[0].id : var.aws_acm_certificate_arn
"alb.ingress.kubernetes.io/listen-ports" = "[{\"HTTP\": 80}, {\"HTTPS\":443}]"
"alb.ingress.kubernetes.io/actions.ssl-redirect" = "{\"Type\": \"redirect\", \"RedirectConfig\": { \"Protocol\": \"HTTPS\", \"Port\": \"443\", \"StatusCode\": \"HTTP_301\"}}"
"alb.ingress.kubernetes.io/ssl-policy" = "ELBSecurityPolicy-TLS-1-2-Ext-2018-06"
"alb.ingress.kubernetes.io/healthcheck-path" = "/healthz"
}
along with this segment several times in the spec:
path {
backend {
service_name = "ssl-redirect"
service_port = "use-annotation"
}
path = "/*"
}
However, the kubernetes_ingress_v1 requires a format like:
path {
backend {
service {
name = "ssl-redirect"
port {
number = <number_value>
}
}
}
path = "/*"
}
where port is an actual number and not "use-annotation". Is there any way to replicate this "use-annotation" behavior in a kubernetes_ingress_v1 resource? Or, even better, is there a simpler way to handle this ssl-redirect rule in a kubernetes_ingress_v1?
You can achive that using the following sintaxis:
backend {
service {
name = "ssl-redirect"
port {
name = "use-annotation"
}
}
}
As you can see, you need to use the argument name instead port.
I am trying to find a way to conditionally populate the pages block of the github_repository resource, when the resource itself is created using for_each. The dynamic block seems to be the appropriate way to achieve this, but I am struggling with the syntax.
I've tried with the code below and it fails.
variables.tf:
variable "repositories" {
description = "The repositories to create using terraform"
type = list(object({
name = string,
description = string,
vulnerability_alerts = bool,
pages_cname = string
}))
}
terraform.tfvars.json:
{
"repositories": [
{
"name": "repo-with-pages",
"description": "Repository with pages enabled.",
"vulnerability_alerts": true,
"pages_cname": "www.example.com"
},
{
"name": "repo-without-pages",
"description": "Repository without pages enabled.",
"vulnerability_alerts": true
}
]
}
main.tf:
resource "github_repository" "this" {
for_each = { for repo in var.repositories : repo.name => repo }
name = each.value.name
description = each.value.description
vulnerability_alerts = each.value.vulnerability_alerts
dynamic "pages" {
for_each = { for repo in var.repositories : repo.name => repo }
content {
source {
branch = "main"
path = "/docs"
}
cname = each.value.pages_cname
}
}
}
Result:
Error: Too many pages blocks
on main.tf line 43, in resource "github_repository" "this":
43: content {
No more than 1 "pages" blocks are allowed
This makes perfect sense because the for expression within the dynamic block for_each returns two values (repo-with-pages and repo-without-pages) and only 1 page block is allowed. So, what needs to happen is the for_each / for expression combination needs to return 1 value - the name of the repository created - IF pages are enabled.
Been looking at this for a while and beginning to wonder if what I want to do is even possible or if I am over complicating things. Any help most welcome. Thanks.
To make pages_cname optional it should be:
variable "repositories" {
description = "The repositories to create using terraform"
type = list(any)
then
dynamic "pages" {
for_each = contains(keys(each.value), "pages_cname") ? [1] : []
content {
source {
branch = "main"
path = "/docs"
}
cname = each.value.pages_cname
}
}
}
I need to have nested loop logic. F.ex.
I have one local:
locals {
build_args = {
api: {
RAILS_ENV: "production"
}
client: {
NODE_ENV: "production"
}
}
}
Now I would like to connect to CircleCI with terraform and set these environments in adequate circleCI projects (api and client). The knowledge about circleci projects (name of a project) I keep here:
apps: {
api: {
desired_count: 1,
load_balancer: {
container_name: "api",
container_port: 5000,
health_check_path: "/",
listener: {
path: "api",
},
},
circleci_project: "some-api",
},
client: {
desired_count: 1,
load_balancer: {
container_name: "client",
container_port: 3000,
health_check_path: "/",
listener: {
path: "web",
},
},
circleci_project: "some-client",
}
}
Now, I need to create resource:
resource "circleci_environment_variable" "this" {
project = projects_from_apps_var
name = names_from_local_build_args
value = value_from_local_build_args
}
So as you can see I need two loops one in another to generate many name/values env pairs for many projects.
Just create a map keyed by project and variable name and apply a bunch of resources for each combination:
locals {
map = merge([
for project, env in local.build_args : {
for name, value in env : "${project}-${name}" => {
name = name,
value = value,
project = project
}
}
]...)
}
resource "circleci_environment_variable" "this" {
for_each = local.map
project = each.value.project
name = each.value.name
value = each.value.value
}
I am implementing a Cloudflare Terraform script and I am having issues deduplicating a block for zone_settings:
resource "cloudflare_zone_settings_override" "my-domain-settings-1" {
zone_id = cloudflare_zone.my_domain1.id
settings {
# very_long_list_of_settings
}
}
resource "cloudflare_zone_settings_override" "my-domain-settings-2" {
zone_id = cloudflare_zone.my_domain2.id
settings {
# very_long_list_of_settings
}
}
The settings block has ~60 lines as is completely equal. I tried to implement this via foreach:
variable domains {
default = [
# ERROR: Variables may not be used here.
cloudflare_zone.my_domain1,
cloudflare_zone.my_domain2,
]
}
resource "cloudflare_zone_settings_override" "partners-zone-settings" {
name = var.domains[count.index].name
zone_id = var.domains[count.index].id
settings {
# very_long_list_of_settings
}
}
(full code: https://gist.github.com/knyttl/ee10aa7d63adda866fe39151a9e015d9)
But this is failing with Variables may not be used here. within the variables block.
So I tried with locals:
locals {
zone_settings {
# very_long_list_of_settings
# ERROR: cannot have nested blocks
}
}
resource "cloudflare_zone_settings_override" "my-domain-settings-1" {
zone_id = cloudflare_zone.divadlouhasicu_cz.id
# ERROR: An argument named "settings" is not expected here. Did you mean to define a block of type "settings"?
settings = var.zone_settings
}
resource "cloudflare_zone_settings_override" "my-domain-settings-2" {
zone_id = cloudflare_zone.divadlouhasicu_net.id
# ERROR: An argument named "settings" is not expected here. Did you mean to define a block of type "settings"?
settings = var.zone_settings
}
(full code: https://gist.github.com/knyttl/76ff39f3baf68df19dfbd05a37eb0ea5)
But this ends with errors in the two comments above.
Is there even a way to do this?
čau Vojto,
in case you're Terraform 12 or higher you can use for_each construct. In lower version there is no way.
https://www.hashicorp.com/blog/hashicorp-terraform-0-12-preview-for-and-for-each
Code would look something like this (I didn't test this so some modification will be needed almost for sure):
variable domains {
default = [
# ERROR: Variables may not be used here.
cloudflare_zone.my_domain1,
cloudflare_zone.my_domain2,
]
}
resource "cloudflare_zone_settings_override" "my-domain-settings-1" {
for_each = var.domains
zone_id = each.value
settings {
# very_long_list_of_settings
}
}
An azurerm_service_fabric_cluster resource requires one or more node_type blocks:
resource "azurerm_service_fabric_cluster" "processing" {
...
node_type {
name = "foo"
client_endpoint_port = 19000
...
}
node_type {
name = "bar"
client_endpoint_port = 19001
...
}
}
I want to access an attribute of a specific node_type block, either by name or by index. Surprisingly this doesn't give an error when calling terraform plan:
resource "azurerm_lb_probe" "service_fabric_client_endpoint" {
port = "${azurerm_service_fabric_cluster.processing.node_type.client_endpoint_port}"
...
}