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  }