Skip to content

Commit 6c4e066

Browse files
authored
Merge pull request #42494 from hashicorp/b-controltower_control-parameter-removal
r/aws_controltower_control: handle removal of parameters block
2 parents 0ab7ba7 + 3c63649 commit 6c4e066

File tree

5 files changed

+116
-40
lines changed

5 files changed

+116
-40
lines changed

.changelog/42494.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```release-note:bug
2+
resource/aws_controltower_control: Fix handling of `parameters` block removal
3+
```
4+
```release-note:bug
5+
resource/aws_controltower_control: Fix handling `ResourceNotFound` exceptions during delete
6+
```

docs/acc-test-environment-variables.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ Environment variables (beyond standard AWS Go SDK ones) used by acceptance testi
9494
| `TF_ACC` | Enables Go tests containing `resource.Test()` and `resource.ParallelTest()`. |
9595
| `TF_ACC_ASSUME_ROLE_ARN` | Amazon Resource Name of existing IAM Role to use for limited permissions acceptance testing. |
9696
| `TF_AWS_BEDROCK_OSS_COLLECTION_NAME` | Name of the OpenSearch Serverless collection to be used with an Amazon Bedrock Knowledge Base. |
97+
| `TF_AWS_CONTROLTOWER_CONTROL_OU_NAME` | Organizational unit name to be targeted by the Control Tower control. |
9798
| `TF_AWS_DATAEXCHANGE_DATA_SET_ID` | ID of DataExchange Data Set to use for testing. |
9899
| `TF_AWS_LICENSE_MANAGER_GRANT_HOME_REGION` | Region where a License Manager license is imported. |
99100
| `TF_AWS_LICENSE_MANAGER_GRANT_LICENSE_ARN` | ARN for a License Manager license imported into the current account. |

internal/service/controltower/control.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ func resourceControlDelete(ctx context.Context, d *schema.ResourceData, meta any
241241
TargetIdentifier: aws.String(targetIdentifier),
242242
}
243243
output, err := conn.DisableControl(ctx, &input)
244+
if errs.IsA[*types.ResourceNotFoundException](err) {
245+
return diags
246+
}
244247

245248
if err != nil {
246249
return sdkdiag.AppendErrorf(diags, "deleting ControlTower Control (%s): %s", d.Id(), err)
@@ -258,11 +261,7 @@ const (
258261
)
259262

260263
func expandControlParameters(input []any) ([]types.EnabledControlParameter, error) {
261-
if len(input) == 0 {
262-
return nil, nil
263-
}
264-
265-
var output []types.EnabledControlParameter
264+
output := []types.EnabledControlParameter{}
266265

267266
for _, v := range input {
268267
val := v.(map[string]any)

internal/service/controltower/control_test.go

Lines changed: 100 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
types "github.com/aws/aws-sdk-go-v2/service/controltower/types"
1212
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
13+
"github.com/hashicorp/terraform-plugin-testing/plancheck"
1314
"github.com/hashicorp/terraform-plugin-testing/terraform"
1415
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
1516
"github.com/hashicorp/terraform-provider-aws/internal/conns"
@@ -18,42 +19,29 @@ import (
1819
"github.com/hashicorp/terraform-provider-aws/names"
1920
)
2021

21-
func TestAccControlTowerControl_serial(t *testing.T) {
22-
t.Parallel()
23-
24-
testCases := map[string]map[string]func(t *testing.T){
25-
"Control": {
26-
acctest.CtBasic: testAccControl_basic,
27-
acctest.CtDisappears: testAccControl_disappears,
28-
},
29-
}
30-
31-
acctest.RunSerialTests2Levels(t, testCases, 0)
32-
}
33-
3422
func testAccControl_basic(t *testing.T) {
3523
ctx := acctest.Context(t)
3624
var control types.EnabledControlSummary
3725
resourceName := "aws_controltower_control.test"
38-
controlName := "AWS-GR_EC2_VOLUME_INUSE_CHECK"
39-
ouName := "Security"
40-
region := "us-west-2" //lintignore:AWSAT003
26+
ouDataSourceName := "data.aws_organizations_organizational_unit.test"
27+
ouName := acctest.SkipIfEnvVarNotSet(t, "TF_AWS_CONTROLTOWER_CONTROL_OU_NAME")
4128

4229
resource.Test(t, resource.TestCase{
4330
PreCheck: func() {
4431
acctest.PreCheck(ctx, t)
4532
acctest.PreCheckOrganizationManagementAccount(ctx, t)
46-
testAccPreCheck(ctx, t)
4733
},
4834
ErrorCheck: acctest.ErrorCheck(t, names.ControlTowerServiceID),
4935
CheckDestroy: testAccCheckControlDestroy(ctx),
5036
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
5137
Steps: []resource.TestStep{
5238
{
53-
Config: testAccControlConfig_basic(controlName, ouName, region),
39+
Config: testAccControlConfig_basic(ouName),
5440
Check: resource.ComposeTestCheckFunc(
5541
testAccCheckControlExists(ctx, resourceName, &control),
5642
resource.TestCheckResourceAttrSet(resourceName, "control_identifier"),
43+
resource.TestCheckResourceAttrPair(resourceName, "target_identifier", ouDataSourceName, names.AttrARN),
44+
resource.TestCheckResourceAttr(resourceName, "parameters.#", "0"),
5745
),
5846
},
5947
},
@@ -64,27 +52,84 @@ func testAccControl_disappears(t *testing.T) {
6452
ctx := acctest.Context(t)
6553
var control types.EnabledControlSummary
6654
resourceName := "aws_controltower_control.test"
67-
controlName := "AWS-GR_EC2_VOLUME_INUSE_CHECK"
68-
ouName := "Security"
69-
region := "us-west-2" //lintignore:AWSAT003
55+
ouName := acctest.SkipIfEnvVarNotSet(t, "TF_AWS_CONTROLTOWER_CONTROL_OU_NAME")
7056

7157
resource.Test(t, resource.TestCase{
7258
PreCheck: func() {
7359
acctest.PreCheck(ctx, t)
7460
acctest.PreCheckOrganizationManagementAccount(ctx, t)
75-
testAccPreCheck(ctx, t)
7661
},
7762
ErrorCheck: acctest.ErrorCheck(t, names.ControlTowerServiceID),
7863
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
7964
CheckDestroy: testAccCheckControlDestroy(ctx),
8065
Steps: []resource.TestStep{
8166
{
82-
Config: testAccControlConfig_basic(controlName, ouName, region),
67+
Config: testAccControlConfig_basic(ouName),
8368
Check: resource.ComposeTestCheckFunc(
8469
testAccCheckControlExists(ctx, resourceName, &control),
8570
acctest.CheckResourceDisappears(ctx, acctest.Provider, tfcontroltower.ResourceControl(), resourceName),
8671
),
8772
ExpectNonEmptyPlan: true,
73+
ConfigPlanChecks: resource.ConfigPlanChecks{
74+
PostApplyPostRefresh: []plancheck.PlanCheck{
75+
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate),
76+
},
77+
},
78+
},
79+
},
80+
})
81+
}
82+
83+
func testAccControl_parameters(t *testing.T) {
84+
ctx := acctest.Context(t)
85+
var control types.EnabledControlSummary
86+
resourceName := "aws_controltower_control.test"
87+
ouName := acctest.SkipIfEnvVarNotSet(t, "TF_AWS_CONTROLTOWER_CONTROL_OU_NAME")
88+
89+
resource.Test(t, resource.TestCase{
90+
PreCheck: func() {
91+
acctest.PreCheck(ctx, t)
92+
acctest.PreCheckOrganizationManagementAccount(ctx, t)
93+
},
94+
ErrorCheck: acctest.ErrorCheck(t, names.ControlTowerServiceID),
95+
CheckDestroy: testAccCheckControlDestroy(ctx),
96+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
97+
Steps: []resource.TestStep{
98+
{
99+
Config: testAccControlConfig_parameters(ouName),
100+
Check: resource.ComposeTestCheckFunc(
101+
testAccCheckControlExists(ctx, resourceName, &control),
102+
resource.TestCheckResourceAttrSet(resourceName, "control_identifier"),
103+
resource.TestCheckResourceAttr(resourceName, "parameters.#", "1"),
104+
resource.TestCheckResourceAttr(resourceName, "parameters.0.key", "ExemptedPrincipalArns"),
105+
),
106+
},
107+
{
108+
Config: testAccControlConfig_basic(ouName),
109+
Check: resource.ComposeTestCheckFunc(
110+
testAccCheckControlExists(ctx, resourceName, &control),
111+
resource.TestCheckResourceAttrSet(resourceName, "control_identifier"),
112+
resource.TestCheckResourceAttr(resourceName, "parameters.#", "0"),
113+
),
114+
ConfigPlanChecks: resource.ConfigPlanChecks{
115+
PreApply: []plancheck.PlanCheck{
116+
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate),
117+
},
118+
},
119+
},
120+
{
121+
Config: testAccControlConfig_parameters(ouName),
122+
Check: resource.ComposeTestCheckFunc(
123+
testAccCheckControlExists(ctx, resourceName, &control),
124+
resource.TestCheckResourceAttrSet(resourceName, "control_identifier"),
125+
resource.TestCheckResourceAttr(resourceName, "parameters.#", "1"),
126+
resource.TestCheckResourceAttr(resourceName, "parameters.0.key", "ExemptedPrincipalArns"),
127+
),
128+
ConfigPlanChecks: resource.ConfigPlanChecks{
129+
PreApply: []plancheck.PlanCheck{
130+
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate),
131+
},
132+
},
88133
},
89134
},
90135
})
@@ -137,29 +182,49 @@ func testAccCheckControlDestroy(ctx context.Context) resource.TestCheckFunc {
137182
}
138183
}
139184

140-
func testAccControlConfig_basic(controlName, ouName, region string) string {
185+
func testAccControlConfigBase(ouName string) string {
141186
return fmt.Sprintf(`
142-
data "aws_region" "current" {}
143-
144187
data "aws_partition" "current" {}
188+
data "aws_region" "current" {}
189+
data "aws_caller_identity" "current" {}
145190
146191
data "aws_organizations_organization" "test" {}
147192
148-
data "aws_organizations_organizational_units" "test" {
193+
data "aws_organizations_organizational_unit" "test" {
149194
parent_id = data.aws_organizations_organization.test.roots[0].id
195+
name = %[1]q
196+
}
197+
`, ouName)
198+
}
199+
200+
func testAccControlConfig_basic(ouName string) string {
201+
return acctest.ConfigCompose(
202+
testAccControlConfigBase(ouName),
203+
`
204+
resource "aws_controltower_control" "test" {
205+
control_identifier = "arn:${data.aws_partition.current.partition}:controltower:${data.aws_region.current.name}::control/AWS-GR_DISALLOW_CROSS_REGION_NETWORKING"
206+
target_identifier = data.aws_organizations_organizational_unit.test.arn
207+
}
208+
`)
150209
}
151210

211+
// See the AWS documentation for a list of parameterized controls.
212+
//
213+
// Ref:
214+
// - https://docs.aws.amazon.com/controltower/latest/controlreference/control-parameter-concepts.html
215+
// - https://docs.aws.amazon.com/controltower/latest/controlreference/elective-preventive-controls.html
216+
func testAccControlConfig_parameters(ouName string) string {
217+
return acctest.ConfigCompose(
218+
testAccControlConfigBase(ouName),
219+
`
152220
resource "aws_controltower_control" "test" {
153-
control_identifier = "arn:${data.aws_partition.current.partition}:controltower:${data.aws_region.current.name}::control/%[1]s"
154-
target_identifier = [
155-
for x in data.aws_organizations_organizational_units.test.children :
156-
x.arn if x.name == "%[2]s"
157-
][0]
221+
control_identifier = "arn:${data.aws_partition.current.partition}:controltower:${data.aws_region.current.name}::control/AWS-GR_DISALLOW_CROSS_REGION_NETWORKING"
222+
target_identifier = data.aws_organizations_organizational_unit.test.arn
158223
159224
parameters {
160-
key = "AllowedRegions"
161-
value = jsonencode([%[3]q])
225+
key = "ExemptedPrincipalArns"
226+
value = jsonencode(["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role/tf-acctest-example"])
162227
}
163228
}
164-
`, controlName, ouName, region)
229+
`)
165230
}

internal/service/controltower/controltower_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ func TestAccControlTower_serial(t *testing.T) {
1818
acctest.CtDisappears: testAccLandingZone_disappears,
1919
"tags": testAccLandingZone_tags,
2020
},
21+
"Control": {
22+
acctest.CtBasic: testAccControl_basic,
23+
acctest.CtDisappears: testAccControl_disappears,
24+
"parameters": testAccControl_parameters,
25+
},
2126
}
2227

2328
acctest.RunSerialTests2Levels(t, testCases, 0)

0 commit comments

Comments
 (0)