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  }