github.com/mohanarpit/terraform@v0.6.16-0.20160909104007-291f29853544/builtin/providers/aws/resource_aws_alb_target_group.go (about) 1 package aws 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "strconv" 8 "strings" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/service/elbv2" 13 "github.com/hashicorp/errwrap" 14 "github.com/hashicorp/terraform/helper/schema" 15 ) 16 17 func resourceAwsAlbTargetGroup() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceAwsAlbTargetGroupCreate, 20 Read: resourceAwsAlbTargetGroupRead, 21 Update: resourceAwsAlbTargetGroupUpdate, 22 Delete: resourceAwsAlbTargetGroupDelete, 23 Importer: &schema.ResourceImporter{ 24 State: schema.ImportStatePassthrough, 25 }, 26 27 Schema: map[string]*schema.Schema{ 28 "arn": { 29 Type: schema.TypeString, 30 Computed: true, 31 }, 32 33 "name": { 34 Type: schema.TypeString, 35 Required: true, 36 }, 37 38 "port": { 39 Type: schema.TypeInt, 40 Required: true, 41 ValidateFunc: validateAwsAlbTargetGroupPort, 42 }, 43 44 "protocol": { 45 Type: schema.TypeString, 46 Required: true, 47 ValidateFunc: validateAwsAlbTargetGroupProtocol, 48 }, 49 50 "vpc_id": { 51 Type: schema.TypeString, 52 Required: true, 53 }, 54 55 "deregistration_delay": { 56 Type: schema.TypeInt, 57 Optional: true, 58 Default: 300, 59 ValidateFunc: validateAwsAlbTargetGroupDeregistrationDelay, 60 }, 61 62 "stickiness": { 63 Type: schema.TypeList, 64 Optional: true, 65 MaxItems: 1, 66 Elem: &schema.Resource{ 67 Schema: map[string]*schema.Schema{ 68 "type": { 69 Type: schema.TypeString, 70 Required: true, 71 ValidateFunc: validateAwsAlbTargetGroupStickinessType, 72 }, 73 "cookie_duration": { 74 Type: schema.TypeInt, 75 Optional: true, 76 Default: 86400, 77 ValidateFunc: validateAwsAlbTargetGroupStickinessCookieDuration, 78 }, 79 }, 80 }, 81 }, 82 83 "health_check": { 84 Type: schema.TypeList, 85 Optional: true, 86 Computed: true, 87 MaxItems: 1, 88 Elem: &schema.Resource{ 89 Schema: map[string]*schema.Schema{ 90 "interval": { 91 Type: schema.TypeInt, 92 Optional: true, 93 Default: 30, 94 }, 95 96 "path": { 97 Type: schema.TypeString, 98 Optional: true, 99 Default: "/", 100 ValidateFunc: validateAwsAlbTargetGroupHealthCheckPath, 101 }, 102 103 "port": { 104 Type: schema.TypeString, 105 Optional: true, 106 Default: "traffic-port", 107 ValidateFunc: validateAwsAlbTargetGroupHealthCheckPort, 108 }, 109 110 "protocol": { 111 Type: schema.TypeString, 112 Optional: true, 113 Default: "HTTP", 114 StateFunc: func(v interface{}) string { 115 return strings.ToUpper(v.(string)) 116 }, 117 ValidateFunc: validateAwsAlbTargetGroupHealthCheckProtocol, 118 }, 119 120 "timeout": { 121 Type: schema.TypeInt, 122 Optional: true, 123 Default: 5, 124 ValidateFunc: validateAwsAlbTargetGroupHealthCheckTimeout, 125 }, 126 127 "healthy_threshold": { 128 Type: schema.TypeInt, 129 Optional: true, 130 Default: 5, 131 ValidateFunc: validateAwsAlbTargetGroupHealthCheckHealthyThreshold, 132 }, 133 134 "matcher": { 135 Type: schema.TypeString, 136 Optional: true, 137 Default: "200", 138 }, 139 140 "unhealthy_threshold": { 141 Type: schema.TypeInt, 142 Optional: true, 143 Default: 2, 144 ValidateFunc: validateAwsAlbTargetGroupHealthCheckHealthyThreshold, 145 }, 146 }, 147 }, 148 }, 149 150 "tags": tagsSchema(), 151 }, 152 } 153 } 154 155 func resourceAwsAlbTargetGroupCreate(d *schema.ResourceData, meta interface{}) error { 156 elbconn := meta.(*AWSClient).elbv2conn 157 158 params := &elbv2.CreateTargetGroupInput{ 159 Name: aws.String(d.Get("name").(string)), 160 Port: aws.Int64(int64(d.Get("port").(int))), 161 Protocol: aws.String(d.Get("protocol").(string)), 162 VpcId: aws.String(d.Get("vpc_id").(string)), 163 } 164 165 if healthChecks := d.Get("health_check").([]interface{}); len(healthChecks) == 1 { 166 healthCheck := healthChecks[0].(map[string]interface{}) 167 168 params.HealthCheckIntervalSeconds = aws.Int64(int64(healthCheck["interval"].(int))) 169 params.HealthCheckPath = aws.String(healthCheck["path"].(string)) 170 params.HealthCheckPort = aws.String(healthCheck["port"].(string)) 171 params.HealthCheckProtocol = aws.String(healthCheck["protocol"].(string)) 172 params.HealthCheckTimeoutSeconds = aws.Int64(int64(healthCheck["timeout"].(int))) 173 params.HealthyThresholdCount = aws.Int64(int64(healthCheck["healthy_threshold"].(int))) 174 params.UnhealthyThresholdCount = aws.Int64(int64(healthCheck["unhealthy_threshold"].(int))) 175 params.Matcher = &elbv2.Matcher{ 176 HttpCode: aws.String(healthCheck["matcher"].(string)), 177 } 178 } 179 180 resp, err := elbconn.CreateTargetGroup(params) 181 if err != nil { 182 return errwrap.Wrapf("Error creating ALB Target Group: {{err}}", err) 183 } 184 185 if len(resp.TargetGroups) == 0 { 186 return errors.New("Error creating ALB Target Group: no groups returned in response") 187 } 188 189 targetGroupArn := resp.TargetGroups[0].TargetGroupArn 190 d.SetId(*targetGroupArn) 191 192 return resourceAwsAlbTargetGroupUpdate(d, meta) 193 } 194 195 func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) error { 196 elbconn := meta.(*AWSClient).elbv2conn 197 198 resp, err := elbconn.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{ 199 TargetGroupArns: []*string{aws.String(d.Id())}, 200 }) 201 if err != nil { 202 if isTargetGroupNotFound(err) { 203 log.Printf("[DEBUG] DescribeTargetGroups - removing %s from state", d.Id()) 204 d.SetId("") 205 return nil 206 } 207 return errwrap.Wrapf("Error retrieving Target Group: {{err}}", err) 208 } 209 210 if len(resp.TargetGroups) != 1 { 211 return fmt.Errorf("Error retrieving Target Group %q", d.Id()) 212 } 213 214 targetGroup := resp.TargetGroups[0] 215 216 d.Set("arn", targetGroup.TargetGroupArn) 217 d.Set("name", targetGroup.TargetGroupName) 218 d.Set("port", targetGroup.Port) 219 d.Set("protocol", targetGroup.Protocol) 220 d.Set("vpc_id", targetGroup.VpcId) 221 222 healthCheck := make(map[string]interface{}) 223 healthCheck["interval"] = *targetGroup.HealthCheckIntervalSeconds 224 healthCheck["path"] = *targetGroup.HealthCheckPath 225 healthCheck["port"] = *targetGroup.HealthCheckPort 226 healthCheck["protocol"] = *targetGroup.HealthCheckProtocol 227 healthCheck["timeout"] = *targetGroup.HealthCheckTimeoutSeconds 228 healthCheck["healthy_threshold"] = *targetGroup.HealthyThresholdCount 229 healthCheck["unhealthy_threshold"] = *targetGroup.UnhealthyThresholdCount 230 healthCheck["matcher"] = *targetGroup.Matcher.HttpCode 231 d.Set("health_check", []interface{}{healthCheck}) 232 233 attrResp, err := elbconn.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ 234 TargetGroupArn: aws.String(d.Id()), 235 }) 236 if err != nil { 237 return errwrap.Wrapf("Error retrieving Target Group Attributes: {{err}}", err) 238 } 239 240 stickinessMap := map[string]interface{}{} 241 for _, attr := range attrResp.Attributes { 242 switch *attr.Key { 243 case "stickiness.type": 244 stickinessMap["type"] = *attr.Value 245 case "stickiness.lb_cookie.duration_seconds": 246 stickinessMap["cookie_duration"] = *attr.Value 247 case "deregistration_delay.timeout_seconds": 248 timeout, err := strconv.Atoi(*attr.Value) 249 if err != nil { 250 return fmt.Errorf("Error converting deregistration_delay.timeout_seconds to int: %s", *attr.Value) 251 } 252 d.Set("deregistration_delay", timeout) 253 } 254 } 255 d.Set("stickiness", []interface{}{stickinessMap}) 256 257 return nil 258 } 259 260 func resourceAwsAlbTargetGroupUpdate(d *schema.ResourceData, meta interface{}) error { 261 elbconn := meta.(*AWSClient).elbv2conn 262 263 if err := setElbV2Tags(elbconn, d); err != nil { 264 return errwrap.Wrapf("Error Modifying Tags on ALB Target Group: {{err}}", err) 265 } 266 267 if d.HasChange("health_check") { 268 healthChecks := d.Get("health_check").([]interface{}) 269 270 var params *elbv2.ModifyTargetGroupInput 271 if len(healthChecks) == 1 { 272 healthCheck := healthChecks[0].(map[string]interface{}) 273 274 params = &elbv2.ModifyTargetGroupInput{ 275 TargetGroupArn: aws.String(d.Id()), 276 HealthCheckIntervalSeconds: aws.Int64(int64(healthCheck["interval"].(int))), 277 HealthCheckPath: aws.String(healthCheck["path"].(string)), 278 HealthCheckPort: aws.String(healthCheck["port"].(string)), 279 HealthCheckProtocol: aws.String(healthCheck["protocol"].(string)), 280 HealthCheckTimeoutSeconds: aws.Int64(int64(healthCheck["timeout"].(int))), 281 HealthyThresholdCount: aws.Int64(int64(healthCheck["healthy_threshold"].(int))), 282 UnhealthyThresholdCount: aws.Int64(int64(healthCheck["unhealthy_threshold"].(int))), 283 Matcher: &elbv2.Matcher{ 284 HttpCode: aws.String(healthCheck["matcher"].(string)), 285 }, 286 } 287 } else { 288 params = &elbv2.ModifyTargetGroupInput{ 289 TargetGroupArn: aws.String(d.Id()), 290 } 291 } 292 293 _, err := elbconn.ModifyTargetGroup(params) 294 if err != nil { 295 return errwrap.Wrapf("Error modifying Target Group: {{err}}", err) 296 } 297 } 298 299 var attrs []*elbv2.TargetGroupAttribute 300 301 if d.HasChange("deregistration_delay") { 302 attrs = append(attrs, &elbv2.TargetGroupAttribute{ 303 Key: aws.String("deregistration_delay.timeout_seconds"), 304 Value: aws.String(fmt.Sprintf("%d", d.Get("deregistration_delay").(int))), 305 }) 306 } 307 308 if d.HasChange("stickiness") { 309 stickinessBlocks := d.Get("stickiness").([]interface{}) 310 if len(stickinessBlocks) == 1 { 311 stickiness := stickinessBlocks[0].(map[string]interface{}) 312 313 attrs = append(attrs, 314 &elbv2.TargetGroupAttribute{ 315 Key: aws.String("stickiness.enabled"), 316 Value: aws.String("true"), 317 }, 318 &elbv2.TargetGroupAttribute{ 319 Key: aws.String("stickiness.type"), 320 Value: aws.String(stickiness["type"].(string)), 321 }, 322 &elbv2.TargetGroupAttribute{ 323 Key: aws.String("stickiness.lb_cookie.duration_seconds"), 324 Value: aws.String(fmt.Sprintf("%d", stickiness["cookie_duration"].(int))), 325 }) 326 } else if len(stickinessBlocks) == 0 { 327 attrs = append(attrs, &elbv2.TargetGroupAttribute{ 328 Key: aws.String("stickiness.enabled"), 329 Value: aws.String("false"), 330 }) 331 } 332 } 333 334 if len(attrs) > 0 { 335 params := &elbv2.ModifyTargetGroupAttributesInput{ 336 TargetGroupArn: aws.String(d.Id()), 337 Attributes: attrs, 338 } 339 340 _, err := elbconn.ModifyTargetGroupAttributes(params) 341 if err != nil { 342 return errwrap.Wrapf("Error modifying Target Group Attributes: {{err}}", err) 343 } 344 } 345 346 return resourceAwsAlbTargetGroupRead(d, meta) 347 } 348 349 func resourceAwsAlbTargetGroupDelete(d *schema.ResourceData, meta interface{}) error { 350 elbconn := meta.(*AWSClient).elbv2conn 351 352 _, err := elbconn.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ 353 TargetGroupArn: aws.String(d.Id()), 354 }) 355 if err != nil { 356 return errwrap.Wrapf("Error deleting Target Group: {{err}}", err) 357 } 358 359 return nil 360 } 361 362 func isTargetGroupNotFound(err error) bool { 363 elberr, ok := err.(awserr.Error) 364 return ok && elberr.Code() == "TargetGroupNotFound" 365 } 366 367 func validateAwsAlbTargetGroupHealthCheckPath(v interface{}, k string) (ws []string, errors []error) { 368 value := v.(string) 369 if len(value) > 1024 { 370 errors = append(errors, fmt.Errorf( 371 "%q cannot be longer than 1024 characters: %q", k, value)) 372 } 373 return 374 } 375 376 func validateAwsAlbTargetGroupHealthCheckPort(v interface{}, k string) (ws []string, errors []error) { 377 value := v.(string) 378 379 if value == "traffic-port" { 380 return 381 } 382 383 port, err := strconv.Atoi(value) 384 if err != nil { 385 errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536) or %q", k, "traffic-port")) 386 } 387 388 if port < 1 || port > 65536 { 389 errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536) or %q", k, "traffic-port")) 390 } 391 392 return 393 } 394 395 func validateAwsAlbTargetGroupHealthCheckHealthyThreshold(v interface{}, k string) (ws []string, errors []error) { 396 value := v.(int) 397 if value < 2 || value > 10 { 398 errors = append(errors, fmt.Errorf("%q must be an integer between 2 and 10", k)) 399 } 400 return 401 } 402 403 func validateAwsAlbTargetGroupHealthCheckTimeout(v interface{}, k string) (ws []string, errors []error) { 404 value := v.(int) 405 if value < 2 || value > 60 { 406 errors = append(errors, fmt.Errorf("%q must be an integer between 2 and 60", k)) 407 } 408 return 409 } 410 411 func validateAwsAlbTargetGroupHealthCheckProtocol(v interface{}, k string) (ws []string, errors []error) { 412 value := strings.ToLower(v.(string)) 413 if value == "http" || value == "https" { 414 return 415 } 416 417 errors = append(errors, fmt.Errorf("%q must be either %q or %q", k, "HTTP", "HTTPS")) 418 return 419 } 420 421 func validateAwsAlbTargetGroupPort(v interface{}, k string) (ws []string, errors []error) { 422 port := v.(int) 423 if port < 1 || port > 65536 { 424 errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536)", k)) 425 } 426 return 427 } 428 429 func validateAwsAlbTargetGroupProtocol(v interface{}, k string) (ws []string, errors []error) { 430 protocol := strings.ToLower(v.(string)) 431 if protocol == "http" || protocol == "https" { 432 return 433 } 434 435 errors = append(errors, fmt.Errorf("%q must be either %q or %q", k, "HTTP", "HTTPS")) 436 return 437 } 438 439 func validateAwsAlbTargetGroupDeregistrationDelay(v interface{}, k string) (ws []string, errors []error) { 440 delay := v.(int) 441 if delay < 0 || delay > 3600 { 442 errors = append(errors, fmt.Errorf("%q must be in the range 0-3600 seconds", k)) 443 } 444 return 445 } 446 447 func validateAwsAlbTargetGroupStickinessType(v interface{}, k string) (ws []string, errors []error) { 448 stickinessType := v.(string) 449 if stickinessType != "lb_cookie" { 450 errors = append(errors, fmt.Errorf("%q must have the value %q", k, "lb_cookie")) 451 } 452 return 453 } 454 455 func validateAwsAlbTargetGroupStickinessCookieDuration(v interface{}, k string) (ws []string, errors []error) { 456 duration := v.(int) 457 if duration < 1 || duration > 604800 { 458 errors = append(errors, fmt.Errorf("%q must be a between 1 second and 1 week (1-604800 seconds))", k)) 459 } 460 return 461 }