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