diff --git a/.changelog/18006.txt b/.changelog/18006.txt new file mode 100644 index 000000000000..544efe7c5488 --- /dev/null +++ b/.changelog/18006.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_efs_backup_policy +``` \ No newline at end of file diff --git a/aws/internal/service/efs/finder/finder.go b/aws/internal/service/efs/finder/finder.go new file mode 100644 index 000000000000..244884f792da --- /dev/null +++ b/aws/internal/service/efs/finder/finder.go @@ -0,0 +1,36 @@ +package finder + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/efs" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func BackupPolicyByID(conn *efs.EFS, id string) (*efs.BackupPolicy, error) { + input := &efs.DescribeBackupPolicyInput{ + FileSystemId: aws.String(id), + } + + output, err := conn.DescribeBackupPolicy(input) + + if tfawserr.ErrCodeEquals(err, efs.ErrCodeFileSystemNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.BackupPolicy == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.BackupPolicy, nil +} diff --git a/aws/internal/service/efs/waiter/status.go b/aws/internal/service/efs/waiter/status.go index 97d4f7d1612d..b539a8be0b52 100644 --- a/aws/internal/service/efs/waiter/status.go +++ b/aws/internal/service/efs/waiter/status.go @@ -4,6 +4,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/efs" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/efs/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) // AccessPointLifeCycleState fetches the Access Point and its LifecycleState @@ -29,6 +31,22 @@ func AccessPointLifeCycleState(conn *efs.EFS, accessPointId string) resource.Sta } } +func BackupPolicyStatus(conn *efs.EFS, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.BackupPolicyByID(conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Status), nil + } +} + // FileSystemLifeCycleState fetches the Access Point and its LifecycleState func FileSystemLifeCycleState(conn *efs.EFS, fileSystemID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { diff --git a/aws/internal/service/efs/waiter/waiter.go b/aws/internal/service/efs/waiter/waiter.go index fd065e5c89d4..2e18ddccc0ca 100644 --- a/aws/internal/service/efs/waiter/waiter.go +++ b/aws/internal/service/efs/waiter/waiter.go @@ -17,6 +17,9 @@ const ( FileSystemDeletedTimeout = 10 * time.Minute FileSystemDeletedDelayTimeout = 2 * time.Second FileSystemDeletedMinTimeout = 3 * time.Second + + BackupPolicyDisabledTimeout = 10 * time.Minute + BackupPolicyEnabledTimeout = 10 * time.Minute ) // AccessPointCreated waits for an Operation to return Success @@ -37,7 +40,7 @@ func AccessPointCreated(conn *efs.EFS, accessPointId string) (*efs.AccessPointDe return nil, err } -// AccessPointDelete waits for an Access Point to return Deleted +// AccessPointDeleted waits for an Access Point to return Deleted func AccessPointDeleted(conn *efs.EFS, accessPointId string) (*efs.AccessPointDescription, error) { stateConf := &resource.StateChangeConf{ Pending: []string{efs.LifeCycleStateAvailable, efs.LifeCycleStateDeleting, efs.LifeCycleStateDeleted}, @@ -94,3 +97,37 @@ func FileSystemDeleted(conn *efs.EFS, fileSystemID string) (*efs.FileSystemDescr return nil, err } + +func BackupPolicyDisabled(conn *efs.EFS, id string) (*efs.BackupPolicy, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{efs.StatusDisabling}, + Target: []string{efs.StatusDisabled}, + Refresh: BackupPolicyStatus(conn, id), + Timeout: BackupPolicyDisabledTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*efs.BackupPolicy); ok { + return output, err + } + + return nil, err +} + +func BackupPolicyEnabled(conn *efs.EFS, id string) (*efs.BackupPolicy, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{efs.StatusEnabling}, + Target: []string{efs.StatusEnabled}, + Refresh: BackupPolicyStatus(conn, id), + Timeout: BackupPolicyEnabledTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*efs.BackupPolicy); ok { + return output, err + } + + return nil, err +} diff --git a/aws/provider.go b/aws/provider.go index d0d6e9b9846d..1954a1bc0ebe 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -713,6 +713,7 @@ func Provider() *schema.Provider { "aws_ecs_service": resourceAwsEcsService(), "aws_ecs_task_definition": resourceAwsEcsTaskDefinition(), "aws_efs_access_point": resourceAwsEfsAccessPoint(), + "aws_efs_backup_policy": resourceAwsEfsBackupPolicy(), "aws_efs_file_system": resourceAwsEfsFileSystem(), "aws_efs_file_system_policy": resourceAwsEfsFileSystemPolicy(), "aws_efs_mount_target": resourceAwsEfsMountTarget(), diff --git a/aws/resource_aws_backup_vault.go b/aws/resource_aws_backup_vault.go index 2b15de218ad1..ec0018f6da80 100644 --- a/aws/resource_aws_backup_vault.go +++ b/aws/resource_aws_backup_vault.go @@ -139,13 +139,13 @@ func resourceAwsBackupVaultUpdate(d *schema.ResourceData, meta interface{}) erro func resourceAwsBackupVaultDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).backupconn - input := &backup.DeleteBackupVaultInput{ + log.Printf("[DEBUG] Deleting Backup Vault: %s", d.Id()) + _, err := conn.DeleteBackupVault(&backup.DeleteBackupVaultInput{ BackupVaultName: aws.String(d.Id()), - } + }) - _, err := conn.DeleteBackupVault(input) if err != nil { - return fmt.Errorf("error deleting Backup Vault (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Backup Vault (%s): %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_backup_vault_test.go b/aws/resource_aws_backup_vault_test.go index fa9e1f2726ca..5d2ad43ed483 100644 --- a/aws/resource_aws_backup_vault_test.go +++ b/aws/resource_aws_backup_vault_test.go @@ -3,12 +3,11 @@ package aws import ( "fmt" "log" - "strings" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/backup" - "github.com/hashicorp/go-multierror" + multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -31,12 +30,10 @@ func testSweepBackupVaults(region string) error { if err != nil { return fmt.Errorf("Error getting client: %w", err) } - conn := client.(*AWSClient).backupconn - sweepResources := make([]*testSweepResource, 0) - var errs *multierror.Error - input := &backup.ListBackupVaultsInput{} + var sweeperErrs *multierror.Error + sweepResources := make([]*testSweepResource, 0) err = conn.ListBackupVaultsPages(input, func(page *backup.ListBackupVaultsOutput, lastPage bool) bool { if page == nil { @@ -44,22 +41,48 @@ func testSweepBackupVaults(region string) error { } for _, vault := range page.BackupVaultList { - if vault == nil { - continue + failedToDeleteRecoveryPoint := false + name := aws.StringValue(vault.BackupVaultName) + input := &backup.ListRecoveryPointsByBackupVaultInput{ + BackupVaultName: aws.String(name), } - name := aws.StringValue(vault.BackupVaultName) + err := conn.ListRecoveryPointsByBackupVaultPages(input, func(page *backup.ListRecoveryPointsByBackupVaultOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, recoveryPoint := range page.RecoveryPoints { + arn := aws.StringValue(recoveryPoint.RecoveryPointArn) + + log.Printf("[INFO] Deleting Recovery Point (%s) in Backup Vault (%s)", arn, name) + _, err := conn.DeleteRecoveryPoint(&backup.DeleteRecoveryPointInput{ + BackupVaultName: aws.String(name), + RecoveryPointArn: aws.String(arn), + }) - // Ignore Default Backup Vault in region (cannot be deleted) - // and automated Backups that result in AccessDeniedException when deleted - if name == "Default" || strings.Contains(name, "automatic-backup-vault") { + if err != nil { + log.Printf("[WARN] Failed to delete Recovery Point (%s) in Backup Vault (%s): %s", arn, name, err) + failedToDeleteRecoveryPoint = true + } + } + + return !lastPage + }) + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Reovery Points in Backup Vault (%s) for %s: %w", name, region, err)) + } + + // Ignore Default and Automatic EFS Backup Vaults in region (cannot be deleted) + if name == "Default" || name == "aws/efs/automatic-backup-vault" { log.Printf("[INFO] Skipping Backup Vault: %s", name) continue } // Backup Vault deletion only supported when empty // Reference: https://docs.aws.amazon.com/aws-backup/latest/devguide/API_DeleteBackupVault.html - if aws.Int64Value(vault.NumberOfRecoveryPoints) != 0 { + if failedToDeleteRecoveryPoint { log.Printf("[INFO] Skipping Backup Vault (%s): not empty", name) continue } @@ -74,20 +97,20 @@ func testSweepBackupVaults(region string) error { return !lastPage }) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing Backup Vaults for %s: %w", region, err)) + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping Backup Vaults sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() } - if err := testSweepResourceOrchestrator(sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping Backup Vaults for %s: %w", region, err)) + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Backup Vaults for %s: %w", region, err)) } - if testSweepSkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping Backup Vaults sweep for %s: %s", region, errs) - return nil + if err := testSweepResourceOrchestrator(sweepResources); err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Backup Vaults for %s: %w", region, err)) } - return errs.ErrorOrNil() + return sweeperErrs.ErrorOrNil() } func TestAccAwsBackupVault_basic(t *testing.T) { diff --git a/aws/resource_aws_efs_backup_policy.go b/aws/resource_aws_efs_backup_policy.go new file mode 100644 index 000000000000..854e74cf4cd3 --- /dev/null +++ b/aws/resource_aws_efs_backup_policy.go @@ -0,0 +1,175 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/efs" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/efs/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/efs/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func resourceAwsEfsBackupPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsEfsBackupPolicyCreate, + Read: resourceAwsEfsBackupPolicyRead, + Update: resourceAwsEfsBackupPolicyUpdate, + Delete: resourceAwsEfsBackupPolicyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "backup_policy": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "status": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + efs.StatusDisabled, + efs.StatusEnabled, + }, false), + }, + }, + }, + }, + + "file_system_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsEfsBackupPolicyCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).efsconn + + fsID := d.Get("file_system_id").(string) + + if err := efsBackupPolicyPut(conn, fsID, d.Get("backup_policy").([]interface{})[0].(map[string]interface{})); err != nil { + return err + } + + d.SetId(fsID) + + return resourceAwsEfsBackupPolicyRead(d, meta) +} + +func resourceAwsEfsBackupPolicyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).efsconn + + output, err := finder.BackupPolicyByID(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EFS Backup Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading EFS Backup Policy (%s): %w", d.Id(), err) + } + + if err := d.Set("backup_policy", []interface{}{flattenEfsBackupPolicy(output)}); err != nil { + return fmt.Errorf("error setting backup_policy: %w", err) + } + + d.Set("file_system_id", d.Id()) + + return nil +} + +func resourceAwsEfsBackupPolicyUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).efsconn + + if err := efsBackupPolicyPut(conn, d.Id(), d.Get("backup_policy").([]interface{})[0].(map[string]interface{})); err != nil { + return err + } + + return resourceAwsEfsBackupPolicyRead(d, meta) +} + +func resourceAwsEfsBackupPolicyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).efsconn + + err := efsBackupPolicyPut(conn, d.Id(), map[string]interface{}{ + "status": efs.StatusDisabled, + }) + + if tfawserr.ErrCodeEquals(err, efs.ErrCodeFileSystemNotFound) { + return nil + } + + if err != nil { + return err + } + + return nil +} + +// efsBackupPolicyPut attempts to update the file system's backup policy. +// Any error is returned. +func efsBackupPolicyPut(conn *efs.EFS, fsID string, tfMap map[string]interface{}) error { + input := &efs.PutBackupPolicyInput{ + BackupPolicy: expandEfsBackupPolicy(tfMap), + FileSystemId: aws.String(fsID), + } + + log.Printf("[DEBUG] Putting EFS Backup Policy: %s", input) + _, err := conn.PutBackupPolicy(input) + + if err != nil { + return fmt.Errorf("error putting EFS Backup Policy (%s): %w", fsID, err) + } + + if aws.StringValue(input.BackupPolicy.Status) == efs.StatusEnabled { + if _, err := waiter.BackupPolicyEnabled(conn, fsID); err != nil { + return fmt.Errorf("error waiting for EFS Backup Policy (%s) to enable: %w", fsID, err) + } + } else { + if _, err := waiter.BackupPolicyDisabled(conn, fsID); err != nil { + return fmt.Errorf("error waiting for EFS Backup Policy (%s) to disable: %w", fsID, err) + } + } + + return nil +} + +func expandEfsBackupPolicy(tfMap map[string]interface{}) *efs.BackupPolicy { + if tfMap == nil { + return nil + } + + apiObject := &efs.BackupPolicy{} + + if v, ok := tfMap["status"].(string); ok && v != "" { + apiObject.Status = aws.String(v) + } + + return apiObject +} + +func flattenEfsBackupPolicy(apiObject *efs.BackupPolicy) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Status; v != nil { + tfMap["status"] = aws.StringValue(v) + } + + return tfMap +} diff --git a/aws/resource_aws_efs_backup_policy_test.go b/aws/resource_aws_efs_backup_policy_test.go new file mode 100644 index 000000000000..b45e1dfef24e --- /dev/null +++ b/aws/resource_aws_efs_backup_policy_test.go @@ -0,0 +1,179 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/efs" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/efs/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func TestAccAWSEFSBackupPolicy_basic(t *testing.T) { + var v efs.BackupPolicy + resourceName := "aws_efs_backup_policy.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, efs.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckEfsBackupPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEFSBackupPolicyConfig(rName, "ENABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckEFSBackupPolicyExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "backup_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "backup_policy.0.status", "ENABLED"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEFSBackupPolicy_disappears_fs(t *testing.T) { + var v efs.BackupPolicy + resourceName := "aws_efs_backup_policy.test" + fsResourceName := "aws_efs_file_system.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, efs.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckEfsBackupPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEFSBackupPolicyConfig(rName, "ENABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckEFSBackupPolicyExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsEfsFileSystem(), fsResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSEFSBackupPolicy_update(t *testing.T) { + var v efs.BackupPolicy + resourceName := "aws_efs_backup_policy.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, efs.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckEfsBackupPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEFSBackupPolicyConfig(rName, "DISABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckEFSBackupPolicyExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "backup_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "backup_policy.0.status", "DISABLED"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSEFSBackupPolicyConfig(rName, "ENABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckEFSBackupPolicyExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "backup_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "backup_policy.0.status", "ENABLED"), + ), + }, + { + Config: testAccAWSEFSBackupPolicyConfig(rName, "DISABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckEFSBackupPolicyExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "backup_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "backup_policy.0.status", "DISABLED"), + ), + }, + }, + }) +} + +func testAccCheckEFSBackupPolicyExists(name string, v *efs.BackupPolicy) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).efsconn + + output, err := finder.BackupPolicyByID(conn, rs.Primary.ID) + + if err != nil { + return err + } + + *v = *output + + return nil + } +} + +func testAccCheckEfsBackupPolicyDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).efsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_efs_backup_policy" { + continue + } + + output, err := finder.BackupPolicyByID(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + if aws.StringValue(output.Status) == efs.StatusDisabled { + continue + } + + return fmt.Errorf("Transfer Server %s still exists", rs.Primary.ID) + } + + return nil +} + +func testAccAWSEFSBackupPolicyConfig(rName, status string) string { + return fmt.Sprintf(` +resource "aws_efs_file_system" "test" { + creation_token = %[1]q +} + +resource "aws_efs_backup_policy" "test" { + file_system_id = aws_efs_file_system.test.id + + backup_policy { + status = %[2]q + } +} +`, rName, status) +} diff --git a/website/docs/r/efs_backup_policy.html.markdown b/website/docs/r/efs_backup_policy.html.markdown new file mode 100644 index 000000000000..bd10cec39104 --- /dev/null +++ b/website/docs/r/efs_backup_policy.html.markdown @@ -0,0 +1,54 @@ +--- +subcategory: "EFS" +layout: "aws" +page_title: "AWS: aws_efs_backup_policy" +description: |- + Provides an Elastic File System (EFS) Backup Policy resource. +--- + +# Resource: aws_efs_backup_policy + +Provides an Elastic File System (EFS) Backup Policy resource. +Backup policies turn automatic backups on or off for an existing file system. + +## Example Usage + +```terraform +resource "aws_efs_file_system" "fs" { + creation_token = "my-product" +} + +resource "aws_efs_backup_policy" "policy" { + file_system_id = aws_efs_file_system.fs.id + + backup_policy { + status = "ENABLED" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `file_system_id` - (Required) The ID of the EFS file system. +* `backup_policy` - (Required) A backup_policy object (documented below). + +### Backup Policy Arguments +For **backup_policy** the following attributes are supported: + +* `status` - (Required) A status of the backup policy. Valid values: `ENABLED`, `DISABLED`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID that identifies the file system (e.g. fs-ccfc0d65). + +## Import + +The EFS backup policies can be imported using the `id`, e.g. + +``` +$ terraform import aws_efs_backup_policy.example fs-6fa144c6 +```