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