Iterating over nested yaml values in terraform - terraform

I have the following config
config:
groups:
group1:
capabilities:
- create
- read
- update
members:
- robert#gmail.com
- paul#gmail.com
group2:
capabilities:
- create
- list
members:
- peter#gmail.com
group3:
capabilities:
- read
- list
members:
- john#gmail.com
So I want to create a set of vault identities in terraform
resource "vault_identity_entity" "this" {
for_each = ?1
name = each.key
}
How should I iterate (or should I say parse the yaml config) so that the vault_identity_entity is created for everything under config.groups.*.members , i.e. for all the email entries?

for_each accepts a set of strings, so you can use:
resource "vault_identity_entity" "this" {
for_each = toset(flatten(values(local.config.config.groups)[*].members))
name = each.key
}

Related

how to access keys and values from list of map dictionary using Terraform for loop

I tried this so much but could not figure out how to do this. I have a following YAML file below.
---
project_team_mapping:
- Terraform-Project-1: ## this is project name
- team_name: example-team-1
roles:
- GROUP_READ_ONLY
- GROUP_DATA_ACCESS_READ_WRITE
- team_name: example-team-2
roles:
- GROUP_READ_ONLY
- GROUP_DATA_ACCESS_READ_WRITE
- Terraform-Project-2: ## this is 2nd project name
- team_name: example-team-3
roles:
- GROUP_READ_ONLY
- GROUP_DATA_ACCESS_READ_WRITE
- team_name: example-team-4
roles:
- GROUP_READ_ONLY
- GROUP_DATA_ACCESS_READ_WRITE
I'm using some TF module to decode the YAML files and here is the module and the YAML decoded output JSON.
module "yaml_json_multidecoder" {
source = "levmel/yaml_json/multidecoder"
version = "0.2.1"
filepaths = ["./examples/*.yaml"]
}
output "project_team_mapping" {
value = module.yaml_json_multidecoder.files.project.project_team_mapping
}
below is the output when using above decoder module.
project_team_mapping = [
{
"Terraform-Project-1" = [
{
"roles" = [
"GROUP_READ_ONLY",
"GROUP_DATA_ACCESS_READ_WRITE",
]
"team_name" = "dx-example-team-3"
},
{
"roles" = [
"GROUP_READ_ONLY",
"GROUP_DATA_ACCESS_READ_WRITE",
]
"team_name" = "dx-example-team-4"
},
]
}
]
i would like to create resources using Terraform for/for_each function and dynamic functions for a particular resource block below, i'm strugging not able to understand the outcome.
Below is the resource block going to use
resource "mongodbatlas_project" "test" {
name = "project-name"
org_id = "<ORG_ID>"
project_owner_id = "<OWNER_ACCOUNT_ID>"
# want to use dynamic function here to create multiple teams
teams {
team_id = "5e0fa8c99ccf641c722fe645"
role_names = ["GROUP_OWNER"]
}
}
can someone help please?
resource "mongodbatlas_project" "test" {
name = "project-name"
org_id = "<ORG_ID>"
project_owner_id = "<OWNER_ACCOUNT_ID>"
# want to use dynamic function here to create multiple teams
teams {
team_id = "5e0fa8c99ccf641c722fe645"
role_names = ["GROUP_OWNER"]
}
}
I might use wrong YAML structuring, please suggest good structure to fit my needs. i can change the YAML.
above question i'm using team_name in YAML but the resource block takes team_id. i'm planning to use data function to get ID but if you happen to have a easy solution please suggest
Many unknowns you have left. But a best guess would be the example below.
Assuming you have decoded yaml into a local.decodedyaml. Also you possibly would need team_id instead of team_name, maybe you can load them via data source, or will have to retrieve and add to yaml manually.
resource "mongodbatlas_project" "test" {
for_each = local.decodedyaml.project_team_mapping
name = each.key
org_id = "<ORG_ID>"
project_owner_id = "<OWNER_ACCOUNT_ID>"
dynamic "teams" {
for_each = {
for team in each.value: team.team_name => team.roles
}
content {
team_id = each.key
role_names = each.value
}
}
}
This will work if you change the data design to "unwrap" the initial lists:
Instead of
project_team_mapping:
- Terraform-Project Name-1: ## this is project name
- team_name: example-team-1
roles:
- GROUP_READ_ONLY
- GROUP_DATA_ACCESS_READ_WRITE
...
- Terraform-Project Name-2: ## this is 2nd project name
- team_name: example-team-3
roles:
- GROUP_READ_ONLY
- GROUP_DATA_ACCESS_READ_WRITE
...
Use
project_team_mapping:
Terraform-Project Name-1: ## this is project name
- team_name: example-team-1
roles:
- GROUP_READ_ONLY
- GROUP_DATA_ACCESS_READ_WRITE
...
Terraform-Project Name-2: ## this is 2nd project name
- team_name: example-team-3
roles:
- GROUP_READ_ONLY
- GROUP_DATA_ACCESS_READ_WRITE
...
This eases the data structure and greatly eases the hcl parsing.
Lastly, I am not sure yaml keys with spaces are a risk-free endevour.

How to add multiple secrets to azure key vault using terraform

I have seen examples to add one secret (or) key to azure key vault. but I have a requirement now to add multiple secrets to azure key vault using terraform.
How can I achieve that? Can anyone suggest?
Thank You.
I tried to add resource for each secret. added multiple resources like below. but that did not work.
module "keyvault_secret" {
source = "../../modules/keyvault_secret"
count = length(var.secrets)
keyVaultSecretName = keys(var.secrets)[count.index]
keyVaultSecretValue = values(var.secrets)[count.index]
keyVaultId = data.azurerm_key_vault.key_vault.id
}
variables:
variable "secrets" {
type = map(string)
}
variables.tfvars:
secrets = $(secrets)
in YAML pipeline:
displayName: DEV
variables:
- group: 'Environment - Dev'
- name: secrets
value: '{"testAPIKey1" = $(testAPIKey1) , "testAPIKey2" = $(testAPIKey2) }'
i have defined those key values in above variable group - Environment - Dev
This is what the error throws
Expected a closing parenthesis to terminate the expression.
##[error]Terraform command 'plan' failed with exit code '1'.: Unbalanced parentheses
##[error]
Error: Unbalanced parentheses
You need to run it in a loop.
See this link for more info about Terraform loops (for each or count):
https://www.cloudbolt.io/terraform-best-practices/terraform-for-loops/
Untested but something like this:
#Reference AKV in data block
data "azurerm_key_vault" "kvexample" {
name = "mykeyvault"
resource_group_name = "some-resource-group"
}
variable "secret_maps" {
type = map(string)
default = {
"name1"= "value1"
"name2" = "value2"
"name3" = "value3"
}
}
# Count loop
resource "azurerm_key_vault_secret" "kvsecrettest" {
count = length(var.secret_maps)
name = keys(var.secret_maps)[count.index]
value = values(var.secret_maps)[count.index]
key_vault_id = azurerm_key_vault.kvexample.id
}
#----------------- Or use For Each instead of Count
# For Each loop
resource "azurerm_key_vault_secret" "kvsecrettest" {
for_each = var.secret_maps
name = each.key
value = each.value
key_vault_id = azurerm_key_vault.kvexample.id
}

Checkov - checking array values within an attribute

I am looking for the operator logic to check values in an array (terraform) - has anyone tackled a similar problem and has a solution?
the resource is like this
resource "google_project_iam_binding" "my_project_iam_bigquery_dataviewer" {
provider = google.my-project
project = "my-project"
role = "roles/bigquery.admin"
members = [
"group:my-first-group#my-domain.com",
"group:my-second-group#my-domain.com"
]
}
I have tried adding a * (like with lists) to the attribute but - without success
- cond_type: "attribute"
resource_types:
- "google_project_iam_member"
- "google_project_iam_binding"
attribute: "members.*"
operator: "starting_with"
value: "group"
otherwise, my thoughts of an operator that knows to iterate over the array
- cond_type: "attribute"
resource_types:
- "google_project_iam_member"
- "google_project_iam_binding"
attribute: "members"
operator: "iterate_array.starting_with"
value: "group"
edit: this is how the python custom policy checks each value of the members array: https://github.com/bridgecrewio/checkov/blob/HEAD/checkov/terraform/checks/resource/gcp/ArtifactRegistryPrivateRepo.py#L34-L42
for context. If I was to check the value of an attribute that isn't an array i.e. member in a resource:
resource "google_project_iam_binding" "my_project_iam_bigquery_dataviewer" {
provider = google.my-project
project = "my-project"
role = "roles/bigquery.admin"
member = "group:my-group#mydomain.com"
}
I can (and do) use this yaml
- cond_type: "attribute"
resource_types:
- "google_project_iam_member"
- "google_project_iam_binding"
attribute: "member"
operator: "starting_with"
value: "group"
I cannot find a way to do the same check for members

Iterating over multi-level yaml values in terraform

I'm trying to shorten my terraform code for deploying azure vnets by iterating over values I provide in a yaml file. I want to write one .tf file with the code for the vnets, the subnets, the NSGs, etc. but I'm struggling to get the locals block right to correctly iterate through my yaml file (see below)
vnets:
- name: adds
location: eastus
address_space: ["10.1.0.0/24"]
subnets:
- name: adds
address_prefix: "10.1.0.0/27"
- name: dns
location: eastus
address_space: ["10.1.53.0/24"]
subnets:
- name: dns-inbound
address_prefix: "10.1.53.0/28"
- name: dns-outbound
address_prefix: "10.1.53.16/28"
Any help on how I should right my locals block would be appreciated!
This code will transform your yaml file into local map:
locals {
vnets = yamldecode(file("./test.yaml"))
vnets_map = {
for vnet in local.vnets.vnets :
vnet.name => {
address_space = vnet.address_space
location = vnet.location
subnets = {
for subnet in vnet.subnets :
subnet.name => subnet.address_prefix
}
}
}
}
output "example-output-dns-inbound-subnet" {
value = local.vnets_map.dns.subnets.dns-inbound
}
I took the liberty of changing lists to maps - it is better to navigate in terraform. Entire vnets_map object looks like this:
{
"adds" = {
"address_space" = [
"10.1.0.0/24",
]
"location" = "eastus"
"subnets" = {
"adds" = "10.1.0.0/27"
}
}
"dns" = {
"address_space" = [
"10.1.53.0/24",
]
"location" = "eastus"
"subnets" = {
"dns-inbound" = "10.1.53.0/28"
"dns-outbound" = "10.1.53.16/28"
}
}
}

How to use tags from yaml file - terraform

I am trying to extract certain tags from a YAML file with Terraform, but i just don't know how.
Yaml file looks like this:
---
name: subscriptionName
emailContact: someemail#domain.com
tags:
- key: "tagKey1"
value: "tagValue1"
- key: "tagKey2"
value: "tagValue2"
- key: "tagKey3"
value: "tagValue3"
- key: "tagKey4"
value: "tagValue4"
What i am interested in is getting 2 (let's say key1 and key3) key-value pairs as tags and tag resouces. I know that 'locals' plays a role here, but i am kinda new to terraform and cannot get any resource tagged.
The resources are Azure (if it matters).
The resource i am trying to tag is:
resource "azurerm_network_security_group" "nsg" {
name = "randomname"
location = "westeurope"
resource_group_name = "random_rg"
tags { }
}
If you really want two random tags, you can use random_shuffle:
locals {
loaded_yaml = yamldecode(file("./your_file.yaml"))
}
resource "random_shuffle" "indices" {
input = range(0, length(local.loaded_yaml.tags))
result_count = 2
seed = timestamp()
}
output "random_tags" {
value = [for idx in random_shuffle.indices.result:
local.loaded_yaml.tags[idx]]
}
update
For example:
tags = {
(local.loaded_yaml.tags[0].key) = local.loaded_yaml.tags[0].value
(local.loaded_yaml.tags[3].key) = local.loaded_yaml.tags[3].value
}

Resources