github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_alb.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "regexp" 7 "strconv" 8 9 "github.com/aws/aws-sdk-go/aws" 10 "github.com/aws/aws-sdk-go/service/elbv2" 11 "github.com/hashicorp/errwrap" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceAwsAlb() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceAwsAlbCreate, 19 Read: resourceAwsAlbRead, 20 Update: resourceAwsAlbUpdate, 21 Delete: resourceAwsAlbDelete, 22 Importer: &schema.ResourceImporter{ 23 State: schema.ImportStatePassthrough, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "arn": { 28 Type: schema.TypeString, 29 Computed: true, 30 }, 31 32 "arn_suffix": { 33 Type: schema.TypeString, 34 Computed: true, 35 }, 36 37 "name": { 38 Type: schema.TypeString, 39 Optional: true, 40 Computed: true, 41 ForceNew: true, 42 ConflictsWith: []string{"name_prefix"}, 43 ValidateFunc: validateElbName, 44 }, 45 46 "name_prefix": { 47 Type: schema.TypeString, 48 Optional: true, 49 ForceNew: true, 50 ValidateFunc: validateElbName, 51 }, 52 53 "internal": { 54 Type: schema.TypeBool, 55 Optional: true, 56 ForceNew: true, 57 Computed: true, 58 }, 59 60 "security_groups": { 61 Type: schema.TypeSet, 62 Elem: &schema.Schema{Type: schema.TypeString}, 63 Computed: true, 64 ForceNew: true, 65 Optional: true, 66 Set: schema.HashString, 67 }, 68 69 "subnets": { 70 Type: schema.TypeSet, 71 Elem: &schema.Schema{Type: schema.TypeString}, 72 ForceNew: true, 73 Required: true, 74 Set: schema.HashString, 75 }, 76 77 "access_logs": { 78 Type: schema.TypeList, 79 Optional: true, 80 MaxItems: 1, 81 Elem: &schema.Resource{ 82 Schema: map[string]*schema.Schema{ 83 "bucket": { 84 Type: schema.TypeString, 85 Required: true, 86 }, 87 "prefix": { 88 Type: schema.TypeString, 89 Optional: true, 90 }, 91 "enabled": { 92 Type: schema.TypeBool, 93 Optional: true, 94 Default: true, 95 }, 96 }, 97 }, 98 }, 99 100 "enable_deletion_protection": { 101 Type: schema.TypeBool, 102 Optional: true, 103 Default: false, 104 }, 105 106 "idle_timeout": { 107 Type: schema.TypeInt, 108 Optional: true, 109 Default: 60, 110 }, 111 112 "vpc_id": { 113 Type: schema.TypeString, 114 Computed: true, 115 }, 116 117 "zone_id": { 118 Type: schema.TypeString, 119 Computed: true, 120 }, 121 122 "dns_name": { 123 Type: schema.TypeString, 124 Computed: true, 125 }, 126 127 "tags": tagsSchema(), 128 }, 129 } 130 } 131 132 func resourceAwsAlbCreate(d *schema.ResourceData, meta interface{}) error { 133 elbconn := meta.(*AWSClient).elbv2conn 134 135 var name string 136 if v, ok := d.GetOk("name"); ok { 137 name = v.(string) 138 } else if v, ok := d.GetOk("name_prefix"); ok { 139 name = resource.PrefixedUniqueId(v.(string)) 140 } else { 141 name = resource.PrefixedUniqueId("tf-lb-") 142 } 143 d.Set("name", name) 144 145 elbOpts := &elbv2.CreateLoadBalancerInput{ 146 Name: aws.String(name), 147 Tags: tagsFromMapELBv2(d.Get("tags").(map[string]interface{})), 148 } 149 150 if scheme, ok := d.GetOk("internal"); ok && scheme.(bool) { 151 elbOpts.Scheme = aws.String("internal") 152 } 153 154 if v, ok := d.GetOk("security_groups"); ok { 155 elbOpts.SecurityGroups = expandStringList(v.(*schema.Set).List()) 156 } 157 158 if v, ok := d.GetOk("subnets"); ok { 159 elbOpts.Subnets = expandStringList(v.(*schema.Set).List()) 160 } 161 162 log.Printf("[DEBUG] ALB create configuration: %#v", elbOpts) 163 164 resp, err := elbconn.CreateLoadBalancer(elbOpts) 165 if err != nil { 166 return errwrap.Wrapf("Error creating Application Load Balancer: {{err}}", err) 167 } 168 169 if len(resp.LoadBalancers) != 1 { 170 return fmt.Errorf("No load balancers returned following creation of %s", d.Get("name").(string)) 171 } 172 173 d.SetId(*resp.LoadBalancers[0].LoadBalancerArn) 174 log.Printf("[INFO] ALB ID: %s", d.Id()) 175 176 return resourceAwsAlbUpdate(d, meta) 177 } 178 179 func resourceAwsAlbRead(d *schema.ResourceData, meta interface{}) error { 180 elbconn := meta.(*AWSClient).elbv2conn 181 albArn := d.Id() 182 183 describeAlbOpts := &elbv2.DescribeLoadBalancersInput{ 184 LoadBalancerArns: []*string{aws.String(albArn)}, 185 } 186 187 describeResp, err := elbconn.DescribeLoadBalancers(describeAlbOpts) 188 if err != nil { 189 if isLoadBalancerNotFound(err) { 190 // The ALB is gone now, so just remove it from the state 191 log.Printf("[WARN] ALB %s not found in AWS, removing from state", d.Id()) 192 d.SetId("") 193 return nil 194 } 195 196 return errwrap.Wrapf("Error retrieving ALB: {{err}}", err) 197 } 198 if len(describeResp.LoadBalancers) != 1 { 199 return fmt.Errorf("Unable to find ALB: %#v", describeResp.LoadBalancers) 200 } 201 202 alb := describeResp.LoadBalancers[0] 203 204 d.Set("arn", alb.LoadBalancerArn) 205 d.Set("arn_suffix", albSuffixFromARN(alb.LoadBalancerArn)) 206 d.Set("name", alb.LoadBalancerName) 207 d.Set("internal", (alb.Scheme != nil && *alb.Scheme == "internal")) 208 d.Set("security_groups", flattenStringList(alb.SecurityGroups)) 209 d.Set("subnets", flattenSubnetsFromAvailabilityZones(alb.AvailabilityZones)) 210 d.Set("vpc_id", alb.VpcId) 211 d.Set("zone_id", alb.CanonicalHostedZoneId) 212 d.Set("dns_name", alb.DNSName) 213 214 respTags, err := elbconn.DescribeTags(&elbv2.DescribeTagsInput{ 215 ResourceArns: []*string{alb.LoadBalancerArn}, 216 }) 217 if err != nil { 218 return errwrap.Wrapf("Error retrieving ALB Tags: {{err}}", err) 219 } 220 221 var et []*elbv2.Tag 222 if len(respTags.TagDescriptions) > 0 { 223 et = respTags.TagDescriptions[0].Tags 224 } 225 d.Set("tags", tagsToMapELBv2(et)) 226 227 attributesResp, err := elbconn.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ 228 LoadBalancerArn: aws.String(d.Id()), 229 }) 230 if err != nil { 231 return errwrap.Wrapf("Error retrieving ALB Attributes: {{err}}", err) 232 } 233 234 accessLogMap := map[string]interface{}{} 235 for _, attr := range attributesResp.Attributes { 236 switch *attr.Key { 237 case "access_logs.s3.enabled": 238 accessLogMap["enabled"] = *attr.Value 239 case "access_logs.s3.bucket": 240 accessLogMap["bucket"] = *attr.Value 241 case "access_logs.s3.prefix": 242 accessLogMap["prefix"] = *attr.Value 243 case "idle_timeout.timeout_seconds": 244 timeout, err := strconv.Atoi(*attr.Value) 245 if err != nil { 246 return errwrap.Wrapf("Error parsing ALB timeout: {{err}}", err) 247 } 248 log.Printf("[DEBUG] Setting ALB Timeout Seconds: %d", timeout) 249 d.Set("idle_timeout", timeout) 250 case "deletion_protection.enabled": 251 protectionEnabled := (*attr.Value) == "true" 252 log.Printf("[DEBUG] Setting ALB Deletion Protection Enabled: %t", protectionEnabled) 253 d.Set("enable_deletion_protection", protectionEnabled) 254 } 255 } 256 257 log.Printf("[DEBUG] Setting ALB Access Logs: %#v", accessLogMap) 258 if accessLogMap["bucket"] != "" || accessLogMap["prefix"] != "" { 259 d.Set("access_logs", []interface{}{accessLogMap}) 260 } else { 261 d.Set("access_logs", []interface{}{}) 262 } 263 264 return nil 265 } 266 267 func resourceAwsAlbUpdate(d *schema.ResourceData, meta interface{}) error { 268 elbconn := meta.(*AWSClient).elbv2conn 269 270 if !d.IsNewResource() { 271 if err := setElbV2Tags(elbconn, d); err != nil { 272 return errwrap.Wrapf("Error Modifying Tags on ALB: {{err}}", err) 273 } 274 } 275 276 attributes := make([]*elbv2.LoadBalancerAttribute, 0) 277 278 if d.HasChange("access_logs") { 279 logs := d.Get("access_logs").([]interface{}) 280 if len(logs) == 1 { 281 log := logs[0].(map[string]interface{}) 282 283 attributes = append(attributes, 284 &elbv2.LoadBalancerAttribute{ 285 Key: aws.String("access_logs.s3.enabled"), 286 Value: aws.String(strconv.FormatBool(log["enabled"].(bool))), 287 }, 288 &elbv2.LoadBalancerAttribute{ 289 Key: aws.String("access_logs.s3.bucket"), 290 Value: aws.String(log["bucket"].(string)), 291 }) 292 293 if prefix, ok := log["prefix"]; ok { 294 attributes = append(attributes, &elbv2.LoadBalancerAttribute{ 295 Key: aws.String("access_logs.s3.prefix"), 296 Value: aws.String(prefix.(string)), 297 }) 298 } 299 } else if len(logs) == 0 { 300 attributes = append(attributes, &elbv2.LoadBalancerAttribute{ 301 Key: aws.String("access_logs.s3.enabled"), 302 Value: aws.String("false"), 303 }) 304 } 305 } 306 307 if d.HasChange("enable_deletion_protection") { 308 attributes = append(attributes, &elbv2.LoadBalancerAttribute{ 309 Key: aws.String("deletion_protection.enabled"), 310 Value: aws.String(fmt.Sprintf("%t", d.Get("enable_deletion_protection").(bool))), 311 }) 312 } 313 314 if d.HasChange("idle_timeout") { 315 attributes = append(attributes, &elbv2.LoadBalancerAttribute{ 316 Key: aws.String("idle_timeout.timeout_seconds"), 317 Value: aws.String(fmt.Sprintf("%d", d.Get("idle_timeout").(int))), 318 }) 319 } 320 321 if len(attributes) != 0 { 322 input := &elbv2.ModifyLoadBalancerAttributesInput{ 323 LoadBalancerArn: aws.String(d.Id()), 324 Attributes: attributes, 325 } 326 327 log.Printf("[DEBUG] ALB Modify Load Balancer Attributes Request: %#v", input) 328 _, err := elbconn.ModifyLoadBalancerAttributes(input) 329 if err != nil { 330 return fmt.Errorf("Failure configuring ALB attributes: %s", err) 331 } 332 } 333 334 return resourceAwsAlbRead(d, meta) 335 } 336 337 func resourceAwsAlbDelete(d *schema.ResourceData, meta interface{}) error { 338 albconn := meta.(*AWSClient).elbv2conn 339 340 log.Printf("[INFO] Deleting ALB: %s", d.Id()) 341 342 // Destroy the load balancer 343 deleteElbOpts := elbv2.DeleteLoadBalancerInput{ 344 LoadBalancerArn: aws.String(d.Id()), 345 } 346 if _, err := albconn.DeleteLoadBalancer(&deleteElbOpts); err != nil { 347 return fmt.Errorf("Error deleting ALB: %s", err) 348 } 349 350 return nil 351 } 352 353 // flattenSubnetsFromAvailabilityZones creates a slice of strings containing the subnet IDs 354 // for the ALB based on the AvailabilityZones structure returned by the API. 355 func flattenSubnetsFromAvailabilityZones(availabilityZones []*elbv2.AvailabilityZone) []string { 356 var result []string 357 for _, az := range availabilityZones { 358 result = append(result, *az.SubnetId) 359 } 360 return result 361 } 362 363 func albSuffixFromARN(arn *string) string { 364 if arn == nil { 365 return "" 366 } 367 368 if arnComponents := regexp.MustCompile(`arn:.*:loadbalancer/(.*)`).FindAllStringSubmatch(*arn, -1); len(arnComponents) == 1 { 369 if len(arnComponents[0]) == 2 { 370 return arnComponents[0][1] 371 } 372 } 373 374 return "" 375 }