how to enable dynamic block conditionally for creating GCS buckets - google-cloud-platform

Im trying to add retention policy but I want to enable it conditionally, as you can see from the code
buckets.tf
locals {
team_buckets = {
arc = { app_id = "20390", num_buckets = 2, retention_period = null }
ana = { app_id = "25402", num_buckets = 2, retention_period = 631139040 }
cha = { app_id = "20391", num_buckets = 2, retention_period = 631139040 } #20 year
}
}
module "team_bucket" {
source = "../../../../modules/gcs_bucket"
for_each = {
for bucket in flatten([
for product_name, bucket_info in local.team_buckets : [
for i in range(bucket_info.num_buckets) : {
name = format("%s-%02d", product_name, i + 1)
team = "ei_${product_name}"
app_id = bucket_info.app_id
retention_period = bucket_info.retention_period
}
]
]) : bucket.name => bucket
}
project_id = var.project
name = "teambucket-${each.value.name}"
app_id = each.value.app_id
team = each.value.team
retention_period = each.value.retention_period
}
root module is defined as follows
main.tf
resource "google_storage_bucket" "bucket" {
project = var.project_id
name = "${var.project_id}-${var.name}"
location = var.location
labels = {
app_id = var.app_id
ei_team = var.team
cost_center = var.cost_center
}
uniform_bucket_level_access = var.uniform_bucket_level_access
dynamic "retention_policy" {
for_each = var.retention_policy == null ? [] : [var.retention_period]
content {
retention_period = var.retention_period
}
}
}
but I can't seem to make the code pick up the value,
for example as you see below the value doesn't get implemented
~ resource "google_storage_bucket" "bucket" {
id = "teambucket-cha-02"
name = "teambucket-cha-02"
# (11 unchanged attributes hidden)
- retention_policy {
- is_locked = false -> null
- retention_period = 3155760000 -> null
}
}
variables.tf for retention policy is as follows
variable "retention_policy" {
description = "Configuation of the bucket's data retention policy for how long objects in the bucket should be retained"
type = any
default = null
}
variable "retention_period" {
default = null
}

Your var.retention_policy is always null, as its default value. You are not changing the default value at all. Probably you wanted the following:
for_each = var.retention_period == null ? [] : [var.retention_period]
instead of
for_each = var.retention_policy == null ? [] : [var.retention_period]

Related

Terraform trying to replace existing resource when new user is added

i have this yaml file
team1:
test#gmail,com: ${live_user_role}
some#gmail.com: ${live_user_role}
tem#gmail.com: ${live_admin_role}
terraform code
locals {
render_membership = templatefile("${path.module}/teammembers.yaml",
{
live_user_role = var.liveteam_user_role_id
live_admin_role = var.liveteam_admin_role_id
}
)
membership_nested = yamldecode(local.render_membership)
team_names = keys(local.membership_nested)
membership_flat = flatten(
[
for team_name in local.team_names : [
for user_email, roles in local.membership_nested[team_name] : {
team_name = team_name
user_name = user_email
roles = [roles]
}
]
]
)
}
resource "squadcast_team_member" "membership" {
for_each = { for i, v in local.membership_flat : i => v }
team_id = data.squadcast_team.teams[each.key].id
user_id = data.squadcast_user.users[each.key].id
role_ids = each.value.roles
lifecycle {
create_before_destroy = true
}
}
data "squadcast_team" "teams" {
for_each = { for i, v in local.membership_flat : i => v }
name = each.value.team_name
}
data "squadcast_user" "users" {
for_each = { for i, v in local.membership_flat : i => v }
email = each.value.user_name
}
Now when i add the new member in the list
let's say like this:
team1:
test#gmail,com: ${live_user_role}
some#gmail.com: ${live_user_role}
tem#gmail.com: ${live_admin_role}
roy#gmail.com: ${live_admin_role}
terraform is deleting the previous user and recreating all the users again
squadcast_team_member.membership["1"] must be replaced
+/- resource "squadcast_team_member" "membership" {
~ id = "62ed115ab4b4017fa2a4b786" -> (known after apply)
~ role_ids = [
- "61b08676e4466d68c4866db0",
+ "61b08676e4466d68c4866db1",
]
~ user_id = "62ed115ab4b4017fa2a4b786" -> "61b7d915a14c2569ea9edde6" # forces replacement
# (1 unchanged attribute hidden)
}
how to modify the code that will create a new member only and not change the other members during its creation
This happens because membership_flat results in a list of maps. In a list, order is important. Thus its better to flatten your data, to get a map instead:
membership_flat = merge([
for team_name in local.team_names : {
for user_email, roles in local.membership_nested[team_name] :
"${team_name}-${user_email}" => {
team_name = team_name
user_name = user_email
roles = [roles]
}
}
]...) # dots are important. Do not Remove them.
then
data "squadcast_team" "teams" {
for_each = local.membership_flat
name = each.value.team_name
}
data "squadcast_user" "users" {
for_each = local.membership_flat
email = each.value.user_name
}
resource "squadcast_team_member" "membership" {
for_each = local.membership_flat
team_id = data.squadcast_team.teams[each.key].id
user_id = data.squadcast_user.users[each.key].id
role_ids = each.value.roles
lifecycle {
create_before_destroy = true
}
}

How to create iam-user module in terraform to cover 3 type of iam-user scenarios

Can you please help here on how to create iam-user module in terraform to cover 3 type of iam-user scenarios ?
PS: I don't want to create nested directory under modules/iam/iam-user/ to make each iam-user cases separately.
Following are the scenarios:
// Type 1
resource "aws_iam_user" "aws_iam_user_000" {
name = "user-000"
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
}
resource "aws_iam_user_policy_attachment" "aws_iam_user_000" {
policy_arn = aws_iam_policy.s3_iam_policy.arn
user = aws_iam_user.aws_iam_user_000.name
}
// Type 2
resource "aws_iam_user" "aws_iam_user_001" {
path = "/"
for_each = toset(var.user_lists)
name = each.value
force_destroy = true
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
}
resource "aws_iam_group" "aws_iam_group_001" {
name = "group-0001"
}
resource "aws_iam_user_group_membership" "group-membership" {
for_each = toset(var.user_lists)
user = aws_iam_user.aws_iam_user_001[each.value].name
groups = [aws_iam_group.aws_iam_group_001.name]
}
// Type 3
resource "aws_iam_user" "aws_iam_user_0002" {
name = "user-002"
tags = { "user_type" = "admin_account" }
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
}
If I understand you correctly, you should be able to accomplish this using count and for_each with variables as below.
variables.tf
variable "is_admin" {
type = bool
default = false
}
variable "user_lists" {
type = list(any)
default = null
}
main.tf
// Type 1 and Type 3
resource "aws_iam_user" "this" {
count = var.user_lists == null ? 1 : 0
name = var.is_admin ? "user-000" : "user-002"
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
tags = var.is_admin ? { "user_type" = "admin_account" } : null
}
resource "aws_iam_user_policy_attachment" "this" {
count = var.user_lists == null ? 1 : 0
policy_arn = aws_iam_policy.s3_iam_policy.arn
user = aws_iam_user.this[0].name
}
// Type 2
resource "aws_iam_user" "from_list" {
for_each = var.user_lists != null ? toset(var.user_lists) : []
path = "/"
name = each.value
force_destroy = true
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
}
resource "aws_iam_group" "from_list" {
count = var.user_lists == null ? 1 : 0
name = "group-0001"
}
resource "aws_iam_user_group_membership" "this" {
for_each = var.user_lists != null ? toset(var.user_lists) : []
user = aws_iam_user.from_list[each.value].name
groups = [aws_iam_group.from_list[0].name]
}

Conditionally Calling Terraform Resource Based on The Module's Variable

Say that I have two modules that require the same source (append_only_history_bq_tables). Their difference is only the from_dwh_core variable which the default set to false
module "dwh_core_append_only_history_tables" {
source = "../append_only_history_bq_tables"
gcp_project = var.gcp_project
bq_location = var.bq_location
dataset_id = google_bigquery_dataset.jago_pagerduty.dataset_id
module_base_path = path.module
tables = {
"list_incident" = { "business_key_columns" = "id", "partition_type" = "DAY" },
"raw_incident_information" = { "business_key_columns" = "id", "partition_type" = "DAY" }
}
}
module "daily_closing_balance" {
source = "../append_only_history_bq_tables"
gcp_project = var.gcp_project
bq_location = var.bq_location
dataset_id = google_bigquery_dataset.dwh_core_dataset.dataset_id
module_base_path = path.module
use_source_ord = false
from_dwh_core = true
tables = {
"daily_closing_balance" = { "business_key_columns" = "full_date,account_number,customer_id", "partition_type" = "DAY" }
}
depends_on = [google_bigquery_dataset.dwh_core_dataset]
}
The append_only_history_bq_tables contains this resource
resource "google_bigquery_table" "dwh_core_snapshot_daily" {
for_each = var.tables
dataset_id = var.dataset_id
project = var.gcp_project
table_id = "${each.key}_snapshot_current_daily"
schema = file("${local.schema_def_folder}/${each.key}.json")
deletion_protection=false
}
How to conditionally call the dwh_core_snapshot_daily resource only if the from_dwh_core variable is set to true?
Thank you for your help
You can use Conditional Expression:
resource "google_bigquery_table" "dwh_core_snapshot_daily" {
for_each = var.from_dwh_core == true ? var.tables : {}
dataset_id = var.dataset_id
project = var.gcp_project
table_id = "${each.key}_snapshot_current_daily"
schema = file("${local.schema_def_folder}/${each.key}.json")
deletion_protection=false
}

Iterate over map of objects in Terraform

I working on a module, provided below, to manage AWS KMS keys via Terraform and I'm using the flatten function but the output I'm getting is empty when I call this module.
Any thought why I'm getting empty output?
module
main.tf
locals {
kms_keys = flatten([
for key, kms_key in var.kms_key_list : [
for index in range(kms_key.key_id) : {
key_id = index
aws_kms_alias = kms_key.alias
is_rotating = kms_key.enable_key_rotation
deletion_window_in_days = kms_key.deletion_window_in_days
is_enabled = kms_key.is_enabled
description = kms_key.description
policy = kms_key.policy
}
]
])
}
resource "aws_kms_key" "main" {
for_each = {
for k, v in local.kms_keys: k => v if v.key_id > 0
}
deletion_window_in_days = each.value.deletion_window_in_days
is_enabled = each.value.is_enabled
enable_key_rotation = each.value.enable_key_rotation
description = each.value.description
policy = each.value.policy
tags = merge({
Name = each.value.aws_kms_alias
}, var.common_tags)
}
resource "aws_kms_alias" "alias" {
for_each = aws_kms_key.main
name = "alias/${each.value.tags.Name}"
target_key_id = each.value.key_id
}
variables.tf
variable "kms_key_list" {
type = map(object({
key_id = number
deletion_window_in_days = number
is_enabled = bool
enable_key_rotation = bool
description = string
policy = string
key_usage = string
customer_master_key_spec = string
alias = string
}))
}
calling the module in main.tf
module "kms_keys" {
source = "../module/kms"
kms_key_list = local.kms_keys
}
kms_keys.tf
locals {
kms_keys = {
name_1 = {
key_id = 1
deletion_window_in_days = 7
is_enabled = true
enable_key_rotation = true
description = "description_1"
policy = ""
key_usage = "ENCRYPT_DECRYPT"
customer_master_key_spec = "SYMMETRIC_DEFAULT"
alias = "alias_1"
}
}
}
TF Plan Output looks like this:
Changes to Outputs:
+ kms_info = {
+ kms_key = {}
}
This seems odd:
for index in range(kms_key.key_id)
This is going to loop through all values from 0 to the key_id value; is that really what you want? To add an entry into kms_keys for each value from 0 to key_id?
I doubt it, because the way you have this coded, if your var.kms_key_list contains a key config with key_id = 10, it's going to create 10 different KMS keys, all with the same configuration values.
Essentially, I'm not understanding the purpose of the nested for loop.
If you can provide samples of:
The input variable, but with a key_id > 1
The output that you expect to see
Then we might be able to help. Also, I don't see any output declared either in the module or in the parent file, so those must be missing; please include them.

Loop over maps and assign value to local - Terraform

I am trying to pass the values s3 name and create_user into local block in main.tf so that both of them have the value in list and then I am passing list_of_bucket in local block in module s3 to create the buckets and looping of user_to_create in module s3_user to create the user if the boolean is set to true. All of these values are passed to variable.tf and then to main.tf
dev.tfvars
wea-nonprod = {
services = {
s3 = [
sthree = {
create_user = true,
}
sfour = {
create_user = true,
}
sfive = {
create_user = true,
}
]
}
}
variable.tf
variable "s3_buckets" {
type = list(map)
}
main.tf
locals {
users_to_create = ""
list_of_buckets = ""
}
module "s3" {
source = "../../s3"
name = join("-", [var.name_prefix, "s3"])
tags = merge(var.tags, {Name = join("-", [var.name_prefix, "s3"])})
buckets = list_of_buckets
sse_algorithm = "AES256"
access_log_bucket_name = var.access_log_bucket_name
}
module "s3_user" {
for_each = local.users_to_create
source = "./service-s3-bucket-user"
name = join("-", [var.name_prefix, each.key])
tags = var.tags
bucket_arn = module.s3.bucket_arns[each.key]
depends_on = [module.s3]
}
Just iterate over your wea-nonprod map:
locals {
users_to_create = [ for name in var.wea-nonprod.services.s3 if name.create_user == true ]
list_of_buckets = [ for bucket in var.wea-nonprod.services.s3 ]
}
And a few changes to your module blocks:
module "s3" {
source = "../../s3"
name = "${var.name_prefix}-s3"
tags = merge(var.tags, { Name = "${var.name_prefix}-s3" })
buckets = local.list_of_buckets
sse_algorithm = "AES256"
access_log_bucket_name = var.access_log_bucket_name
}
module "s3_user" {
count = length(local.users_to_create)
source = "./service-s3-bucket-user"
name = "${var.name_prefix}${local.users_to_create[count.index]}"
tags = var.tags
bucket_arn = module.s3.bucket_arns[local.users_to_create[count.index]]
depends_on = [module.s3]
}