From 8afe0ef60ad5b80c06d2eab80210b35e2a4a0e68 Mon Sep 17 00:00:00 2001 From: Sergiusz Urbaniak Date: Wed, 26 Apr 2017 18:53:11 +0200 Subject: [PATCH] providers/aws: add tags for resource_aws_autoscaling_group The existing "tag" field on autoscaling groups is very limited in that it cannot be used in conjunction with interpolation preventing from adding dynamic tag entries. Other AWS resources don't have this restriction on tags because they work directly on the map type. AWS autoscaling groups on the other hand have an additional field "propagate_at_launch" which is not usable with a pure map type. This fixes it by introducing an additional field called "tags" which allows specifying a list of maps. This preserves the possibility to declare tags as with the "tag" field but additionally allows to construct lists of maps using interpolation syntax. --- builtin/providers/aws/autoscaling_tags.go | 191 ++++++++++++++---- .../providers/aws/autoscaling_tags_test.go | 24 ++- .../aws/import_aws_autoscaling_group_test.go | 2 +- .../aws/resource_aws_autoscaling_group.go | 79 +++++++- .../resource_aws_autoscaling_group_test.go | 125 ++++++++++-- .../aws/r/autoscaling_group.html.markdown | 56 +++++ 6 files changed, 414 insertions(+), 63 deletions(-) diff --git a/builtin/providers/aws/autoscaling_tags.go b/builtin/providers/aws/autoscaling_tags.go index e9ca8531aedf..aac563d2f202 100644 --- a/builtin/providers/aws/autoscaling_tags.go +++ b/builtin/providers/aws/autoscaling_tags.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "regexp" + "strconv" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/autoscaling" @@ -12,8 +13,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" ) -// tagsSchema returns the schema to use for tags. -func autoscalingTagsSchema() *schema.Schema { +// autoscalingTagSchema returns the schema to use for the tag element. +func autoscalingTagSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -35,11 +36,11 @@ func autoscalingTagsSchema() *schema.Schema { }, }, }, - Set: autoscalingTagsToHash, + Set: autoscalingTagToHash, } } -func autoscalingTagsToHash(v interface{}) int { +func autoscalingTagToHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) buf.WriteString(fmt.Sprintf("%s-", m["key"].(string))) @@ -52,35 +53,74 @@ func autoscalingTagsToHash(v interface{}) int { // setTags is a helper to set the tags for a resource. It expects the // tags field to be named "tag" func setAutoscalingTags(conn *autoscaling.AutoScaling, d *schema.ResourceData) error { - if d.HasChange("tag") { + resourceID := d.Get("name").(string) + var createTags, removeTags []*autoscaling.Tag + + if d.HasChange("tag") || d.HasChange("tags") { oraw, nraw := d.GetChange("tag") o := setToMapByKey(oraw.(*schema.Set), "key") n := setToMapByKey(nraw.(*schema.Set), "key") - resourceID := d.Get("name").(string) - c, r := diffAutoscalingTags( - autoscalingTagsFromMap(o, resourceID), - autoscalingTagsFromMap(n, resourceID), - resourceID) - create := autoscaling.CreateOrUpdateTagsInput{ - Tags: c, + old, err := autoscalingTagsFromMap(o, resourceID) + if err != nil { + return err + } + + new, err := autoscalingTagsFromMap(n, resourceID) + if err != nil { + return err } + + c, r, err := diffAutoscalingTags(old, new, resourceID) + if err != nil { + return err + } + + createTags = append(createTags, c...) + removeTags = append(removeTags, r...) + + oraw, nraw = d.GetChange("tags") + old, err = autoscalingTagsFromList(oraw.([]interface{}), resourceID) + if err != nil { + return err + } + + new, err = autoscalingTagsFromList(nraw.([]interface{}), resourceID) + if err != nil { + return err + } + + c, r, err = diffAutoscalingTags(old, new, resourceID) + if err != nil { + return err + } + + createTags = append(createTags, c...) + removeTags = append(removeTags, r...) + } + + // Set tags + if len(removeTags) > 0 { + log.Printf("[DEBUG] Removing autoscaling tags: %#v", removeTags) + remove := autoscaling.DeleteTagsInput{ - Tags: r, + Tags: removeTags, + } + + if _, err := conn.DeleteTags(&remove); err != nil { + return err } + } + + if len(createTags) > 0 { + log.Printf("[DEBUG] Creating autoscaling tags: %#v", createTags) - // Set tags - if len(r) > 0 { - log.Printf("[DEBUG] Removing autoscaling tags: %#v", r) - if _, err := conn.DeleteTags(&remove); err != nil { - return err - } + create := autoscaling.CreateOrUpdateTagsInput{ + Tags: createTags, } - if len(c) > 0 { - log.Printf("[DEBUG] Creating autoscaling tags: %#v", c) - if _, err := conn.CreateOrUpdateTags(&create); err != nil { - return err - } + + if _, err := conn.CreateOrUpdateTags(&create); err != nil { + return err } } @@ -90,11 +130,12 @@ func setAutoscalingTags(conn *autoscaling.AutoScaling, d *schema.ResourceData) e // diffTags takes our tags locally and the ones remotely and returns // the set of tags that must be created, and the set of tags that must // be destroyed. -func diffAutoscalingTags(oldTags, newTags []*autoscaling.Tag, resourceID string) ([]*autoscaling.Tag, []*autoscaling.Tag) { +func diffAutoscalingTags(oldTags, newTags []*autoscaling.Tag, resourceID string) ([]*autoscaling.Tag, []*autoscaling.Tag, error) { // First, we're creating everything we have create := make(map[string]interface{}) for _, t := range newTags { tag := map[string]interface{}{ + "key": *t.Key, "value": *t.Value, "propagate_at_launch": *t.PropagateAtLaunch, } @@ -112,27 +153,99 @@ func diffAutoscalingTags(oldTags, newTags []*autoscaling.Tag, resourceID string) } } - return autoscalingTagsFromMap(create, resourceID), remove + createTags, err := autoscalingTagsFromMap(create, resourceID) + if err != nil { + return nil, nil, err + } + + return createTags, remove, nil +} + +func autoscalingTagsFromList(vs []interface{}, resourceID string) ([]*autoscaling.Tag, error) { + result := make([]*autoscaling.Tag, 0, len(vs)) + for _, tag := range vs { + attr, ok := tag.(map[string]interface{}) + if !ok { + continue + } + + t, err := autoscalingTagFromMap(attr, resourceID) + if err != nil { + return nil, err + } + + if t != nil { + result = append(result, t) + } + } + return result, nil } // tagsFromMap returns the tags for the given map of data. -func autoscalingTagsFromMap(m map[string]interface{}, resourceID string) []*autoscaling.Tag { +func autoscalingTagsFromMap(m map[string]interface{}, resourceID string) ([]*autoscaling.Tag, error) { result := make([]*autoscaling.Tag, 0, len(m)) - for k, v := range m { - attr := v.(map[string]interface{}) - t := &autoscaling.Tag{ - Key: aws.String(k), - Value: aws.String(attr["value"].(string)), - PropagateAtLaunch: aws.Bool(attr["propagate_at_launch"].(bool)), - ResourceId: aws.String(resourceID), - ResourceType: aws.String("auto-scaling-group"), - } - if !tagIgnoredAutoscaling(t) { + for _, v := range m { + attr, ok := v.(map[string]interface{}) + if !ok { + continue + } + + t, err := autoscalingTagFromMap(attr, resourceID) + if err != nil { + return nil, err + } + + if t != nil { result = append(result, t) } } - return result + return result, nil +} + +func autoscalingTagFromMap(attr map[string]interface{}, resourceID string) (*autoscaling.Tag, error) { + if _, ok := attr["key"]; !ok { + return nil, fmt.Errorf("%s: invalid tag attributes: key missing", resourceID) + } + + if _, ok := attr["value"]; !ok { + return nil, fmt.Errorf("%s: invalid tag attributes: value missing", resourceID) + } + + if _, ok := attr["propagate_at_launch"]; !ok { + return nil, fmt.Errorf("%s: invalid tag attributes: propagate_at_launch missing", resourceID) + } + + var propagate_at_launch bool + var err error + + if v, ok := attr["propagate_at_launch"].(bool); ok { + propagate_at_launch = v + } + + if v, ok := attr["propagate_at_launch"].(string); ok { + if propagate_at_launch, err = strconv.ParseBool(v); err != nil { + return nil, fmt.Errorf( + "%s: invalid tag attribute: invalid value for propagate_at_launch: %s", + resourceID, + v, + ) + } + } + + t := &autoscaling.Tag{ + Key: aws.String(attr["key"].(string)), + Value: aws.String(attr["value"].(string)), + PropagateAtLaunch: aws.Bool(propagate_at_launch), + ResourceId: aws.String(resourceID), + ResourceType: aws.String("auto-scaling-group"), + } + + if tagIgnoredAutoscaling(t) { + return nil, nil + } + + return t, nil } // autoscalingTagsToMap turns the list of tags into a map. @@ -140,6 +253,7 @@ func autoscalingTagsToMap(ts []*autoscaling.Tag) map[string]interface{} { tags := make(map[string]interface{}) for _, t := range ts { tag := map[string]interface{}{ + "key": *t.Key, "value": *t.Value, "propagate_at_launch": *t.PropagateAtLaunch, } @@ -154,6 +268,7 @@ func autoscalingTagDescriptionsToMap(ts *[]*autoscaling.TagDescription) map[stri tags := make(map[string]map[string]interface{}) for _, t := range *ts { tag := map[string]interface{}{ + "key": *t.Key, "value": *t.Value, "propagate_at_launch": *t.PropagateAtLaunch, } diff --git a/builtin/providers/aws/autoscaling_tags_test.go b/builtin/providers/aws/autoscaling_tags_test.go index 04d8c15cba13..0107764d149c 100644 --- a/builtin/providers/aws/autoscaling_tags_test.go +++ b/builtin/providers/aws/autoscaling_tags_test.go @@ -20,24 +20,28 @@ func TestDiffAutoscalingTags(t *testing.T) { { Old: map[string]interface{}{ "Name": map[string]interface{}{ + "key": "Name", "value": "bar", "propagate_at_launch": true, }, }, New: map[string]interface{}{ "DifferentTag": map[string]interface{}{ + "key": "DifferentTag", "value": "baz", "propagate_at_launch": true, }, }, Create: map[string]interface{}{ "DifferentTag": map[string]interface{}{ + "key": "DifferentTag", "value": "baz", "propagate_at_launch": true, }, }, Remove: map[string]interface{}{ "Name": map[string]interface{}{ + "key": "Name", "value": "bar", "propagate_at_launch": true, }, @@ -48,24 +52,28 @@ func TestDiffAutoscalingTags(t *testing.T) { { Old: map[string]interface{}{ "Name": map[string]interface{}{ + "key": "Name", "value": "bar", "propagate_at_launch": true, }, }, New: map[string]interface{}{ "Name": map[string]interface{}{ + "key": "Name", "value": "baz", "propagate_at_launch": false, }, }, Create: map[string]interface{}{ "Name": map[string]interface{}{ + "key": "Name", "value": "baz", "propagate_at_launch": false, }, }, Remove: map[string]interface{}{ "Name": map[string]interface{}{ + "key": "Name", "value": "bar", "propagate_at_launch": true, }, @@ -76,10 +84,20 @@ func TestDiffAutoscalingTags(t *testing.T) { var resourceID = "sample" for i, tc := range cases { - awsTagsOld := autoscalingTagsFromMap(tc.Old, resourceID) - awsTagsNew := autoscalingTagsFromMap(tc.New, resourceID) + awsTagsOld, err := autoscalingTagsFromMap(tc.Old, resourceID) + if err != nil { + t.Fatalf("%d: unexpected error convertig old tags: %v", i, err) + } + + awsTagsNew, err := autoscalingTagsFromMap(tc.New, resourceID) + if err != nil { + t.Fatalf("%d: unexpected error convertig new tags: %v", i, err) + } - c, r := diffAutoscalingTags(awsTagsOld, awsTagsNew, resourceID) + c, r, err := diffAutoscalingTags(awsTagsOld, awsTagsNew, resourceID) + if err != nil { + t.Fatalf("%d: unexpected error diff'ing tags: %v", i, err) + } cm := autoscalingTagsToMap(c) rm := autoscalingTagsToMap(r) diff --git a/builtin/providers/aws/import_aws_autoscaling_group_test.go b/builtin/providers/aws/import_aws_autoscaling_group_test.go index 920df152cf11..666563b506bb 100644 --- a/builtin/providers/aws/import_aws_autoscaling_group_test.go +++ b/builtin/providers/aws/import_aws_autoscaling_group_test.go @@ -18,7 +18,7 @@ func TestAccAWSAutoScalingGroup_importBasic(t *testing.T) { CheckDestroy: testAccCheckAWSAutoScalingGroupDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAWSAutoScalingGroupConfig(randName), + Config: testAccAWSAutoScalingGroupImport(randName), }, resource.TestStep{ diff --git a/builtin/providers/aws/resource_aws_autoscaling_group.go b/builtin/providers/aws/resource_aws_autoscaling_group.go index ef21f2492c00..be23aa02f779 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group.go @@ -244,7 +244,14 @@ func resourceAwsAutoscalingGroup() *schema.Resource { }, }, - "tag": autoscalingTagsSchema(), + "tag": autoscalingTagSchema(), + + "tags": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeMap}, + ConflictsWith: []string{"tag"}, + }, }, } } @@ -344,9 +351,23 @@ func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) createOpts.AvailabilityZones = expandStringList(v.(*schema.Set).List()) } + resourceID := d.Get("name").(string) if v, ok := d.GetOk("tag"); ok { - createOpts.Tags = autoscalingTagsFromMap( - setToMapByKey(v.(*schema.Set), "key"), d.Get("name").(string)) + var err error + createOpts.Tags, err = autoscalingTagsFromMap( + setToMapByKey(v.(*schema.Set), "key"), resourceID) + if err != nil { + return err + } + } + + if v, ok := d.GetOk("tags"); ok { + tags, err := autoscalingTagsFromList(v.([]interface{}), resourceID) + if err != nil { + return err + } + + createOpts.Tags = append(createOpts.Tags, tags...) } if v, ok := d.GetOk("default_cooldown"); ok { @@ -457,7 +478,49 @@ func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) e d.Set("max_size", g.MaxSize) d.Set("placement_group", g.PlacementGroup) d.Set("name", g.AutoScalingGroupName) - d.Set("tag", autoscalingTagDescriptionsToSlice(g.Tags)) + + var tagList, tagsList []*autoscaling.TagDescription + var tagOk, tagsOk bool + var v interface{} + + if v, tagOk = d.GetOk("tag"); tagOk { + tags := setToMapByKey(v.(*schema.Set), "key") + for _, t := range g.Tags { + if _, ok := tags[*t.Key]; ok { + tagList = append(tagList, t) + } + } + d.Set("tag", autoscalingTagDescriptionsToSlice(tagList)) + } + + if v, tagsOk = d.GetOk("tags"); tagsOk { + tags := map[string]struct{}{} + for _, tag := range v.([]interface{}) { + attr, ok := tag.(map[string]interface{}) + if !ok { + continue + } + + key, ok := attr["key"].(string) + if !ok { + continue + } + + tags[key] = struct{}{} + } + + for _, t := range g.Tags { + if _, ok := tags[*t.Key]; ok { + tagsList = append(tagsList, t) + } + } + d.Set("tags", autoscalingTagDescriptionsToSlice(tagsList)) + } + + if !tagOk && !tagsOk { + d.Set("tag", autoscalingTagDescriptionsToSlice(g.Tags)) + } + d.Set("vpc_zone_identifier", strings.Split(*g.VPCZoneIdentifier, ",")) d.Set("protect_from_scale_in", g.NewInstancesProtectedFromScaleIn) @@ -549,10 +612,16 @@ func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) if err := setAutoscalingTags(conn, d); err != nil { return err - } else { + } + + if d.HasChange("tag") { d.SetPartial("tag") } + if d.HasChange("tags") { + d.SetPartial("tags") + } + log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts) _, err := conn.UpdateAutoScalingGroup(&opts) if err != nil { diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_test.go b/builtin/providers/aws/resource_aws_autoscaling_group_test.go index ee17f88444ef..b60694a7bc4a 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_test.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_test.go @@ -74,8 +74,16 @@ func TestAccAWSAutoScalingGroup_basic(t *testing.T) { resource.TestCheckResourceAttr( "aws_autoscaling_group.bar", "protect_from_scale_in", "true"), testLaunchConfigurationName("aws_autoscaling_group.bar", &lc), - testAccCheckAutoscalingTags(&group.Tags, "Bar", map[string]interface{}{ - "value": "bar-foo", + testAccCheckAutoscalingTags(&group.Tags, "FromTags1Changed", map[string]interface{}{ + "value": "value1changed", + "propagate_at_launch": true, + }), + testAccCheckAutoscalingTags(&group.Tags, "FromTags2", map[string]interface{}{ + "value": "value2changed", + "propagate_at_launch": true, + }), + testAccCheckAutoscalingTags(&group.Tags, "FromTags3", map[string]interface{}{ + "value": "value3", "propagate_at_launch": true, }), ), @@ -185,8 +193,16 @@ func TestAccAWSAutoScalingGroup_tags(t *testing.T) { Config: testAccAWSAutoScalingGroupConfig(randName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAutoScalingGroupExists("aws_autoscaling_group.bar", &group), - testAccCheckAutoscalingTags(&group.Tags, "Foo", map[string]interface{}{ - "value": "foo-bar", + testAccCheckAutoscalingTags(&group.Tags, "FromTags1", map[string]interface{}{ + "value": "value1", + "propagate_at_launch": true, + }), + testAccCheckAutoscalingTags(&group.Tags, "FromTags2", map[string]interface{}{ + "value": "value2", + "propagate_at_launch": true, + }), + testAccCheckAutoscalingTags(&group.Tags, "FromTags3", map[string]interface{}{ + "value": "value3", "propagate_at_launch": true, }), ), @@ -197,8 +213,16 @@ func TestAccAWSAutoScalingGroup_tags(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSAutoScalingGroupExists("aws_autoscaling_group.bar", &group), testAccCheckAutoscalingTagNotExists(&group.Tags, "Foo"), - testAccCheckAutoscalingTags(&group.Tags, "Bar", map[string]interface{}{ - "value": "bar-foo", + testAccCheckAutoscalingTags(&group.Tags, "FromTags1Changed", map[string]interface{}{ + "value": "value1changed", + "propagate_at_launch": true, + }), + testAccCheckAutoscalingTags(&group.Tags, "FromTags2", map[string]interface{}{ + "value": "value2changed", + "propagate_at_launch": true, + }), + testAccCheckAutoscalingTags(&group.Tags, "FromTags3", map[string]interface{}{ + "value": "value3", "propagate_at_launch": true, }), ), @@ -572,8 +596,8 @@ func testAccCheckAWSAutoScalingGroupAttributes(group *autoscaling.Group, name st } t := &autoscaling.TagDescription{ - Key: aws.String("Foo"), - Value: aws.String("foo-bar"), + Key: aws.String("FromTags1"), + Value: aws.String("value1"), PropagateAtLaunch: aws.Bool(true), ResourceType: aws.String("auto-scaling-group"), ResourceId: group.AutoScalingGroupName, @@ -850,11 +874,23 @@ resource "aws_autoscaling_group" "bar" { launch_configuration = "${aws_launch_configuration.foobar.name}" - tag { - key = "Foo" - value = "foo-bar" - propagate_at_launch = true - } + tags = [ + { + key = "FromTags1" + value = "value1" + propagate_at_launch = true + }, + { + key = "FromTags2" + value = "value2" + propagate_at_launch = true + }, + { + key = "FromTags3" + value = "value3" + propagate_at_launch = true + }, + ] } `, name, name) } @@ -885,13 +921,70 @@ resource "aws_autoscaling_group" "bar" { launch_configuration = "${aws_launch_configuration.new.name}" + tags = [ + { + key = "FromTags1Changed" + value = "value1changed" + propagate_at_launch = true + }, + { + key = "FromTags2" + value = "value2changed" + propagate_at_launch = true + }, + { + key = "FromTags3" + value = "value3" + propagate_at_launch = true + }, + ] +} +`, name) +} + +func testAccAWSAutoScalingGroupImport(name string) string { + return fmt.Sprintf(` +resource "aws_launch_configuration" "foobar" { + image_id = "ami-21f78e11" + instance_type = "t1.micro" +} + +resource "aws_placement_group" "test" { + name = "asg_pg_%s" + strategy = "cluster" +} + +resource "aws_autoscaling_group" "bar" { + availability_zones = ["us-west-2a"] + name = "%s" + max_size = 5 + min_size = 2 + health_check_type = "ELB" + desired_capacity = 4 + force_delete = true + termination_policies = ["OldestInstance","ClosestToNextInstanceHour"] + + launch_configuration = "${aws_launch_configuration.foobar.name}" + + tag { + key = "FromTags1" + value = "value1" + propagate_at_launch = true + } + tag { - key = "Bar" - value = "bar-foo" + key = "FromTags2" + value = "value2" + propagate_at_launch = true + } + + tag { + key = "FromTags3" + value = "value3" propagate_at_launch = true } } -`, name) +`, name, name) } const testAccAWSAutoScalingGroupConfigWithLoadBalancer = ` diff --git a/website/source/docs/providers/aws/r/autoscaling_group.html.markdown b/website/source/docs/providers/aws/r/autoscaling_group.html.markdown index 07e21766d8d6..fff6031f76cd 100644 --- a/website/source/docs/providers/aws/r/autoscaling_group.html.markdown +++ b/website/source/docs/providers/aws/r/autoscaling_group.html.markdown @@ -60,6 +60,54 @@ EOF } ``` +## Interpolated tags + +``` +variable extra_tags { + default = [ + { + key = "Foo" + value = "Bar" + propagate_at_launch = true + }, + { + key = "Baz" + value = "Bam" + propagate_at_launch = true + }, + ] +} + +resource "aws_autoscaling_group" "bar" { + availability_zones = ["us-east-1a"] + name = "foobar3-terraform-test" + max_size = 5 + min_size = 2 + launch_configuration = "${aws_launch_configuration.foobar.name}" + + tags = [ + { + key = "explicit1" + value = "value1" + propagate_at_launch = true + }, + { + key = "explicit2" + value = "value2" + propagate_at_launch = true + }, + ] + + tags = ["${concat( + list( + map("key", "interpolation1", "value", "value3", "propagate_at_launch", true), + map("key", "interpolation2", "value", "value4", "propagate_at_launch", true) + ), + var.extra_tags) + }"] +} +``` + ## Argument Reference The following arguments are supported: @@ -100,6 +148,7 @@ Application Load Balancing * `suspended_processes` - (Optional) A list of processes to suspend for the AutoScaling Group. The allowed values are `Launch`, `Terminate`, `HealthCheck`, `ReplaceUnhealthy`, `AZRebalance`, `AlarmNotification`, `ScheduledActions`, `AddToLoadBalancer`. Note that if you suspend either the `Launch` or `Terminate` process types, it can prevent your autoscaling group from functioning properly. * `tag` (Optional) A list of tag blocks. Tags documented below. +* `tags` (Optional) A list of tag blocks (maps). Tags documented below. * `placement_group` (Optional) The name of the placement group into which you'll launch your instances, if any. * `metrics_granularity` - (Optional) The granularity to associate with the metrics to collect. The only valid value is `1Minute`. Default is `1Minute`. * `enabled_metrics` - (Optional) A list of metrics to collect. The allowed values are `GroupMinSize`, `GroupMaxSize`, `GroupDesiredCapacity`, `GroupInServiceInstances`, `GroupPendingInstances`, `GroupStandbyInstances`, `GroupTerminatingInstances`, `GroupTotalInstances`. @@ -123,11 +172,18 @@ Note that if you suspend either the `Launch` or `Terminate` process types, it ca Tags support the following: +The `tag` attribute accepts exactly one tag declaration with the following fields: + * `key` - (Required) Key * `value` - (Required) Value * `propagate_at_launch` - (Required) Enables propagation of the tag to Amazon EC2 instances launched via this ASG +To declare multiple tags additional `tag` blocks can be specified. +Alternatively the `tags` attributes can be used, which accepts a list of maps containing the above field names as keys and their respective values. +This allows the construction of dynamic lists of tags which is not possible using the single `tag` attribute. +`tag` and `tags` are mutually exclusive, only one of them can be specified. + ## Attributes Reference The following attributes are exported: