github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/builtin/providers/aws/resource_aws_elb.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 8 "github.com/awslabs/aws-sdk-go/aws" 9 "github.com/awslabs/aws-sdk-go/service/elb" 10 "github.com/hashicorp/terraform/helper/hashcode" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceAwsElb() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceAwsElbCreate, 17 Read: resourceAwsElbRead, 18 Update: resourceAwsElbUpdate, 19 Delete: resourceAwsElbDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "name": &schema.Schema{ 23 Type: schema.TypeString, 24 Required: true, 25 ForceNew: true, 26 }, 27 28 "internal": &schema.Schema{ 29 Type: schema.TypeBool, 30 Optional: true, 31 ForceNew: true, 32 Computed: true, 33 }, 34 35 "cross_zone_load_balancing": &schema.Schema{ 36 Type: schema.TypeBool, 37 Optional: true, 38 }, 39 40 "availability_zones": &schema.Schema{ 41 Type: schema.TypeSet, 42 Elem: &schema.Schema{Type: schema.TypeString}, 43 Optional: true, 44 ForceNew: true, 45 Computed: true, 46 Set: func(v interface{}) int { 47 return hashcode.String(v.(string)) 48 }, 49 }, 50 51 "instances": &schema.Schema{ 52 Type: schema.TypeSet, 53 Elem: &schema.Schema{Type: schema.TypeString}, 54 Optional: true, 55 Computed: true, 56 Set: func(v interface{}) int { 57 return hashcode.String(v.(string)) 58 }, 59 }, 60 61 // TODO: could be not ForceNew 62 "security_groups": &schema.Schema{ 63 Type: schema.TypeSet, 64 Elem: &schema.Schema{Type: schema.TypeString}, 65 Optional: true, 66 ForceNew: true, 67 Computed: true, 68 Set: func(v interface{}) int { 69 return hashcode.String(v.(string)) 70 }, 71 }, 72 73 "subnets": &schema.Schema{ 74 Type: schema.TypeSet, 75 Elem: &schema.Schema{Type: schema.TypeString}, 76 Optional: true, 77 ForceNew: true, 78 Computed: true, 79 Set: func(v interface{}) int { 80 return hashcode.String(v.(string)) 81 }, 82 }, 83 84 "listener": &schema.Schema{ 85 Type: schema.TypeSet, 86 Required: true, 87 Elem: &schema.Resource{ 88 Schema: map[string]*schema.Schema{ 89 "instance_port": &schema.Schema{ 90 Type: schema.TypeInt, 91 Required: true, 92 }, 93 94 "instance_protocol": &schema.Schema{ 95 Type: schema.TypeString, 96 Required: true, 97 }, 98 99 "lb_port": &schema.Schema{ 100 Type: schema.TypeInt, 101 Required: true, 102 }, 103 104 "lb_protocol": &schema.Schema{ 105 Type: schema.TypeString, 106 Required: true, 107 }, 108 109 "ssl_certificate_id": &schema.Schema{ 110 Type: schema.TypeString, 111 Optional: true, 112 }, 113 }, 114 }, 115 Set: resourceAwsElbListenerHash, 116 }, 117 118 "health_check": &schema.Schema{ 119 Type: schema.TypeSet, 120 Optional: true, 121 Computed: true, 122 Elem: &schema.Resource{ 123 Schema: map[string]*schema.Schema{ 124 "healthy_threshold": &schema.Schema{ 125 Type: schema.TypeInt, 126 Required: true, 127 }, 128 129 "unhealthy_threshold": &schema.Schema{ 130 Type: schema.TypeInt, 131 Required: true, 132 }, 133 134 "target": &schema.Schema{ 135 Type: schema.TypeString, 136 Required: true, 137 }, 138 139 "interval": &schema.Schema{ 140 Type: schema.TypeInt, 141 Required: true, 142 }, 143 144 "timeout": &schema.Schema{ 145 Type: schema.TypeInt, 146 Required: true, 147 }, 148 }, 149 }, 150 Set: resourceAwsElbHealthCheckHash, 151 }, 152 153 "dns_name": &schema.Schema{ 154 Type: schema.TypeString, 155 Computed: true, 156 }, 157 158 "tags": tagsSchema(), 159 }, 160 } 161 } 162 163 func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { 164 elbconn := meta.(*AWSClient).elbconn 165 166 // Expand the "listener" set to aws-sdk-go compat []*elb.Listener 167 listeners, err := expandListenersSDK(d.Get("listener").(*schema.Set).List()) 168 if err != nil { 169 return err 170 } 171 172 tags := tagsFromMapELB(d.Get("tags").(map[string]interface{})) 173 // Provision the elb 174 elbOpts := &elb.CreateLoadBalancerInput{ 175 LoadBalancerName: aws.String(d.Get("name").(string)), 176 Listeners: listeners, 177 Tags: tags, 178 } 179 180 if scheme, ok := d.GetOk("internal"); ok && scheme.(bool) { 181 elbOpts.Scheme = aws.String("internal") 182 } 183 184 if v, ok := d.GetOk("availability_zones"); ok { 185 elbOpts.AvailabilityZones = expandStringListSDK(v.(*schema.Set).List()) 186 } 187 188 if v, ok := d.GetOk("security_groups"); ok { 189 elbOpts.SecurityGroups = expandStringListSDK(v.(*schema.Set).List()) 190 } 191 192 if v, ok := d.GetOk("subnets"); ok { 193 elbOpts.Subnets = expandStringListSDK(v.(*schema.Set).List()) 194 } 195 196 log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts) 197 if _, err := elbconn.CreateLoadBalancer(elbOpts); err != nil { 198 return fmt.Errorf("Error creating ELB: %s", err) 199 } 200 201 // Assign the elb's unique identifier for use later 202 d.SetId(d.Get("name").(string)) 203 log.Printf("[INFO] ELB ID: %s", d.Id()) 204 205 // Enable partial mode and record what we set 206 d.Partial(true) 207 d.SetPartial("name") 208 d.SetPartial("internal") 209 d.SetPartial("availability_zones") 210 d.SetPartial("listener") 211 d.SetPartial("security_groups") 212 d.SetPartial("subnets") 213 214 d.Set("tags", tagsToMapELB(tags)) 215 216 if d.HasChange("health_check") { 217 vs := d.Get("health_check").(*schema.Set).List() 218 if len(vs) > 0 { 219 check := vs[0].(map[string]interface{}) 220 221 configureHealthCheckOpts := elb.ConfigureHealthCheckInput{ 222 LoadBalancerName: aws.String(d.Id()), 223 HealthCheck: &elb.HealthCheck{ 224 HealthyThreshold: aws.Long(int64(check["healthy_threshold"].(int))), 225 UnhealthyThreshold: aws.Long(int64(check["unhealthy_threshold"].(int))), 226 Interval: aws.Long(int64(check["interval"].(int))), 227 Target: aws.String(check["target"].(string)), 228 Timeout: aws.Long(int64(check["timeout"].(int))), 229 }, 230 } 231 232 _, err = elbconn.ConfigureHealthCheck(&configureHealthCheckOpts) 233 if err != nil { 234 return fmt.Errorf("Failure configuring health check: %s", err) 235 } 236 } 237 } 238 239 return resourceAwsElbUpdate(d, meta) 240 } 241 242 func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { 243 elbconn := meta.(*AWSClient).elbconn 244 245 // Retrieve the ELB properties for updating the state 246 describeElbOpts := &elb.DescribeLoadBalancersInput{ 247 LoadBalancerNames: []*string{aws.String(d.Id())}, 248 } 249 250 describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) 251 if err != nil { 252 if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "LoadBalancerNotFound" { 253 // The ELB is gone now, so just remove it from the state 254 d.SetId("") 255 return nil 256 } 257 258 return fmt.Errorf("Error retrieving ELB: %s", err) 259 } 260 if len(describeResp.LoadBalancerDescriptions) != 1 { 261 return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) 262 } 263 264 lb := describeResp.LoadBalancerDescriptions[0] 265 266 d.Set("name", *lb.LoadBalancerName) 267 d.Set("dns_name", *lb.DNSName) 268 d.Set("internal", *lb.Scheme == "internal") 269 d.Set("availability_zones", lb.AvailabilityZones) 270 d.Set("instances", flattenInstancesSDK(lb.Instances)) 271 d.Set("listener", flattenListenersSDK(lb.ListenerDescriptions)) 272 d.Set("security_groups", lb.SecurityGroups) 273 d.Set("subnets", lb.Subnets) 274 275 resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{ 276 LoadBalancerNames: []*string{lb.LoadBalancerName}, 277 }) 278 279 var et []*elb.Tag 280 if len(resp.TagDescriptions) > 0 { 281 et = resp.TagDescriptions[0].Tags 282 } 283 d.Set("tags", tagsToMapELB(et)) 284 // There's only one health check, so save that to state as we 285 // currently can 286 if *lb.HealthCheck.Target != "" { 287 d.Set("health_check", flattenHealthCheckSDK(lb.HealthCheck)) 288 } 289 290 return nil 291 } 292 293 func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { 294 elbconn := meta.(*AWSClient).elbconn 295 296 d.Partial(true) 297 298 // If we currently have instances, or did have instances, 299 // we want to figure out what to add and remove from the load 300 // balancer 301 if d.HasChange("instances") { 302 o, n := d.GetChange("instances") 303 os := o.(*schema.Set) 304 ns := n.(*schema.Set) 305 remove := expandInstanceStringSDK(os.Difference(ns).List()) 306 add := expandInstanceStringSDK(ns.Difference(os).List()) 307 308 if len(add) > 0 { 309 registerInstancesOpts := elb.RegisterInstancesWithLoadBalancerInput{ 310 LoadBalancerName: aws.String(d.Id()), 311 Instances: add, 312 } 313 314 _, err := elbconn.RegisterInstancesWithLoadBalancer(®isterInstancesOpts) 315 if err != nil { 316 return fmt.Errorf("Failure registering instances: %s", err) 317 } 318 } 319 if len(remove) > 0 { 320 deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{ 321 LoadBalancerName: aws.String(d.Id()), 322 Instances: remove, 323 } 324 325 _, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts) 326 if err != nil { 327 return fmt.Errorf("Failure deregistering instances: %s", err) 328 } 329 } 330 331 d.SetPartial("instances") 332 } 333 334 log.Println("[INFO] outside modify attributes") 335 if d.HasChange("cross_zone_load_balancing") { 336 log.Println("[INFO] inside modify attributes") 337 attrs := elb.ModifyLoadBalancerAttributesInput{ 338 LoadBalancerName: aws.String(d.Get("name").(string)), 339 LoadBalancerAttributes: &elb.LoadBalancerAttributes{ 340 CrossZoneLoadBalancing: &elb.CrossZoneLoadBalancing{ 341 Enabled: aws.Boolean(d.Get("cross_zone_load_balancing").(bool)), 342 }, 343 }, 344 } 345 _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) 346 if err != nil { 347 return fmt.Errorf("Failure configuring cross zone balancing: %s", err) 348 } 349 d.SetPartial("cross_zone_load_balancing") 350 } 351 352 if d.HasChange("health_check") { 353 vs := d.Get("health_check").(*schema.Set).List() 354 if len(vs) > 0 { 355 check := vs[0].(map[string]interface{}) 356 configureHealthCheckOpts := elb.ConfigureHealthCheckInput{ 357 LoadBalancerName: aws.String(d.Id()), 358 HealthCheck: &elb.HealthCheck{ 359 HealthyThreshold: aws.Long(int64(check["healthy_threshold"].(int))), 360 UnhealthyThreshold: aws.Long(int64(check["unhealthy_threshold"].(int))), 361 Interval: aws.Long(int64(check["interval"].(int))), 362 Target: aws.String(check["target"].(string)), 363 Timeout: aws.Long(int64(check["timeout"].(int))), 364 }, 365 } 366 _, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts) 367 if err != nil { 368 return fmt.Errorf("Failure configuring health check: %s", err) 369 } 370 d.SetPartial("health_check") 371 } 372 } 373 374 if err := setTagsELB(elbconn, d); err != nil { 375 return err 376 } else { 377 d.SetPartial("tags") 378 } 379 d.Partial(false) 380 381 return resourceAwsElbRead(d, meta) 382 } 383 384 func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error { 385 elbconn := meta.(*AWSClient).elbconn 386 387 log.Printf("[INFO] Deleting ELB: %s", d.Id()) 388 389 // Destroy the load balancer 390 deleteElbOpts := elb.DeleteLoadBalancerInput{ 391 LoadBalancerName: aws.String(d.Id()), 392 } 393 if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil { 394 return fmt.Errorf("Error deleting ELB: %s", err) 395 } 396 397 return nil 398 } 399 400 func resourceAwsElbHealthCheckHash(v interface{}) int { 401 var buf bytes.Buffer 402 m := v.(map[string]interface{}) 403 buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int))) 404 buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int))) 405 buf.WriteString(fmt.Sprintf("%s-", m["target"].(string))) 406 buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) 407 buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int))) 408 409 return hashcode.String(buf.String()) 410 } 411 412 func resourceAwsElbListenerHash(v interface{}) int { 413 var buf bytes.Buffer 414 m := v.(map[string]interface{}) 415 buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int))) 416 buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string))) 417 buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int))) 418 buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string))) 419 420 if v, ok := m["ssl_certificate_id"]; ok { 421 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 422 } 423 424 return hashcode.String(buf.String()) 425 }