Skip to content
2 changes: 1 addition & 1 deletion modules/trusted-profile-template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ No modules.
| Name | Description |
|------|-------------|
| <a name="output_enterprise_account_ids"></a> [enterprise\_account\_ids](#output\_enterprise\_account\_ids) | List of child enterprise account IDs |
| <a name="output_trusted_profile_template_assignment_ids"></a> [trusted\_profile\_template\_assignment\_ids](#output\_trusted\_profile\_template\_assignment\_ids) | The list of assignment IDs to child accounts |
| <a name="output_trusted_profile_template_assignment_ids"></a> [trusted\_profile\_template\_assignment\_ids](#output\_trusted\_profile\_template\_assignment\_ids) | The map of assignment IDs for account groups and accounts |
| <a name="output_trusted_profile_template_id"></a> [trusted\_profile\_template\_id](#output\_trusted\_profile\_template\_id) | The ID of the trusted profile template |
| <a name="output_trusted_profile_template_id_raw"></a> [trusted\_profile\_template\_id\_raw](#output\_trusted\_profile\_template\_id\_raw) | Full raw ID (including version) of the trusted profile template |
| <a name="output_trusted_profile_template_version"></a> [trusted\_profile\_template\_version](#output\_trusted\_profile\_template\_version) | The version of the Trusted Profile Template |
Expand Down
69 changes: 24 additions & 45 deletions modules/trusted-profile-template/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -75,63 +75,42 @@ data "ibm_enterprise_account_groups" "all_groups" {
}

locals {
group_targets = [
for group in data.ibm_enterprise_account_groups.all_groups.account_groups : {
# Check if "all" is requested
all_groups = length(var.account_group_ids_to_assign) > 0 ? try(var.account_group_ids_to_assign[0], "") == "all" : false
all_accounts = length(var.account_ids_to_assign) > 0 ? try(var.account_ids_to_assign[0], "") == "all" : false

# Account group targets (static keys to avoid for_each dependency issues)
group_targets = local.all_groups ? {
for group in data.ibm_enterprise_account_groups.all_groups.account_groups :
"AccountGroup-${group.id}" => {
id = group.id
type = "AccountGroup"
}
]

compared_list = flatten(
[
for group in local.group_targets :
[
for provided_group in var.account_group_ids_to_assign :
provided_group if group.id == provided_group
]
]
)

all_groups = length(var.account_group_ids_to_assign) > 0 ? var.account_group_ids_to_assign[0] == "all" ? true : false : false
# tflint-ignore: terraform_unused_declarations
validate_group_ids = !local.all_groups ? length(local.compared_list) != length(var.account_group_ids_to_assign) ? tobool("Could not find all of the groups listed in the 'account_group_ids_to_assign' value. Please verify all values are correct") : true : true

combined_group_targets = local.all_groups ? {
for target in local.group_targets :
"${target.type}-${target.id}" => target
} : {
for target in local.group_targets :
"${target.type}-${target.id}" => target if contains(var.account_group_ids_to_assign, target.id)
for group_id in toset(var.account_group_ids_to_assign) :
"AccountGroup-${group_id}" => {
id = group_id
type = "AccountGroup"
} if group_id != "" && group_id != "all"
}

account_targets = [
for account in data.ibm_enterprise_accounts.all_accounts.accounts : {
# Account targets (static keys to avoid for_each dependency issues)
account_targets = local.all_accounts ? {
for account in data.ibm_enterprise_accounts.all_accounts.accounts :
"Account-${account.id}" => {
id = account.id
type = "Account"
}
]

compared_account_list = flatten(
[
for account in local.account_targets :
[
for provided_account in var.account_ids_to_assign :
provided_account if account.id == provided_account
]
]
)
all_accounts = length(var.account_ids_to_assign) > 0 ? var.account_ids_to_assign[0] == "all" ? true : false : false
# tflint-ignore: terraform_unused_declarations
validate_account_ids = !local.all_accounts ? length(local.compared_account_list) != length(var.account_ids_to_assign) ? tobool("Could not find all of the accounts listed in the 'account_ids_to_assign' value. Please verify all values are correct") : true : true
combined_account_targets = local.all_accounts ? {
for target in local.account_targets :
"${target.type}-${target.id}" => target
} : {
for target in local.account_targets :
"${target.type}-${target.id}" => target if contains(var.account_ids_to_assign, target.id)
for account_id in toset(var.account_ids_to_assign) :
"Account-${account_id}" => {
id = account_id
type = "Account"
} if account_id != "" && account_id != "all"
}
combined_targets = merge(local.combined_group_targets, local.combined_account_targets)

# Combine all targets
combined_targets = merge(local.group_targets, local.account_targets)
}

resource "ibm_iam_trusted_profile_template_assignment" "account_settings_template_assignment_instance" {
Expand Down
6 changes: 4 additions & 2 deletions modules/trusted-profile-template/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ output "trusted_profile_template_version" {
}

output "trusted_profile_template_assignment_ids" {
description = "The list of assignment IDs to child accounts"
value = split("/", ibm_iam_trusted_profile_template.trusted_profile_template_instance.id)[0]
description = "The map of assignment IDs for account groups and accounts"
value = {
for k, v in ibm_iam_trusted_profile_template_assignment.account_settings_template_assignment_instance : k => v.id
}
}
4 changes: 2 additions & 2 deletions modules/trusted-profile-template/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ variable "account_group_ids_to_assign" {
nullable = false

validation {
condition = contains(var.account_group_ids_to_assign, "all") ? length(var.account_group_ids_to_assign) == 1 : true
condition = !contains(var.account_group_ids_to_assign, "all") || length(var.account_group_ids_to_assign) == 1
error_message = "When specifying 'all' in the list, you cannot add any other values to the list"
}
}
Expand All @@ -43,7 +43,7 @@ variable "account_ids_to_assign" {
nullable = false

validation {
condition = contains(var.account_ids_to_assign, "all") ? length(var.account_ids_to_assign) == 1 : true
condition = !contains(var.account_ids_to_assign, "all") || length(var.account_ids_to_assign) == 1
error_message = "When specifying 'all' in the list, you cannot add any other values to the list"
}
}
Expand Down
35 changes: 35 additions & 0 deletions tests/key-stability-test/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Test the trusted-profile-template module for resource key stability
# This test validates that reordering account/group lists doesn't cause resource recreation

resource "random_string" "suffix" {
length = 8
special = false
upper = false
}

# Test the actual trusted-profile-template module for key stability
module "trusted_profile_template" {
source = "../../modules/trusted-profile-template"

template_name = "${var.prefix}-key-test-template"
template_description = "Template to test resource key stability"
profile_name = "${var.prefix}-key-test-profile"
profile_description = "Profile to test resource key stability"

# Test with specific account and group IDs to validate key generation
account_group_ids_to_assign = var.account_group_ids_to_assign
account_ids_to_assign = var.account_ids_to_assign

policy_templates = [
{
name = "${var.prefix}-key-test-policy"
description = "Test policy for key stability validation"
roles = ["Viewer"]
attributes = [{
key = "serviceName"
value = "iam-identity"
operator = "stringEquals"
}]
}
]
}
19 changes: 19 additions & 0 deletions tests/key-stability-test/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
output "assignment_keys" {
value = keys(module.trusted_profile_template.trusted_profile_template_assignment_ids)
description = "The keys used for trusted profile template assignments - validates stable key generation"
}

output "template_id" {
value = module.trusted_profile_template.trusted_profile_template_id
description = "The ID of the created trusted profile template"
}

output "test_account_groups" {
value = var.account_group_ids_to_assign
description = "The account group IDs used in this test"
}

output "test_accounts" {
value = var.account_ids_to_assign
description = "The account IDs used in this test"
}
8 changes: 8 additions & 0 deletions tests/key-stability-test/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
########################################################################################################################
# Provider config
########################################################################################################################

provider "ibm" {
ibmcloud_api_key = var.ibmcloud_api_key
region = "us-south"
}
22 changes: 22 additions & 0 deletions tests/key-stability-test/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
variable "prefix" {
type = string
description = "Prefix for test resources"
}

variable "ibmcloud_api_key" {
type = string
description = "IBM Cloud API key for authentication"
sensitive = true
}

variable "account_group_ids_to_assign" {
type = list(string)
description = "List of account group IDs to test key stability"
default = []
}

variable "account_ids_to_assign" {
type = list(string)
description = "List of account IDs to test key stability"
default = []
}
13 changes: 13 additions & 0 deletions tests/key-stability-test/version.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
ibm = {
source = "IBM-Cloud/ibm"
version = ">= 1.49.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.1.0"
}
}
}
69 changes: 69 additions & 0 deletions tests/module-with-dynamic-accounts/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Test the actual trusted-profile-template module with dynamic account IDs
# This will demonstrate whether the fix-profile branch resolves the for_each dependency issue

# Use actual IBM data source like the real module does
data "ibm_iam_account_settings" "current" {}

# Create mock account data that includes computed values from the data source
# This more accurately reproduces the for_each dependency issue
locals {
mock_accounts = [
{
id = data.ibm_iam_account_settings.current.account_id
name = "current-account"
iam_service_id = "ServiceId-${random_id.suffix[0].hex}"
},
{
# This computed key pattern is what causes the original for_each bug
id = "${data.ibm_iam_account_settings.current.account_id}-child-${random_id.suffix[1].hex}"
name = "mock-child-account"
iam_service_id = "ServiceId-${random_id.suffix[1].hex}"
}
]
}

resource "random_id" "suffix" {
count = 2
byte_length = 4
}

# Use the actual trusted-profile-template module with dynamic account IDs
# On main branch: this should fail with "Invalid for_each argument"
# On fix-profile branch: this should succeed because of static keys
module "trusted_profile_template" {
source = "../../modules/trusted-profile-template"

template_name = "${var.prefix}-dynamic-test-template"
template_description = "Template to test for_each dependency fix"
profile_name = "${var.prefix}-dynamic-test-profile"
profile_description = "Profile to test for_each dependency fix"

# Pass dynamic account IDs from data source - this is what triggers the original bug
# The for_each keys will depend on data.ibm_iam_account_settings.current.account_id
account_ids_to_assign = [
for account in local.mock_accounts : account.id
]

account_group_ids_to_assign = []

identities = [
{
type = "serviceid"
iam_id = local.mock_accounts[0].iam_service_id
identifier = replace(local.mock_accounts[0].iam_service_id, "ServiceId-", "")
}
]

policy_templates = [
{
name = "${var.prefix}-test-policy"
description = "Test policy for for_each dependency fix"
roles = ["Viewer"]
attributes = [{
key = "service_name"
value = "iam-identity"
operator = "stringEquals"
}]
}
]
}
16 changes: 16 additions & 0 deletions tests/module-with-dynamic-accounts/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
output "template_assignment_ids" {
value = module.trusted_profile_template.trusted_profile_template_assignment_ids
description = "Template assignment IDs from the module"
}

output "dynamic_account_ids" {
value = [
for account in local.mock_accounts : account.id
]
description = "The dynamic account IDs used in the test (based on current account)"
}

output "current_account_id" {
value = data.ibm_iam_account_settings.current.account_id
description = "The current IBM Cloud account ID from data source"
}
7 changes: 7 additions & 0 deletions tests/module-with-dynamic-accounts/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
########################################################################################################################
# Provider config
########################################################################################################################

provider "ibm" {
ibmcloud_api_key = var.ibmcloud_api_key
}
11 changes: 11 additions & 0 deletions tests/module-with-dynamic-accounts/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "prefix" {
type = string
description = "Prefix for test resources"
default = "tp-module-test"
}

variable "ibmcloud_api_key" {
type = string
description = "IBM Cloud API key for authentication"
sensitive = true
}
13 changes: 13 additions & 0 deletions tests/module-with-dynamic-accounts/version.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
ibm = {
source = "IBM-Cloud/ibm"
version = ">= 1.49.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.1.0"
}
}
}
Loading