diff --git a/.changelog/35029.txt b/.changelog/35029.txt new file mode 100644 index 000000000000..3123b6eb8c4d --- /dev/null +++ b/.changelog/35029.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_efs_file_system: Add `protection` configuration block +``` + +```release-note:enhancement +data-source/aws_efs_file_system: Add `protection` attribute +``` \ No newline at end of file diff --git a/internal/service/efs/file_system.go b/internal/service/efs/file_system.go index da404602c99c..41d216f3c5ea 100644 --- a/internal/service/efs/file_system.go +++ b/internal/service/efs/file_system.go @@ -125,6 +125,25 @@ func ResourceFileSystem() *schema.Resource { ForceNew: true, ValidateFunc: validation.StringInSlice(efs.PerformanceMode_Values(), false), }, + "protection": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "replication_overwrite": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + efs.ReplicationOverwriteProtectionEnabled, + efs.ReplicationOverwriteProtectionDisabled, + }, false), + }, + }, + }, + }, "provisioned_throughput_in_mibps": { Type: schema.TypeFloat, Optional: true, @@ -219,16 +238,28 @@ func resourceFileSystemCreate(ctx context.Context, d *schema.ResourceData, meta } if v, ok := d.GetOk("lifecycle_policy"); ok { - _, err := conn.PutLifecycleConfigurationWithContext(ctx, &efs.PutLifecycleConfigurationInput{ + input := &efs.PutLifecycleConfigurationInput{ FileSystemId: aws.String(d.Id()), LifecyclePolicies: expandFileSystemLifecyclePolicies(v.([]interface{})), - }) + } + + _, err := conn.PutLifecycleConfigurationWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "putting EFS file system (%s) lifecycle configuration: %s", d.Id(), err) } } + if v, ok := d.GetOk("protection"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input := expandUpdateFileSystemProtectionInput(d.Id(), v.([]interface{})[0].(map[string]interface{})) + + _, err := conn.UpdateFileSystemProtectionWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating EFS file system (%s) protection: %s", d.Id(), err) + } + } + return append(diags, resourceFileSystemRead(ctx, d, meta)...) } @@ -260,6 +291,9 @@ func resourceFileSystemRead(ctx context.Context, d *schema.ResourceData, meta in d.Set("number_of_mount_targets", fs.NumberOfMountTargets) d.Set("owner_id", fs.OwnerId) d.Set("performance_mode", fs.PerformanceMode) + if err := d.Set("protection", flattenFileSystemProtection(fs.FileSystemProtection)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting protection: %s", err) + } d.Set("provisioned_throughput_in_mibps", fs.ProvisionedThroughputInMibps) if err := d.Set("size_in_bytes", flattenFileSystemSizeInBytes(fs.SizeInBytes)); err != nil { return sdkdiag.AppendErrorf(diags, "setting size_in_bytes: %s", err) @@ -330,6 +364,18 @@ func resourceFileSystemUpdate(ctx context.Context, d *schema.ResourceData, meta } } + if d.HasChanges("protection") { + if v, ok := d.GetOk("protection"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input := expandUpdateFileSystemProtectionInput(d.Id(), v.([]interface{})[0].(map[string]interface{})) + + _, err := conn.UpdateFileSystemProtectionWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating EFS file system (%s) protection: %s", d.Id(), err) + } + } + } + return append(diags, resourceFileSystemRead(ctx, d, meta)...) } @@ -547,3 +593,33 @@ func flattenFileSystemSizeInBytes(sizeInBytes *efs.FileSystemSize) []interface{} return []interface{}{m} } + +func expandUpdateFileSystemProtectionInput(id string, tfMap map[string]interface{}) *efs.UpdateFileSystemProtectionInput { + if tfMap == nil { + return nil + } + + apiObject := &efs.UpdateFileSystemProtectionInput{ + FileSystemId: aws.String(id), + } + + if v, ok := tfMap["replication_overwrite"].(string); ok && v != "" { + apiObject.ReplicationOverwriteProtection = aws.String(v) + } + + return apiObject +} + +func flattenFileSystemProtection(protection *efs.FileSystemProtectionDescription) []interface{} { + if protection == nil { + return []interface{}{} + } + + tfMap := map[string]interface{}{} + + if protection.ReplicationOverwriteProtection != nil { + tfMap["replication_overwrite"] = aws.StringValue(protection.ReplicationOverwriteProtection) + } + + return []interface{}{tfMap} +} diff --git a/internal/service/efs/file_system_data_source.go b/internal/service/efs/file_system_data_source.go index ff614d0a350a..f360fa3ce70c 100644 --- a/internal/service/efs/file_system_data_source.go +++ b/internal/service/efs/file_system_data_source.go @@ -88,6 +88,18 @@ func DataSourceFileSystem() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "protection": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "replication_overwrite": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, "provisioned_throughput_in_mibps": { Type: schema.TypeFloat, Computed: true, @@ -145,6 +157,9 @@ func dataSourceFileSystemRead(ctx context.Context, d *schema.ResourceData, meta d.Set("kms_key_id", fs.KmsKeyId) d.Set("name", fs.Name) d.Set("performance_mode", fs.PerformanceMode) + if err := d.Set("protection", flattenFileSystemProtection(fs.FileSystemProtection)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting protection: %s", err) + } d.Set("provisioned_throughput_in_mibps", fs.ProvisionedThroughputInMibps) if fs.SizeInBytes != nil { d.Set("size_in_bytes", fs.SizeInBytes.Value) diff --git a/internal/service/efs/file_system_data_source_test.go b/internal/service/efs/file_system_data_source_test.go index d4913f6ef01e..40e408616342 100644 --- a/internal/service/efs/file_system_data_source_test.go +++ b/internal/service/efs/file_system_data_source_test.go @@ -39,6 +39,7 @@ func TestAccEFSFileSystemDataSource_id(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName, "throughput_mode", resourceName, "throughput_mode"), resource.TestCheckResourceAttrPair(dataSourceName, "lifecycle_policy", resourceName, "lifecycle_policy"), resource.TestMatchResourceAttr(dataSourceName, "size_in_bytes", regexache.MustCompile(`^\d+$`)), + resource.TestCheckResourceAttrPair(dataSourceName, "protection", resourceName, "protection"), ), }, }, @@ -70,6 +71,7 @@ func TestAccEFSFileSystemDataSource_tags(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName, "throughput_mode", resourceName, "throughput_mode"), resource.TestCheckResourceAttrPair(dataSourceName, "lifecycle_policy", resourceName, "lifecycle_policy"), resource.TestMatchResourceAttr(dataSourceName, "size_in_bytes", regexache.MustCompile(`^\d+$`)), + resource.TestCheckResourceAttrPair(dataSourceName, "protection", resourceName, "protection"), ), }, }, @@ -100,6 +102,7 @@ func TestAccEFSFileSystemDataSource_name(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName, "throughput_mode", resourceName, "throughput_mode"), resource.TestCheckResourceAttrPair(dataSourceName, "lifecycle_policy", resourceName, "lifecycle_policy"), resource.TestMatchResourceAttr(dataSourceName, "size_in_bytes", regexache.MustCompile(`^\d+$`)), + resource.TestCheckResourceAttrPair(dataSourceName, "protection", resourceName, "protection"), ), }, }, diff --git a/internal/service/efs/file_system_test.go b/internal/service/efs/file_system_test.go index 2f62d88e30f2..2f65f69183a9 100644 --- a/internal/service/efs/file_system_test.go +++ b/internal/service/efs/file_system_test.go @@ -43,6 +43,8 @@ func TestAccEFSFileSystem_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "number_of_mount_targets", "0"), acctest.MatchResourceAttrAccountID(resourceName, "owner_id"), resource.TestCheckResourceAttr(resourceName, "performance_mode", "generalPurpose"), + resource.TestCheckResourceAttr(resourceName, "protection.#", "1"), + resource.TestCheckResourceAttr(resourceName, "protection.0.replication_overwrite", "ENABLED"), resource.TestCheckResourceAttr(resourceName, "size_in_bytes.#", "1"), resource.TestCheckResourceAttrSet(resourceName, "size_in_bytes.0.value"), resource.TestCheckResourceAttrSet(resourceName, "size_in_bytes.0.value_in_ia"), @@ -110,6 +112,41 @@ func TestAccEFSFileSystem_performanceMode(t *testing.T) { }) } +func TestAccEFSFileSystem_protection(t *testing.T) { + ctx := acctest.Context(t) + var desc efs.FileSystemDescription + resourceName := "aws_efs_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, efs.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFileSystemDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFileSystemConfig_protection("DISABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFileSystem(ctx, resourceName, &desc), + resource.TestCheckResourceAttr(resourceName, "protection.#", "1"), + resource.TestCheckResourceAttr(resourceName, "protection.0.replication_overwrite", "DISABLED"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccFileSystemConfig_protection("ENABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFileSystem(ctx, resourceName, &desc), + resource.TestCheckResourceAttr(resourceName, "protection.0.replication_overwrite", "ENABLED"), + ), + }, + }, + }) +} + func TestAccEFSFileSystem_availabilityZoneName(t *testing.T) { ctx := acctest.Context(t) var desc efs.FileSystemDescription @@ -477,6 +514,16 @@ resource "aws_efs_file_system" "test" { } ` +func testAccFileSystemConfig_protection(replicationOverwwrite string) string { + return fmt.Sprintf(` +resource "aws_efs_file_system" "test" { + protection { + replication_overwrite = %[1]q + } +} +`, replicationOverwwrite) +} + func testAccFileSystemConfig_availabilityZoneName(rName string) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_efs_file_system" "test" { diff --git a/website/docs/r/efs_file_system.html.markdown b/website/docs/r/efs_file_system.html.markdown index d3791a33b311..6b8dfea9c0c9 100644 --- a/website/docs/r/efs_file_system.html.markdown +++ b/website/docs/r/efs_file_system.html.markdown @@ -48,6 +48,7 @@ user guide for more information. * `encrypted` - (Optional) If true, the disk will be encrypted. * `kms_key_id` - (Optional) The ARN for the KMS encryption key. When specifying kms_key_id, encrypted needs to be set to true. * `lifecycle_policy` - (Optional) A file system [lifecycle policy](https://docs.aws.amazon.com/efs/latest/ug/API_LifecyclePolicy.html) object (documented below). +* `protection` - (Optional) A file system [protection](https://docs.aws.amazon.com/efs/latest/ug/API_FileSystemProtectionDescription.html) object (documented below). * `performance_mode` - (Optional) The file system performance mode. Can be either `"generalPurpose"` or `"maxIO"` (Default: `"generalPurpose"`). * `provisioned_throughput_in_mibps` - (Optional) The throughput, measured in MiB/s, that you want to provision for the file system. Only applicable with `throughput_mode` set to `provisioned`. * `tags` - (Optional) A map of tags to assign to the file system. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. @@ -61,6 +62,12 @@ user guide for more information. * `transition_to_primary_storage_class` - (Optional) Describes the policy used to transition a file from infequent access storage to primary storage. Valid values: `AFTER_1_ACCESS`. * `transition_to_archive` - (Optional) Indicates how long it takes to transition files to the archive storage class. Requires transition_to_ia, Elastic Throughput and General Purpose performance mode. Valid values: `AFTER_1_DAY`, `AFTER_7_DAYS`, `AFTER_14_DAYS`, `AFTER_30_DAYS`, `AFTER_60_DAYS`, or `AFTER_90_DAYS`. +### Protection Arguments + +`protection` supports the following arguments: + +* `replication_overwrite` - (Optional) Indicates whether replication overwrite protection is enabled. Valid values: `ENABLED` or `DISABLED`. + ## Attribute Reference This resource exports the following attributes in addition to the arguments above: