github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_cloudfront_distribution.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/awserr" 10 "github.com/aws/aws-sdk-go/service/cloudfront" 11 "github.com/hashicorp/errwrap" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 "github.com/hashicorp/terraform/helper/validation" 15 ) 16 17 func resourceAwsCloudFrontDistribution() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceAwsCloudFrontDistributionCreate, 20 Read: resourceAwsCloudFrontDistributionRead, 21 Update: resourceAwsCloudFrontDistributionUpdate, 22 Delete: resourceAwsCloudFrontDistributionDelete, 23 Importer: &schema.ResourceImporter{ 24 State: resourceAwsCloudFrontDistributionImport, 25 }, 26 27 Schema: map[string]*schema.Schema{ 28 "arn": { 29 Type: schema.TypeString, 30 Computed: true, 31 }, 32 "aliases": { 33 Type: schema.TypeSet, 34 Optional: true, 35 Elem: &schema.Schema{Type: schema.TypeString}, 36 Set: aliasesHash, 37 }, 38 "cache_behavior": { 39 Type: schema.TypeSet, 40 Optional: true, 41 Set: cacheBehaviorHash, 42 Elem: &schema.Resource{ 43 Schema: map[string]*schema.Schema{ 44 "allowed_methods": { 45 Type: schema.TypeList, 46 Required: true, 47 Elem: &schema.Schema{Type: schema.TypeString}, 48 }, 49 "cached_methods": { 50 Type: schema.TypeList, 51 Required: true, 52 Elem: &schema.Schema{Type: schema.TypeString}, 53 }, 54 "compress": { 55 Type: schema.TypeBool, 56 Optional: true, 57 Default: false, 58 }, 59 "default_ttl": { 60 Type: schema.TypeInt, 61 Required: true, 62 }, 63 "forwarded_values": { 64 Type: schema.TypeSet, 65 Required: true, 66 Set: forwardedValuesHash, 67 MaxItems: 1, 68 Elem: &schema.Resource{ 69 Schema: map[string]*schema.Schema{ 70 "cookies": { 71 Type: schema.TypeSet, 72 Required: true, 73 Set: cookiePreferenceHash, 74 MaxItems: 1, 75 Elem: &schema.Resource{ 76 Schema: map[string]*schema.Schema{ 77 "forward": { 78 Type: schema.TypeString, 79 Required: true, 80 }, 81 "whitelisted_names": { 82 Type: schema.TypeList, 83 Optional: true, 84 Elem: &schema.Schema{Type: schema.TypeString}, 85 }, 86 }, 87 }, 88 }, 89 "headers": { 90 Type: schema.TypeList, 91 Optional: true, 92 Elem: &schema.Schema{Type: schema.TypeString}, 93 }, 94 "query_string": { 95 Type: schema.TypeBool, 96 Required: true, 97 }, 98 "query_string_cache_keys": { 99 Type: schema.TypeList, 100 Optional: true, 101 Elem: &schema.Schema{Type: schema.TypeString}, 102 }, 103 }, 104 }, 105 }, 106 "lambda_function_association": { 107 Type: schema.TypeSet, 108 Optional: true, 109 MaxItems: 4, 110 Elem: &schema.Resource{ 111 Schema: map[string]*schema.Schema{ 112 "event_type": { 113 Type: schema.TypeString, 114 Required: true, 115 }, 116 "lambda_arn": { 117 Type: schema.TypeString, 118 Required: true, 119 }, 120 }, 121 }, 122 Set: lambdaFunctionAssociationHash, 123 }, 124 "max_ttl": { 125 Type: schema.TypeInt, 126 Required: true, 127 }, 128 "min_ttl": { 129 Type: schema.TypeInt, 130 Required: true, 131 }, 132 "path_pattern": { 133 Type: schema.TypeString, 134 Required: true, 135 }, 136 "smooth_streaming": { 137 Type: schema.TypeBool, 138 Optional: true, 139 }, 140 "target_origin_id": { 141 Type: schema.TypeString, 142 Required: true, 143 }, 144 "trusted_signers": { 145 Type: schema.TypeList, 146 Optional: true, 147 Elem: &schema.Schema{Type: schema.TypeString}, 148 }, 149 "viewer_protocol_policy": { 150 Type: schema.TypeString, 151 Required: true, 152 }, 153 }, 154 }, 155 }, 156 "comment": { 157 Type: schema.TypeString, 158 Optional: true, 159 }, 160 "custom_error_response": { 161 Type: schema.TypeSet, 162 Optional: true, 163 Set: customErrorResponseHash, 164 Elem: &schema.Resource{ 165 Schema: map[string]*schema.Schema{ 166 "error_caching_min_ttl": { 167 Type: schema.TypeInt, 168 Optional: true, 169 }, 170 "error_code": { 171 Type: schema.TypeInt, 172 Required: true, 173 }, 174 "response_code": { 175 Type: schema.TypeInt, 176 Optional: true, 177 }, 178 "response_page_path": { 179 Type: schema.TypeString, 180 Optional: true, 181 }, 182 }, 183 }, 184 }, 185 "default_cache_behavior": { 186 Type: schema.TypeSet, 187 Required: true, 188 Set: defaultCacheBehaviorHash, 189 MaxItems: 1, 190 Elem: &schema.Resource{ 191 Schema: map[string]*schema.Schema{ 192 "allowed_methods": { 193 Type: schema.TypeList, 194 Required: true, 195 Elem: &schema.Schema{Type: schema.TypeString}, 196 }, 197 "cached_methods": { 198 Type: schema.TypeList, 199 Required: true, 200 Elem: &schema.Schema{Type: schema.TypeString}, 201 }, 202 "compress": { 203 Type: schema.TypeBool, 204 Optional: true, 205 Default: false, 206 }, 207 "default_ttl": { 208 Type: schema.TypeInt, 209 Required: true, 210 }, 211 "forwarded_values": { 212 Type: schema.TypeSet, 213 Required: true, 214 Set: forwardedValuesHash, 215 MaxItems: 1, 216 Elem: &schema.Resource{ 217 Schema: map[string]*schema.Schema{ 218 "cookies": { 219 Type: schema.TypeSet, 220 Optional: true, 221 Set: cookiePreferenceHash, 222 MaxItems: 1, 223 Elem: &schema.Resource{ 224 Schema: map[string]*schema.Schema{ 225 "forward": { 226 Type: schema.TypeString, 227 Required: true, 228 }, 229 "whitelisted_names": { 230 Type: schema.TypeList, 231 Optional: true, 232 Elem: &schema.Schema{Type: schema.TypeString}, 233 }, 234 }, 235 }, 236 }, 237 "headers": { 238 Type: schema.TypeList, 239 Optional: true, 240 Elem: &schema.Schema{Type: schema.TypeString}, 241 }, 242 "query_string": { 243 Type: schema.TypeBool, 244 Required: true, 245 }, 246 "query_string_cache_keys": { 247 Type: schema.TypeList, 248 Optional: true, 249 Elem: &schema.Schema{Type: schema.TypeString}, 250 }, 251 }, 252 }, 253 }, 254 "lambda_function_association": { 255 Type: schema.TypeSet, 256 Optional: true, 257 MaxItems: 4, 258 Elem: &schema.Resource{ 259 Schema: map[string]*schema.Schema{ 260 "event_type": { 261 Type: schema.TypeString, 262 Required: true, 263 }, 264 "lambda_arn": { 265 Type: schema.TypeString, 266 Required: true, 267 }, 268 }, 269 }, 270 Set: lambdaFunctionAssociationHash, 271 }, 272 "max_ttl": { 273 Type: schema.TypeInt, 274 Required: true, 275 }, 276 "min_ttl": { 277 Type: schema.TypeInt, 278 Required: true, 279 }, 280 "smooth_streaming": { 281 Type: schema.TypeBool, 282 Optional: true, 283 }, 284 "target_origin_id": { 285 Type: schema.TypeString, 286 Required: true, 287 }, 288 "trusted_signers": { 289 Type: schema.TypeList, 290 Optional: true, 291 Elem: &schema.Schema{Type: schema.TypeString}, 292 }, 293 "viewer_protocol_policy": { 294 Type: schema.TypeString, 295 Required: true, 296 }, 297 }, 298 }, 299 }, 300 "default_root_object": { 301 Type: schema.TypeString, 302 Optional: true, 303 }, 304 "enabled": { 305 Type: schema.TypeBool, 306 Required: true, 307 }, 308 "http_version": { 309 Type: schema.TypeString, 310 Optional: true, 311 Default: "http2", 312 ValidateFunc: validateHTTP, 313 }, 314 "logging_config": { 315 Type: schema.TypeSet, 316 Optional: true, 317 Set: loggingConfigHash, 318 MaxItems: 1, 319 Elem: &schema.Resource{ 320 Schema: map[string]*schema.Schema{ 321 "bucket": { 322 Type: schema.TypeString, 323 Required: true, 324 }, 325 "include_cookies": { 326 Type: schema.TypeBool, 327 Optional: true, 328 Default: false, 329 }, 330 "prefix": { 331 Type: schema.TypeString, 332 Optional: true, 333 Default: "", 334 }, 335 }, 336 }, 337 }, 338 "origin": { 339 Type: schema.TypeSet, 340 Required: true, 341 Set: originHash, 342 Elem: &schema.Resource{ 343 Schema: map[string]*schema.Schema{ 344 "custom_origin_config": { 345 Type: schema.TypeSet, 346 Optional: true, 347 ConflictsWith: []string{"origin.s3_origin_config"}, 348 Set: customOriginConfigHash, 349 MaxItems: 1, 350 Elem: &schema.Resource{ 351 Schema: map[string]*schema.Schema{ 352 "http_port": { 353 Type: schema.TypeInt, 354 Required: true, 355 }, 356 "https_port": { 357 Type: schema.TypeInt, 358 Required: true, 359 }, 360 "origin_keepalive_timeout": { 361 Type: schema.TypeInt, 362 Optional: true, 363 Default: 5, 364 ValidateFunc: validation.IntBetween(1, 60), 365 }, 366 "origin_read_timeout": { 367 Type: schema.TypeInt, 368 Optional: true, 369 Default: 30, 370 ValidateFunc: validation.IntBetween(4, 60), 371 }, 372 "origin_protocol_policy": { 373 Type: schema.TypeString, 374 Required: true, 375 }, 376 "origin_ssl_protocols": { 377 Type: schema.TypeList, 378 Required: true, 379 Elem: &schema.Schema{Type: schema.TypeString}, 380 }, 381 }, 382 }, 383 }, 384 "domain_name": { 385 Type: schema.TypeString, 386 Required: true, 387 }, 388 "custom_header": { 389 Type: schema.TypeSet, 390 Optional: true, 391 Set: originCustomHeaderHash, 392 Elem: &schema.Resource{ 393 Schema: map[string]*schema.Schema{ 394 "name": { 395 Type: schema.TypeString, 396 Required: true, 397 }, 398 "value": { 399 Type: schema.TypeString, 400 Required: true, 401 }, 402 }, 403 }, 404 }, 405 "origin_id": { 406 Type: schema.TypeString, 407 Required: true, 408 }, 409 "origin_path": { 410 Type: schema.TypeString, 411 Optional: true, 412 }, 413 "s3_origin_config": { 414 Type: schema.TypeSet, 415 Optional: true, 416 ConflictsWith: []string{"origin.custom_origin_config"}, 417 Set: s3OriginConfigHash, 418 MaxItems: 1, 419 Elem: &schema.Resource{ 420 Schema: map[string]*schema.Schema{ 421 "origin_access_identity": { 422 Type: schema.TypeString, 423 Required: true, 424 }, 425 }, 426 }, 427 }, 428 }, 429 }, 430 }, 431 "price_class": { 432 Type: schema.TypeString, 433 Optional: true, 434 Default: "PriceClass_All", 435 }, 436 "restrictions": { 437 Type: schema.TypeSet, 438 Required: true, 439 Set: restrictionsHash, 440 MaxItems: 1, 441 Elem: &schema.Resource{ 442 Schema: map[string]*schema.Schema{ 443 "geo_restriction": { 444 Type: schema.TypeSet, 445 Required: true, 446 Set: geoRestrictionHash, 447 MaxItems: 1, 448 Elem: &schema.Resource{ 449 Schema: map[string]*schema.Schema{ 450 "locations": { 451 Type: schema.TypeList, 452 Optional: true, 453 Elem: &schema.Schema{Type: schema.TypeString}, 454 }, 455 "restriction_type": { 456 Type: schema.TypeString, 457 Required: true, 458 }, 459 }, 460 }, 461 }, 462 }, 463 }, 464 }, 465 "viewer_certificate": { 466 Type: schema.TypeSet, 467 Required: true, 468 Set: viewerCertificateHash, 469 MaxItems: 1, 470 Elem: &schema.Resource{ 471 Schema: map[string]*schema.Schema{ 472 "acm_certificate_arn": { 473 Type: schema.TypeString, 474 Optional: true, 475 ConflictsWith: []string{"viewer_certificate.cloudfront_default_certificate", "viewer_certificate.iam_certificate_id"}, 476 }, 477 "cloudfront_default_certificate": { 478 Type: schema.TypeBool, 479 Optional: true, 480 ConflictsWith: []string{"viewer_certificate.acm_certificate_arn", "viewer_certificate.iam_certificate_id"}, 481 }, 482 "iam_certificate_id": { 483 Type: schema.TypeString, 484 Optional: true, 485 ConflictsWith: []string{"viewer_certificate.acm_certificate_arn", "viewer_certificate.cloudfront_default_certificate"}, 486 }, 487 "minimum_protocol_version": { 488 Type: schema.TypeString, 489 Optional: true, 490 Default: "SSLv3", 491 }, 492 "ssl_support_method": { 493 Type: schema.TypeString, 494 Optional: true, 495 }, 496 }, 497 }, 498 }, 499 "web_acl_id": { 500 Type: schema.TypeString, 501 Optional: true, 502 }, 503 "caller_reference": { 504 Type: schema.TypeString, 505 Computed: true, 506 }, 507 "status": { 508 Type: schema.TypeString, 509 Computed: true, 510 }, 511 "active_trusted_signers": { 512 Type: schema.TypeMap, 513 Computed: true, 514 }, 515 "domain_name": { 516 Type: schema.TypeString, 517 Computed: true, 518 }, 519 "last_modified_time": { 520 Type: schema.TypeString, 521 Computed: true, 522 }, 523 "in_progress_validation_batches": { 524 Type: schema.TypeInt, 525 Computed: true, 526 }, 527 "etag": { 528 Type: schema.TypeString, 529 Computed: true, 530 }, 531 "hosted_zone_id": { 532 Type: schema.TypeString, 533 Computed: true, 534 }, 535 // retain_on_delete is a non-API attribute that may help facilitate speedy 536 // deletion of a resoruce. It's mainly here for testing purposes, so 537 // enable at your own risk. 538 "retain_on_delete": { 539 Type: schema.TypeBool, 540 Optional: true, 541 Default: false, 542 }, 543 "is_ipv6_enabled": { 544 Type: schema.TypeBool, 545 Optional: true, 546 Default: false, 547 }, 548 549 "tags": tagsSchema(), 550 }, 551 } 552 } 553 554 func resourceAwsCloudFrontDistributionCreate(d *schema.ResourceData, meta interface{}) error { 555 conn := meta.(*AWSClient).cloudfrontconn 556 557 params := &cloudfront.CreateDistributionWithTagsInput{ 558 DistributionConfigWithTags: &cloudfront.DistributionConfigWithTags{ 559 DistributionConfig: expandDistributionConfig(d), 560 Tags: tagsFromMapCloudFront(d.Get("tags").(map[string]interface{})), 561 }, 562 } 563 564 resp, err := conn.CreateDistributionWithTags(params) 565 if err != nil { 566 return err 567 } 568 d.SetId(*resp.Distribution.Id) 569 return resourceAwsCloudFrontDistributionRead(d, meta) 570 } 571 572 func resourceAwsCloudFrontDistributionRead(d *schema.ResourceData, meta interface{}) error { 573 conn := meta.(*AWSClient).cloudfrontconn 574 params := &cloudfront.GetDistributionInput{ 575 Id: aws.String(d.Id()), 576 } 577 578 resp, err := conn.GetDistribution(params) 579 if err != nil { 580 if errcode, ok := err.(awserr.Error); ok && errcode.Code() == "NoSuchDistribution" { 581 log.Printf("[WARN] No Distribution found: %s", d.Id()) 582 d.SetId("") 583 return nil 584 } 585 586 return err 587 } 588 589 // Update attributes from DistributionConfig 590 err = flattenDistributionConfig(d, resp.Distribution.DistributionConfig) 591 if err != nil { 592 return err 593 } 594 // Update other attributes outside of DistributionConfig 595 d.SetId(*resp.Distribution.Id) 596 err = d.Set("active_trusted_signers", flattenActiveTrustedSigners(resp.Distribution.ActiveTrustedSigners)) 597 if err != nil { 598 return err 599 } 600 d.Set("status", resp.Distribution.Status) 601 d.Set("domain_name", resp.Distribution.DomainName) 602 d.Set("last_modified_time", aws.String(resp.Distribution.LastModifiedTime.String())) 603 d.Set("in_progress_validation_batches", resp.Distribution.InProgressInvalidationBatches) 604 d.Set("etag", resp.ETag) 605 d.Set("arn", resp.Distribution.ARN) 606 607 tagResp, err := conn.ListTagsForResource(&cloudfront.ListTagsForResourceInput{ 608 Resource: aws.String(d.Get("arn").(string)), 609 }) 610 611 if err != nil { 612 return errwrap.Wrapf(fmt.Sprintf( 613 "Error retrieving EC2 tags for CloudFront Distribution %q (ARN: %q): {{err}}", 614 d.Id(), d.Get("arn").(string)), err) 615 } 616 617 if err := d.Set("tags", tagsToMapCloudFront(tagResp.Tags)); err != nil { 618 return err 619 } 620 621 return nil 622 } 623 624 func resourceAwsCloudFrontDistributionUpdate(d *schema.ResourceData, meta interface{}) error { 625 conn := meta.(*AWSClient).cloudfrontconn 626 params := &cloudfront.UpdateDistributionInput{ 627 Id: aws.String(d.Id()), 628 DistributionConfig: expandDistributionConfig(d), 629 IfMatch: aws.String(d.Get("etag").(string)), 630 } 631 _, err := conn.UpdateDistribution(params) 632 if err != nil { 633 return err 634 } 635 636 if err := setTagsCloudFront(conn, d, d.Get("arn").(string)); err != nil { 637 return err 638 } 639 640 return resourceAwsCloudFrontDistributionRead(d, meta) 641 } 642 643 func resourceAwsCloudFrontDistributionDelete(d *schema.ResourceData, meta interface{}) error { 644 conn := meta.(*AWSClient).cloudfrontconn 645 646 // manually disable the distribution first 647 d.Set("enabled", false) 648 err := resourceAwsCloudFrontDistributionUpdate(d, meta) 649 if err != nil { 650 return err 651 } 652 653 // skip delete if retain_on_delete is enabled 654 if d.Get("retain_on_delete").(bool) { 655 log.Printf("[WARN] Removing CloudFront Distribution ID %q with `retain_on_delete` set. Please delete this distribution manually.", d.Id()) 656 d.SetId("") 657 return nil 658 } 659 660 // Distribution needs to be in deployed state again before it can be deleted. 661 err = resourceAwsCloudFrontDistributionWaitUntilDeployed(d.Id(), meta) 662 if err != nil { 663 return err 664 } 665 666 // now delete 667 params := &cloudfront.DeleteDistributionInput{ 668 Id: aws.String(d.Id()), 669 IfMatch: aws.String(d.Get("etag").(string)), 670 } 671 672 _, err = conn.DeleteDistribution(params) 673 if err != nil { 674 return err 675 } 676 677 // Done 678 d.SetId("") 679 return nil 680 } 681 682 // resourceAwsCloudFrontWebDistributionWaitUntilDeployed blocks until the 683 // distribution is deployed. It currently takes exactly 15 minutes to deploy 684 // but that might change in the future. 685 func resourceAwsCloudFrontDistributionWaitUntilDeployed(id string, meta interface{}) error { 686 stateConf := &resource.StateChangeConf{ 687 Pending: []string{"InProgress"}, 688 Target: []string{"Deployed"}, 689 Refresh: resourceAwsCloudFrontWebDistributionStateRefreshFunc(id, meta), 690 Timeout: 70 * time.Minute, 691 MinTimeout: 15 * time.Second, 692 Delay: 10 * time.Minute, 693 } 694 695 _, err := stateConf.WaitForState() 696 return err 697 } 698 699 // The refresh function for resourceAwsCloudFrontWebDistributionWaitUntilDeployed. 700 func resourceAwsCloudFrontWebDistributionStateRefreshFunc(id string, meta interface{}) resource.StateRefreshFunc { 701 return func() (interface{}, string, error) { 702 conn := meta.(*AWSClient).cloudfrontconn 703 params := &cloudfront.GetDistributionInput{ 704 Id: aws.String(id), 705 } 706 707 resp, err := conn.GetDistribution(params) 708 if err != nil { 709 log.Printf("[WARN] Error retrieving CloudFront Distribution %q details: %s", id, err) 710 return nil, "", err 711 } 712 713 if resp == nil { 714 return nil, "", nil 715 } 716 717 return resp.Distribution, *resp.Distribution.Status, nil 718 } 719 } 720 721 // validateHTTP ensures that the http_version resource parameter is 722 // correct. 723 func validateHTTP(v interface{}, k string) (ws []string, errors []error) { 724 value := v.(string) 725 726 if value != "http1.1" && value != "http2" { 727 errors = append(errors, fmt.Errorf( 728 "%q contains an invalid HTTP version parameter %q. Valid parameters are either %q or %q.", 729 k, value, "http1.1", "http2")) 730 } 731 return 732 }