From 7f120c5a3aba90f87c576e912a12ff308941f7ec Mon Sep 17 00:00:00 2001 From: Chris Hatko Date: Thu, 16 Oct 2025 19:39:43 -0400 Subject: [PATCH 1/3] Fix stringExists operator in subject attributes for IAM policy templates This fix addresses an issue where the stringExists operator in subject attributes was not working correctly. The bug was in the comparison of the attributesItemModel.Value pointer to string literals without dereferencing it first. The fix dereferences the pointer before comparing it to the string literals 'true' and 'false'. Added a test case that reproduces the issue by using stringExists operator in subject attributes. Fixes IAM/AM-issues#3970 --- .../resource_ibm_iam_policy_template.go | 6 +-- .../resource_ibm_iam_policy_template_test.go | 53 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/ibm/service/iampolicy/resource_ibm_iam_policy_template.go b/ibm/service/iampolicy/resource_ibm_iam_policy_template.go index a00617d4e6..2b1e50c24d 100644 --- a/ibm/service/iampolicy/resource_ibm_iam_policy_template.go +++ b/ibm/service/iampolicy/resource_ibm_iam_policy_template.go @@ -395,12 +395,12 @@ func generateTemplatePolicy(d *schema.ResourceData, iamPolicyManagementClient *i sourceServiceName = item.((map[string]interface{}))["value"].(string) } if *attributesItemModel.Operator == "stringExists" { - if attributesItemModel.Value == "true" { + if *attributesItemModel.Value == "true" { attributesItemModel.Value = true - } else if attributesItemModel.Value == "false" { + } else if *attributesItemModel.Value == "false" { attributesItemModel.Value = false } else { - return model, fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %s.", attributesItemModel.Value) + return model, fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %s.", *attributesItemModel.Value) } } if *model.Type == "authorization" && *attributesItemModel.Operator == "" && attributesItemModel.Value == "*" && *attributesItemModel.Key == "resourceGroupId" { diff --git a/ibm/service/iampolicy/resource_ibm_iam_policy_template_test.go b/ibm/service/iampolicy/resource_ibm_iam_policy_template_test.go index 65e82dae13..1c920f6eed 100644 --- a/ibm/service/iampolicy/resource_ibm_iam_policy_template_test.go +++ b/ibm/service/iampolicy/resource_ibm_iam_policy_template_test.go @@ -221,6 +221,26 @@ func TestAccIBMIAMPolicyTemplateBasicS2SUpdate(t *testing.T) { }) } +func TestAccIBMIAMPolicyTemplateSubjectStringExists(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMPolicyTemplateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMPolicyS2STemplateSubjectStringExists("TerraformS2SSubjectTest", "is", "is", "true"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMPolicyTemplateExists("ibm_iam_policy_template.policy_template", conf), + resource.TestCheckResourceAttr("ibm_iam_policy_template.policy_template", "name", "TerraformS2SSubjectTest"), + resource.TestCheckResourceAttr("ibm_iam_policy_template.policy_template", "policy.0.resource.0.attributes.0.value", "is"), + resource.TestCheckResourceAttr("ibm_iam_policy_template.policy_template", "policy.0.subject.0.attributes.0.value", "is"), + resource.TestCheckResourceAttr("ibm_iam_policy_template.policy_template", "policy.0.subject.0.attributes.1.value", "true"), + ), + }, + }, + }) +} + func testAccCheckIBMPolicyTemplateExists(n string, obj iampolicymanagementv1.PolicyTemplate) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -515,3 +535,36 @@ func testAccCheckIBMPolicyTemplateConfigBasicWithTags(name string, serviceName s } `, name, serviceName, tagValue) } + +func testAccCheckIBMPolicyS2STemplateSubjectStringExists(name string, resourceServiceName string, subjectServiceName string, vpcIdValue string) string { + return fmt.Sprintf(` + resource "ibm_iam_policy_template" "policy_template" { + name = "%s" + policy { + type = "authorization" + description = "Test terraform enterprise S2S with stringExists in subject" + resource { + attributes { + key = "serviceName" + operator = "stringEquals" + value = "%s" + } + } + subject { + attributes { + key = "serviceName" + operator = "stringEquals" + value = "%s" + } + attributes { + key = "vpcId" + operator = "stringExists" + value = "%s" + } + } + roles = ["Reader"] + } + committed=true + } + `, name, resourceServiceName, subjectServiceName, vpcIdValue) +} From 8c10ce791628f7c695d406a175ecf1164e327222 Mon Sep 17 00:00:00 2001 From: Chris Hatko Date: Thu, 16 Oct 2025 21:23:25 -0400 Subject: [PATCH 2/3] fix errors from new go version --- ibm/service/iampolicy/resource_ibm_iam_policy_template.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ibm/service/iampolicy/resource_ibm_iam_policy_template.go b/ibm/service/iampolicy/resource_ibm_iam_policy_template.go index 2b1e50c24d..3e2bc23210 100644 --- a/ibm/service/iampolicy/resource_ibm_iam_policy_template.go +++ b/ibm/service/iampolicy/resource_ibm_iam_policy_template.go @@ -395,12 +395,13 @@ func generateTemplatePolicy(d *schema.ResourceData, iamPolicyManagementClient *i sourceServiceName = item.((map[string]interface{}))["value"].(string) } if *attributesItemModel.Operator == "stringExists" { - if *attributesItemModel.Value == "true" { + valueStr := fmt.Sprintf("%v", attributesItemModel.Value) + if valueStr == "true" { attributesItemModel.Value = true - } else if *attributesItemModel.Value == "false" { + } else if valueStr == "false" { attributesItemModel.Value = false } else { - return model, fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %s.", *attributesItemModel.Value) + return model, fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %s.", valueStr) } } if *model.Type == "authorization" && *attributesItemModel.Operator == "" && attributesItemModel.Value == "*" && *attributesItemModel.Key == "resourceGroupId" { From 5927e1cc88322b26500348b9cb9cb2c59d1be41d Mon Sep 17 00:00:00 2001 From: Chris Hatko Date: Fri, 17 Oct 2025 16:37:34 -0400 Subject: [PATCH 3/3] fix errors found in testing --- .../resource_ibm_iam_authorization_policy.go | 8 ++++---- .../iampolicy/resource_ibm_iam_policy_template.go | 14 +++++++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/ibm/service/iampolicy/resource_ibm_iam_authorization_policy.go b/ibm/service/iampolicy/resource_ibm_iam_authorization_policy.go index 6563ec6435..09ce77d039 100644 --- a/ibm/service/iampolicy/resource_ibm_iam_authorization_policy.go +++ b/ibm/service/iampolicy/resource_ibm_iam_authorization_policy.go @@ -262,7 +262,7 @@ func resourceIBMIAMAuthorizationPolicyCreate(d *schema.ResourceData, meta interf } else if value == "false" { resourceValue = false } else { - return fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %s.", value) + return fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %v.", value) } at := iampolicymanagementv1.V2PolicySubjectAttribute{ Key: &name, @@ -365,7 +365,7 @@ func resourceIBMIAMAuthorizationPolicyCreate(d *schema.ResourceData, meta interf } else if value == "false" { resourceValue = false } else { - return fmt.Errorf("[ERROR] When operator equals stringExists, value should be either \"true\" or \"false\", instead of %s", value) + return fmt.Errorf("[ERROR] When operator equals stringExists, value should be either \"true\" or \"false\", instead of %v", value) } at := iampolicymanagementv1.V2PolicyResourceAttribute{ Key: &name, @@ -592,7 +592,7 @@ func resourceIBMIAMAuthorizationPolicyUpdate(d *schema.ResourceData, meta interf } else if value == "false" { resourceValue = false } else { - return fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %s.", value) + return fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %v.", value) } at := iampolicymanagementv1.V2PolicySubjectAttribute{ Key: &name, @@ -695,7 +695,7 @@ func resourceIBMIAMAuthorizationPolicyUpdate(d *schema.ResourceData, meta interf } else if value == "false" { resourceValue = false } else { - return fmt.Errorf("[ERROR] When operator equals stringExists, value should be either \"true\" or \"false\", instead of %s", value) + return fmt.Errorf("[ERROR] When operator equals stringExists, value should be either \"true\" or \"false\", instead of %v", value) } at := iampolicymanagementv1.V2PolicyResourceAttribute{ Key: &name, diff --git a/ibm/service/iampolicy/resource_ibm_iam_policy_template.go b/ibm/service/iampolicy/resource_ibm_iam_policy_template.go index 3e2bc23210..2bdf0c26fb 100644 --- a/ibm/service/iampolicy/resource_ibm_iam_policy_template.go +++ b/ibm/service/iampolicy/resource_ibm_iam_policy_template.go @@ -357,7 +357,7 @@ func generateTemplatePolicy(d *schema.ResourceData, iamPolicyManagementClient *i } else if attributesItemModel.Value == "false" { attributesItemModel.Value = false } else { - return model, fmt.Errorf("[ERROR] When operator equals stringExists, value should be either \"true\" or \"false\", instead of %s", + return model, fmt.Errorf("[ERROR] When operator equals stringExists, value should be either \"true\" or \"false\", instead of %v", attributesItemModel.Value) } } @@ -395,13 +395,21 @@ func generateTemplatePolicy(d *schema.ResourceData, iamPolicyManagementClient *i sourceServiceName = item.((map[string]interface{}))["value"].(string) } if *attributesItemModel.Operator == "stringExists" { - valueStr := fmt.Sprintf("%v", attributesItemModel.Value) + // Get the string value regardless of whether it's a pointer or not + var valueStr string + if strPtr, ok := attributesItemModel.Value.(*string); ok { + valueStr = *strPtr + } else { + valueStr = fmt.Sprintf("%v", attributesItemModel.Value) + } + + // Now handle the string value if valueStr == "true" { attributesItemModel.Value = true } else if valueStr == "false" { attributesItemModel.Value = false } else { - return model, fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %s.", valueStr) + return model, fmt.Errorf("[ERROR] Only values \"true\" and \"false\" are allowed when operator is \"stringExists\". Received %v.", valueStr) } } if *model.Type == "authorization" && *attributesItemModel.Operator == "" && attributesItemModel.Value == "*" && *attributesItemModel.Key == "resourceGroupId" {