Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for default_tags and tags on EC2 block devices #37441

Merged
merged 18 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/37441.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_instance: Fixed issues with `volume_tags`, `root_block_device.*.tags`, and `ebs_block_device.*.tags` where tags overlapped with default tags. These are now handled consistently with top-level tags throughout the provider. Specifically, tags defined in both locations are no longer removed, preventing erroneous differences.
```
2 changes: 1 addition & 1 deletion internal/provider/intercept.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ func (r tagsResourceInterceptor) run(ctx context.Context, d schemaResourceData,
tags := tagsInContext.TagsOut.UnwrapOrDefault().IgnoreSystem(inContext.ServicePackageName).IgnoreConfig(tagsInContext.IgnoreConfig)

// The resource's configured tags can now include duplicate tags that have been configured on the provider.
if err := d.Set(names.AttrTags, tags.ResolveDuplicates(ctx, tagsInContext.DefaultConfig, tagsInContext.IgnoreConfig, d).Map()); err != nil {
if err := d.Set(names.AttrTags, tags.ResolveDuplicates(ctx, tagsInContext.DefaultConfig, tagsInContext.IgnoreConfig, d, names.AttrTags, nil).Map()); err != nil {
return ctx, sdkdiag.AppendErrorf(diags, "setting %s: %s", names.AttrTags, err)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/provider/tags_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func tagsReadFunc(ctx context.Context, d schemaResourceData, sp conns.ServicePac
toAdd := tagsInContext.TagsOut.UnwrapOrDefault().IgnoreSystem(inContext.ServicePackageName).IgnoreConfig(tagsInContext.IgnoreConfig)

// The resource's configured tags can now include duplicate tags that have been configured on the provider.
if err := d.Set(names.AttrTags, toAdd.ResolveDuplicates(ctx, tagsInContext.DefaultConfig, tagsInContext.IgnoreConfig, d).Map()); err != nil {
if err := d.Set(names.AttrTags, toAdd.ResolveDuplicates(ctx, tagsInContext.DefaultConfig, tagsInContext.IgnoreConfig, d, names.AttrTags, nil).Map()); err != nil {
return ctx, sdkdiag.AppendErrorf(diags, "setting %s: %s", names.AttrTags, err)
}

Expand Down
40 changes: 32 additions & 8 deletions internal/service/ec2/ec2_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ec2"
awstypes "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/hashicorp/aws-sdk-go-base/v2/tfawserr"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
Expand Down Expand Up @@ -1335,7 +1336,7 @@ func resourceInstanceRead(ctx context.Context, d *schema.ResourceData, meta inte
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig
tags := keyValueTags(ctx, volumeTags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)

if err := d.Set("volume_tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
if err := d.Set("volume_tags", tags.ResolveDuplicates(ctx, defaultTagsConfig, ignoreTagsConfig, d, "volume_tags", nil).Map()); err != nil {
return sdkdiag.AppendErrorf(diags, "setting volume_tags: %s", err)
}
}
Expand Down Expand Up @@ -2352,13 +2353,36 @@ func readBlockDevicesFromInstance(ctx context.Context, d *schema.ResourceData, m
if instanceBd.DeviceName != nil {
bd[names.AttrDeviceName] = aws.ToString(instanceBd.DeviceName)
}
if v, ok := d.GetOk("volume_tags"); !ok || v == nil || len(v.(map[string]interface{})) == 0 {
if ds {
bd[names.AttrTags] = keyValueTags(ctx, vol.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()
} else {
tags := keyValueTags(ctx, vol.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)
bd[names.AttrTags] = tags.RemoveDefaultConfig(defaultTagsConfig).Map()
bd[names.AttrTagsAll] = tags.Map()
if v, ok := d.GetOk("volume_tags"); (!ok || v == nil || len(v.(map[string]interface{})) == 0) && ds {
bd[names.AttrTags] = keyValueTags(ctx, vol.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()
}

if v, ok := d.GetOk("volume_tags"); (!ok || v == nil || len(v.(map[string]interface{})) == 0) && !ds {
tags := keyValueTags(ctx, vol.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)

// default setup, in case we don't find config for the block device (don't resolve duplicates)
bd[names.AttrTags] = tags.Map()
bd[names.AttrTagsAll] = tags.Map()

if v, ok := d.GetOk("ebs_block_device"); ok && v.(*schema.Set).Len() > 0 {
ebdList := v.(*schema.Set).List()
for _, ebd := range ebdList {
ebd, ok := ebd.(map[string]interface{})
if !ok {
continue
}

if ebd[names.AttrDeviceName] == aws.ToString(instanceBd.DeviceName) {
bd[names.AttrTags] = tags.ResolveDuplicates(ctx, defaultTagsConfig, ignoreTagsConfig, d, fmt.Sprintf("ebs_block_device[%s].tags", aws.ToString(instanceBd.DeviceName)), func(attr string, val cty.Value) bool {
return val.GetAttr(names.AttrDeviceName).AsString() == attr
}).Map()
break
}
}
}

if v, ok := d.GetOk("root_block_device"); ok && len(v.([]interface{})) > 0 && blockDeviceIsRoot(instanceBd, instance) {
bd[names.AttrTags] = tags.ResolveDuplicates(ctx, defaultTagsConfig, ignoreTagsConfig, d, "root_block_device[0].tags", nil).Map()
}
}

Expand Down
203 changes: 192 additions & 11 deletions internal/service/ec2/ec2_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1587,7 +1587,7 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsVolumeTags(t *testing.T) {

emptyMap := map[string]string{}
mapWithOneKey1 := map[string]string{"brodo": "baggins"}
mapWithOneKey2 := map[string]string{"every": "gnomes"}
mapWithOneKey2 := map[string]string{"every": "gnomesie"}
mapWithTwoKeys := map[string]string{"brodo": "baggins", "jelly": "bean"}

resource.ParallelTest(t, resource.TestCase{
Expand All @@ -1605,13 +1605,13 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsVolumeTags(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "volume_tags.%", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags.%", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.%", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.every", "gnomes"),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.every", "gnomesie"),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags.%", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags_all.%", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags_all.every", "gnomes"),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags_all.every", "gnomesie"),
),
},
{ // 1 defaultTags + 1 volumeTags
{ // 1 defaultTags + 1 volumeTags (no overlap)
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(mapWithOneKey2, mapWithOneKey1, emptyMap, emptyMap),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &v),
Expand All @@ -1621,7 +1621,7 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsVolumeTags(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "volume_tags.brodo", "baggins"),
),
},
{ // 1 defaultTags + 2 volumeTags
{ // 1 defaultTags + 2 volumeTags (no overlap)
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(mapWithOneKey2, mapWithTwoKeys, emptyMap, emptyMap),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &v),
Expand All @@ -1632,7 +1632,7 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsVolumeTags(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "volume_tags.jelly", "bean"),
),
},
{ // 1 defaultTags
{ // 1 defaultTags (no overlap)
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(mapWithOneKey2, emptyMap, emptyMap, emptyMap),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &v),
Expand All @@ -1641,7 +1641,7 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsVolumeTags(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "volume_tags.%", acctest.Ct0),
),
},
{ // no tags
{ // no tags (no overlap)
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(emptyMap, emptyMap, emptyMap, emptyMap),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &v),
Expand All @@ -1662,7 +1662,7 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsEBSRoot(t *testing.T) {

emptyMap := map[string]string{}
mapWithOneKey1 := map[string]string{"gigi": "kitty"}
mapWithOneKey2 := map[string]string{"every": "gnomes"}
mapWithOneKey2 := map[string]string{"every": "gnomesie"}
mapWithTwoKeys1 := map[string]string{"brodo": "baggins", "jelly": "bean"}
mapWithTwoKeys2 := map[string]string{"brodo": "baggins", "jelly": "andrew"}

Expand All @@ -1684,7 +1684,7 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsEBSRoot(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags.%", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags_all.%", acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags_all.gigi", "kitty"),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags_all.every", "gnomes"),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags_all.every", "gnomesie"),
),
},
{ // 1 defaultTags + 2 rootTags + 1 ebsTags
Expand All @@ -1696,7 +1696,7 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsEBSRoot(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "volume_tags.%", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags.%", acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.%", acctest.Ct3),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.every", "gnomes"),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.every", "gnomesie"),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.brodo", "baggins"),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.jelly", "bean"),
),
Expand All @@ -1710,7 +1710,7 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsEBSRoot(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "volume_tags.%", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags.%", acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.%", acctest.Ct3),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.every", "gnomes"),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.every", "gnomesie"),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.brodo", "baggins"),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.jelly", "andrew"),
),
Expand All @@ -1732,6 +1732,140 @@ func TestAccEC2Instance_BlockDeviceTags_defaultTagsEBSRoot(t *testing.T) {
})
}

func TestAccEC2Instance_BlockDeviceTags_defaultTagsRBDOverlap(t *testing.T) {
ctx := acctest.Context(t)
var v awstypes.Instance
resourceName := "aws_instance.test"

emptyMap := map[string]string{}

// default tags and root tags overlapping is causing perpetual diffs
defTags := map[string]string{"every": "gnomesie", "iz": "paws", "gigi": "kitty", "brodo": "baggins"}
rbdTags := map[string]string{"gigi": "kitty", "brodo": "baggins", "tristy": "boo", "jelly": "bean"}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckInstanceDestroy(ctx),
Steps: []resource.TestStep{
{ // 4 defaultTags + 4 rbdTags with 2 in common
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(defTags, emptyMap, rbdTags, emptyMap),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsAllPercent, acctest.Ct4),
resource.TestCheckResourceAttr(resourceName, "volume_tags.%", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags.%", acctest.Ct4),
resource.TestCheckResourceAttr(resourceName, "root_block_device.0.tags_all.%", "6"),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags.%", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags_all.%", acctest.Ct4),
),
},
{ // 4 defaultTags + 4 rbdTags with 2 in common
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(defTags, emptyMap, rbdTags, emptyMap),
PlanOnly: true,
},
},
})
}

func TestAccEC2Instance_BlockDeviceTags_defaultTagsEBDOverlaps(t *testing.T) {
ctx := acctest.Context(t)
var v awstypes.Instance
resourceName := "aws_instance.test"

emptyMap := map[string]string{}

// default tags and root tags overlapping is causing perpetual diffs
defTags := map[string]string{"every": "gnomesie", "iz": "paws", "gigi": "kitty", "brodo": "baggins"}
ebdTags2 := map[string]string{"gigi": "kitty", "brodo": "baggins", "tristy": "boo", "jelly": "bean"}
ebdTags3 := map[string]string{"gigi": "kitty", "brodo": "baggins"}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckInstanceDestroy(ctx),
Steps: []resource.TestStep{
{ // 4 defaultTags + 4 rbdTags with 2 in common
Config: testAccInstanceConfig_blockDeviceTagsDefault3EBS(defTags, emptyMap, ebdTags2, ebdTags3),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsAllPercent, acctest.Ct4),
resource.TestCheckResourceAttr(resourceName, "volume_tags.%", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags.%", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.0.tags_all.%", acctest.Ct4),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.1.tags.%", acctest.Ct4),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.1.tags_all.%", "6"),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.2.tags.%", acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, "ebs_block_device.2.tags_all.%", acctest.Ct4),
),
},
},
})
}

func TestAccEC2Instance_BlockDeviceTags_defaultTagsVolumeTagsOverlap(t *testing.T) {
ctx := acctest.Context(t)
var v awstypes.Instance
resourceName := "aws_instance.test"

emptyMap := map[string]string{}
defTags := map[string]string{"brodo": "baggins", "jelly": "bean"}
volTags1 := map[string]string{"every": "gnomesie"}
volTags2 := map[string]string{"brodo": "baggins"}
volTags3 := map[string]string{"every": "gnomesie", "iz": "paws", "gigi": "kitty", "brodo": "baggins", "jelly": "bean"}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckInstanceDestroy(ctx),
Steps: []resource.TestStep{
{ // 2 defaultTags + 1 volumeTags (no overlap)
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(defTags, volTags1, emptyMap, emptyMap),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsAllPercent, acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, "volume_tags.%", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "volume_tags.every", "gnomesie"),
),
},
{ // 2 defaultTags + 1 volumeTags (overlap)
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(defTags, volTags2, emptyMap, emptyMap),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsAllPercent, acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, "volume_tags.%", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "volume_tags.brodo", "baggins"),
),
},
{ // 2 defaultTags + 5 volumeTags (overlap)
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(defTags, volTags3, emptyMap, emptyMap),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsAllPercent, acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, "volume_tags.%", "5"),
resource.TestCheckResourceAttr(resourceName, "volume_tags.brodo", "baggins"), // overlap
resource.TestCheckResourceAttr(resourceName, "volume_tags.jelly", "bean"), // overlap
resource.TestCheckResourceAttr(resourceName, "volume_tags.every", "gnomesie"), // non-overlap
resource.TestCheckResourceAttr(resourceName, "volume_tags.iz", "paws"), // non-overlap
resource.TestCheckResourceAttr(resourceName, "volume_tags.gigi", "kitty"), // non-overlap
),
},
{ // 2 defaultTags + 5 volumeTags (overlap)
Config: testAccInstanceConfig_blockDeviceTagsDefaultVolumeRBDEBS(defTags, volTags3, emptyMap, emptyMap),
PlanOnly: true,
},
},
})
}

func TestAccEC2Instance_instanceProfileChange(t *testing.T) {
ctx := acctest.Context(t)
var v awstypes.Instance
Expand Down Expand Up @@ -7030,6 +7164,53 @@ resource "aws_instance" "test" {
`, defTgCfg, volTgCfg, rbdTgCfg, ebsTgCfg))
}

func testAccInstanceConfig_blockDeviceTagsDefault3EBS(defTg, ebsTg1, ebsTg2, ebsTg3 map[string]string) string {
//lintignore:AT004
return acctest.ConfigCompose(
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
fmt.Sprintf(`
provider "aws" {
default_tags {
tags = {
%[1]s
}
}
}

resource "aws_instance" "test" {
ami = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
instance_type = "t2.medium"

ebs_block_device {
device_name = "/dev/sdb"
volume_size = 1

tags = {
%[2]s
}
}

ebs_block_device {
device_name = "/dev/sdc"
volume_size = 1

tags = {
%[3]s
}
}

ebs_block_device {
device_name = "/dev/sdd"
volume_size = 1

tags = {
%[4]s
}
}
}
`, mapToTagConfig(defTg, 6), mapToTagConfig(ebsTg1, 6), mapToTagConfig(ebsTg2, 6), mapToTagConfig(ebsTg3, 6)))
}

func testAccInstanceConfig_blockDeviceTagsEBSTags(rName string) string {
return acctest.ConfigCompose(acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(), fmt.Sprintf(`
resource "aws_instance" "test" {
Expand Down
Loading
Loading