Skip to content

Commit cc7e96e

Browse files
authored
Merge pull request hashicorp#31284 from joshmyers/ddb_update_replica_amazon_owned_sse
DDB update replica Amazon Owned SSE
2 parents cc01709 + ffb8e5a commit cc7e96e

File tree

4 files changed

+96
-38
lines changed

4 files changed

+96
-38
lines changed

.changelog/31284.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
resource/aws_dynamodb_table: Fix changing replicas to the default `Managed by DynamoDB` encryption setting
3+
```

internal/service/dynamodb/table.go

+38-24
Original file line numberDiff line numberDiff line change
@@ -1072,33 +1072,34 @@ func resourceTableUpdate(ctx context.Context, d *schema.ResourceData, meta inter
10721072
}
10731073

10741074
if d.HasChange("server_side_encryption") {
1075-
if replicas := d.Get("replica").(*schema.Set); replicas.Len() > 0 {
1075+
if replicas, sseSpecification := d.Get("replica").(*schema.Set), expandEncryptAtRestOptions(d.Get("server_side_encryption").([]interface{})); replicas.Len() > 0 && sseSpecification.KMSMasterKeyId != nil {
10761076
log.Printf("[DEBUG] Using SSE update on replicas")
10771077
var replicaInputs []awstypes.ReplicationGroupUpdate
1078-
var replicaRegions []string
10791078
for _, replica := range replicas.List() {
10801079
tfMap, ok := replica.(map[string]interface{})
10811080
if !ok {
10821081
continue
10831082
}
1084-
var regionName string
1085-
var KMSMasterKeyId string
1086-
if v, ok := tfMap["region_name"].(string); ok {
1087-
regionName = v
1088-
replicaRegions = append(replicaRegions, v)
1083+
1084+
region, ok := tfMap["region_name"].(string)
1085+
if !ok {
1086+
continue
10891087
}
1090-
if v, ok := tfMap[names.AttrKMSKeyARN].(string); ok && v != "" {
1091-
KMSMasterKeyId = v
1088+
1089+
key, ok := tfMap[names.AttrKMSKeyARN].(string)
1090+
if !ok || key == "" {
1091+
continue
10921092
}
1093+
10931094
var input = &awstypes.UpdateReplicationGroupMemberAction{
1094-
RegionName: aws.String(regionName),
1095-
KMSMasterKeyId: aws.String(KMSMasterKeyId),
1095+
RegionName: aws.String(region),
1096+
KMSMasterKeyId: aws.String(key),
10961097
}
10971098
var update = awstypes.ReplicationGroupUpdate{Update: input}
10981099
replicaInputs = append(replicaInputs, update)
10991100
}
11001101
var input = &awstypes.UpdateReplicationGroupMemberAction{
1101-
KMSMasterKeyId: expandEncryptAtRestOptions(d.Get("server_side_encryption").([]interface{})).KMSMasterKeyId,
1102+
KMSMasterKeyId: sseSpecification.KMSMasterKeyId,
11021103
RegionName: aws.String(meta.(*conns.AWSClient).Region),
11031104
}
11041105
var update = awstypes.ReplicationGroupUpdate{Update: input}
@@ -1110,28 +1111,41 @@ func resourceTableUpdate(ctx context.Context, d *schema.ResourceData, meta inter
11101111
if err != nil {
11111112
return sdkdiag.AppendErrorf(diags, "updating DynamoDB Table (%s) SSE: %s", d.Id(), err)
11121113
}
1113-
for _, region := range replicaRegions {
1114-
if _, err := waitReplicaSSEUpdated(ctx, conn, region, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
1115-
return sdkdiag.AppendErrorf(diags, "waiting for DynamoDB Table (%s) replica SSE update in region %q: %s", d.Id(), region, err)
1116-
}
1117-
}
1118-
if _, err := waitSSEUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
1119-
return sdkdiag.AppendErrorf(diags, "waiting for DynamoDB Table (%s) SSE update: %s", d.Id(), err)
1120-
}
11211114
} else {
11221115
log.Printf("[DEBUG] Using normal update for SSE")
11231116
_, err := conn.UpdateTable(ctx, &dynamodb.UpdateTableInput{
11241117
TableName: aws.String(d.Id()),
1125-
SSESpecification: expandEncryptAtRestOptions(d.Get("server_side_encryption").([]interface{})),
1118+
SSESpecification: sseSpecification,
11261119
})
11271120
if err != nil {
11281121
return sdkdiag.AppendErrorf(diags, "updating DynamoDB Table (%s) SSE: %s", d.Id(), err)
11291122
}
1123+
}
11301124

1131-
if _, err := waitSSEUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
1132-
return sdkdiag.AppendErrorf(diags, "waiting for DynamoDB Table (%s) SSE update: %s", d.Id(), err)
1125+
// since we don't update replicas unless there is a KMS key, we need to wait for replica
1126+
// updates for the scenario where 1) there are replicas, 2) we are updating SSE (such as
1127+
// disabling), and 3) we have no KMS key
1128+
if replicas := d.Get("replica").(*schema.Set); replicas.Len() > 0 {
1129+
var replicaRegions []string
1130+
for _, replica := range replicas.List() {
1131+
tfMap, ok := replica.(map[string]interface{})
1132+
if !ok {
1133+
continue
1134+
}
1135+
if v, ok := tfMap["region_name"].(string); ok {
1136+
replicaRegions = append(replicaRegions, v)
1137+
}
1138+
}
1139+
for _, region := range replicaRegions {
1140+
if _, err := waitReplicaSSEUpdated(ctx, conn, region, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
1141+
return sdkdiag.AppendErrorf(diags, "waiting for DynamoDB Table (%s) replica SSE update in region %q: %s", d.Id(), region, err)
1142+
}
11331143
}
11341144
}
1145+
1146+
if _, err := waitSSEUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
1147+
return sdkdiag.AppendErrorf(diags, "waiting for DynamoDB Table (%s) SSE update: %s", d.Id(), err)
1148+
}
11351149
}
11361150

11371151
if d.HasChange("ttl") {
@@ -1902,7 +1916,7 @@ func clearSSEDefaultKey(ctx context.Context, client *conns.AWSClient, sseList []
19021916
return sseList
19031917
}
19041918

1905-
if sse[names.AttrKMSKeyARN].(string) == dk {
1919+
if v, ok := sse[names.AttrKMSKeyARN].(string); ok && v == dk {
19061920
sse[names.AttrKMSKeyARN] = ""
19071921
return []interface{}{sse}
19081922
}

internal/service/dynamodb/table_test.go

+45-6
Original file line numberDiff line numberDiff line change
@@ -1882,29 +1882,68 @@ func TestAccDynamoDBTable_Replica_singleDefaultKeyEncrypted(t *testing.T) {
18821882
CheckDestroy: testAccCheckTableDestroy(ctx),
18831883
Steps: []resource.TestStep{
18841884
{
1885-
Config: testAccTableConfig_replicaEncryptedDefault(rName),
1885+
Config: testAccTableConfig_replicaEncryptedDefault(rName, true),
18861886
Check: resource.ComposeAggregateTestCheckFunc(
18871887
testAccCheckInitialTableExists(ctx, resourceName, &conf),
18881888
resource.TestCheckResourceAttr(resourceName, "replica.#", acctest.Ct1),
18891889
resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.enabled", acctest.CtTrue),
18901890
),
18911891
},
18921892
{
1893-
Config: testAccTableConfig_replicaEncryptedDefault(rName),
1893+
Config: testAccTableConfig_replicaEncryptedDefault(rName, true),
18941894
Check: resource.ComposeAggregateTestCheckFunc(
18951895
testAccCheckInitialTableExists(ctx, resourceName, &conf),
18961896
resource.TestCheckResourceAttr(resourceName, "replica.#", acctest.Ct1),
18971897
resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.enabled", acctest.CtTrue),
18981898
),
18991899
},
19001900
{
1901-
Config: testAccTableConfig_replicaEncryptedDefault(rName),
1901+
Config: testAccTableConfig_replicaEncryptedDefault(rName, true),
19021902
PlanOnly: true,
19031903
},
19041904
},
19051905
})
19061906
}
19071907

1908+
func TestAccDynamoDBTable_Replica_singleDefaultKeyEncryptedAmazonOwned(t *testing.T) {
1909+
ctx := acctest.Context(t)
1910+
if testing.Short() {
1911+
t.Skip("skipping long-running test in short mode")
1912+
}
1913+
1914+
var conf awstypes.TableDescription
1915+
resourceName := "aws_dynamodb_table.test"
1916+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
1917+
1918+
resource.ParallelTest(t, resource.TestCase{
1919+
PreCheck: func() {
1920+
acctest.PreCheck(ctx, t)
1921+
acctest.PreCheckMultipleRegion(t, 2)
1922+
},
1923+
ErrorCheck: acctest.ErrorCheck(t, names.DynamoDBServiceID),
1924+
ProtoV5ProviderFactories: acctest.ProtoV5FactoriesMultipleRegions(ctx, t, 3), // 3 due to shared test configuration
1925+
CheckDestroy: testAccCheckTableDestroy(ctx),
1926+
Steps: []resource.TestStep{
1927+
{
1928+
Config: testAccTableConfig_replicaEncryptedDefault(rName, true),
1929+
Check: resource.ComposeAggregateTestCheckFunc(
1930+
testAccCheckInitialTableExists(ctx, resourceName, &conf),
1931+
resource.TestCheckResourceAttr(resourceName, "replica.#", acctest.Ct1),
1932+
resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.enabled", acctest.CtTrue),
1933+
),
1934+
},
1935+
{
1936+
Config: testAccTableConfig_replicaEncryptedDefault(rName, false),
1937+
Check: resource.ComposeAggregateTestCheckFunc(
1938+
testAccCheckInitialTableExists(ctx, resourceName, &conf),
1939+
resource.TestCheckResourceAttr(resourceName, "replica.#", acctest.Ct1),
1940+
resource.TestCheckResourceAttr(resourceName, "server_side_encryption.#", acctest.Ct0),
1941+
),
1942+
},
1943+
},
1944+
})
1945+
}
1946+
19081947
func TestAccDynamoDBTable_Replica_singleCMK(t *testing.T) {
19091948
ctx := acctest.Context(t)
19101949
if testing.Short() {
@@ -3827,7 +3866,7 @@ resource "aws_dynamodb_table" "test" {
38273866
`, rName))
38283867
}
38293868

3830-
func testAccTableConfig_replicaEncryptedDefault(rName string) string {
3869+
func testAccTableConfig_replicaEncryptedDefault(rName string, sseEnabled bool) string {
38313870
return acctest.ConfigCompose(
38323871
acctest.ConfigMultipleRegionProvider(3), // Prevent "Provider configuration not present" errors
38333872
fmt.Sprintf(`
@@ -3848,14 +3887,14 @@ resource "aws_dynamodb_table" "test" {
38483887
}
38493888
38503889
server_side_encryption {
3851-
enabled = true
3890+
enabled = %[2]t
38523891
}
38533892
38543893
replica {
38553894
region_name = data.aws_region.alternate.name
38563895
}
38573896
}
3858-
`, rName))
3897+
`, rName, sseEnabled))
38593898
}
38603899

38613900
func testAccTableConfig_replicaCMK(rName string) string {

internal/service/dynamodb/wait.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -204,13 +204,14 @@ func waitTTLUpdated(ctx context.Context, conn *dynamodb.Client, tableName string
204204
return nil, err
205205
}
206206

207-
func waitSSEUpdated(ctx context.Context, conn *dynamodb.Client, tableName string, timeout time.Duration) (*awstypes.TableDescription, error) { //nolint:unparam
207+
func waitSSEUpdated(ctx context.Context, conn *dynamodb.Client, tableName string, timeout time.Duration) (*awstypes.TableDescription, error) {
208208
stateConf := &retry.StateChangeConf{
209-
Pending: enum.Slice(awstypes.SSEStatusDisabling, awstypes.SSEStatusEnabling, awstypes.SSEStatusUpdating),
210-
Target: enum.Slice(awstypes.SSEStatusDisabled, awstypes.SSEStatusEnabled),
211-
Refresh: statusSSE(ctx, conn, tableName),
212-
Timeout: max(updateTableTimeout, timeout),
213-
Delay: 30 * time.Second,
209+
Pending: enum.Slice(awstypes.SSEStatusDisabling, awstypes.SSEStatusEnabling, awstypes.SSEStatusUpdating),
210+
Target: enum.Slice(awstypes.SSEStatusDisabled, awstypes.SSEStatusEnabled),
211+
Refresh: statusSSE(ctx, conn, tableName),
212+
Timeout: max(updateTableTimeout, timeout),
213+
ContinuousTargetOccurence: 2,
214+
Delay: 30 * time.Second,
214215
}
215216

216217
outputRaw, err := stateConf.WaitForStateContext(ctx)
@@ -229,8 +230,9 @@ func waitReplicaSSEUpdated(ctx context.Context, conn *dynamodb.Client, region, t
229230
Refresh: statusSSE(ctx, conn, tableName, func(o *dynamodb.Options) {
230231
o.Region = region
231232
}),
232-
Timeout: max(updateTableTimeout, timeout),
233-
Delay: 30 * time.Second,
233+
Timeout: max(updateTableTimeout, timeout),
234+
ContinuousTargetOccurence: 2,
235+
Delay: 30 * time.Second,
234236
}
235237

236238
outputRaw, err := stateConf.WaitForStateContext(ctx)

0 commit comments

Comments
 (0)