diff --git a/.changelog/38498.txt b/.changelog/38498.txt new file mode 100644 index 00000000000..d0e17423771 --- /dev/null +++ b/.changelog/38498.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_cloudformation_stack_set_instance: Add `operation_preferences.concurrency_mode` argument +``` diff --git a/internal/service/cloudformation/stack_set_instance.go b/internal/service/cloudformation/stack_set_instance.go index de0f381e6e5..92f6023db84 100644 --- a/internal/service/cloudformation/stack_set_instance.go +++ b/internal/service/cloudformation/stack_set_instance.go @@ -145,6 +145,11 @@ func resourceStackSetInstance() *schema.Resource { ValidateFunc: validation.IntBetween(1, 100), ConflictsWith: []string{"operation_preferences.0.max_concurrent_count"}, }, + "concurrency_mode": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[awstypes.ConcurrencyMode](), + }, "region_concurrency_type": { Type: schema.TypeString, Optional: true, diff --git a/internal/service/cloudformation/stack_set_instance_test.go b/internal/service/cloudformation/stack_set_instance_test.go index 255935248e4..b6ddc351901 100644 --- a/internal/service/cloudformation/stack_set_instance_test.go +++ b/internal/service/cloudformation/stack_set_instance_test.go @@ -38,7 +38,7 @@ func TestAccCloudFormationStackSetInstance_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccStackSetInstanceConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckStackSetInstanceExists(ctx, resourceName, &stackInstance1), acctest.CheckResourceAttrAccountID(resourceName, names.AttrAccountID), resource.TestCheckResourceAttr(resourceName, "call_as", "SELF"), @@ -310,9 +310,45 @@ func TestAccCloudFormationStackSetInstance_operationPreferences(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccStackSetInstanceConfig_operationPreferences(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckStackSetInstanceForOrganizationalUnitExists(ctx, resourceName, stackInstanceSummaries), resource.TestCheckResourceAttr(resourceName, "operation_preferences.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.concurrency_mode", ""), + resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.failure_tolerance_count", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.failure_tolerance_percentage", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.max_concurrent_count", acctest.Ct10), + resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.max_concurrent_percentage", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.region_concurrency_type", ""), + ), + }, + }, + }) +} + +func TestAccCloudFormationStackSetInstance_concurrencyMode(t *testing.T) { + ctx := acctest.Context(t) + var stackInstanceSummaries []awstypes.StackInstanceSummary + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_cloudformation_stack_set_instance.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckStackSet(ctx, t) + acctest.PreCheckOrganizationsEnabled(ctx, t) + acctest.PreCheckOrganizationManagementAccount(ctx, t) + acctest.PreCheckIAMServiceLinkedRole(ctx, t, "/aws-service-role/stacksets.cloudformation.amazonaws.com") + }, + ErrorCheck: acctest.ErrorCheck(t, names.CloudFormationServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckStackSetInstanceForOrganizationalUnitDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccStackSetInstanceConfig_concurrencyMode(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckStackSetInstanceForOrganizationalUnitExists(ctx, resourceName, stackInstanceSummaries), + resource.TestCheckResourceAttr(resourceName, "operation_preferences.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.concurrency_mode", "SOFT_FAILURE_TOLERANCE"), resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.failure_tolerance_count", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.failure_tolerance_percentage", acctest.Ct0), resource.TestCheckResourceAttr(resourceName, "operation_preferences.0.max_concurrent_count", acctest.Ct10), @@ -864,6 +900,26 @@ resource "aws_cloudformation_stack_set_instance" "test" { `) } +func testAccStackSetInstanceConfig_concurrencyMode(rName string) string { + return acctest.ConfigCompose(testAccStackSetInstanceBaseConfig_ServiceManagedStackSet(rName), ` +resource "aws_cloudformation_stack_set_instance" "test" { + depends_on = [aws_iam_role_policy.Administration, aws_iam_role_policy.Execution] + + operation_preferences { + failure_tolerance_count = 1 + max_concurrent_count = 10 + concurrency_mode = "SOFT_FAILURE_TOLERANCE" + } + + deployment_targets { + organizational_unit_ids = [data.aws_organizations_organization.test.roots[0].id] + } + + stack_set_name = aws_cloudformation_stack_set.test.name +} +`) +} + func testAccStackSetInstanceConfig_delegatedAdministrator(rName string) string { return acctest.ConfigCompose(testAccStackSetConfig_delegatedAdministrator(rName), ` data "aws_organizations_organization" "test" {} diff --git a/internal/service/cloudformation/type.go b/internal/service/cloudformation/type.go index 403f4c6f7ff..2276bdddd44 100644 --- a/internal/service/cloudformation/type.go +++ b/internal/service/cloudformation/type.go @@ -442,6 +442,9 @@ func expandOperationPreferences(tfMap map[string]interface{}) *awstypes.StackSet if v, ok := tfMap["max_concurrent_percentage"].(int); ok { apiObject.MaxConcurrentPercentage = aws.Int32(int32(v)) } + if v, ok := tfMap["concurrency_mode"].(string); ok && v != "" { + apiObject.ConcurrencyMode = awstypes.ConcurrencyMode(v) + } if v, ok := tfMap["region_concurrency_type"].(string); ok && v != "" { apiObject.RegionConcurrencyType = awstypes.RegionConcurrencyType(v) } diff --git a/website/docs/r/cloudformation_stack_set_instance.html.markdown b/website/docs/r/cloudformation_stack_set_instance.html.markdown index e180b086d57..a2279f52a7d 100644 --- a/website/docs/r/cloudformation_stack_set_instance.html.markdown +++ b/website/docs/r/cloudformation_stack_set_instance.html.markdown @@ -111,6 +111,7 @@ The `operation_preferences` configuration block supports the following arguments * `failure_tolerance_percentage` - (Optional) Percentage of accounts, per Region, for which this stack operation can fail before AWS CloudFormation stops the operation in that Region. * `max_concurrent_count` - (Optional) Maximum number of accounts in which to perform this operation at one time. * `max_concurrent_percentage` - (Optional) Maximum percentage of accounts in which to perform this operation at one time. +* `concurrency_mode` - (Optional) Specifies how the concurrency level behaves during the operation execution. Valid values are `STRICT_FAILURE_TOLERANCE` and `SOFT_FAILURE_TOLERANCE`. * `region_concurrency_type` - (Optional) Concurrency type of deploying StackSets operations in Regions, could be in parallel or one Region at a time. Valid values are `SEQUENTIAL` and `PARALLEL`. * `region_order` - (Optional) Order of the Regions in where you want to perform the stack operation.