github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/builtin/providers/aws/resource_aws_autoscaling_group.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/hashcode" 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 13 "github.com/awslabs/aws-sdk-go/aws" 14 "github.com/awslabs/aws-sdk-go/service/autoscaling" 15 ) 16 17 func resourceAwsAutoscalingGroup() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceAwsAutoscalingGroupCreate, 20 Read: resourceAwsAutoscalingGroupRead, 21 Update: resourceAwsAutoscalingGroupUpdate, 22 Delete: resourceAwsAutoscalingGroupDelete, 23 24 Schema: map[string]*schema.Schema{ 25 "name": &schema.Schema{ 26 Type: schema.TypeString, 27 Required: true, 28 ForceNew: true, 29 }, 30 31 "launch_configuration": &schema.Schema{ 32 Type: schema.TypeString, 33 Required: true, 34 }, 35 36 "desired_capacity": &schema.Schema{ 37 Type: schema.TypeInt, 38 Optional: true, 39 Computed: true, 40 }, 41 42 "min_size": &schema.Schema{ 43 Type: schema.TypeInt, 44 Required: true, 45 }, 46 47 "max_size": &schema.Schema{ 48 Type: schema.TypeInt, 49 Required: true, 50 }, 51 52 "default_cooldown": &schema.Schema{ 53 Type: schema.TypeInt, 54 Optional: true, 55 Computed: true, 56 ForceNew: true, 57 }, 58 59 "force_delete": &schema.Schema{ 60 Type: schema.TypeBool, 61 Optional: true, 62 Computed: true, 63 ForceNew: true, 64 }, 65 66 "health_check_grace_period": &schema.Schema{ 67 Type: schema.TypeInt, 68 Optional: true, 69 Computed: true, 70 ForceNew: true, 71 }, 72 73 "health_check_type": &schema.Schema{ 74 Type: schema.TypeString, 75 Optional: true, 76 Computed: true, 77 ForceNew: true, 78 }, 79 80 "availability_zones": &schema.Schema{ 81 Type: schema.TypeSet, 82 Required: true, 83 ForceNew: true, 84 Elem: &schema.Schema{Type: schema.TypeString}, 85 Set: func(v interface{}) int { 86 return hashcode.String(v.(string)) 87 }, 88 }, 89 90 "load_balancers": &schema.Schema{ 91 Type: schema.TypeSet, 92 Optional: true, 93 ForceNew: true, 94 Elem: &schema.Schema{Type: schema.TypeString}, 95 Set: func(v interface{}) int { 96 return hashcode.String(v.(string)) 97 }, 98 }, 99 100 "vpc_zone_identifier": &schema.Schema{ 101 Type: schema.TypeSet, 102 Optional: true, 103 Computed: true, 104 ForceNew: true, 105 Elem: &schema.Schema{Type: schema.TypeString}, 106 Set: func(v interface{}) int { 107 return hashcode.String(v.(string)) 108 }, 109 }, 110 111 "termination_policies": &schema.Schema{ 112 Type: schema.TypeSet, 113 Optional: true, 114 Computed: true, 115 ForceNew: true, 116 Elem: &schema.Schema{Type: schema.TypeString}, 117 Set: func(v interface{}) int { 118 return hashcode.String(v.(string)) 119 }, 120 }, 121 122 "tag": autoscalingTagsSchema(), 123 }, 124 } 125 } 126 127 func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error { 128 autoscalingconn := meta.(*AWSClient).asgconn 129 130 var autoScalingGroupOpts autoscaling.CreateAutoScalingGroupInput 131 autoScalingGroupOpts.AutoScalingGroupName = aws.String(d.Get("name").(string)) 132 autoScalingGroupOpts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string)) 133 autoScalingGroupOpts.MinSize = aws.Long(int64(d.Get("min_size").(int))) 134 autoScalingGroupOpts.MaxSize = aws.Long(int64(d.Get("max_size").(int))) 135 autoScalingGroupOpts.AvailabilityZones = expandStringListSDK( 136 d.Get("availability_zones").(*schema.Set).List()) 137 138 if v, ok := d.GetOk("tag"); ok { 139 autoScalingGroupOpts.Tags = autoscalingTagsFromMap( 140 setToMapByKey(v.(*schema.Set), "key"), d.Get("name").(string)) 141 } 142 143 if v, ok := d.GetOk("default_cooldown"); ok { 144 autoScalingGroupOpts.DefaultCooldown = aws.Long(int64(v.(int))) 145 } 146 147 if v, ok := d.GetOk("health_check_type"); ok && v.(string) != "" { 148 autoScalingGroupOpts.HealthCheckType = aws.String(v.(string)) 149 } 150 151 if v, ok := d.GetOk("desired_capacity"); ok { 152 autoScalingGroupOpts.DesiredCapacity = aws.Long(int64(v.(int))) 153 } 154 155 if v, ok := d.GetOk("health_check_grace_period"); ok { 156 autoScalingGroupOpts.HealthCheckGracePeriod = aws.Long(int64(v.(int))) 157 } 158 159 if v, ok := d.GetOk("load_balancers"); ok && v.(*schema.Set).Len() > 0 { 160 autoScalingGroupOpts.LoadBalancerNames = expandStringListSDK( 161 v.(*schema.Set).List()) 162 } 163 164 if v, ok := d.GetOk("vpc_zone_identifier"); ok && v.(*schema.Set).Len() > 0 { 165 exp := expandStringListSDK(v.(*schema.Set).List()) 166 strs := make([]string, len(exp)) 167 for _, s := range exp { 168 strs = append(strs, *s) 169 } 170 autoScalingGroupOpts.VPCZoneIdentifier = aws.String(strings.Join(strs, ",")) 171 } 172 173 if v, ok := d.GetOk("termination_policies"); ok && v.(*schema.Set).Len() > 0 { 174 autoScalingGroupOpts.TerminationPolicies = expandStringListSDK( 175 v.(*schema.Set).List()) 176 } 177 178 log.Printf("[DEBUG] AutoScaling Group create configuration: %#v", autoScalingGroupOpts) 179 _, err := autoscalingconn.CreateAutoScalingGroup(&autoScalingGroupOpts) 180 if err != nil { 181 return fmt.Errorf("Error creating Autoscaling Group: %s", err) 182 } 183 184 d.SetId(d.Get("name").(string)) 185 log.Printf("[INFO] AutoScaling Group ID: %s", d.Id()) 186 187 return resourceAwsAutoscalingGroupRead(d, meta) 188 } 189 190 func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error { 191 g, err := getAwsAutoscalingGroup(d, meta) 192 if err != nil { 193 return err 194 } 195 if g == nil { 196 return nil 197 } 198 199 d.Set("availability_zones", g.AvailabilityZones) 200 d.Set("default_cooldown", g.DefaultCooldown) 201 d.Set("desired_capacity", g.DesiredCapacity) 202 d.Set("health_check_grace_period", g.HealthCheckGracePeriod) 203 d.Set("health_check_type", g.HealthCheckType) 204 d.Set("launch_configuration", g.LaunchConfigurationName) 205 d.Set("load_balancers", g.LoadBalancerNames) 206 d.Set("min_size", g.MinSize) 207 d.Set("max_size", g.MaxSize) 208 d.Set("name", g.AutoScalingGroupName) 209 d.Set("tag", g.Tags) 210 d.Set("vpc_zone_identifier", strings.Split(*g.VPCZoneIdentifier, ",")) 211 d.Set("termination_policies", g.TerminationPolicies) 212 213 return nil 214 } 215 216 func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error { 217 autoscalingconn := meta.(*AWSClient).asgconn 218 219 opts := autoscaling.UpdateAutoScalingGroupInput{ 220 AutoScalingGroupName: aws.String(d.Id()), 221 } 222 223 if d.HasChange("desired_capacity") { 224 opts.DesiredCapacity = aws.Long(int64(d.Get("desired_capacity").(int))) 225 } 226 227 if d.HasChange("launch_configuration") { 228 opts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string)) 229 } 230 231 if d.HasChange("min_size") { 232 opts.MinSize = aws.Long(int64(d.Get("min_size").(int))) 233 } 234 235 if d.HasChange("max_size") { 236 opts.MaxSize = aws.Long(int64(d.Get("max_size").(int))) 237 } 238 239 if err := setAutoscalingTags(autoscalingconn, d); err != nil { 240 return err 241 } else { 242 d.SetPartial("tag") 243 } 244 245 log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts) 246 _, err := autoscalingconn.UpdateAutoScalingGroup(&opts) 247 if err != nil { 248 d.Partial(true) 249 return fmt.Errorf("Error updating Autoscaling group: %s", err) 250 } 251 252 return resourceAwsAutoscalingGroupRead(d, meta) 253 } 254 255 func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error { 256 autoscalingconn := meta.(*AWSClient).asgconn 257 258 // Read the autoscaling group first. If it doesn't exist, we're done. 259 // We need the group in order to check if there are instances attached. 260 // If so, we need to remove those first. 261 g, err := getAwsAutoscalingGroup(d, meta) 262 if err != nil { 263 return err 264 } 265 if g == nil { 266 return nil 267 } 268 if len(g.Instances) > 0 || *g.DesiredCapacity > 0 { 269 if err := resourceAwsAutoscalingGroupDrain(d, meta); err != nil { 270 return err 271 } 272 } 273 274 log.Printf("[DEBUG] AutoScaling Group destroy: %v", d.Id()) 275 deleteopts := autoscaling.DeleteAutoScalingGroupInput{AutoScalingGroupName: aws.String(d.Id())} 276 277 // You can force an autoscaling group to delete 278 // even if it's in the process of scaling a resource. 279 // Normally, you would set the min-size and max-size to 0,0 280 // and then delete the group. This bypasses that and leaves 281 // resources potentially dangling. 282 if d.Get("force_delete").(bool) { 283 deleteopts.ForceDelete = aws.Boolean(true) 284 } 285 286 if _, err := autoscalingconn.DeleteAutoScalingGroup(&deleteopts); err != nil { 287 autoscalingerr, ok := err.(aws.APIError) 288 if ok && autoscalingerr.Code == "InvalidGroup.NotFound" { 289 return nil 290 } 291 return err 292 } 293 294 return resource.Retry(5*time.Minute, func() error { 295 if g, _ = getAwsAutoscalingGroup(d, meta); g != nil { 296 return fmt.Errorf("Auto Scaling Group still exists") 297 } 298 return nil 299 }) 300 } 301 302 func getAwsAutoscalingGroup( 303 d *schema.ResourceData, 304 meta interface{}) (*autoscaling.AutoScalingGroup, error) { 305 autoscalingconn := meta.(*AWSClient).asgconn 306 307 describeOpts := autoscaling.DescribeAutoScalingGroupsInput{ 308 AutoScalingGroupNames: []*string{aws.String(d.Id())}, 309 } 310 311 log.Printf("[DEBUG] AutoScaling Group describe configuration: %#v", describeOpts) 312 describeGroups, err := autoscalingconn.DescribeAutoScalingGroups(&describeOpts) 313 if err != nil { 314 autoscalingerr, ok := err.(aws.APIError) 315 if ok && autoscalingerr.Code == "InvalidGroup.NotFound" { 316 d.SetId("") 317 return nil, nil 318 } 319 320 return nil, fmt.Errorf("Error retrieving AutoScaling groups: %s", err) 321 } 322 323 // Search for the autoscaling group 324 for idx, asc := range describeGroups.AutoScalingGroups { 325 if *asc.AutoScalingGroupName == d.Id() { 326 return describeGroups.AutoScalingGroups[idx], nil 327 } 328 } 329 330 // ASG not found 331 d.SetId("") 332 return nil, nil 333 } 334 335 func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error { 336 autoscalingconn := meta.(*AWSClient).asgconn 337 338 // First, set the capacity to zero so the group will drain 339 log.Printf("[DEBUG] Reducing autoscaling group capacity to zero") 340 opts := autoscaling.UpdateAutoScalingGroupInput{ 341 AutoScalingGroupName: aws.String(d.Id()), 342 DesiredCapacity: aws.Long(0), 343 MinSize: aws.Long(0), 344 MaxSize: aws.Long(0), 345 } 346 if _, err := autoscalingconn.UpdateAutoScalingGroup(&opts); err != nil { 347 return fmt.Errorf("Error setting capacity to zero to drain: %s", err) 348 } 349 350 // Next, wait for the autoscale group to drain 351 log.Printf("[DEBUG] Waiting for group to have zero instances") 352 return resource.Retry(10*time.Minute, func() error { 353 g, err := getAwsAutoscalingGroup(d, meta) 354 if err != nil { 355 return resource.RetryError{Err: err} 356 } 357 if g == nil { 358 return nil 359 } 360 361 if len(g.Instances) == 0 { 362 return nil 363 } 364 365 return fmt.Errorf("group still has %d instances", len(g.Instances)) 366 }) 367 }