From 15deb64f6e96d6fdf74835093f22ad5c7106fa66 Mon Sep 17 00:00:00 2001 From: Dmitry Shevchuk Date: Thu, 20 Feb 2020 15:42:24 +0200 Subject: [PATCH 1/7] resourceAwsSnapshotCreateVolumePermission: Fail if the account is snapshot owner --- ...e_aws_snapshot_create_volume_permission.go | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/aws/resource_aws_snapshot_create_volume_permission.go b/aws/resource_aws_snapshot_create_volume_permission.go index 6b937e1dc684..09b892b2c9c1 100644 --- a/aws/resource_aws_snapshot_create_volume_permission.go +++ b/aws/resource_aws_snapshot_create_volume_permission.go @@ -49,9 +49,19 @@ func resourceAwsSnapshotCreateVolumePermissionCreate(d *schema.ResourceData, met snapshot_id := d.Get("snapshot_id").(string) account_id := d.Get("account_id").(string) - _, err := conn.ModifySnapshotAttribute(&ec2.ModifySnapshotAttributeInput{ + accountIsOwner, err := isAccountSnapshotOwner(conn, snapshot_id, account_id) + if err != nil { + return fmt.Errorf("Error adding snapshot %s: %s", ec2.SnapshotAttributeNameCreateVolumePermission, err) + } + + if accountIsOwner { + return fmt.Errorf("Error adding snapshot %s: specified account %s is the snapshot owner", + ec2.SnapshotAttributeNameCreateVolumePermission, account_id) + } + + _, err = conn.ModifySnapshotAttribute(&ec2.ModifySnapshotAttributeInput{ SnapshotId: aws.String(snapshot_id), - Attribute: aws.String("createVolumePermission"), + Attribute: aws.String(ec2.SnapshotAttributeNameCreateVolumePermission), CreateVolumePermission: &ec2.CreateVolumePermissionModifications{ Add: []*ec2.CreateVolumePermission{ {UserId: aws.String(account_id)}, @@ -59,7 +69,7 @@ func resourceAwsSnapshotCreateVolumePermissionCreate(d *schema.ResourceData, met }, }) if err != nil { - return fmt.Errorf("Error adding snapshot createVolumePermission: %s", err) + return fmt.Errorf("Error adding snapshot %s: %s", ec2.SnapshotAttributeNameCreateVolumePermission, err) } d.SetId(fmt.Sprintf("%s-%s", snapshot_id, account_id)) @@ -75,8 +85,8 @@ func resourceAwsSnapshotCreateVolumePermissionCreate(d *schema.ResourceData, met } if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf( - "Error waiting for snapshot createVolumePermission (%s) to be added: %s", - d.Id(), err) + "Error waiting for snapshot %s (%s) to be added: %s", + ec2.SnapshotAttributeNameCreateVolumePermission, d.Id(), err) } return nil @@ -96,7 +106,7 @@ func resourceAwsSnapshotCreateVolumePermissionDelete(d *schema.ResourceData, met _, err = conn.ModifySnapshotAttribute(&ec2.ModifySnapshotAttributeInput{ SnapshotId: aws.String(snapshotID), - Attribute: aws.String("createVolumePermission"), + Attribute: aws.String(ec2.SnapshotAttributeNameCreateVolumePermission), CreateVolumePermission: &ec2.CreateVolumePermissionModifications{ Remove: []*ec2.CreateVolumePermission{ {UserId: aws.String(accountID)}, @@ -104,7 +114,7 @@ func resourceAwsSnapshotCreateVolumePermissionDelete(d *schema.ResourceData, met }, }) if err != nil { - return fmt.Errorf("Error removing snapshot createVolumePermission: %s", err) + return fmt.Errorf("Error removing snapshot %s: %s", ec2.SnapshotAttributeNameCreateVolumePermission, err) } // Wait for the account to disappear from the permission list @@ -118,13 +128,29 @@ func resourceAwsSnapshotCreateVolumePermissionDelete(d *schema.ResourceData, met } if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf( - "Error waiting for snapshot createVolumePermission (%s) to be removed: %s", - d.Id(), err) + "Error waiting for snapshot %s (%s) to be removed: %s", + ec2.SnapshotAttributeNameCreateVolumePermission, d.Id(), err) } return nil } +func isAccountSnapshotOwner(conn *ec2.EC2, snapshot_id string, account_id string) (bool, error) { + output, err := conn.DescribeSnapshots(&ec2.DescribeSnapshotsInput{ + SnapshotIds: aws.StringSlice([]string{snapshot_id}), + }) + if err != nil { + return false, fmt.Errorf("Error describing snapshot %s: %s", snapshot_id, err) + } + + if len(output.Snapshots) != 1 { + return false, fmt.Errorf("Error locating snapshot %s: found %d snapshots, expected 1", + snapshot_id, len(output.Snapshots)) + } + + return *output.Snapshots[0].OwnerId == account_id, nil +} + func hasCreateVolumePermission(conn *ec2.EC2, snapshot_id string, account_id string) (bool, error) { _, state, err := resourceAwsSnapshotCreateVolumePermissionStateRefreshFunc(conn, snapshot_id, account_id)() if err != nil { @@ -141,10 +167,10 @@ func resourceAwsSnapshotCreateVolumePermissionStateRefreshFunc(conn *ec2.EC2, sn return func() (interface{}, string, error) { attrs, err := conn.DescribeSnapshotAttribute(&ec2.DescribeSnapshotAttributeInput{ SnapshotId: aws.String(snapshot_id), - Attribute: aws.String("createVolumePermission"), + Attribute: aws.String(ec2.SnapshotAttributeNameCreateVolumePermission), }) if err != nil { - return nil, "", fmt.Errorf("Error refreshing snapshot createVolumePermission state: %s", err) + return nil, "", fmt.Errorf("Error refreshing snapshot %s state: %s", ec2.SnapshotAttributeNameCreateVolumePermission, err) } for _, vp := range attrs.CreateVolumePermissions { From c454c09d318132b0f6e4f82118930c2fad51f687 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Jun 2022 17:07:33 -0400 Subject: [PATCH 2/7] Revert "resourceAwsSnapshotCreateVolumePermission: Fail if the account is snapshot owner" This reverts commit 15deb64f6e96d6fdf74835093f22ad5c7106fa66. --- ...e_aws_snapshot_create_volume_permission.go | 48 +++++-------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/aws/resource_aws_snapshot_create_volume_permission.go b/aws/resource_aws_snapshot_create_volume_permission.go index 09b892b2c9c1..6b937e1dc684 100644 --- a/aws/resource_aws_snapshot_create_volume_permission.go +++ b/aws/resource_aws_snapshot_create_volume_permission.go @@ -49,19 +49,9 @@ func resourceAwsSnapshotCreateVolumePermissionCreate(d *schema.ResourceData, met snapshot_id := d.Get("snapshot_id").(string) account_id := d.Get("account_id").(string) - accountIsOwner, err := isAccountSnapshotOwner(conn, snapshot_id, account_id) - if err != nil { - return fmt.Errorf("Error adding snapshot %s: %s", ec2.SnapshotAttributeNameCreateVolumePermission, err) - } - - if accountIsOwner { - return fmt.Errorf("Error adding snapshot %s: specified account %s is the snapshot owner", - ec2.SnapshotAttributeNameCreateVolumePermission, account_id) - } - - _, err = conn.ModifySnapshotAttribute(&ec2.ModifySnapshotAttributeInput{ + _, err := conn.ModifySnapshotAttribute(&ec2.ModifySnapshotAttributeInput{ SnapshotId: aws.String(snapshot_id), - Attribute: aws.String(ec2.SnapshotAttributeNameCreateVolumePermission), + Attribute: aws.String("createVolumePermission"), CreateVolumePermission: &ec2.CreateVolumePermissionModifications{ Add: []*ec2.CreateVolumePermission{ {UserId: aws.String(account_id)}, @@ -69,7 +59,7 @@ func resourceAwsSnapshotCreateVolumePermissionCreate(d *schema.ResourceData, met }, }) if err != nil { - return fmt.Errorf("Error adding snapshot %s: %s", ec2.SnapshotAttributeNameCreateVolumePermission, err) + return fmt.Errorf("Error adding snapshot createVolumePermission: %s", err) } d.SetId(fmt.Sprintf("%s-%s", snapshot_id, account_id)) @@ -85,8 +75,8 @@ func resourceAwsSnapshotCreateVolumePermissionCreate(d *schema.ResourceData, met } if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf( - "Error waiting for snapshot %s (%s) to be added: %s", - ec2.SnapshotAttributeNameCreateVolumePermission, d.Id(), err) + "Error waiting for snapshot createVolumePermission (%s) to be added: %s", + d.Id(), err) } return nil @@ -106,7 +96,7 @@ func resourceAwsSnapshotCreateVolumePermissionDelete(d *schema.ResourceData, met _, err = conn.ModifySnapshotAttribute(&ec2.ModifySnapshotAttributeInput{ SnapshotId: aws.String(snapshotID), - Attribute: aws.String(ec2.SnapshotAttributeNameCreateVolumePermission), + Attribute: aws.String("createVolumePermission"), CreateVolumePermission: &ec2.CreateVolumePermissionModifications{ Remove: []*ec2.CreateVolumePermission{ {UserId: aws.String(accountID)}, @@ -114,7 +104,7 @@ func resourceAwsSnapshotCreateVolumePermissionDelete(d *schema.ResourceData, met }, }) if err != nil { - return fmt.Errorf("Error removing snapshot %s: %s", ec2.SnapshotAttributeNameCreateVolumePermission, err) + return fmt.Errorf("Error removing snapshot createVolumePermission: %s", err) } // Wait for the account to disappear from the permission list @@ -128,29 +118,13 @@ func resourceAwsSnapshotCreateVolumePermissionDelete(d *schema.ResourceData, met } if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf( - "Error waiting for snapshot %s (%s) to be removed: %s", - ec2.SnapshotAttributeNameCreateVolumePermission, d.Id(), err) + "Error waiting for snapshot createVolumePermission (%s) to be removed: %s", + d.Id(), err) } return nil } -func isAccountSnapshotOwner(conn *ec2.EC2, snapshot_id string, account_id string) (bool, error) { - output, err := conn.DescribeSnapshots(&ec2.DescribeSnapshotsInput{ - SnapshotIds: aws.StringSlice([]string{snapshot_id}), - }) - if err != nil { - return false, fmt.Errorf("Error describing snapshot %s: %s", snapshot_id, err) - } - - if len(output.Snapshots) != 1 { - return false, fmt.Errorf("Error locating snapshot %s: found %d snapshots, expected 1", - snapshot_id, len(output.Snapshots)) - } - - return *output.Snapshots[0].OwnerId == account_id, nil -} - func hasCreateVolumePermission(conn *ec2.EC2, snapshot_id string, account_id string) (bool, error) { _, state, err := resourceAwsSnapshotCreateVolumePermissionStateRefreshFunc(conn, snapshot_id, account_id)() if err != nil { @@ -167,10 +141,10 @@ func resourceAwsSnapshotCreateVolumePermissionStateRefreshFunc(conn *ec2.EC2, sn return func() (interface{}, string, error) { attrs, err := conn.DescribeSnapshotAttribute(&ec2.DescribeSnapshotAttributeInput{ SnapshotId: aws.String(snapshot_id), - Attribute: aws.String(ec2.SnapshotAttributeNameCreateVolumePermission), + Attribute: aws.String("createVolumePermission"), }) if err != nil { - return nil, "", fmt.Errorf("Error refreshing snapshot %s state: %s", ec2.SnapshotAttributeNameCreateVolumePermission, err) + return nil, "", fmt.Errorf("Error refreshing snapshot createVolumePermission state: %s", err) } for _, vp := range attrs.CreateVolumePermissions { From e5a9b15fe55f4a10626527a69206ee2b7b1361de Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Jun 2022 17:33:43 -0400 Subject: [PATCH 3/7] d/aws_ebs_snapshot_ids: Tidy up. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2EBSSnapshotIDsDataSource_' PKG=ec2 ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 2 -run=TestAccEC2EBSSnapshotIDsDataSource_ -timeout 180m === RUN TestAccEC2EBSSnapshotIDsDataSource_basic === PAUSE TestAccEC2EBSSnapshotIDsDataSource_basic === RUN TestAccEC2EBSSnapshotIDsDataSource_sorted === PAUSE TestAccEC2EBSSnapshotIDsDataSource_sorted === RUN TestAccEC2EBSSnapshotIDsDataSource_empty === PAUSE TestAccEC2EBSSnapshotIDsDataSource_empty === CONT TestAccEC2EBSSnapshotIDsDataSource_basic === CONT TestAccEC2EBSSnapshotIDsDataSource_empty --- PASS: TestAccEC2EBSSnapshotIDsDataSource_empty (14.20s) === CONT TestAccEC2EBSSnapshotIDsDataSource_sorted --- PASS: TestAccEC2EBSSnapshotIDsDataSource_basic (55.92s) --- PASS: TestAccEC2EBSSnapshotIDsDataSource_sorted (72.67s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 93.012s --- .../ec2/ebs_snapshot_ids_data_source.go | 59 ++++++++-------- .../ec2/ebs_snapshot_ids_data_source_test.go | 70 +++++++++++-------- 2 files changed, 70 insertions(+), 59 deletions(-) diff --git a/internal/service/ec2/ebs_snapshot_ids_data_source.go b/internal/service/ec2/ebs_snapshot_ids_data_source.go index 7545d164d167..339e9bf3bf44 100644 --- a/internal/service/ec2/ebs_snapshot_ids_data_source.go +++ b/internal/service/ec2/ebs_snapshot_ids_data_source.go @@ -2,7 +2,6 @@ package ec2 import ( "fmt" - "log" "sort" "github.com/aws/aws-sdk-go/aws" @@ -18,19 +17,19 @@ func DataSourceEBSSnapshotIDs() *schema.Resource { Schema: map[string]*schema.Schema{ "filter": DataSourceFiltersSchema(), - "owners": { + "ids": { Type: schema.TypeList, - Optional: true, + Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "restorable_by_user_ids": { + "owners": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "ids": { + "restorable_by_user_ids": { Type: schema.TypeList, - Computed: true, + Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, }, @@ -40,44 +39,42 @@ func DataSourceEBSSnapshotIDs() *schema.Resource { func dataSourceEBSSnapshotIDsRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - restorableUsers, restorableUsersOk := d.GetOk("restorable_by_user_ids") - filters, filtersOk := d.GetOk("filter") - owners, ownersOk := d.GetOk("owners") + input := &ec2.DescribeSnapshotsInput{} - if restorableUsers == false && !filtersOk && !ownersOk { - return fmt.Errorf("One of filters, restorable_by_user_ids, or owners must be assigned") + if v, ok := d.GetOk("owners"); ok && len(v.([]interface{})) > 0 { + input.OwnerIds = flex.ExpandStringList(v.([]interface{})) } - params := &ec2.DescribeSnapshotsInput{} - - if restorableUsersOk { - params.RestorableByUserIds = flex.ExpandStringList(restorableUsers.([]interface{})) - } - if filtersOk { - params.Filters = BuildFiltersDataSource(filters.(*schema.Set)) + if v, ok := d.GetOk("restorable_by_user_ids"); ok && len(v.([]interface{})) > 0 { + input.RestorableByUserIds = flex.ExpandStringList(v.([]interface{})) } - if ownersOk { - params.OwnerIds = flex.ExpandStringList(owners.([]interface{})) + + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) + + if len(input.Filters) == 0 { + input.Filters = nil } - log.Printf("[DEBUG] Reading EBS Snapshot IDs: %s", params) - resp, err := conn.DescribeSnapshots(params) + snapshots, err := FindSnapshots(conn, input) + if err != nil { - return err + return fmt.Errorf("error reading EBS Snapshots: %w", err) } - snapshotIds := make([]string, 0) - - sort.Slice(resp.Snapshots, func(i, j int) bool { - return aws.TimeValue(resp.Snapshots[i].StartTime).Unix() > aws.TimeValue(resp.Snapshots[j].StartTime).Unix() + sort.Slice(snapshots, func(i, j int) bool { + return aws.TimeValue(snapshots[i].StartTime).Unix() > aws.TimeValue(snapshots[j].StartTime).Unix() }) - for _, snapshot := range resp.Snapshots { - snapshotIds = append(snapshotIds, *snapshot.SnapshotId) + + var snapshotIDs []string + + for _, v := range snapshots { + snapshotIDs = append(snapshotIDs, aws.StringValue(v.SnapshotId)) } d.SetId(meta.(*conns.AWSClient).Region) - - d.Set("ids", snapshotIds) + d.Set("ids", snapshotIDs) return nil } diff --git a/internal/service/ec2/ebs_snapshot_ids_data_source_test.go b/internal/service/ec2/ebs_snapshot_ids_data_source_test.go index 6e71447e8880..4352bb8a2f6a 100644 --- a/internal/service/ec2/ebs_snapshot_ids_data_source_test.go +++ b/internal/service/ec2/ebs_snapshot_ids_data_source_test.go @@ -11,15 +11,19 @@ import ( ) func TestAccEC2EBSSnapshotIDsDataSource_basic(t *testing.T) { + datasourceName := "data.aws_ebs_snapshot_ids.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), ProviderFactories: acctest.ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccEBSSnapshotIdsDataSourceConfig_basic(), + Config: testAccEBSSnapshotIdsDataSourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEBSSnapshotIDDataSource("data.aws_ebs_snapshot_ids.test"), + acctest.CheckResourceAttrGreaterThanValue(datasourceName, "ids.#", "0"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "ids.*", "aws_ebs_snapshot.test", "id"), ), }, }, @@ -27,6 +31,9 @@ func TestAccEC2EBSSnapshotIDsDataSource_basic(t *testing.T) { } func TestAccEC2EBSSnapshotIDsDataSource_sorted(t *testing.T) { + datasourceName := "data.aws_ebs_snapshot_ids.test" + resource1Name := "aws_ebs_snapshot.a" + resource2Name := "aws_ebs_snapshot.b" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ @@ -35,23 +42,11 @@ func TestAccEC2EBSSnapshotIDsDataSource_sorted(t *testing.T) { ProviderFactories: acctest.ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccEBSSnapshotIdsDataSourceConfig_sorted1(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("aws_ebs_snapshot.a", "id"), - resource.TestCheckResourceAttrSet("aws_ebs_snapshot.b", "id"), - ), - }, - { - Config: testAccEBSSnapshotIdsDataSourceConfig_sorted2(rName), + Config: testAccEBSSnapshotIdsDataSourceConfig_sorted(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEBSSnapshotIDDataSource("data.aws_ebs_snapshot_ids.test"), - resource.TestCheckResourceAttr("data.aws_ebs_snapshot_ids.test", "ids.#", "2"), - resource.TestCheckResourceAttrPair( - "data.aws_ebs_snapshot_ids.test", "ids.0", - "aws_ebs_snapshot.b", "id"), - resource.TestCheckResourceAttrPair( - "data.aws_ebs_snapshot_ids.test", "ids.1", - "aws_ebs_snapshot.a", "id"), + resource.TestCheckResourceAttr(datasourceName, "ids.#", "2"), + resource.TestCheckResourceAttrPair(datasourceName, "ids.0", resource2Name, "id"), + resource.TestCheckResourceAttrPair(datasourceName, "ids.1", resource1Name, "id"), ), }, }, @@ -67,7 +62,6 @@ func TestAccEC2EBSSnapshotIDsDataSource_empty(t *testing.T) { { Config: testAccEBSSnapshotIdsDataSourceConfig_empty, Check: resource.ComposeTestCheckFunc( - testAccCheckEBSSnapshotIDDataSource("data.aws_ebs_snapshot_ids.empty"), resource.TestCheckResourceAttr("data.aws_ebs_snapshot_ids.empty", "ids.#", "0"), ), }, @@ -75,35 +69,53 @@ func TestAccEC2EBSSnapshotIDsDataSource_empty(t *testing.T) { }) } -func testAccEBSSnapshotIdsDataSourceConfig_basic() string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), ` +func testAccEBSSnapshotIdsDataSourceConfig_basic(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_ebs_volume" "test" { availability_zone = data.aws_availability_zones.available.names[0] size = 1 + + tags = { + Name = %[1]q + } } resource "aws_ebs_snapshot" "test" { volume_id = aws_ebs_volume.test.id + + tags = { + Name = %[1]q + } } data "aws_ebs_snapshot_ids" "test" { owners = ["self"] + + depends_on = [aws_ebs_snapshot.test] } -`) +`, rName)) } -func testAccEBSSnapshotIdsDataSourceConfig_sorted1(rName string) string { +func testAccEBSSnapshotIdsDataSourceConfig_sorted(rName string) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_ebs_volume" "test" { availability_zone = data.aws_availability_zones.available.names[0] size = 1 count = 2 + + tags = { + Name = %[1]q + } } resource "aws_ebs_snapshot" "a" { volume_id = aws_ebs_volume.test.*.id[0] description = %[1]q + + tags = { + Name = %[1]q + } } resource "aws_ebs_snapshot" "b" { @@ -114,19 +126,21 @@ resource "aws_ebs_snapshot" "b" { # 'aws_ebs_snapshot.b.creation_date'/ so that we can ensure that the # snapshots are being sorted correctly. depends_on = [aws_ebs_snapshot.a] -} -`, rName)) + + tags = { + Name = %[1]q + } } -func testAccEBSSnapshotIdsDataSourceConfig_sorted2(rName string) string { - return acctest.ConfigCompose(testAccEBSSnapshotIdsDataSourceConfig_sorted1(rName), fmt.Sprintf(` data "aws_ebs_snapshot_ids" "test" { owners = ["self"] filter { name = "description" - values = [%q] + values = [%[1]q] } + + depends_on = [aws_ebs_snapshot.a, aws_ebs_snapshot.b] } `, rName)) } From b97c63712c26478a0b857ee10c5e4694c4da1006 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Jun 2022 18:05:07 -0400 Subject: [PATCH 4/7] d/aws_ebs_snapshot: Tidy up. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2EBSSnapshotDataSource_' PKG=ec2 ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 2 -run=TestAccEC2EBSSnapshotDataSource_ -timeout 180m === RUN TestAccEC2EBSSnapshotDataSource_basic === PAUSE TestAccEC2EBSSnapshotDataSource_basic === RUN TestAccEC2EBSSnapshotDataSource_filter === PAUSE TestAccEC2EBSSnapshotDataSource_filter === RUN TestAccEC2EBSSnapshotDataSource_mostRecent === PAUSE TestAccEC2EBSSnapshotDataSource_mostRecent === CONT TestAccEC2EBSSnapshotDataSource_basic === CONT TestAccEC2EBSSnapshotDataSource_mostRecent --- PASS: TestAccEC2EBSSnapshotDataSource_basic (58.73s) === CONT TestAccEC2EBSSnapshotDataSource_filter --- PASS: TestAccEC2EBSSnapshotDataSource_mostRecent (74.99s) --- PASS: TestAccEC2EBSSnapshotDataSource_filter (55.59s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 123.516s --- .../service/ec2/ebs_snapshot_data_source.go | 85 +++++++++--------- .../ec2/ebs_snapshot_data_source_test.go | 87 +++++++++++-------- .../ec2/ebs_snapshot_ids_data_source.go | 2 +- .../ec2/ebs_snapshot_ids_data_source_test.go | 14 +-- .../ec2/ec2_ami_ids_data_source_test.go | 1 - 5 files changed, 98 insertions(+), 91 deletions(-) diff --git a/internal/service/ec2/ebs_snapshot_data_source.go b/internal/service/ec2/ebs_snapshot_data_source.go index 1cf61870cd9e..4db0bf4e1019 100644 --- a/internal/service/ec2/ebs_snapshot_data_source.go +++ b/internal/service/ec2/ebs_snapshot_data_source.go @@ -2,7 +2,6 @@ package ec2 import ( "fmt" - "log" "sort" "github.com/aws/aws-sdk-go/aws" @@ -99,83 +98,77 @@ func DataSourceEBSSnapshot() *schema.Resource { func dataSourceEBSSnapshotRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - restorableUsers, restorableUsersOk := d.GetOk("restorable_by_user_ids") - filters, filtersOk := d.GetOk("filter") - snapshotIds, snapshotIdsOk := d.GetOk("snapshot_ids") - owners, ownersOk := d.GetOk("owners") + input := &ec2.DescribeSnapshotsInput{} - if !restorableUsersOk && !filtersOk && !snapshotIdsOk && !ownersOk { - return fmt.Errorf("One of snapshot_ids, filters, restorable_by_user_ids, or owners must be assigned") + if v, ok := d.GetOk("owners"); ok && len(v.([]interface{})) > 0 { + input.OwnerIds = flex.ExpandStringList(v.([]interface{})) } - params := &ec2.DescribeSnapshotsInput{} - if restorableUsersOk { - params.RestorableByUserIds = flex.ExpandStringList(restorableUsers.([]interface{})) - } - if filtersOk { - params.Filters = BuildFiltersDataSource(filters.(*schema.Set)) + if v, ok := d.GetOk("restorable_by_user_ids"); ok && len(v.([]interface{})) > 0 { + input.RestorableByUserIds = flex.ExpandStringList(v.([]interface{})) } - if ownersOk { - params.OwnerIds = flex.ExpandStringList(owners.([]interface{})) + + if v, ok := d.GetOk("snapshot_ids"); ok && len(v.([]interface{})) > 0 { + input.SnapshotIds = flex.ExpandStringList(v.([]interface{})) } - if snapshotIdsOk { - params.SnapshotIds = flex.ExpandStringList(snapshotIds.([]interface{})) + + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) + + if len(input.Filters) == 0 { + input.Filters = nil } - log.Printf("[DEBUG] Reading EBS Snapshot: %s", params) - resp, err := conn.DescribeSnapshots(params) + snapshots, err := FindSnapshots(conn, input) + if err != nil { - return err + return fmt.Errorf("reading EBS Snapshots: %w", err) } - if len(resp.Snapshots) < 1 { + if len(snapshots) < 1 { return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") } - if len(resp.Snapshots) > 1 { + if len(snapshots) > 1 { if !d.Get("most_recent").(bool) { return fmt.Errorf("Your query returned more than one result. Please try a more " + "specific search criteria, or set `most_recent` attribute to true.") } - sort.Slice(resp.Snapshots, func(i, j int) bool { - return aws.TimeValue(resp.Snapshots[i].StartTime).Unix() > aws.TimeValue(resp.Snapshots[j].StartTime).Unix() + + sort.Slice(snapshots, func(i, j int) bool { + return aws.TimeValue(snapshots[i].StartTime).Unix() > aws.TimeValue(snapshots[j].StartTime).Unix() }) } - //Single Snapshot found so set to state - return snapshotDescriptionAttributes(d, resp.Snapshots[0], meta) -} - -func snapshotDescriptionAttributes(d *schema.ResourceData, snapshot *ec2.Snapshot, meta interface{}) error { - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + snapshot := snapshots[0] d.SetId(aws.StringValue(snapshot.SnapshotId)) - d.Set("snapshot_id", snapshot.SnapshotId) - d.Set("volume_id", snapshot.VolumeId) + arn := arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Service: ec2.ServiceName, + Region: meta.(*conns.AWSClient).Region, + Resource: fmt.Sprintf("snapshot/%s", d.Id()), + }.String() + d.Set("arn", arn) d.Set("data_encryption_key_id", snapshot.DataEncryptionKeyId) d.Set("description", snapshot.Description) d.Set("encrypted", snapshot.Encrypted) d.Set("kms_key_id", snapshot.KmsKeyId) - d.Set("volume_size", snapshot.VolumeSize) - d.Set("state", snapshot.State) - d.Set("owner_id", snapshot.OwnerId) + d.Set("outpost_arn", snapshot.OutpostArn) d.Set("owner_alias", snapshot.OwnerAlias) + d.Set("owner_id", snapshot.OwnerId) + d.Set("snapshot_id", snapshot.SnapshotId) + d.Set("state", snapshot.State) d.Set("storage_tier", snapshot.StorageTier) - d.Set("outpost_arn", snapshot.OutpostArn) + d.Set("volume_id", snapshot.VolumeId) + d.Set("volume_size", snapshot.VolumeSize) if err := d.Set("tags", KeyValueTags(snapshot.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %w", err) + return fmt.Errorf("setting tags: %w", err) } - snapshotArn := arn.ARN{ - Partition: meta.(*conns.AWSClient).Partition, - Region: meta.(*conns.AWSClient).Region, - Resource: fmt.Sprintf("snapshot/%s", d.Id()), - Service: ec2.ServiceName, - }.String() - - d.Set("arn", snapshotArn) - return nil } diff --git a/internal/service/ec2/ebs_snapshot_data_source_test.go b/internal/service/ec2/ebs_snapshot_data_source_test.go index e0ed64c617b1..a0fa6fb98a8b 100644 --- a/internal/service/ec2/ebs_snapshot_data_source_test.go +++ b/internal/service/ec2/ebs_snapshot_data_source_test.go @@ -5,14 +5,15 @@ import ( "testing" "github.com/aws/aws-sdk-go/service/ec2" + sdkacctest "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/hashicorp/terraform-provider-aws/internal/acctest" ) func TestAccEC2EBSSnapshotDataSource_basic(t *testing.T) { dataSourceName := "data.aws_ebs_snapshot.test" resourceName := "aws_ebs_snapshot.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -20,20 +21,19 @@ func TestAccEC2EBSSnapshotDataSource_basic(t *testing.T) { ProviderFactories: acctest.ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccEBSSnapshotDataSourceConfig_basic, + Config: testAccEBSSnapshotDataSourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEBSSnapshotIDDataSource(dataSourceName), - resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"), resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName, "description"), resource.TestCheckResourceAttrPair(dataSourceName, "encrypted", resourceName, "encrypted"), + resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"), resource.TestCheckResourceAttrPair(dataSourceName, "kms_key_id", resourceName, "kms_key_id"), resource.TestCheckResourceAttrPair(dataSourceName, "owner_alias", resourceName, "owner_alias"), resource.TestCheckResourceAttrPair(dataSourceName, "owner_id", resourceName, "owner_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "storage_tier", resourceName, "storage_tier"), resource.TestCheckResourceAttrPair(dataSourceName, "tags.%", resourceName, "tags.%"), resource.TestCheckResourceAttrPair(dataSourceName, "volume_id", resourceName, "volume_id"), resource.TestCheckResourceAttrPair(dataSourceName, "volume_size", resourceName, "volume_size"), - resource.TestCheckResourceAttrPair(dataSourceName, "storage_tier", resourceName, "storage_tier"), ), }, }, @@ -43,6 +43,7 @@ func TestAccEC2EBSSnapshotDataSource_basic(t *testing.T) { func TestAccEC2EBSSnapshotDataSource_filter(t *testing.T) { dataSourceName := "data.aws_ebs_snapshot.test" resourceName := "aws_ebs_snapshot.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -50,9 +51,8 @@ func TestAccEC2EBSSnapshotDataSource_filter(t *testing.T) { ProviderFactories: acctest.ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccEBSSnapshotDataSourceConfig_filter, + Config: testAccEBSSnapshotDataSourceConfig_filter(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEBSSnapshotIDDataSource(dataSourceName), resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"), ), }, @@ -62,7 +62,8 @@ func TestAccEC2EBSSnapshotDataSource_filter(t *testing.T) { func TestAccEC2EBSSnapshotDataSource_mostRecent(t *testing.T) { dataSourceName := "data.aws_ebs_snapshot.test" - resourceName := "aws_ebs_snapshot.test" + resourceName := "aws_ebs_snapshot.b" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -70,9 +71,8 @@ func TestAccEC2EBSSnapshotDataSource_mostRecent(t *testing.T) { ProviderFactories: acctest.ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccEBSSnapshotDataSourceConfig_mostRecent, + Config: testAccEBSSnapshotDataSourceConfig_mostRecent(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEBSSnapshotIDDataSource(dataSourceName), resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"), ), }, @@ -80,45 +80,50 @@ func TestAccEC2EBSSnapshotDataSource_mostRecent(t *testing.T) { }) } -func testAccCheckEBSSnapshotIDDataSource(n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Can't find snapshot data source: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("Snapshot data source ID not set") - } - return nil - } -} - -var testAccEBSSnapshotDataSourceConfig_basic = acctest.ConfigAvailableAZsNoOptIn() + ` +func testAccEBSSnapshotDataSourceConfig_basic(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_ebs_volume" "test" { availability_zone = data.aws_availability_zones.available.names[0] type = "gp2" size = 1 + + tags = { + Name = %[1]q + } } resource "aws_ebs_snapshot" "test" { volume_id = aws_ebs_volume.test.id + + tags = { + Name = %[1]q + } } data "aws_ebs_snapshot" "test" { snapshot_ids = [aws_ebs_snapshot.test.id] } -` +`, rName)) +} -var testAccEBSSnapshotDataSourceConfig_filter = acctest.ConfigAvailableAZsNoOptIn() + ` +func testAccEBSSnapshotDataSourceConfig_filter(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_ebs_volume" "test" { availability_zone = data.aws_availability_zones.available.names[0] type = "gp2" size = 1 + + tags = { + Name = %[1]q + } } resource "aws_ebs_snapshot" "test" { volume_id = aws_ebs_volume.test.id + + tags = { + Name = %[1]q + } } data "aws_ebs_snapshot" "test" { @@ -127,28 +132,35 @@ data "aws_ebs_snapshot" "test" { values = [aws_ebs_snapshot.test.id] } } -` +`, rName)) +} -var testAccEBSSnapshotDataSourceConfig_mostRecent = acctest.ConfigAvailableAZsNoOptIn() + ` +func testAccEBSSnapshotDataSourceConfig_mostRecent(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_ebs_volume" "test" { availability_zone = data.aws_availability_zones.available.names[0] type = "gp2" size = 1 + + tags = { + Name = %[1]q + } } -resource "aws_ebs_snapshot" "incorrect" { +resource "aws_ebs_snapshot" "a" { volume_id = aws_ebs_volume.test.id tags = { - Name = "tf-acc-test-ec2-ebs-snapshot-data-source-most-recent" + Name = %[1]q } } -resource "aws_ebs_snapshot" "test" { - volume_id = aws_ebs_snapshot.incorrect.volume_id +resource "aws_ebs_snapshot" "b" { + # Ensure that this snapshot is created after the other. + volume_id = aws_ebs_snapshot.a.volume_id tags = { - Name = "tf-acc-test-ec2-ebs-snapshot-data-source-most-recent" + Name = %[1]q } } @@ -157,7 +169,10 @@ data "aws_ebs_snapshot" "test" { filter { name = "tag:Name" - values = [aws_ebs_snapshot.test.tags.Name] + values = [%[1]q] } + + depends_on = [aws_ebs_snapshot.a, aws_ebs_snapshot.b] +} +`, rName)) } -` diff --git a/internal/service/ec2/ebs_snapshot_ids_data_source.go b/internal/service/ec2/ebs_snapshot_ids_data_source.go index 339e9bf3bf44..4c2d3de28739 100644 --- a/internal/service/ec2/ebs_snapshot_ids_data_source.go +++ b/internal/service/ec2/ebs_snapshot_ids_data_source.go @@ -60,7 +60,7 @@ func dataSourceEBSSnapshotIDsRead(d *schema.ResourceData, meta interface{}) erro snapshots, err := FindSnapshots(conn, input) if err != nil { - return fmt.Errorf("error reading EBS Snapshots: %w", err) + return fmt.Errorf("reading EBS Snapshots: %w", err) } sort.Slice(snapshots, func(i, j int) bool { diff --git a/internal/service/ec2/ebs_snapshot_ids_data_source_test.go b/internal/service/ec2/ebs_snapshot_ids_data_source_test.go index 4352bb8a2f6a..3bc4ac26149a 100644 --- a/internal/service/ec2/ebs_snapshot_ids_data_source_test.go +++ b/internal/service/ec2/ebs_snapshot_ids_data_source_test.go @@ -11,7 +11,7 @@ import ( ) func TestAccEC2EBSSnapshotIDsDataSource_basic(t *testing.T) { - datasourceName := "data.aws_ebs_snapshot_ids.test" + dataSourceName := "data.aws_ebs_snapshot_ids.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ @@ -22,8 +22,8 @@ func TestAccEC2EBSSnapshotIDsDataSource_basic(t *testing.T) { { Config: testAccEBSSnapshotIdsDataSourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - acctest.CheckResourceAttrGreaterThanValue(datasourceName, "ids.#", "0"), - resource.TestCheckTypeSetElemAttrPair(datasourceName, "ids.*", "aws_ebs_snapshot.test", "id"), + acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "ids.#", "0"), + resource.TestCheckTypeSetElemAttrPair(dataSourceName, "ids.*", "aws_ebs_snapshot.test", "id"), ), }, }, @@ -31,7 +31,7 @@ func TestAccEC2EBSSnapshotIDsDataSource_basic(t *testing.T) { } func TestAccEC2EBSSnapshotIDsDataSource_sorted(t *testing.T) { - datasourceName := "data.aws_ebs_snapshot_ids.test" + dataSourceName := "data.aws_ebs_snapshot_ids.test" resource1Name := "aws_ebs_snapshot.a" resource2Name := "aws_ebs_snapshot.b" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -44,9 +44,9 @@ func TestAccEC2EBSSnapshotIDsDataSource_sorted(t *testing.T) { { Config: testAccEBSSnapshotIdsDataSourceConfig_sorted(rName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(datasourceName, "ids.#", "2"), - resource.TestCheckResourceAttrPair(datasourceName, "ids.0", resource2Name, "id"), - resource.TestCheckResourceAttrPair(datasourceName, "ids.1", resource1Name, "id"), + resource.TestCheckResourceAttr(dataSourceName, "ids.#", "2"), + resource.TestCheckResourceAttrPair(dataSourceName, "ids.0", resource2Name, "id"), + resource.TestCheckResourceAttrPair(dataSourceName, "ids.1", resource1Name, "id"), ), }, }, diff --git a/internal/service/ec2/ec2_ami_ids_data_source_test.go b/internal/service/ec2/ec2_ami_ids_data_source_test.go index f7d1c387af5c..5d33d8827f25 100644 --- a/internal/service/ec2/ec2_ami_ids_data_source_test.go +++ b/internal/service/ec2/ec2_ami_ids_data_source_test.go @@ -34,7 +34,6 @@ func TestAccEC2AMIIDsDataSource_sorted(t *testing.T) { { Config: testAccAMIIdsDataSourceConfig_sorted(false), Check: resource.ComposeTestCheckFunc( - testAccCheckEBSSnapshotIDDataSource("data.aws_ami_ids.test"), resource.TestCheckResourceAttr("data.aws_ami_ids.test", "ids.#", "2"), resource.TestCheckResourceAttrPair( "data.aws_ami_ids.test", "ids.0", From 2d0969262b703a053d13c8aa2f7c368bebefdf18 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Jun 2022 09:22:58 -0400 Subject: [PATCH 5/7] r/aws_snapshot_create_volume_permission: Tidy up. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2EBSSnapshotCreateVolumePermission_' PKG=ec2 ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 2 -run=TestAccEC2EBSSnapshotCreateVolumePermission_ -timeout 180m === RUN TestAccEC2EBSSnapshotCreateVolumePermission_basic === PAUSE TestAccEC2EBSSnapshotCreateVolumePermission_basic === RUN TestAccEC2EBSSnapshotCreateVolumePermission_disappears === PAUSE TestAccEC2EBSSnapshotCreateVolumePermission_disappears === CONT TestAccEC2EBSSnapshotCreateVolumePermission_basic === CONT TestAccEC2EBSSnapshotCreateVolumePermission_disappears --- PASS: TestAccEC2EBSSnapshotCreateVolumePermission_disappears (73.08s) --- PASS: TestAccEC2EBSSnapshotCreateVolumePermission_basic (73.37s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 86.886s --- .../ebs_snapshot_create_volume_permission.go | 152 ++++++++---------- ..._snapshot_create_volume_permission_test.go | 149 ++++++++--------- internal/service/ec2/find.go | 42 +++++ 3 files changed, 182 insertions(+), 161 deletions(-) diff --git a/internal/service/ec2/ebs_snapshot_create_volume_permission.go b/internal/service/ec2/ebs_snapshot_create_volume_permission.go index 5cfe5b8ef89c..8b9392145d09 100644 --- a/internal/service/ec2/ebs_snapshot_create_volume_permission.go +++ b/internal/service/ec2/ebs_snapshot_create_volume_permission.go @@ -8,9 +8,10 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceSnapshotCreateVolumePermission() *schema.Resource { @@ -19,13 +20,18 @@ func ResourceSnapshotCreateVolumePermission() *schema.Resource { Read: resourceSnapshotCreateVolumePermissionRead, Delete: resourceSnapshotCreateVolumePermissionDelete, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(5 * time.Minute), + }, + Schema: map[string]*schema.Schema{ - "snapshot_id": { + "account_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "account_id": { + "snapshot_id": { Type: schema.TypeString, Required: true, ForceNew: true, @@ -37,37 +43,34 @@ func ResourceSnapshotCreateVolumePermission() *schema.Resource { func resourceSnapshotCreateVolumePermissionCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - snapshot_id := d.Get("snapshot_id").(string) - account_id := d.Get("account_id").(string) - - _, err := conn.ModifySnapshotAttribute(&ec2.ModifySnapshotAttributeInput{ - SnapshotId: aws.String(snapshot_id), - Attribute: aws.String("createVolumePermission"), + snapshotID := d.Get("snapshot_id").(string) + accountID := d.Get("account_id").(string) + id := EBSSnapshotCreateVolumePermissionCreateResourceID(snapshotID, accountID) + input := &ec2.ModifySnapshotAttributeInput{ + Attribute: aws.String(ec2.SnapshotAttributeNameCreateVolumePermission), CreateVolumePermission: &ec2.CreateVolumePermissionModifications{ Add: []*ec2.CreateVolumePermission{ - {UserId: aws.String(account_id)}, + {UserId: aws.String(accountID)}, }, }, - }) - if err != nil { - return fmt.Errorf("Error adding snapshot createVolumePermission: %s", err) + SnapshotId: aws.String(snapshotID), } - d.SetId(fmt.Sprintf("%s-%s", snapshot_id, account_id)) + log.Printf("[DEBUG] Creating EBS Snapshot CreateVolumePermission: %s", input) + _, err := conn.ModifySnapshotAttribute(input) - // Wait for the account to appear in the permission list - stateConf := &resource.StateChangeConf{ - Pending: []string{"denied"}, - Target: []string{"granted"}, - Refresh: resourceSnapshotCreateVolumePermissionStateRefreshFunc(conn, snapshot_id, account_id), - Timeout: 20 * time.Minute, - Delay: 10 * time.Second, - MinTimeout: 10 * time.Second, + if err != nil { + return fmt.Errorf("creating EBS Snapshot CreateVolumePermission (%s): %w", id, err) } - if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf( - "Error waiting for snapshot createVolumePermission (%s) to be added: %s", - d.Id(), err) + + d.SetId(id) + + _, err = tfresource.RetryWhenNotFound(d.Timeout(schema.TimeoutCreate), func() (interface{}, error) { + return FindCreateSnapshotCreateVolumePermissionByTwoPartKey(conn, snapshotID, accountID) + }) + + if err != nil { + return fmt.Errorf("waiting for EBS Snapshot CreateVolumePermission create (%s): %w", d.Id(), err) } return nil @@ -76,98 +79,81 @@ func resourceSnapshotCreateVolumePermissionCreate(d *schema.ResourceData, meta i func resourceSnapshotCreateVolumePermissionRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - snapshotID, accountID, err := SnapshotCreateVolumePermissionParseID(d.Id()) - if err != nil { - return err - } + snapshotID, accountID, err := EBSSnapshotCreateVolumePermissionParseResourceID(d.Id()) - exists, err := HasCreateVolumePermission(conn, snapshotID, accountID) if err != nil { return err } - if !exists { - log.Printf("[WARN] snapshot createVolumePermission (%s) not found, removing from state", d.Id()) + + _, err = FindCreateSnapshotCreateVolumePermissionByTwoPartKey(conn, snapshotID, accountID) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EBS Snapshot CreateVolumePermission %s not found, removing from state", d.Id()) d.SetId("") return nil } + if err != nil { + return fmt.Errorf("reading EBS Snapshot CreateVolumePermission (%s): %w", d.Id(), err) + } + return nil } func resourceSnapshotCreateVolumePermissionDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - snapshotID, accountID, err := SnapshotCreateVolumePermissionParseID(d.Id()) + snapshotID, accountID, err := EBSSnapshotCreateVolumePermissionParseResourceID(d.Id()) + if err != nil { return err } + log.Printf("[DEBUG] Deleting EBS Snapshot CreateVolumePermission: %s", d.Id()) _, err = conn.ModifySnapshotAttribute(&ec2.ModifySnapshotAttributeInput{ - SnapshotId: aws.String(snapshotID), - Attribute: aws.String("createVolumePermission"), + Attribute: aws.String(ec2.SnapshotAttributeNameCreateVolumePermission), CreateVolumePermission: &ec2.CreateVolumePermissionModifications{ Remove: []*ec2.CreateVolumePermission{ {UserId: aws.String(accountID)}, }, }, + SnapshotId: aws.String(snapshotID), }) - if err != nil { - return fmt.Errorf("Error removing snapshot createVolumePermission: %s", err) - } - // Wait for the account to disappear from the permission list - stateConf := &resource.StateChangeConf{ - Pending: []string{"granted"}, - Target: []string{"denied"}, - Refresh: resourceSnapshotCreateVolumePermissionStateRefreshFunc(conn, snapshotID, accountID), - Timeout: 5 * time.Minute, - Delay: 10 * time.Second, - MinTimeout: 10 * time.Second, + if tfawserr.ErrCodeEquals(err, errCodeInvalidSnapshotNotFound) { + return nil } - if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf( - "Error waiting for snapshot createVolumePermission (%s) to be removed: %s", - d.Id(), err) + + if err != nil { + return fmt.Errorf("deleting EBS Snapshot CreateVolumePermission (%s): %w", d.Id(), err) } - return nil -} + _, err = tfresource.RetryUntilNotFound(d.Timeout(schema.TimeoutDelete), func() (interface{}, error) { + return FindCreateSnapshotCreateVolumePermissionByTwoPartKey(conn, snapshotID, accountID) + }) -func HasCreateVolumePermission(conn *ec2.EC2, snapshot_id string, account_id string) (bool, error) { - _, state, err := resourceSnapshotCreateVolumePermissionStateRefreshFunc(conn, snapshot_id, account_id)() if err != nil { - return false, err - } - if state == "granted" { - return true, nil - } else { - return false, nil + return fmt.Errorf("waiting for EBS Snapshot CreateVolumePermission delete (%s): %w", d.Id(), err) } + + return nil } -func resourceSnapshotCreateVolumePermissionStateRefreshFunc(conn *ec2.EC2, snapshot_id string, account_id string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - attrs, err := conn.DescribeSnapshotAttribute(&ec2.DescribeSnapshotAttributeInput{ - SnapshotId: aws.String(snapshot_id), - Attribute: aws.String("createVolumePermission"), - }) - if err != nil { - return nil, "", fmt.Errorf("Error refreshing snapshot createVolumePermission state: %s", err) - } - - for _, vp := range attrs.CreateVolumePermissions { - if aws.StringValue(vp.UserId) == account_id { - return attrs, "granted", nil - } - } - return attrs, "denied", nil - } +const ebsSnapshotCreateVolumePermissionIDSeparator = "-" + +func EBSSnapshotCreateVolumePermissionCreateResourceID(snapshotID, accountID string) string { + parts := []string{snapshotID, accountID} + id := strings.Join(parts, ebsSnapshotCreateVolumePermissionIDSeparator) + + return id } -func SnapshotCreateVolumePermissionParseID(id string) (string, string, error) { - idParts := strings.SplitN(id, "-", 3) - if len(idParts) != 3 || idParts[0] != "snap" || idParts[1] == "" || idParts[2] == "" { - return "", "", fmt.Errorf("unexpected format of ID (%s), expected SNAPSHOT_ID-ACCOUNT_ID", id) +func EBSSnapshotCreateVolumePermissionParseResourceID(id string) (string, string, error) { + parts := strings.SplitN(id, ebsSnapshotCreateVolumePermissionIDSeparator, 3) + + if len(parts) != 3 || parts[0] != "snap" || parts[1] == "" || parts[2] == "" { + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected SNAPSHOT_ID%[2]sACCOUNT_ID", id, ebsSnapshotCreateVolumePermissionIDSeparator) } - return fmt.Sprintf("%s-%s", idParts[0], idParts[1]), idParts[2], nil + + return strings.Join([]string{parts[0], parts[1]}, ebsSnapshotCreateVolumePermissionIDSeparator), parts[2], nil } diff --git a/internal/service/ec2/ebs_snapshot_create_volume_permission_test.go b/internal/service/ec2/ebs_snapshot_create_volume_permission_test.go index 77b4e73aac3d..8a3d9ac62f1a 100644 --- a/internal/service/ec2/ebs_snapshot_create_volume_permission_test.go +++ b/internal/service/ec2/ebs_snapshot_create_volume_permission_test.go @@ -4,38 +4,37 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" + sdkacctest "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/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccEC2EBSSnapshotCreateVolumePermission_basic(t *testing.T) { - var snapshotId string - accountId := "111122223333" + var providers []*schema.Provider + resourceName := "aws_snapshot_create_volume_permission.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, + PreCheck: func() { + acctest.PreCheck(t) + acctest.PreCheckAlternateAccount(t) + }, ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), - ProviderFactories: acctest.ProviderFactories, + ProviderFactories: acctest.FactoriesAlternate(&providers), CheckDestroy: testAccSnapshotCreateVolumePermissionDestroy, Steps: []resource.TestStep{ - // Scaffold everything { - Config: testAccEBSSnapshotCreateVolumePermissionConfig_basic(true, accountId), + Config: testAccEBSSnapshotCreateVolumePermissionConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testCheckResourceGetAttr("aws_ebs_snapshot.test", "id", &snapshotId), - testAccSnapshotCreateVolumePermissionExists(&accountId, &snapshotId), - ), - }, - // Drop just create volume permission to test destruction - { - Config: testAccEBSSnapshotCreateVolumePermissionConfig_basic(false, accountId), - Check: resource.ComposeTestCheckFunc( - testAccSnapshotCreateVolumePermissionDestroyed(&accountId, &snapshotId), + testAccSnapshotCreateVolumePermissionExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "account_id"), + resource.TestCheckResourceAttrSet(resourceName, "snapshot_id"), ), }, }, @@ -43,21 +42,24 @@ func TestAccEC2EBSSnapshotCreateVolumePermission_basic(t *testing.T) { } func TestAccEC2EBSSnapshotCreateVolumePermission_disappears(t *testing.T) { - var snapshotId string - accountId := "111122223333" + var providers []*schema.Provider + resourceName := "aws_snapshot_create_volume_permission.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, + PreCheck: func() { + acctest.PreCheck(t) + acctest.PreCheckAlternateAccount(t) + }, ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), - ProviderFactories: acctest.ProviderFactories, + ProviderFactories: acctest.FactoriesAlternate(&providers), CheckDestroy: testAccSnapshotCreateVolumePermissionDestroy, Steps: []resource.TestStep{ { - Config: testAccEBSSnapshotCreateVolumePermissionConfig_basic(true, accountId), + Config: testAccEBSSnapshotCreateVolumePermissionConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testCheckResourceGetAttr("aws_ebs_snapshot.test", "id", &snapshotId), - testAccSnapshotCreateVolumePermissionExists(&accountId, &snapshotId), - acctest.CheckResourceDisappears(acctest.Provider, tfec2.ResourceSnapshotCreateVolumePermission(), "aws_snapshot_create_volume_permission.test"), + testAccSnapshotCreateVolumePermissionExists(resourceName), + acctest.CheckResourceDisappears(acctest.Provider, tfec2.ResourceSnapshotCreateVolumePermission(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -65,24 +67,6 @@ func TestAccEC2EBSSnapshotCreateVolumePermission_disappears(t *testing.T) { }) } -func testCheckResourceGetAttr(name, key string, value *string) resource.TestCheckFunc { - return func(s *terraform.State) error { - ms := s.RootModule() - rs, ok := ms.Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } - - is := rs.Primary - if is == nil { - return fmt.Errorf("No primary instance: %s", name) - } - - *value = is.Attributes[key] - return nil - } -} - func testAccSnapshotCreateVolumePermissionDestroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn @@ -91,77 +75,86 @@ func testAccSnapshotCreateVolumePermissionDestroy(s *terraform.State) error { continue } - snapshotID, accountID, err := tfec2.SnapshotCreateVolumePermissionParseID(rs.Primary.ID) + snapshotID, accountID, err := tfec2.EBSSnapshotCreateVolumePermissionParseResourceID(rs.Primary.ID) + if err != nil { return err } - if has, err := tfec2.HasCreateVolumePermission(conn, snapshotID, accountID); err != nil { + + _, err = tfec2.FindCreateSnapshotCreateVolumePermissionByTwoPartKey(conn, snapshotID, accountID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { return err - } else if has { - return fmt.Errorf("create volume permission still exist for '%s' on '%s'", accountID, snapshotID) } + + return fmt.Errorf("EBS Snapshot CreateVolumePermission %s still exists", rs.Primary.ID) } return nil } -func testAccSnapshotCreateVolumePermissionExists(accountId, snapshotId *string) resource.TestCheckFunc { +func testAccSnapshotCreateVolumePermissionExists(n string) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn - if has, err := tfec2.HasCreateVolumePermission(conn, aws.StringValue(snapshotId), aws.StringValue(accountId)); err != nil { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EBS Snapshot CreateVolumePermission ID is set") + } + + snapshotID, accountID, err := tfec2.EBSSnapshotCreateVolumePermissionParseResourceID(rs.Primary.ID) + + if err != nil { return err - } else if !has { - return fmt.Errorf("create volume permission does not exist for '%s' on '%s'", aws.StringValue(snapshotId), aws.StringValue(accountId)) } - return nil - } -} -func testAccSnapshotCreateVolumePermissionDestroyed(accountId, snapshotId *string) resource.TestCheckFunc { - return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn - if has, err := tfec2.HasCreateVolumePermission(conn, aws.StringValue(snapshotId), aws.StringValue(accountId)); err != nil { + + _, err = tfec2.FindCreateSnapshotCreateVolumePermissionByTwoPartKey(conn, snapshotID, accountID) + + if err != nil { return err - } else if has { - return fmt.Errorf("create volume permission still exists for '%s' on '%s'", aws.StringValue(snapshotId), aws.StringValue(accountId)) } + return nil } } -func testAccEBSSnapshotCreateVolumePermissionConfig_basic(includeCreateVolumePermission bool, accountID string) string { - base := ` -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - +func testAccEBSSnapshotCreateVolumePermissionConfig_basic(rName string) string { + return acctest.ConfigCompose( + acctest.ConfigAlternateAccountProvider(), + acctest.ConfigAvailableAZsNoOptIn(), + fmt.Sprintf(` resource "aws_ebs_volume" "test" { availability_zone = data.aws_availability_zones.available.names[0] size = 1 tags = { - Name = "ebs_snap_perm" + Name = %[1]q } } resource "aws_ebs_snapshot" "test" { volume_id = aws_ebs_volume.test.id + + tags = { + Name = %[1]q + } } -` - if !includeCreateVolumePermission { - return base - } +data "aws_caller_identity" "test" { + provider = "awsalternate" +} - return base + fmt.Sprintf(` resource "aws_snapshot_create_volume_permission" "test" { snapshot_id = aws_ebs_snapshot.test.id - account_id = %q + account_id = data.aws_caller_identity.test.account_id } -`, accountID) +`, rName)) } diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index f3e19d22ce36..c8b472e7ec0f 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -4640,6 +4640,48 @@ func FindSnapshotByID(conn *ec2.EC2, id string) (*ec2.Snapshot, error) { return output, nil } +func FindSnapshotAttribute(conn *ec2.EC2, input *ec2.DescribeSnapshotAttributeInput) (*ec2.DescribeSnapshotAttributeOutput, error) { + output, err := conn.DescribeSnapshotAttribute(input) + + if tfawserr.ErrCodeEquals(err, errCodeInvalidSnapshotNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} + +func FindCreateSnapshotCreateVolumePermissionByTwoPartKey(conn *ec2.EC2, snapshotID, accountID string) (*ec2.CreateVolumePermission, error) { + input := &ec2.DescribeSnapshotAttributeInput{ + Attribute: aws.String(ec2.SnapshotAttributeNameCreateVolumePermission), + SnapshotId: aws.String(snapshotID), + } + + output, err := FindSnapshotAttribute(conn, input) + + if err != nil { + return nil, err + } + + for _, v := range output.CreateVolumePermissions { + if aws.StringValue(v.UserId) == accountID { + return v, nil + } + } + + return nil, &resource.NotFoundError{LastRequest: input} +} + func FindFindSnapshotTierStatuses(conn *ec2.EC2, input *ec2.DescribeSnapshotTierStatusInput) ([]*ec2.SnapshotTierStatus, error) { var output []*ec2.SnapshotTierStatus From 23ec2a2a1d0c7195957ec3d6e4be558a75d34a16 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Jun 2022 09:56:14 -0400 Subject: [PATCH 6/7] r/aws_snapshot_create_volume_permission: Error if 'account_id' is the snapshot's owner. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2EBSSnapshotCreateVolumePermission_snapshotOwnerExpectError' PKG=ec2 ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 2 -run=TestAccEC2EBSSnapshotCreateVolumePermission_snapshotOwnerExpectError -timeout 180m === RUN TestAccEC2EBSSnapshotCreateVolumePermission_snapshotOwnerExpectError === PAUSE TestAccEC2EBSSnapshotCreateVolumePermission_snapshotOwnerExpectError === CONT TestAccEC2EBSSnapshotCreateVolumePermission_snapshotOwnerExpectError --- PASS: TestAccEC2EBSSnapshotCreateVolumePermission_snapshotOwnerExpectError (49.27s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 58.255s --- .../ebs_snapshot_create_volume_permission.go | 23 +++++++++ ..._snapshot_create_volume_permission_test.go | 47 +++++++++++++++++++ ...hot_create_volume_permission.html.markdown | 2 +- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/internal/service/ec2/ebs_snapshot_create_volume_permission.go b/internal/service/ec2/ebs_snapshot_create_volume_permission.go index 8b9392145d09..f5e205f7b7a2 100644 --- a/internal/service/ec2/ebs_snapshot_create_volume_permission.go +++ b/internal/service/ec2/ebs_snapshot_create_volume_permission.go @@ -1,6 +1,7 @@ package ec2 import ( + "context" "fmt" "log" "strings" @@ -20,6 +21,8 @@ func ResourceSnapshotCreateVolumePermission() *schema.Resource { Read: resourceSnapshotCreateVolumePermissionRead, Delete: resourceSnapshotCreateVolumePermissionDelete, + CustomizeDiff: resourceSnapshotCreateVolumePermissionCustomizeDiff, + Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(20 * time.Minute), Delete: schema.DefaultTimeout(5 * time.Minute), @@ -139,6 +142,26 @@ func resourceSnapshotCreateVolumePermissionDelete(d *schema.ResourceData, meta i return nil } +func resourceSnapshotCreateVolumePermissionCustomizeDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { + if diff.Id() == "" { + if snapshotID := diff.Get("snapshot_id").(string); snapshotID != "" { + conn := meta.(*conns.AWSClient).EC2Conn + + snapshot, err := FindSnapshotByID(conn, snapshotID) + + if err != nil { + return fmt.Errorf("reading EBS Snapshot (%s): %w", snapshotID, err) + } + + if accountID := diff.Get("account_id").(string); aws.StringValue(snapshot.OwnerId) == accountID { + return fmt.Errorf("AWS Account (%s) owns EBS Snapshot (%s)", accountID, snapshotID) + } + } + } + + return nil +} + const ebsSnapshotCreateVolumePermissionIDSeparator = "-" func EBSSnapshotCreateVolumePermissionCreateResourceID(snapshotID, accountID string) string { diff --git a/internal/service/ec2/ebs_snapshot_create_volume_permission_test.go b/internal/service/ec2/ebs_snapshot_create_volume_permission_test.go index 8a3d9ac62f1a..7b18cf307b57 100644 --- a/internal/service/ec2/ebs_snapshot_create_volume_permission_test.go +++ b/internal/service/ec2/ebs_snapshot_create_volume_permission_test.go @@ -2,6 +2,7 @@ package ec2_test import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/service/ec2" @@ -67,6 +68,23 @@ func TestAccEC2EBSSnapshotCreateVolumePermission_disappears(t *testing.T) { }) } +func TestAccEC2EBSSnapshotCreateVolumePermission_snapshotOwnerExpectError(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccSnapshotCreateVolumePermissionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEBSSnapshotCreateVolumePermissionConfig_snapshotOwner(rName), + ExpectError: regexp.MustCompile(`owns EBS Snapshot`), + }, + }, + }) +} + func testAccSnapshotCreateVolumePermissionDestroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn @@ -158,3 +176,32 @@ resource "aws_snapshot_create_volume_permission" "test" { } `, rName)) } + +func testAccEBSSnapshotCreateVolumePermissionConfig_snapshotOwner(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), + fmt.Sprintf(` +resource "aws_ebs_volume" "test" { + availability_zone = data.aws_availability_zones.available.names[0] + size = 1 + + tags = { + Name = %[1]q + } +} + +resource "aws_ebs_snapshot" "test" { + volume_id = aws_ebs_volume.test.id + + tags = { + Name = %[1]q + } +} + +data "aws_caller_identity" "test" {} + +resource "aws_snapshot_create_volume_permission" "test" { + snapshot_id = aws_ebs_snapshot.test.id + account_id = data.aws_caller_identity.test.account_id +} +`, rName)) +} diff --git a/website/docs/r/snapshot_create_volume_permission.html.markdown b/website/docs/r/snapshot_create_volume_permission.html.markdown index e3b27031e616..7e27d842084f 100644 --- a/website/docs/r/snapshot_create_volume_permission.html.markdown +++ b/website/docs/r/snapshot_create_volume_permission.html.markdown @@ -33,7 +33,7 @@ resource "aws_ebs_snapshot" "example_snapshot" { The following arguments are supported: * `snapshot_id` - (required) A snapshot ID -* `account_id` - (required) An AWS Account ID to add create volume permissions +* `account_id` - (required) An AWS Account ID to add create volume permissions. The AWS Account cannot be the snapshot's owner ## Attributes Reference From 84db5c77ee92f574898be07b991a6ada8013238e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Jun 2022 10:06:27 -0400 Subject: [PATCH 7/7] Add CHANGELOG entry. --- .changelog/12103.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/12103.txt diff --git a/.changelog/12103.txt b/.changelog/12103.txt new file mode 100644 index 000000000000..765b74e5a152 --- /dev/null +++ b/.changelog/12103.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_snapshot_create_volume_permission: Error if `account_id` is the snapshot's owner +``` \ No newline at end of file