diff --git a/.changelog/36810.txt b/.changelog/36810.txt new file mode 100644 index 000000000000..8edfb825f20c --- /dev/null +++ b/.changelog/36810.txt @@ -0,0 +1,6 @@ +```release-note:new-resource +aws_redshift_snapshot_copy +``` +```release-note:note +resource/aws_redshift_cluster: The `snapshot_copy` argument is now deprecated. Use the `aws_redshift_snapshot_copy` resource instead. +``` diff --git a/internal/service/redshift/cluster.go b/internal/service/redshift/cluster.go index e13b8c0ae83b..1120218e940d 100644 --- a/internal/service/redshift/cluster.go +++ b/internal/service/redshift/cluster.go @@ -363,9 +363,12 @@ func ResourceCluster() *schema.Resource { ForceNew: true, }, "snapshot_copy": { - Type: schema.TypeList, + Type: schema.TypeList, + Deprecated: "Use the aws_redshift_snapshot_copy resource instead. " + + "This argument will be removed in a future major version.", MaxItems: 1, Optional: true, + Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "destination_region": { diff --git a/internal/service/redshift/exports_test.go b/internal/service/redshift/exports_test.go index 4b45ada77c43..70b2b8b16d5a 100644 --- a/internal/service/redshift/exports_test.go +++ b/internal/service/redshift/exports_test.go @@ -7,7 +7,9 @@ package redshift var ( ResourceDataShareAuthorization = newResourceDataShareAuthorization ResourceDataShareConsumerAssociation = newResourceDataShareConsumerAssociation + ResourceSnapshotCopy = newResourceSnapshotCopy FindDataShareAuthorizationByID = findDataShareAuthorizationByID FindDataShareConsumerAssociationByID = findDataShareConsumerAssociationByID + FindSnapshotCopyByID = findSnapshotCopyByID ) diff --git a/internal/service/redshift/service_package_gen.go b/internal/service/redshift/service_package_gen.go index 86dc7ec4a5ad..d57a3737be8b 100644 --- a/internal/service/redshift/service_package_gen.go +++ b/internal/service/redshift/service_package_gen.go @@ -40,6 +40,10 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic Factory: newResourceDataShareConsumerAssociation, Name: "Data Share Consumer Association", }, + { + Factory: newResourceSnapshotCopy, + Name: "Snapshot Copy", + }, } } diff --git a/internal/service/redshift/snapshot_copy.go b/internal/service/redshift/snapshot_copy.go new file mode 100644 index 000000000000..a4869c7532f2 --- /dev/null +++ b/internal/service/redshift/snapshot_copy.go @@ -0,0 +1,265 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package redshift + +import ( + "context" + "errors" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" + awstypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource(name="Snapshot Copy") +func newResourceSnapshotCopy(_ context.Context) (resource.ResourceWithConfigure, error) { + return &resourceSnapshotCopy{}, nil +} + +const ( + ResNameSnapshotCopy = "Snapshot Copy" +) + +type resourceSnapshotCopy struct { + framework.ResourceWithConfigure +} + +func (r *resourceSnapshotCopy) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_redshift_snapshot_copy" +} + +func (r *resourceSnapshotCopy) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "cluster_identifier": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "destination_region": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "id": framework.IDAttribute(), + "manual_snapshot_retention_period": schema.Int64Attribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + int64planmodifier.UseStateForUnknown(), + }, + }, + "retention_period": schema.Int64Attribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + "snapshot_copy_grant_name": schema.StringAttribute{ + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + } +} + +func (r *resourceSnapshotCopy) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().RedshiftClient(ctx) + + var plan resourceSnapshotCopyData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + plan.ID = types.StringValue(plan.ClusterIdentifier.ValueString()) + + in := &redshift.EnableSnapshotCopyInput{} + resp.Diagnostics.Append(flex.Expand(ctx, plan, in)...) + if resp.Diagnostics.HasError() { + return + } + + out, err := conn.EnableSnapshotCopy(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Redshift, create.ErrActionCreating, ResNameSnapshotCopy, plan.ClusterIdentifier.String(), err), + err.Error(), + ) + return + } + if out == nil || out.Cluster == nil || out.Cluster.ClusterSnapshotCopyStatus == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Redshift, create.ErrActionCreating, ResNameSnapshotCopy, plan.ClusterIdentifier.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, out.Cluster.ClusterSnapshotCopyStatus, &plan)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourceSnapshotCopy) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().RedshiftClient(ctx) + + var state resourceSnapshotCopyData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + out, err := findSnapshotCopyByID(ctx, conn, state.ID.ValueString()) + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Redshift, create.ErrActionSetting, ResNameSnapshotCopy, state.ID.String(), err), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceSnapshotCopy) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + conn := r.Meta().RedshiftClient(ctx) + + var plan, state resourceSnapshotCopyData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if !plan.RetentionPeriod.Equal(state.RetentionPeriod) { + in := &redshift.ModifySnapshotCopyRetentionPeriodInput{ + ClusterIdentifier: aws.String(plan.ClusterIdentifier.ValueString()), + RetentionPeriod: aws.Int32(int32(plan.RetentionPeriod.ValueInt64())), + } + + out, err := conn.ModifySnapshotCopyRetentionPeriod(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Redshift, create.ErrActionUpdating, ResNameSnapshotCopy, plan.ID.String(), err), + err.Error(), + ) + return + } + if out == nil || out.Cluster == nil || out.Cluster.ClusterSnapshotCopyStatus == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Redshift, create.ErrActionUpdating, ResNameSnapshotCopy, plan.ID.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, out.Cluster.ClusterSnapshotCopyStatus, &plan)...) + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourceSnapshotCopy) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().RedshiftClient(ctx) + + var state resourceSnapshotCopyData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + in := &redshift.DisableSnapshotCopyInput{ + ClusterIdentifier: aws.String(state.ID.ValueString()), + } + + _, err := conn.DisableSnapshotCopy(ctx, in) + if err != nil { + if errs.IsA[*awstypes.ClusterNotFoundFault](err) { + return + } else if errs.IsA[*awstypes.SnapshotCopyAlreadyDisabledFault](err) { + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Redshift, create.ErrActionDeleting, ResNameSnapshotCopy, state.ID.String(), err), + err.Error(), + ) + return + } +} + +func (r *resourceSnapshotCopy) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), req.ID)...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cluster_identifier"), req.ID)...) +} + +func findSnapshotCopyByID(ctx context.Context, conn *redshift.Client, id string) (*awstypes.ClusterSnapshotCopyStatus, error) { + in := &redshift.DescribeClustersInput{ + ClusterIdentifier: aws.String(id), + } + + out, err := conn.DescribeClusters(ctx, in) + if err != nil { + if errs.IsA[*awstypes.ClusterNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + return nil, err + } + + if out == nil { + return nil, tfresource.NewEmptyResultError(in) + } + // API should return a ClusterNotFound fault in this case, but check length for + // extra safety + if len(out.Clusters) == 0 { + return nil, &retry.NotFoundError{ + LastError: errors.New("not found"), + LastRequest: in, + } + } + if out.Clusters[0].ClusterSnapshotCopyStatus == nil { + return nil, &retry.NotFoundError{ + LastError: errors.New("snapshot copy not enabled"), + LastRequest: in, + } + } + + return out.Clusters[0].ClusterSnapshotCopyStatus, nil +} + +type resourceSnapshotCopyData struct { + ID types.String `tfsdk:"id"` + ClusterIdentifier types.String `tfsdk:"cluster_identifier"` + DestinationRegion types.String `tfsdk:"destination_region"` + ManualSnapshotRetentionPeriod types.Int64 `tfsdk:"manual_snapshot_retention_period"` + RetentionPeriod types.Int64 `tfsdk:"retention_period"` + SnapshotCopyGrantName types.String `tfsdk:"snapshot_copy_grant_name"` +} diff --git a/internal/service/redshift/snapshot_copy_test.go b/internal/service/redshift/snapshot_copy_test.go new file mode 100644 index 000000000000..df2e5c56f211 --- /dev/null +++ b/internal/service/redshift/snapshot_copy_test.go @@ -0,0 +1,261 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package redshift_test + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/redshift/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + tfredshift "github.com/hashicorp/terraform-provider-aws/internal/service/redshift" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccRedshiftSnapshotCopy_basic(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var snap types.ClusterSnapshotCopyStatus + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_redshift_snapshot_copy.test" + clusterResourceName := "aws_redshift_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.RedshiftEndpointID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.RedshiftServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSnapshotCopyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSnapshotCopyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSnapshotCopyExists(ctx, resourceName, &snap), + resource.TestCheckResourceAttrPair(resourceName, "cluster_identifier", clusterResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "destination_region", acctest.AlternateRegion()), + resource.TestCheckResourceAttr(resourceName, "retention_period", "7"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccRedshiftSnapshotCopy_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var snap types.ClusterSnapshotCopyStatus + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_redshift_snapshot_copy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.RedshiftEndpointID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.RedshiftServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSnapshotCopyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSnapshotCopyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSnapshotCopyExists(ctx, resourceName, &snap), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfredshift.ResourceSnapshotCopy, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccRedshiftSnapshotCopy_disappears_Cluster(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var snap types.ClusterSnapshotCopyStatus + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_redshift_snapshot_copy.test" + clusterResourceName := "aws_redshift_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.RedshiftEndpointID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.RedshiftServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSnapshotCopyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSnapshotCopyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSnapshotCopyExists(ctx, resourceName, &snap), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfredshift.ResourceCluster(), clusterResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccRedshiftSnapshotCopy_retentionPeriod(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var snap types.ClusterSnapshotCopyStatus + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_redshift_snapshot_copy.test" + clusterResourceName := "aws_redshift_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.RedshiftEndpointID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.RedshiftServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSnapshotCopyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSnapshotCopyConfig_retentionPeriod(rName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckSnapshotCopyExists(ctx, resourceName, &snap), + resource.TestCheckResourceAttrPair(resourceName, "cluster_identifier", clusterResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "destination_region", acctest.AlternateRegion()), + resource.TestCheckResourceAttr(resourceName, "retention_period", "10"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccSnapshotCopyConfig_retentionPeriod(rName, 20), + Check: resource.ComposeTestCheckFunc( + testAccCheckSnapshotCopyExists(ctx, resourceName, &snap), + resource.TestCheckResourceAttrPair(resourceName, "cluster_identifier", clusterResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "destination_region", acctest.AlternateRegion()), + resource.TestCheckResourceAttr(resourceName, "retention_period", "20"), + ), + }, + }, + }) +} + +func testAccCheckSnapshotCopyDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).RedshiftClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_redshift_snapshot_copy" { + continue + } + + _, err := tfredshift.FindSnapshotCopyByID(ctx, conn, rs.Primary.ID) + if errs.IsA[*retry.NotFoundError](err) { + return nil + } + if err != nil { + return create.Error(names.Redshift, create.ErrActionCheckingDestroyed, tfredshift.ResNameSnapshotCopy, rs.Primary.ID, err) + } + + return create.Error(names.Redshift, create.ErrActionCheckingDestroyed, tfredshift.ResNameSnapshotCopy, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} + +func testAccCheckSnapshotCopyExists(ctx context.Context, name string, snap *types.ClusterSnapshotCopyStatus) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.Redshift, create.ErrActionCheckingExistence, tfredshift.ResNameSnapshotCopy, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.Redshift, create.ErrActionCheckingExistence, tfredshift.ResNameSnapshotCopy, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).RedshiftClient(ctx) + out, err := tfredshift.FindSnapshotCopyByID(ctx, conn, rs.Primary.ID) + if err != nil { + return create.Error(names.Redshift, create.ErrActionCheckingExistence, tfredshift.ResNameSnapshotCopy, rs.Primary.ID, err) + } + + *snap = *out + + return nil + } +} + +func testAccSnapshotCopyConfigBase(rName string) string { + return acctest.ConfigCompose( + // "InvalidVPCNetworkStateFault: The requested AZ us-west-2a is not a valid AZ." + acctest.ConfigAvailableAZsNoOptInExclude("usw2-az2"), + fmt.Sprintf(` +resource "aws_redshift_cluster" "test" { + cluster_identifier = %[1]q + availability_zone = data.aws_availability_zones.available.names[0] + database_name = "mydb" + master_username = "foo_test" + master_password = "Mustbe8characters" + multi_az = false + node_type = "dc2.large" + automated_snapshot_retention_period = 0 + allow_version_upgrade = false + skip_final_snapshot = true +} +`, rName)) +} + +func testAccSnapshotCopyConfig_basic(rName string) string { + return acctest.ConfigCompose( + testAccSnapshotCopyConfigBase(rName), + fmt.Sprintf(` +resource "aws_redshift_snapshot_copy" "test" { + cluster_identifier = aws_redshift_cluster.test.id + destination_region = %[2]q +} +`, rName, acctest.AlternateRegion())) +} + +func testAccSnapshotCopyConfig_retentionPeriod(rName string, retentionPeriod int) string { + return acctest.ConfigCompose( + testAccSnapshotCopyConfigBase(rName), + fmt.Sprintf(` +resource "aws_redshift_snapshot_copy" "test" { + cluster_identifier = aws_redshift_cluster.test.id + destination_region = %[2]q + retention_period = %[3]d +} +`, rName, acctest.AlternateRegion(), retentionPeriod)) +} diff --git a/website/docs/r/redshift_cluster.html.markdown b/website/docs/r/redshift_cluster.html.markdown index 4cd8a9325c88..d9dc6d4befe1 100644 --- a/website/docs/r/redshift_cluster.html.markdown +++ b/website/docs/r/redshift_cluster.html.markdown @@ -103,7 +103,7 @@ This resource supports the following arguments: * `logging` - (Optional) Logging, documented below. * `maintenance_track_name` - (Optional) The name of the maintenance track for the restored cluster. When you take a snapshot, the snapshot inherits the MaintenanceTrack value from the cluster. The snapshot might be on a different track than the cluster that was the source for the snapshot. For example, suppose that you take a snapshot of a cluster that is on the current track and then change the cluster to be on the trailing track. In this case, the snapshot and the source cluster are on different tracks. Default value is `current`. * `manual_snapshot_retention_period` - (Optional) The default number of days to retain a manual snapshot. If the value is -1, the snapshot is retained indefinitely. This setting doesn't change the retention period of existing snapshots. Valid values are between `-1` and `3653`. Default value is `-1`. -* `snapshot_copy` - (Optional) Configuration of automatic copy of snapshots from one region to another. Documented below. +* `snapshot_copy` - (Optional, **Deprecated**) Configuration of automatic copy of snapshots from one region to another. Documented below. * `tags` - (Optional) A map of tags to assign to the resource. 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. ### Nested Blocks @@ -119,6 +119,8 @@ For more information on the permissions required for the bucket, please read the #### `snapshot_copy` +~> The `snapshot_copy` argument is deprecated. Use the [`aws_redshift_snapshot_copy`](./redshift_snapshot_copy.html.markdown) resource instead. This argument will be removed in a future major version. + * `destination_region` - (Required) The destination region that you want to copy snapshots to. * `retention_period` - (Optional) The number of days to retain automated snapshots in the destination region after they are copied from the source region. Defaults to `7`. * `grant_name` - (Optional) The name of the snapshot copy grant to use when snapshots of an AWS KMS-encrypted cluster are copied to the destination region. diff --git a/website/docs/r/redshift_snapshot_copy.html.markdown b/website/docs/r/redshift_snapshot_copy.html.markdown new file mode 100644 index 000000000000..cb029feef529 --- /dev/null +++ b/website/docs/r/redshift_snapshot_copy.html.markdown @@ -0,0 +1,57 @@ +--- +subcategory: "Redshift" +layout: "aws" +page_title: "AWS: aws_redshift_snapshot_copy" +description: |- + Terraform resource for managing an AWS Redshift Snapshot Copy. +--- +# Resource: aws_redshift_snapshot_copy + +Terraform resource for managing an AWS Redshift Snapshot Copy. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_redshift_snapshot_copy" "example" { + cluster_identifier = aws_redshift_cluster.example.id + destination_region = "us-east-1" +} +``` + +## Argument Reference + +The following arguments are required: + +* `cluster_identifier` - (Required) Identifier of the source cluster. +* `destination_region` - (Required) AWS Region to copy snapshots to. + +The following arguments are optional: + +* `manual_snapshot_retention_period` - (Optional) Number of days to retain newly copied snapshots in the destination AWS Region after they are copied from the source AWS Region. If the value is `-1`, the manual snapshot is retained indefinitely. +* `retention_period` - (Optional) Number of days to retain automated snapshots in the destination region after they are copied from the source region. +* `snapshot_copy_grant_name` - (Optional) Name of the snapshot copy grant to use when snapshots of an AWS KMS-encrypted cluster are copied to the destination region. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `id` - Identifier of the source cluster. + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Redshift Snapshot Copy using the `id`. For example: + +```terraform +import { + to = aws_redshift_snapshot_copy.example + id = "cluster-id-12345678" +} +``` + +Using `terraform import`, import Redshift Snapshot Copy using the `id`. For example: + +```console +% terraform import aws_redshift_snapshot_copy.example cluster-id-12345678 +```