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