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