github.com/inge4pres/terraform@v0.7.5-0.20160930053151-bd083f84f376/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 }, 92 }, 93 }, 94 95 "enable_deletion_protection": { 96 Type: schema.TypeBool, 97 Optional: true, 98 Default: false, 99 }, 100 101 "idle_timeout": { 102 Type: schema.TypeInt, 103 Optional: true, 104 Default: 60, 105 }, 106 107 "vpc_id": { 108 Type: schema.TypeString, 109 Computed: true, 110 }, 111 112 "zone_id": { 113 Type: schema.TypeString, 114 Computed: true, 115 }, 116 117 "dns_name": { 118 Type: schema.TypeString, 119 Computed: true, 120 }, 121 122 "tags": tagsSchema(), 123 }, 124 } 125 } 126 127 func resourceAwsAlbCreate(d *schema.ResourceData, meta interface{}) error { 128 elbconn := meta.(*AWSClient).elbv2conn 129 130 var name string 131 if v, ok := d.GetOk("name"); ok { 132 name = v.(string) 133 } else if v, ok := d.GetOk("name_prefix"); ok { 134 name = resource.PrefixedUniqueId(v.(string)) 135 } else { 136 name = resource.PrefixedUniqueId("tf-lb-") 137 } 138 d.Set("name", name) 139 140 elbOpts := &elbv2.CreateLoadBalancerInput{ 141 Name: aws.String(name), 142 Tags: tagsFromMapELBv2(d.Get("tags").(map[string]interface{})), 143 } 144 145 if scheme, ok := d.GetOk("internal"); ok && scheme.(bool) { 146 elbOpts.Scheme = aws.String("internal") 147 } 148 149 if v, ok := d.GetOk("security_groups"); ok { 150 elbOpts.SecurityGroups = expandStringList(v.(*schema.Set).List()) 151 } 152 153 if v, ok := d.GetOk("subnets"); ok { 154 elbOpts.Subnets = expandStringList(v.(*schema.Set).List()) 155 } 156 157 log.Printf("[DEBUG] ALB create configuration: %#v", elbOpts) 158 159 resp, err := elbconn.CreateLoadBalancer(elbOpts) 160 if err != nil { 161 return errwrap.Wrapf("Error creating Application Load Balancer: {{err}}", err) 162 } 163 164 if len(resp.LoadBalancers) != 1 { 165 return fmt.Errorf("No load balancers returned following creation of %s", d.Get("name").(string)) 166 } 167 168 d.SetId(*resp.LoadBalancers[0].LoadBalancerArn) 169 log.Printf("[INFO] ALB ID: %s", d.Id()) 170 171 return resourceAwsAlbUpdate(d, meta) 172 } 173 174 func resourceAwsAlbRead(d *schema.ResourceData, meta interface{}) error { 175 elbconn := meta.(*AWSClient).elbv2conn 176 albArn := d.Id() 177 178 describeAlbOpts := &elbv2.DescribeLoadBalancersInput{ 179 LoadBalancerArns: []*string{aws.String(albArn)}, 180 } 181 182 describeResp, err := elbconn.DescribeLoadBalancers(describeAlbOpts) 183 if err != nil { 184 if isLoadBalancerNotFound(err) { 185 // The ALB is gone now, so just remove it from the state 186 log.Printf("[WARN] ALB %s not found in AWS, removing from state", d.Id()) 187 d.SetId("") 188 return nil 189 } 190 191 return errwrap.Wrapf("Error retrieving ALB: {{err}}", err) 192 } 193 if len(describeResp.LoadBalancers) != 1 { 194 return fmt.Errorf("Unable to find ALB: %#v", describeResp.LoadBalancers) 195 } 196 197 alb := describeResp.LoadBalancers[0] 198 199 d.Set("arn", alb.LoadBalancerArn) 200 d.Set("arn_suffix", albSuffixFromARN(alb.LoadBalancerArn)) 201 d.Set("name", alb.LoadBalancerName) 202 d.Set("internal", (alb.Scheme != nil && *alb.Scheme == "internal")) 203 d.Set("security_groups", flattenStringList(alb.SecurityGroups)) 204 d.Set("subnets", flattenSubnetsFromAvailabilityZones(alb.AvailabilityZones)) 205 d.Set("vpc_id", alb.VpcId) 206 d.Set("zone_id", alb.CanonicalHostedZoneId) 207 d.Set("dns_name", alb.DNSName) 208 209 respTags, err := elbconn.DescribeTags(&elbv2.DescribeTagsInput{ 210 ResourceArns: []*string{alb.LoadBalancerArn}, 211 }) 212 if err != nil { 213 return errwrap.Wrapf("Error retrieving ALB Tags: {{err}}", err) 214 } 215 216 var et []*elbv2.Tag 217 if len(respTags.TagDescriptions) > 0 { 218 et = respTags.TagDescriptions[0].Tags 219 } 220 d.Set("tags", tagsToMapELBv2(et)) 221 222 attributesResp, err := elbconn.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ 223 LoadBalancerArn: aws.String(d.Id()), 224 }) 225 if err != nil { 226 return errwrap.Wrapf("Error retrieving ALB Attributes: {{err}}", err) 227 } 228 229 accessLogMap := map[string]interface{}{} 230 for _, attr := range attributesResp.Attributes { 231 switch *attr.Key { 232 case "access_logs.s3.bucket": 233 accessLogMap["bucket"] = *attr.Value 234 case "access_logs.s3.prefix": 235 accessLogMap["prefix"] = *attr.Value 236 case "idle_timeout.timeout_seconds": 237 timeout, err := strconv.Atoi(*attr.Value) 238 if err != nil { 239 return errwrap.Wrapf("Error parsing ALB timeout: {{err}}", err) 240 } 241 log.Printf("[DEBUG] Setting ALB Timeout Seconds: %d", timeout) 242 d.Set("idle_timeout", timeout) 243 case "deletion_protection.enabled": 244 protectionEnabled := (*attr.Value) == "true" 245 log.Printf("[DEBUG] Setting ALB Deletion Protection Enabled: %t", protectionEnabled) 246 d.Set("enable_deletion_protection", protectionEnabled) 247 } 248 } 249 250 log.Printf("[DEBUG] Setting ALB Access Logs: %#v", accessLogMap) 251 if accessLogMap["bucket"] != "" || accessLogMap["prefix"] != "" { 252 d.Set("access_logs", []interface{}{accessLogMap}) 253 } else { 254 d.Set("access_logs", []interface{}{}) 255 } 256 257 return nil 258 } 259 260 func resourceAwsAlbUpdate(d *schema.ResourceData, meta interface{}) error { 261 elbconn := meta.(*AWSClient).elbv2conn 262 263 if !d.IsNewResource() { 264 if err := setElbV2Tags(elbconn, d); err != nil { 265 return errwrap.Wrapf("Error Modifying Tags on ALB: {{err}}", err) 266 } 267 } 268 269 attributes := make([]*elbv2.LoadBalancerAttribute, 0) 270 271 if d.HasChange("access_logs") { 272 logs := d.Get("access_logs").([]interface{}) 273 if len(logs) == 1 { 274 log := logs[0].(map[string]interface{}) 275 276 attributes = append(attributes, 277 &elbv2.LoadBalancerAttribute{ 278 Key: aws.String("access_logs.s3.enabled"), 279 Value: aws.String("true"), 280 }, 281 &elbv2.LoadBalancerAttribute{ 282 Key: aws.String("access_logs.s3.bucket"), 283 Value: aws.String(log["bucket"].(string)), 284 }) 285 286 if prefix, ok := log["prefix"]; ok { 287 attributes = append(attributes, &elbv2.LoadBalancerAttribute{ 288 Key: aws.String("access_logs.s3.prefix"), 289 Value: aws.String(prefix.(string)), 290 }) 291 } 292 } else if len(logs) == 0 { 293 attributes = append(attributes, &elbv2.LoadBalancerAttribute{ 294 Key: aws.String("access_logs.s3.enabled"), 295 Value: aws.String("false"), 296 }) 297 } 298 } 299 300 if d.HasChange("enable_deletion_protection") { 301 attributes = append(attributes, &elbv2.LoadBalancerAttribute{ 302 Key: aws.String("deletion_protection.enabled"), 303 Value: aws.String(fmt.Sprintf("%t", d.Get("enable_deletion_protection").(bool))), 304 }) 305 } 306 307 if d.HasChange("idle_timeout") { 308 attributes = append(attributes, &elbv2.LoadBalancerAttribute{ 309 Key: aws.String("idle_timeout.timeout_seconds"), 310 Value: aws.String(fmt.Sprintf("%d", d.Get("idle_timeout").(int))), 311 }) 312 } 313 314 if len(attributes) != 0 { 315 input := &elbv2.ModifyLoadBalancerAttributesInput{ 316 LoadBalancerArn: aws.String(d.Id()), 317 Attributes: attributes, 318 } 319 320 log.Printf("[DEBUG] ALB Modify Load Balancer Attributes Request: %#v", input) 321 _, err := elbconn.ModifyLoadBalancerAttributes(input) 322 if err != nil { 323 return fmt.Errorf("Failure configuring ALB attributes: %s", err) 324 } 325 } 326 327 return resourceAwsAlbRead(d, meta) 328 } 329 330 func resourceAwsAlbDelete(d *schema.ResourceData, meta interface{}) error { 331 albconn := meta.(*AWSClient).elbv2conn 332 333 log.Printf("[INFO] Deleting ALB: %s", d.Id()) 334 335 // Destroy the load balancer 336 deleteElbOpts := elbv2.DeleteLoadBalancerInput{ 337 LoadBalancerArn: aws.String(d.Id()), 338 } 339 if _, err := albconn.DeleteLoadBalancer(&deleteElbOpts); err != nil { 340 return fmt.Errorf("Error deleting ALB: %s", err) 341 } 342 343 return nil 344 } 345 346 // flattenSubnetsFromAvailabilityZones creates a slice of strings containing the subnet IDs 347 // for the ALB based on the AvailabilityZones structure returned by the API. 348 func flattenSubnetsFromAvailabilityZones(availabilityZones []*elbv2.AvailabilityZone) []string { 349 var result []string 350 for _, az := range availabilityZones { 351 result = append(result, *az.SubnetId) 352 } 353 return result 354 } 355 356 func albSuffixFromARN(arn *string) string { 357 if arn == nil { 358 return "" 359 } 360 361 if arnComponents := regexp.MustCompile(`arn:.*:loadbalancer/(.*)`).FindAllStringSubmatch(*arn, -1); len(arnComponents) == 1 { 362 if len(arnComponents[0]) == 2 { 363 return arnComponents[0][1] 364 } 365 } 366 367 return "" 368 }