diff --git a/.changelog/45298.txt b/.changelog/45298.txt new file mode 100644 index 000000000000..9a25b1686966 --- /dev/null +++ b/.changelog/45298.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_cloudwatch_log_group: Add `deletion_protection_enabled` argument +``` + +```release-note:enhancement +data-source/aws_cloudwatch_log_group: Add `deletion_protection_enabled` attribute +``` diff --git a/internal/service/logs/group.go b/internal/service/logs/group.go index d81def67d4a8..975cc6f9483a 100644 --- a/internal/service/logs/group.go +++ b/internal/service/logs/group.go @@ -53,6 +53,11 @@ func resourceGroup() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "deletion_protection_enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, names.AttrKMSKeyID: { Type: schema.TypeString, Optional: true, @@ -127,6 +132,10 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta any) Tags: getTagsIn(ctx), } + if v, ok := d.GetOk("deletion_protection_enabled"); ok { + input.DeletionProtectionEnabled = aws.Bool(v.(bool)) + } + if v, ok := d.GetOk(names.AttrKMSKeyID); ok { input.KmsKeyId = aws.String(v.(string)) } @@ -209,6 +218,29 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta any) } } + if d.HasChange("deletion_protection_enabled") { + var deletionProtectionEnabled bool + if v, ok := d.GetOk("deletion_protection_enabled"); ok { + deletionProtectionEnabled = v.(bool) + } else { + deletionProtectionEnabled = false + } + loggroup, err := findLogGroupByName(ctx, conn, d.Id()) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading CloudWatch Logs Log Group (%s): %s", d.Id(), err) + } + input := cloudwatchlogs.PutLogGroupDeletionProtectionInput{ + LogGroupIdentifier: loggroup.LogGroupArn, + DeletionProtectionEnabled: aws.Bool(deletionProtectionEnabled), + } + + _, err = conn.PutLogGroupDeletionProtection(ctx, &input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating CloudWatch Logs Log Group (%s) deletion protection: %s", d.Id(), err) + } + } + if d.HasChange(names.AttrKMSKeyID) { if v, ok := d.GetOk(names.AttrKMSKeyID); ok { input := cloudwatchlogs.AssociateKmsKeyInput{ @@ -384,6 +416,7 @@ func (l *logGroupListResource) List(ctx context.Context, request list.ListReques func resourceGroupFlatten(_ context.Context, d *schema.ResourceData, lg awstypes.LogGroup) { d.Set(names.AttrARN, trimLogGroupARNWildcardSuffix(aws.ToString(lg.Arn))) + d.Set("deletion_protection_enabled", lg.DeletionProtectionEnabled) d.Set(names.AttrKMSKeyID, lg.KmsKeyId) d.Set("log_group_class", lg.LogGroupClass) d.Set(names.AttrName, lg.LogGroupName) diff --git a/internal/service/logs/group_data_source.go b/internal/service/logs/group_data_source.go index c8de7c9ee731..377dc91c05ee 100644 --- a/internal/service/logs/group_data_source.go +++ b/internal/service/logs/group_data_source.go @@ -30,6 +30,10 @@ func dataSourceGroup() *schema.Resource { Type: schema.TypeInt, Computed: true, }, + "deletion_protection_enabled": { + Type: schema.TypeBool, + Computed: true, + }, names.AttrKMSKeyID: { Type: schema.TypeString, Computed: true, @@ -65,6 +69,7 @@ func dataSourceGroupRead(ctx context.Context, d *schema.ResourceData, meta any) d.SetId(name) d.Set(names.AttrARN, trimLogGroupARNWildcardSuffix(aws.ToString(logGroup.Arn))) d.Set(names.AttrCreationTime, logGroup.CreationTime) + d.Set("deletion_protection_enabled", logGroup.DeletionProtectionEnabled) d.Set(names.AttrKMSKeyID, logGroup.KmsKeyId) d.Set("log_group_class", logGroup.LogGroupClass) d.Set("retention_in_days", logGroup.RetentionInDays) diff --git a/internal/service/logs/group_data_source_test.go b/internal/service/logs/group_data_source_test.go index 4f10cade7f1b..b9715c4a96a2 100644 --- a/internal/service/logs/group_data_source_test.go +++ b/internal/service/logs/group_data_source_test.go @@ -28,6 +28,7 @@ func TestAccLogsGroupDataSource_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(dataSourceName, names.AttrARN, resourceName, names.AttrARN), resource.TestCheckResourceAttrSet(dataSourceName, names.AttrCreationTime), + resource.TestCheckResourceAttrPair(dataSourceName, "deletion_protection_enabled", resourceName, "deletion_protection_enabled"), resource.TestCheckResourceAttrPair(dataSourceName, names.AttrKMSKeyID, resourceName, names.AttrKMSKeyID), resource.TestCheckResourceAttrPair(dataSourceName, "log_group_class", resourceName, "log_group_class"), resource.TestCheckResourceAttrPair(dataSourceName, names.AttrName, resourceName, names.AttrName), diff --git a/internal/service/logs/group_test.go b/internal/service/logs/group_test.go index 017c21cc5551..4fa8cbf2b396 100644 --- a/internal/service/logs/group_test.go +++ b/internal/service/logs/group_test.go @@ -769,6 +769,37 @@ func TestAccLogsLogGroup_requiredTags_disabled(t *testing.T) { }) } +func TestAccLogsLogGroup_deletionProtectionEnabled(t *testing.T) { + ctx := acctest.Context(t) + var v types.LogGroup + rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix) + resourceName := "aws_cloudwatch_log_group.test" + + acctest.ParallelTest(ctx, t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckLogGroupDestroy(ctx, t), + Steps: []resource.TestStep{ + { + Config: testAccGroupConfig_deletionProtectionEnabled(rName, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckLogGroupExists(ctx, t, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "deletion_protection_enabled", acctest.CtTrue), + ), + }, + { + // Disable deletion protection + Config: testAccGroupConfig_deletionProtectionEnabled(rName, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckLogGroupExists(ctx, t, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "deletion_protection_enabled", acctest.CtFalse), + ), + }, + }, + }) +} + func testAccCheckLogGroupExists(ctx context.Context, t *testing.T, n string, v *types.LogGroup) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -953,3 +984,13 @@ resource "aws_cloudwatch_log_group" "test" { } `, rName, key1, value1) } + +func testAccGroupConfig_deletionProtectionEnabled(rName string, deletionProtectionEnabled bool) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_log_group" "test" { + name = %[1]q + + deletion_protection_enabled = %[2]t +} +`, rName, deletionProtectionEnabled) +} diff --git a/website/docs/d/cloudwatch_log_group.html.markdown b/website/docs/d/cloudwatch_log_group.html.markdown index b861dbe54ce4..d5bce5a844dd 100644 --- a/website/docs/d/cloudwatch_log_group.html.markdown +++ b/website/docs/d/cloudwatch_log_group.html.markdown @@ -31,6 +31,7 @@ This data source exports the following attributes in addition to the arguments a * `arn` - ARN of the Cloudwatch log group. Any `:*` suffix added by the API, denoting all CloudWatch Log Streams under the CloudWatch Log Group, is removed for greater compatibility with other AWS services that do not accept the suffix. * `creation_time` - Creation time of the log group, expressed as the number of milliseconds after Jan 1, 1970 00:00:00 UTC. +* `deletion_protection_enabled` - Boolean to indicate whether deletion protection is enabled. * `kms_key_id` - ARN of the KMS Key to use when encrypting log data. * `log_group_class` - The log class of the log group. * `retention_in_days` - Number of days log events retained in the specified log group. diff --git a/website/docs/r/cloudwatch_log_group.html.markdown b/website/docs/r/cloudwatch_log_group.html.markdown index 4b2592b5f8ba..bbd7c0b8d5e4 100644 --- a/website/docs/r/cloudwatch_log_group.html.markdown +++ b/website/docs/r/cloudwatch_log_group.html.markdown @@ -31,6 +31,7 @@ This resource supports the following arguments: * `name` - (Optional, Forces new resource) The name of the log group. If omitted, Terraform will assign a random, unique name. * `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `skip_destroy` - (Optional) Set to true if you do not wish the log group (and any logs it may contain) to be deleted at destroy time, and instead just remove the log group from the Terraform state. +* `deletion_protection_enabled` – (Optional) Boolean to indicate whether deletion protection is enabled. Defaults to `false`. Once set, switching to `false` requires explicitly specifying `false` rather than removing this argument. * `log_group_class` - (Optional) Specified the log class of the log group. Possible values are: `STANDARD`, `INFREQUENT_ACCESS`, or `DELIVERY`. * `retention_in_days` - (Optional) Specifies the number of days you want to retain log events in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1096, 1827, 2192, 2557, 2922, 3288, 3653, and 0.