Nested variable - problem with getting appropriate data - terraform - terraform

I have a problem with getting appropriate data from my object variable.
My variable definition looks as follows:
variable "auth0_org" {
description = "List of organizations"
default = []
type = list(object({
name = string
display_name = string
db_name = list(string)
logo_url = string
colors_primary = string
colors_page_background = string
enable_auto_membership = bool
saml_config = list(object({
connection_name = string
strategy = string
sign_in_endpoint = string
sign_out_endpoint = string
sign_saml_request = bool
user_id_attribute = string
debug = bool
signing_cert = string
enable_auto_membership = bool
fields_map = object({
email = string
nickname = string
given_name = string
family_name = string
})
}))
openid_config = list(object({
connection_name = string
strategy = string
client_id = string
client_secret = string
scopes = list(string)
discovery_url = string
type = string
allowed_applications = list(string)
}))
}))
}
Example data:
auth0_org = [
{
name = "abc"
display_name = "abc"
db_name = []
logo_url = "logo.png"
colors_primary = "#303EFA"
colors_page_background = "#0C204A"
enable_auto_membership = true
saml_config = []
openid_config = [
{
connection_name = "test-oidc"
strategy = "oidc"
client_id = "1234"
client_secret = "abc456"
scopes = ["openid", "profile", "email"]
discovery_url = "https://okta.com/.well-known/openid-configuration"
type = "front_channel"
allowed_applications = ["app01", "app02"]
},
{
connection_name = "test-oidc2"
strategy = "oidc"
client_id = "5678"
client_secret = "abc123"
scopes = ["openid", "profile", "email"]
discovery_url = "https://okta.com/.well-known/openid-configuration"
type = "front_channel"
allowed_applications = ["app01"]
}
]
}
]
I want to create OpenID connection in auth0:
resource "auth0_connection" "oidc" {
for_each = {
for item in local.openid_configs : item.connection_name => item
}
enabled_clients = ????
options {
client_id = each.value.client_id
client_secret = each.value.client_secret
scopes = each.value.scopes
discovery_url = each.value.discovery_url
type = each.value.type
}
}
locals {
openid_configs = flatten([
for org in var.auth0_org: [
for openid in org.openid_config: {
connection_name = openid.connection_name
strategy = openid.strategy
client_id = openid.client_id
client_secret = openid.client_secret
scopes = openid.scopes
discovery_url = openid.discovery_url
type = openid.type
allowed_applications = openid.allowed_applications
}
]
])
allowed_apps = flatten([
for app in local.openid_configs: app["allowed_applications"]
]
}
To be able to do that I need to specify applications IDs list in enabled_clients.
So far I have application names in my variable: openid_config.allowed_applications for example:
allowed_applications = ["app01", "app02"]
Do you have any suggestion how can I do that?
I was trying to get this using data source like this:
data "auth0_client" "application" {
for_each = toset(local.allowed_apps)
name = each.value
}
But I don't know how to use this data source in enabled_clients.
Any suggestions are more than welcome.
(Update)
I've made few different attempts. It's working from code perspective but it's logically wrong. I think I should simplify my question.
In the code as a enabled_clients I can use each.value.allowed_applications like:
resource "auth0_connection" "oidc" {
for_each = {
for item in local.openid_configs : item.connection_name => item
}
enabled_clients = each.value.allowed_applications
options {
client_id = each.value.client_id
client_secret = each.value.client_secret
scopes = each.value.scopes
discovery_url = each.value.discovery_url
type = each.value.type
}
}
But this give me list of app names instead of its IDs: ["app01", "app02"].
Let's say also I have this kind of data structure:
allowed_apps_id = {
app01 = "cbsdibcscbb2323"
app02 = "cjsbsbsbxy666sa"
}
How to change the line:
enabled_clients = each.value.allowed_applications
to get IDs instead of names?

Related

Terraform nested dynamic blocks

I'm trying to deploy an Azure application gateway in Terraform, in particular I need to create a nested dynamic blocks.
I have tried to implement this (this part of the code is in a file called application_gateway.tf):
dynamic "url_path_map" {
for_each = var.path_maps
content {
name = outer_block.value["name"]
default_backend_address_pool_name = outer_block.value["backend"]
default_backend_http_settings_name = outer_block.value["backend_set"]
dynamic "url_path_rule" {
for_each = url_path_map.value["upm"]
content{
name = url_path_rule.value["name_rule"]
paths = url_path_rule.value["path"]
backend_address_pool_name = url_path_rule.value["backend"]
backend_http_settings_name = url_path_rule.value["backend_set"]
}
}
}
}
The correspective variables.tf file is:
variable "path_maps" {
default = []
type = list(object({
name = string
backend = string
backend_set = string
upm = list(object({
name_rule = string
path = string
backend = string
backend_set = string
}))
}))
}
With the following module call (this part of the script is in another file called main.tf):
module "application_gateway" {
source = "../modules/resources-hub/application_gateway"
resource_group_name = module.resource_group.name
resource_group_location = module.resource_group.location
subnet_id = module.agw_subnet.id
public_ip_address_id = module.app_gw_pip.id
firewall_policy_id = module.agw_web_application_firewall.id
log_analytics_workspace_id = module.log_analytics_workspace.id
path_maps = [{name = "dev_url_path_name", backend = "devBackend", backend_set = "devHttpSetting", name_rule = "dev_path_rule_name_app", path = "/app/*"},
{name = "tst_url_path_name", backend = "tstBackend", backend_set = "tstHttpSetting", name_rule = "dev_path_rule_name_edp", path = "/edp/*"},
{name = "uat_url_path_name", backend = "uatBackend", backend_set = "uatHttpSetting", name_rule = "dev_path_rule_name_internal", path = "/internal/*"}]
}
At the end, what I would like to obtain is this but using the nested dynamic blocks:
url_path_map {
name = "dev_url_path_name"
default_backend_address_pool_name = "devBackend"
default_backend_http_settings_name = "devHttpSetting"
path_rule {
name = "dev_path_rule_name_app_edp"
paths = ["/app/*"]
backend_address_pool_name = "devBackend"
backend_http_settings_name = "devHttpSetting"
}
path_rule {
name = "dev_path_rule_name_internal"
paths = ["/edp/*"]
backend_address_pool_name = "devBackend"
backend_http_settings_name = "devHttpSetting"
}
path_rule {
name = "dev_path_rule_name_internal"
paths = ["/internal/*"]
backend_address_pool_name = "sinkPool"
backend_http_settings_name = "devHttpSetting"
}
}
This is the error that I get if I run "terraform validate":
enter image description here
Thank you in advance!
I have tried the code above but I got the error in the image.
The first problem is on the definition of the variable "path_maps", because is different as the path_maps format that you are passing to the module.
You can modify the path_maps before passing to the module with the correct format, or you can change variable to fit the format that you define.
thats why you are getting the error that "upm" is required

for each module reference between modules with a map object

im trying to create an instance group with reference to an GCE, but im unable to refer to the id from instance group to link to the GCE
variable "compute_engine_instances" {
type = map(object({
instance_name = string
machine_type = string
zone = string
tags = list(string)
image_name = string
image_project = string
labels =object({
app_id = number
cost_center = string
owner = string
email = string
})
}))
}
module "qat_hosted_pacs_compute_engines" {
source = "../modules/compute_engine"
for_each = var.compute_engine_instances
project_id = var.project_id
instance_name = each.value.instance_name
machine_type = each.value.machine_type
tags = each.value.tags
labels = each.value.labels
subnetwork = var.subnetwork
zone = each.value.zone
image_name = each.value.image_name
image_project = each.value.image_project
}
variable "instance_group" {
type = map(object({
group_manager_name = string
zone = string
}))
}
module "qat_hosted_pacs_app_grp" {
source = "../modules/instance_groups"
for_each = var.instance_group
group_manager_name = each.value.group_manager_name
zone = each.value.zone
project_id = var.project_id
instances = module.qat_hosted_pacs_compute_engines.vm_name #unable to figure out how to reference the GCE
}
#output.tf looks like this for compute engine module
output "compute_engine_instances" {
value = {
for k, v in module.qat_hosted_pacs_compute_engines : k => v.vm_name
}
}
The root module looks like this for compute engine
data "google_compute_image" "compute_image" {
name = var.image_name
project = var.image_project
}
resource "google_compute_instance" "generic_instance" {
project = var.project_id
name = var.instance_name
machine_type = var.machine_type
zone = var.zone
tags = var.tags
labels = var.labels
boot_disk {
initialize_params {
image = data.google_compute_image.compute_image.self_link
}
auto_delete = true
}
network_interface {
subnetwork = var.subnetwork
}
}
#outputs.tf here looks like this for gce resource
output "vm_name" {
value = google_compute_instance.generic_instance.id
description = "The name of the VM"
}
And the instance group manager looks like this
resource "google_compute_instance_group" "igm" {
name = var.group_manager_name
zone = var.zone
project = var.project_id
instances = var.instances
named_port {
name = "http"
port = "8080"
}
named_port {
name = "https"
port = "8443"
}
lifecycle {
create_before_destroy = true
}
}
i get the foll. error
Error: Unsupported attribute
on main.tf line 45, in module "qat_hosted_pacs_app_grp":
45: instances = module.qat_hosted_pacs_compute_engines.vm_name
|----------------
| module.qat_hosted_pacs_compute_engines is object with 2 attributes
This object does not have an attribute named "vm_name".
terraform tf vars file
compute_engine_instances ={
"test-adi"={
instance_name = "test-vm"
machine_type = "n1-standard-1"
zone = "us-east4-b"
tags = ["foo","bar"]
image_name = "gold-centos-7"
image_project = "dev-cietools"
labels = {
app_id = "56"
cost_center = "156"
owner = "ops"
email = "ops"
}}
"test-adi-2"={
instance_name = "test-vm-2"
machine_type = "n1-standard-1"
zone = "us-east4-b"
tags = ["foo","bar"]
image_name = "centos-7"
image_project = "dev-cietools"
labels = {
app_id = "56"
cost_center = "856"
owner = "ops"
email = "ops"
}
}
}
subnetwork = "sandbox-us-east4"
project_id = "cloudops-dev01-sb"
instance_group = {
"igm" = {
group_manager_name = "test"
zone = "us-east4-b"
}
}
Since you used for_each in your google_compute_instance_group module, you have to use key to refer to individual instances of the module, e.g.
instances = module.qat_hosted_pacs_compute_engines["test-adi"].vm_name
or if you want to pass all vm_name created for all values of for_each, you can do:
instances = values(module.qat_hosted_pacs_compute_engines)[*].vm_name

Using dynamic values for Kubernetes namespace labels

I am managing my on-prem Kubernetes cluster namespaces with Terraform and want to include some custom labels/annotations on them. This is to make auditing easier and also we have mutating webhooks that rely on labels/annotations.
I am trying to do something like this (pseudo code)
resource "kubernetes_namespace" "namespaces" {
for_each = {for k, v in var.namespaces: k => v}
metadata {
name = each.value.name
annotations = {
"linkerd.io/inject" = each.value.linkerd
{{loop over each.value.custom_annotations}}
}
labels = {
"apps.kubernetes.io/app" = each.value.app
"k8s.domain.co/managed-by" = each.value.managed
"k8s.domain.co/owner" = each.value.owner
{{loop over each.value.custom.labels}}
}
}
}
I have my var.namespaces variable constructed like
description = "List of namespaces controlled by Terraform"
type = list(object({
name = string
linkerd = string
app = string
owner = string
managed = string
custom_annotations = list(object({
label = string
value = string
}))
custom_labels = list(object({
label = string
value = string
}))
}))
I am trying to end up with
namespaces = [
{
name = foo
...
custom_annotations = {
label = "myannotation"
value = "myvalue"
custom_labels = {
label = "mylabel"
value = "myvalue"
}]
resource "kubernetes_namespace" "namespaces" {
for_each = {for k, v in var.namespaces: k => v}
metadata {
name = each.value.name
annotations = {
"linkerd.io/inject" = each.value.linkerd
myannotation = myvalue
}
labels = {
"apps.kubernetes.io/app" = each.value.app
"k8s.domain.co/managed-by" = each.value.managed
"k8s.domain.co/owner" = each.value.owner
mylabel = myvalue
}
}
}
I have a feeling some mix of locals and dynamic blocks would be the solution but I can't seem to pin them together in a way that works
Any advice please?
I managed to get this almost working for myself without using locals or dynamic blocks. However I can't include the default labels and annotations
resource "kubernetes_namespace" "namespaces" {
for_each = { for k, v in var.namespaces: k => v} //loop over the namespaces
metadata {
name = each.value.name
annotations = {
for annotation in each.value.custom_annotations: annotation.label => annotation.value
}
labels = {
for label in each.value.custom_labels: label.label => label.value
}
}
}
With this input
namespaces = [
{
name = "metallb-system"
linkerd = "enabled"
app = "metallb"
owner = "KPE"
managed = "Terraform"
custom_annotations = []
custom_labels = [{label="foo.io/bar", value="foobar"}, {label="bar.io/foo", value="barfoo"}]
},
{ name = "test-ns"
linkerd = "enabled"
app = "myapp"
owner = "Me"
managed = "Terraform"
custom_annotations = [{label="foo.io/annotation", value="test"}]
custom_labels = [{label="app.io/label", value="value"}]
}
]
It gives me this output
Changes to Outputs:
+ namespaces = {
+ 0 = {
+ id = "metallb-system"
+ metadata = [
+ {
+ annotations = {}
+ generate_name = ""
+ generation = 0
+ labels = {
+ "bar.io/foo" = "barfoo"
+ "foo.io/bar" = "foobar"
}
+ name = "metallb-system"
+ resource_version = "410142"
+ uid = "02d6b1e1-707a-49cf-9a2d-3f28c9ce1e5a"
},
]
+ timeouts = null
}
+ 1 = {
+ id = (known after apply)
+ metadata = [
+ {
+ annotations = {
+ "foo.io/annotation" = "test"
}
+ generate_name = null
+ generation = (known after apply)
+ labels = {
+ "app.io/label" = "value"
}
+ name = "test-ns"
+ resource_version = (known after apply)
+ uid = (known after apply)
},
]
+ timeouts = null
}
}
I found a way to add default labels and annotations using setunion
locals {
default_annotations = [{label = "foo", value = "bar"}]
default_labels = [{label = "terraform", value = true}]
}
resource "kubernetes_namespace" "namespaces" {
for_each = { for k, v in var.namespaces: k => v} //loop over the namespaces
metadata {
name = each.value.name
annotations = {
for annotation in setunion(each.value.custom_annotations, local.default_annotations) : annotation.label => annotation.value
}
labels = {
for label in setunion(each.value.custom_labels, local.default_labels) : label.label => label.value
}
}
}
I know this doesn't exactly solve your use case, as you are wanting to read the value from your list of namesapces, however I do think it is one step closer!

How to reference the list of objects variables in Terrraform configuration block or modules

How to reference List of objects variables in terraform module block
Variables.tf
variable "list_views_datasets" {
description = "List of Views in the Datasets"
type = list(object({
dataset_id = string
dataset_name = string
views = list(object({
view_id = string,
query = string,
use_legacy_sql = bool,
labels = map(string),
}))
}))
default = []
}
tfvars:
list_views_datasets = [
{
dataset_id = "testservice"
dataset_name = "testservice"
views = [
{
view_id = "issue-data",
use_legacy_sql = false,
query = ".test.sql"
# unfortunately we have to repeat the project id, dataset id and table id in here.
labels = {
env = "dev"
billable = "true"
owner = "dev"
}
},
]
},
Main.tf
module "bigquery_views" {
source = "terraform-google-modules/bigquery/google"
version = "4.3.0"
depends_on = [module.bigquery]
for_each = { for list_view in var.list_views_datasets : list_view.dataset_id => list_view }
dataset_id = each.value.dataset_id
dataset_name = each.value.dataset_name
description = var.views_description
project_id = var.project_id
location = var.location
views = {
view_id = each.value.views[view_id]
labels = each.value.views[labels]
query = file(each.value.views[query])
use_legacy_sql = each.value.views[use_legacy_sql]
}
dataset_labels = var.dataset_labels
access = [
{
role = "roles/bigquery.dataOwner"
special_group = "projectOwners"
}
]
}
Now i am planning to reference the variable in my module block but i am not sure whether i have referenced corretly the views, query,
labels value in my mobule block
Sadly you can't do this. You have to pass entire list_views_datasets into your module as one variable, or use for_each at the module level. In the second case you will create multiple modules.

Adding constraints to terraform variable

I have the following in variables.tf
variable "envoy_config" {
description = "Envoy Docker Image Version"
type = object({
routes = list(object({
cluster_name = string
host_rewrite_url = string
prefix = string
headers = object({
name = string
exact_match = string
})
}))
clusters = list(object({
name = string
address = string
}))
})
default = {
routes = []
clusters = []
}
validation {
condition = <not quite sure what to add here>
error_message = "cluster <name> does not exist"
}
}
and then in my variables.tfvars, I have the following:
envoy_config = {
routes = [{
host_rewrite_url = "myurl"
prefix = "myprefix"
cluster_name = "mycluster"
headers = {
name = ":method"
exact_match = "GET"
}
}]
clusters = [{
name = "mycluster"
address = "myurl"
}]
}
I want to make sure that every ${envoy_config.routes[*].cluster_name and ${envoy_config.routes[*]. host_rewrite_url exists in ${envoy_config.clusters[*].name and ${envoy_config.clusters[*].address respectively.
What condition should I be adding the in the validation step?
The examples I found all deal with regex
I am using terraform version v0.12.28

Resources