github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/service/firehose"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func cloudWatchLoggingOptionsSchema() *schema.Schema {
    17  	return &schema.Schema{
    18  		Type:     schema.TypeSet,
    19  		MaxItems: 1,
    20  		Optional: true,
    21  		Computed: true,
    22  		Elem: &schema.Resource{
    23  			Schema: map[string]*schema.Schema{
    24  				"enabled": {
    25  					Type:     schema.TypeBool,
    26  					Optional: true,
    27  					Default:  false,
    28  				},
    29  
    30  				"log_group_name": {
    31  					Type:     schema.TypeString,
    32  					Optional: true,
    33  				},
    34  
    35  				"log_stream_name": {
    36  					Type:     schema.TypeString,
    37  					Optional: true,
    38  				},
    39  			},
    40  		},
    41  	}
    42  }
    43  
    44  func resourceAwsKinesisFirehoseDeliveryStream() *schema.Resource {
    45  	return &schema.Resource{
    46  		Create: resourceAwsKinesisFirehoseDeliveryStreamCreate,
    47  		Read:   resourceAwsKinesisFirehoseDeliveryStreamRead,
    48  		Update: resourceAwsKinesisFirehoseDeliveryStreamUpdate,
    49  		Delete: resourceAwsKinesisFirehoseDeliveryStreamDelete,
    50  
    51  		SchemaVersion: 1,
    52  		MigrateState:  resourceAwsKinesisFirehoseMigrateState,
    53  		Schema: map[string]*schema.Schema{
    54  			"name": &schema.Schema{
    55  				Type:     schema.TypeString,
    56  				Required: true,
    57  				ForceNew: true,
    58  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    59  					value := v.(string)
    60  					if len(value) > 64 {
    61  						errors = append(errors, fmt.Errorf(
    62  							"%q cannot be longer than 64 characters", k))
    63  					}
    64  					return
    65  				},
    66  			},
    67  
    68  			"destination": &schema.Schema{
    69  				Type:     schema.TypeString,
    70  				Required: true,
    71  				ForceNew: true,
    72  				StateFunc: func(v interface{}) string {
    73  					value := v.(string)
    74  					return strings.ToLower(value)
    75  				},
    76  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    77  					value := v.(string)
    78  					if value != "s3" && value != "redshift" && value != "elasticsearch" {
    79  						errors = append(errors, fmt.Errorf(
    80  							"%q must be one of 's3', 'redshift', 'elasticsearch'", k))
    81  					}
    82  					return
    83  				},
    84  			},
    85  
    86  			// elements removed in v0.7.0
    87  			"role_arn": &schema.Schema{
    88  				Type:     schema.TypeString,
    89  				Optional: true,
    90  				Removed:  "role_arn has been removed. Use a s3_configuration block instead. See https://terraform.io/docs/providers/aws/r/kinesis_firehose_delivery_stream.html",
    91  			},
    92  
    93  			"s3_bucket_arn": &schema.Schema{
    94  				Type:     schema.TypeString,
    95  				Optional: true,
    96  				Removed:  "s3_bucket_arn has been removed. Use a s3_configuration block instead. See https://terraform.io/docs/providers/aws/r/kinesis_firehose_delivery_stream.html",
    97  			},
    98  
    99  			"s3_prefix": &schema.Schema{
   100  				Type:     schema.TypeString,
   101  				Optional: true,
   102  				Removed:  "s3_prefix has been removed. Use a s3_configuration block instead. See https://terraform.io/docs/providers/aws/r/kinesis_firehose_delivery_stream.html",
   103  			},
   104  
   105  			"s3_buffer_size": &schema.Schema{
   106  				Type:     schema.TypeInt,
   107  				Optional: true,
   108  				Removed:  "s3_buffer_size has been removed. Use a s3_configuration block instead. See https://terraform.io/docs/providers/aws/r/kinesis_firehose_delivery_stream.html",
   109  			},
   110  
   111  			"s3_buffer_interval": &schema.Schema{
   112  				Type:     schema.TypeInt,
   113  				Optional: true,
   114  				Removed:  "s3_buffer_interval has been removed. Use a s3_configuration block instead. See https://terraform.io/docs/providers/aws/r/kinesis_firehose_delivery_stream.html",
   115  			},
   116  
   117  			"s3_data_compression": &schema.Schema{
   118  				Type:     schema.TypeString,
   119  				Optional: true,
   120  				Removed:  "s3_data_compression has been removed. Use a s3_configuration block instead. See https://terraform.io/docs/providers/aws/r/kinesis_firehose_delivery_stream.html",
   121  			},
   122  
   123  			"s3_configuration": &schema.Schema{
   124  				Type:     schema.TypeList,
   125  				Required: true,
   126  				MaxItems: 1,
   127  				Elem: &schema.Resource{
   128  					Schema: map[string]*schema.Schema{
   129  						"bucket_arn": &schema.Schema{
   130  							Type:     schema.TypeString,
   131  							Required: true,
   132  						},
   133  
   134  						"buffer_size": &schema.Schema{
   135  							Type:     schema.TypeInt,
   136  							Optional: true,
   137  							Default:  5,
   138  						},
   139  
   140  						"buffer_interval": &schema.Schema{
   141  							Type:     schema.TypeInt,
   142  							Optional: true,
   143  							Default:  300,
   144  						},
   145  
   146  						"compression_format": &schema.Schema{
   147  							Type:     schema.TypeString,
   148  							Optional: true,
   149  							Default:  "UNCOMPRESSED",
   150  						},
   151  
   152  						"kms_key_arn": &schema.Schema{
   153  							Type:     schema.TypeString,
   154  							Optional: true,
   155  						},
   156  
   157  						"role_arn": &schema.Schema{
   158  							Type:     schema.TypeString,
   159  							Required: true,
   160  						},
   161  
   162  						"prefix": &schema.Schema{
   163  							Type:     schema.TypeString,
   164  							Optional: true,
   165  						},
   166  
   167  						"cloudwatch_logging_options": cloudWatchLoggingOptionsSchema(),
   168  					},
   169  				},
   170  			},
   171  
   172  			"redshift_configuration": &schema.Schema{
   173  				Type:     schema.TypeList,
   174  				Optional: true,
   175  				MaxItems: 1,
   176  				Elem: &schema.Resource{
   177  					Schema: map[string]*schema.Schema{
   178  						"cluster_jdbcurl": &schema.Schema{
   179  							Type:     schema.TypeString,
   180  							Required: true,
   181  						},
   182  
   183  						"username": &schema.Schema{
   184  							Type:     schema.TypeString,
   185  							Required: true,
   186  						},
   187  
   188  						"password": &schema.Schema{
   189  							Type:     schema.TypeString,
   190  							Required: true,
   191  						},
   192  
   193  						"role_arn": &schema.Schema{
   194  							Type:     schema.TypeString,
   195  							Required: true,
   196  						},
   197  
   198  						"copy_options": &schema.Schema{
   199  							Type:     schema.TypeString,
   200  							Optional: true,
   201  						},
   202  
   203  						"data_table_columns": &schema.Schema{
   204  							Type:     schema.TypeString,
   205  							Optional: true,
   206  						},
   207  
   208  						"data_table_name": &schema.Schema{
   209  							Type:     schema.TypeString,
   210  							Required: true,
   211  						},
   212  
   213  						"cloudwatch_logging_options": cloudWatchLoggingOptionsSchema(),
   214  					},
   215  				},
   216  			},
   217  
   218  			"elasticsearch_configuration": &schema.Schema{
   219  				Type:     schema.TypeList,
   220  				Optional: true,
   221  				MaxItems: 1,
   222  				Elem: &schema.Resource{
   223  					Schema: map[string]*schema.Schema{
   224  						"buffering_interval": &schema.Schema{
   225  							Type:     schema.TypeInt,
   226  							Optional: true,
   227  							Default:  300,
   228  							ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   229  								value := v.(int)
   230  								if value < 60 || value > 900 {
   231  									errors = append(errors, fmt.Errorf(
   232  										"%q must be in the range from 60 to 900 seconds.", k))
   233  								}
   234  								return
   235  							},
   236  						},
   237  
   238  						"buffering_size": &schema.Schema{
   239  							Type:     schema.TypeInt,
   240  							Optional: true,
   241  							Default:  5,
   242  							ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   243  								value := v.(int)
   244  								if value < 1 || value > 100 {
   245  									errors = append(errors, fmt.Errorf(
   246  										"%q must be in the range from 1 to 100 MB.", k))
   247  								}
   248  								return
   249  							},
   250  						},
   251  
   252  						"domain_arn": &schema.Schema{
   253  							Type:     schema.TypeString,
   254  							Required: true,
   255  						},
   256  
   257  						"index_name": &schema.Schema{
   258  							Type:     schema.TypeString,
   259  							Required: true,
   260  						},
   261  
   262  						"index_rotation_period": &schema.Schema{
   263  							Type:     schema.TypeString,
   264  							Optional: true,
   265  							Default:  "OneDay",
   266  							ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   267  								value := v.(string)
   268  								if value != "NoRotation" && value != "OneHour" && value != "OneDay" && value != "OneWeek" && value != "OneMonth" {
   269  									errors = append(errors, fmt.Errorf(
   270  										"%q must be one of 'NoRotation', 'OneHour', 'OneDay', 'OneWeek', 'OneMonth'", k))
   271  								}
   272  								return
   273  							},
   274  						},
   275  
   276  						"retry_duration": &schema.Schema{
   277  							Type:     schema.TypeInt,
   278  							Optional: true,
   279  							Default:  300,
   280  							ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   281  								value := v.(int)
   282  								if value < 0 || value > 7200 {
   283  									errors = append(errors, fmt.Errorf(
   284  										"%q must be in the range from 0 to 7200 seconds.", k))
   285  								}
   286  								return
   287  							},
   288  						},
   289  
   290  						"role_arn": &schema.Schema{
   291  							Type:     schema.TypeString,
   292  							Required: true,
   293  						},
   294  
   295  						"s3_backup_mode": &schema.Schema{
   296  							Type:     schema.TypeString,
   297  							Optional: true,
   298  							Default:  "FailedDocumentsOnly",
   299  							ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   300  								value := v.(string)
   301  								if value != "FailedDocumentsOnly" && value != "AllDocuments" {
   302  									errors = append(errors, fmt.Errorf(
   303  										"%q must be one of 'FailedDocumentsOnly', 'AllDocuments'", k))
   304  								}
   305  								return
   306  							},
   307  						},
   308  
   309  						"type_name": &schema.Schema{
   310  							Type:     schema.TypeString,
   311  							Optional: true,
   312  							ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   313  								value := v.(string)
   314  								if len(value) > 100 {
   315  									errors = append(errors, fmt.Errorf(
   316  										"%q cannot be longer than 100 characters", k))
   317  								}
   318  								return
   319  							},
   320  						},
   321  
   322  						"cloudwatch_logging_options": cloudWatchLoggingOptionsSchema(),
   323  					},
   324  				},
   325  			},
   326  
   327  			"arn": &schema.Schema{
   328  				Type:     schema.TypeString,
   329  				Optional: true,
   330  				Computed: true,
   331  			},
   332  
   333  			"version_id": &schema.Schema{
   334  				Type:     schema.TypeString,
   335  				Optional: true,
   336  				Computed: true,
   337  			},
   338  
   339  			"destination_id": &schema.Schema{
   340  				Type:     schema.TypeString,
   341  				Optional: true,
   342  				Computed: true,
   343  			},
   344  		},
   345  	}
   346  }
   347  
   348  func createS3Config(d *schema.ResourceData) *firehose.S3DestinationConfiguration {
   349  	s3 := d.Get("s3_configuration").([]interface{})[0].(map[string]interface{})
   350  
   351  	configuration := &firehose.S3DestinationConfiguration{
   352  		BucketARN: aws.String(s3["bucket_arn"].(string)),
   353  		RoleARN:   aws.String(s3["role_arn"].(string)),
   354  		BufferingHints: &firehose.BufferingHints{
   355  			IntervalInSeconds: aws.Int64(int64(s3["buffer_interval"].(int))),
   356  			SizeInMBs:         aws.Int64(int64(s3["buffer_size"].(int))),
   357  		},
   358  		Prefix:                  extractPrefixConfiguration(s3),
   359  		CompressionFormat:       aws.String(s3["compression_format"].(string)),
   360  		EncryptionConfiguration: extractEncryptionConfiguration(s3),
   361  	}
   362  
   363  	if _, ok := s3["cloudwatch_logging_options"]; ok {
   364  		configuration.CloudWatchLoggingOptions = extractCloudWatchLoggingConfiguration(s3)
   365  	}
   366  
   367  	return configuration
   368  }
   369  
   370  func updateS3Config(d *schema.ResourceData) *firehose.S3DestinationUpdate {
   371  	s3 := d.Get("s3_configuration").([]interface{})[0].(map[string]interface{})
   372  
   373  	configuration := &firehose.S3DestinationUpdate{
   374  		BucketARN: aws.String(s3["bucket_arn"].(string)),
   375  		RoleARN:   aws.String(s3["role_arn"].(string)),
   376  		BufferingHints: &firehose.BufferingHints{
   377  			IntervalInSeconds: aws.Int64((int64)(s3["buffer_interval"].(int))),
   378  			SizeInMBs:         aws.Int64((int64)(s3["buffer_size"].(int))),
   379  		},
   380  		Prefix:                   extractPrefixConfiguration(s3),
   381  		CompressionFormat:        aws.String(s3["compression_format"].(string)),
   382  		EncryptionConfiguration:  extractEncryptionConfiguration(s3),
   383  		CloudWatchLoggingOptions: extractCloudWatchLoggingConfiguration(s3),
   384  	}
   385  
   386  	if _, ok := s3["cloudwatch_logging_options"]; ok {
   387  		configuration.CloudWatchLoggingOptions = extractCloudWatchLoggingConfiguration(s3)
   388  	}
   389  
   390  	return configuration
   391  }
   392  
   393  func extractEncryptionConfiguration(s3 map[string]interface{}) *firehose.EncryptionConfiguration {
   394  	if key, ok := s3["kms_key_arn"]; ok && len(key.(string)) > 0 {
   395  		return &firehose.EncryptionConfiguration{
   396  			KMSEncryptionConfig: &firehose.KMSEncryptionConfig{
   397  				AWSKMSKeyARN: aws.String(key.(string)),
   398  			},
   399  		}
   400  	}
   401  
   402  	return &firehose.EncryptionConfiguration{
   403  		NoEncryptionConfig: aws.String("NoEncryption"),
   404  	}
   405  }
   406  
   407  func extractCloudWatchLoggingConfiguration(s3 map[string]interface{}) *firehose.CloudWatchLoggingOptions {
   408  	config := s3["cloudwatch_logging_options"].(*schema.Set).List()
   409  	if len(config) == 0 {
   410  		return nil
   411  	}
   412  
   413  	loggingConfig := config[0].(map[string]interface{})
   414  	loggingOptions := &firehose.CloudWatchLoggingOptions{
   415  		Enabled: aws.Bool(loggingConfig["enabled"].(bool)),
   416  	}
   417  
   418  	if v, ok := loggingConfig["log_group_name"]; ok {
   419  		loggingOptions.LogGroupName = aws.String(v.(string))
   420  	}
   421  
   422  	if v, ok := loggingConfig["log_stream_name"]; ok {
   423  		loggingOptions.LogStreamName = aws.String(v.(string))
   424  	}
   425  
   426  	return loggingOptions
   427  
   428  }
   429  
   430  func extractPrefixConfiguration(s3 map[string]interface{}) *string {
   431  	if v, ok := s3["prefix"]; ok {
   432  		return aws.String(v.(string))
   433  	}
   434  
   435  	return nil
   436  }
   437  
   438  func createRedshiftConfig(d *schema.ResourceData, s3Config *firehose.S3DestinationConfiguration) (*firehose.RedshiftDestinationConfiguration, error) {
   439  	redshiftRaw, ok := d.GetOk("redshift_configuration")
   440  	if !ok {
   441  		return nil, fmt.Errorf("[ERR] Error loading Redshift Configuration for Kinesis Firehose: redshift_configuration not found")
   442  	}
   443  	rl := redshiftRaw.([]interface{})
   444  
   445  	redshift := rl[0].(map[string]interface{})
   446  
   447  	configuration := &firehose.RedshiftDestinationConfiguration{
   448  		ClusterJDBCURL:  aws.String(redshift["cluster_jdbcurl"].(string)),
   449  		Password:        aws.String(redshift["password"].(string)),
   450  		Username:        aws.String(redshift["username"].(string)),
   451  		RoleARN:         aws.String(redshift["role_arn"].(string)),
   452  		CopyCommand:     extractCopyCommandConfiguration(redshift),
   453  		S3Configuration: s3Config,
   454  	}
   455  
   456  	if _, ok := redshift["cloudwatch_logging_options"]; ok {
   457  		configuration.CloudWatchLoggingOptions = extractCloudWatchLoggingConfiguration(redshift)
   458  	}
   459  
   460  	return configuration, nil
   461  }
   462  
   463  func updateRedshiftConfig(d *schema.ResourceData, s3Update *firehose.S3DestinationUpdate) (*firehose.RedshiftDestinationUpdate, error) {
   464  	redshiftRaw, ok := d.GetOk("redshift_configuration")
   465  	if !ok {
   466  		return nil, fmt.Errorf("[ERR] Error loading Redshift Configuration for Kinesis Firehose: redshift_configuration not found")
   467  	}
   468  	rl := redshiftRaw.([]interface{})
   469  
   470  	redshift := rl[0].(map[string]interface{})
   471  
   472  	configuration := &firehose.RedshiftDestinationUpdate{
   473  		ClusterJDBCURL: aws.String(redshift["cluster_jdbcurl"].(string)),
   474  		Password:       aws.String(redshift["password"].(string)),
   475  		Username:       aws.String(redshift["username"].(string)),
   476  		RoleARN:        aws.String(redshift["role_arn"].(string)),
   477  		CopyCommand:    extractCopyCommandConfiguration(redshift),
   478  		S3Update:       s3Update,
   479  	}
   480  
   481  	if _, ok := redshift["cloudwatch_logging_options"]; ok {
   482  		configuration.CloudWatchLoggingOptions = extractCloudWatchLoggingConfiguration(redshift)
   483  	}
   484  
   485  	return configuration, nil
   486  }
   487  
   488  func createElasticsearchConfig(d *schema.ResourceData, s3Config *firehose.S3DestinationConfiguration) (*firehose.ElasticsearchDestinationConfiguration, error) {
   489  	esConfig, ok := d.GetOk("elasticsearch_configuration")
   490  	if !ok {
   491  		return nil, fmt.Errorf("[ERR] Error loading Elasticsearch Configuration for Kinesis Firehose: elasticsearch_configuration not found")
   492  	}
   493  	esList := esConfig.([]interface{})
   494  
   495  	es := esList[0].(map[string]interface{})
   496  
   497  	config := &firehose.ElasticsearchDestinationConfiguration{
   498  		BufferingHints:  extractBufferingHints(es),
   499  		DomainARN:       aws.String(es["domain_arn"].(string)),
   500  		IndexName:       aws.String(es["index_name"].(string)),
   501  		RetryOptions:    extractRetryOptions(es),
   502  		RoleARN:         aws.String(es["role_arn"].(string)),
   503  		TypeName:        aws.String(es["type_name"].(string)),
   504  		S3Configuration: s3Config,
   505  	}
   506  
   507  	if _, ok := es["cloudwatch_logging_options"]; ok {
   508  		config.CloudWatchLoggingOptions = extractCloudWatchLoggingConfiguration(es)
   509  	}
   510  
   511  	if indexRotationPeriod, ok := es["index_rotation_period"]; ok {
   512  		config.IndexRotationPeriod = aws.String(indexRotationPeriod.(string))
   513  	}
   514  	if s3BackupMode, ok := es["s3_backup_mode"]; ok {
   515  		config.S3BackupMode = aws.String(s3BackupMode.(string))
   516  	}
   517  
   518  	return config, nil
   519  }
   520  
   521  func updateElasticsearchConfig(d *schema.ResourceData, s3Update *firehose.S3DestinationUpdate) (*firehose.ElasticsearchDestinationUpdate, error) {
   522  	esConfig, ok := d.GetOk("elasticsearch_configuration")
   523  	if !ok {
   524  		return nil, fmt.Errorf("[ERR] Error loading Elasticsearch Configuration for Kinesis Firehose: elasticsearch_configuration not found")
   525  	}
   526  	esList := esConfig.([]interface{})
   527  
   528  	es := esList[0].(map[string]interface{})
   529  
   530  	update := &firehose.ElasticsearchDestinationUpdate{
   531  		BufferingHints: extractBufferingHints(es),
   532  		DomainARN:      aws.String(es["domain_arn"].(string)),
   533  		IndexName:      aws.String(es["index_name"].(string)),
   534  		RetryOptions:   extractRetryOptions(es),
   535  		RoleARN:        aws.String(es["role_arn"].(string)),
   536  		TypeName:       aws.String(es["type_name"].(string)),
   537  		S3Update:       s3Update,
   538  	}
   539  
   540  	if _, ok := es["cloudwatch_logging_options"]; ok {
   541  		update.CloudWatchLoggingOptions = extractCloudWatchLoggingConfiguration(es)
   542  	}
   543  
   544  	if indexRotationPeriod, ok := es["index_rotation_period"]; ok {
   545  		update.IndexRotationPeriod = aws.String(indexRotationPeriod.(string))
   546  	}
   547  
   548  	return update, nil
   549  }
   550  
   551  func extractBufferingHints(es map[string]interface{}) *firehose.ElasticsearchBufferingHints {
   552  	bufferingHints := &firehose.ElasticsearchBufferingHints{}
   553  
   554  	if bufferingInterval, ok := es["buffering_hints"].(int); ok {
   555  		bufferingHints.IntervalInSeconds = aws.Int64(int64(bufferingInterval))
   556  	}
   557  	if bufferingSize, ok := es["buffering_size"].(int); ok {
   558  		bufferingHints.SizeInMBs = aws.Int64(int64(bufferingSize))
   559  	}
   560  
   561  	return bufferingHints
   562  
   563  }
   564  
   565  func extractRetryOptions(es map[string]interface{}) *firehose.ElasticsearchRetryOptions {
   566  	retryOptions := &firehose.ElasticsearchRetryOptions{}
   567  
   568  	if retryDuration, ok := es["retry_duration"].(int); ok {
   569  		retryOptions.DurationInSeconds = aws.Int64(int64(retryDuration))
   570  	}
   571  
   572  	return retryOptions
   573  }
   574  
   575  func extractCopyCommandConfiguration(redshift map[string]interface{}) *firehose.CopyCommand {
   576  	cmd := &firehose.CopyCommand{
   577  		DataTableName: aws.String(redshift["data_table_name"].(string)),
   578  	}
   579  	if copyOptions, ok := redshift["copy_options"]; ok {
   580  		cmd.CopyOptions = aws.String(copyOptions.(string))
   581  	}
   582  	if columns, ok := redshift["data_table_columns"]; ok {
   583  		cmd.DataTableColumns = aws.String(columns.(string))
   584  	}
   585  
   586  	return cmd
   587  }
   588  
   589  func resourceAwsKinesisFirehoseDeliveryStreamCreate(d *schema.ResourceData, meta interface{}) error {
   590  	conn := meta.(*AWSClient).firehoseconn
   591  
   592  	sn := d.Get("name").(string)
   593  	s3Config := createS3Config(d)
   594  
   595  	createInput := &firehose.CreateDeliveryStreamInput{
   596  		DeliveryStreamName: aws.String(sn),
   597  	}
   598  
   599  	if d.Get("destination").(string) == "s3" {
   600  		createInput.S3DestinationConfiguration = s3Config
   601  	} else if d.Get("destination").(string) == "elasticsearch" {
   602  		esConfig, err := createElasticsearchConfig(d, s3Config)
   603  		if err != nil {
   604  			return err
   605  		}
   606  		createInput.ElasticsearchDestinationConfiguration = esConfig
   607  	} else {
   608  		rc, err := createRedshiftConfig(d, s3Config)
   609  		if err != nil {
   610  			return err
   611  		}
   612  		createInput.RedshiftDestinationConfiguration = rc
   613  	}
   614  
   615  	var lastError error
   616  	err := resource.Retry(1*time.Minute, func() *resource.RetryError {
   617  		_, err := conn.CreateDeliveryStream(createInput)
   618  		if err != nil {
   619  			log.Printf("[DEBUG] Error creating Firehose Delivery Stream: %s", err)
   620  			lastError = err
   621  
   622  			if awsErr, ok := err.(awserr.Error); ok {
   623  				// IAM roles can take ~10 seconds to propagate in AWS:
   624  				// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
   625  				if awsErr.Code() == "InvalidArgumentException" && strings.Contains(awsErr.Message(), "Firehose is unable to assume role") {
   626  					log.Printf("[DEBUG] Firehose could not assume role referenced, retrying...")
   627  					return resource.RetryableError(awsErr)
   628  				}
   629  			}
   630  			// Not retryable
   631  			return resource.NonRetryableError(err)
   632  		}
   633  
   634  		return nil
   635  	})
   636  	if err != nil {
   637  		if awsErr, ok := lastError.(awserr.Error); ok {
   638  			return fmt.Errorf("[WARN] Error creating Kinesis Firehose Delivery Stream: \"%s\", code: \"%s\"", awsErr.Message(), awsErr.Code())
   639  		}
   640  		return err
   641  	}
   642  
   643  	stateConf := &resource.StateChangeConf{
   644  		Pending:    []string{"CREATING"},
   645  		Target:     []string{"ACTIVE"},
   646  		Refresh:    firehoseStreamStateRefreshFunc(conn, sn),
   647  		Timeout:    20 * time.Minute,
   648  		Delay:      10 * time.Second,
   649  		MinTimeout: 3 * time.Second,
   650  	}
   651  
   652  	firehoseStream, err := stateConf.WaitForState()
   653  	if err != nil {
   654  		return fmt.Errorf(
   655  			"Error waiting for Kinesis Stream (%s) to become active: %s",
   656  			sn, err)
   657  	}
   658  
   659  	s := firehoseStream.(*firehose.DeliveryStreamDescription)
   660  	d.SetId(*s.DeliveryStreamARN)
   661  	d.Set("arn", s.DeliveryStreamARN)
   662  
   663  	return resourceAwsKinesisFirehoseDeliveryStreamRead(d, meta)
   664  }
   665  
   666  func resourceAwsKinesisFirehoseDeliveryStreamUpdate(d *schema.ResourceData, meta interface{}) error {
   667  	conn := meta.(*AWSClient).firehoseconn
   668  
   669  	sn := d.Get("name").(string)
   670  	s3Config := updateS3Config(d)
   671  
   672  	updateInput := &firehose.UpdateDestinationInput{
   673  		DeliveryStreamName:             aws.String(sn),
   674  		CurrentDeliveryStreamVersionId: aws.String(d.Get("version_id").(string)),
   675  		DestinationId:                  aws.String(d.Get("destination_id").(string)),
   676  	}
   677  
   678  	if d.Get("destination").(string) == "s3" {
   679  		updateInput.S3DestinationUpdate = s3Config
   680  	} else if d.Get("destination").(string) == "elasticsearch" {
   681  		esUpdate, err := updateElasticsearchConfig(d, s3Config)
   682  		if err != nil {
   683  			return err
   684  		}
   685  		updateInput.ElasticsearchDestinationUpdate = esUpdate
   686  	} else {
   687  		rc, err := updateRedshiftConfig(d, s3Config)
   688  		if err != nil {
   689  			return err
   690  		}
   691  		updateInput.RedshiftDestinationUpdate = rc
   692  	}
   693  
   694  	_, err := conn.UpdateDestination(updateInput)
   695  	if err != nil {
   696  		return fmt.Errorf(
   697  			"Error Updating Kinesis Firehose Delivery Stream: \"%s\"\n%s",
   698  			sn, err)
   699  	}
   700  
   701  	return resourceAwsKinesisFirehoseDeliveryStreamRead(d, meta)
   702  }
   703  
   704  func resourceAwsKinesisFirehoseDeliveryStreamRead(d *schema.ResourceData, meta interface{}) error {
   705  	conn := meta.(*AWSClient).firehoseconn
   706  
   707  	resp, err := conn.DescribeDeliveryStream(&firehose.DescribeDeliveryStreamInput{
   708  		DeliveryStreamName: aws.String(d.Get("name").(string)),
   709  	})
   710  
   711  	if err != nil {
   712  		if awsErr, ok := err.(awserr.Error); ok {
   713  			if awsErr.Code() == "ResourceNotFoundException" {
   714  				d.SetId("")
   715  				return nil
   716  			}
   717  			return fmt.Errorf("[WARN] Error reading Kinesis Firehose Delivery Stream: \"%s\", code: \"%s\"", awsErr.Message(), awsErr.Code())
   718  		}
   719  		return err
   720  	}
   721  
   722  	s := resp.DeliveryStreamDescription
   723  	d.Set("version_id", s.VersionId)
   724  	d.Set("arn", *s.DeliveryStreamARN)
   725  	if len(s.Destinations) > 0 {
   726  		destination := s.Destinations[0]
   727  		d.Set("destination_id", *destination.DestinationId)
   728  	}
   729  
   730  	return nil
   731  }
   732  
   733  func resourceAwsKinesisFirehoseDeliveryStreamDelete(d *schema.ResourceData, meta interface{}) error {
   734  	conn := meta.(*AWSClient).firehoseconn
   735  
   736  	sn := d.Get("name").(string)
   737  	_, err := conn.DeleteDeliveryStream(&firehose.DeleteDeliveryStreamInput{
   738  		DeliveryStreamName: aws.String(sn),
   739  	})
   740  
   741  	if err != nil {
   742  		return err
   743  	}
   744  
   745  	stateConf := &resource.StateChangeConf{
   746  		Pending:    []string{"DELETING"},
   747  		Target:     []string{"DESTROYED"},
   748  		Refresh:    firehoseStreamStateRefreshFunc(conn, sn),
   749  		Timeout:    20 * time.Minute,
   750  		Delay:      10 * time.Second,
   751  		MinTimeout: 3 * time.Second,
   752  	}
   753  
   754  	_, err = stateConf.WaitForState()
   755  	if err != nil {
   756  		return fmt.Errorf(
   757  			"Error waiting for Delivery Stream (%s) to be destroyed: %s",
   758  			sn, err)
   759  	}
   760  
   761  	d.SetId("")
   762  	return nil
   763  }
   764  
   765  func firehoseStreamStateRefreshFunc(conn *firehose.Firehose, sn string) resource.StateRefreshFunc {
   766  	return func() (interface{}, string, error) {
   767  		describeOpts := &firehose.DescribeDeliveryStreamInput{
   768  			DeliveryStreamName: aws.String(sn),
   769  		}
   770  		resp, err := conn.DescribeDeliveryStream(describeOpts)
   771  		if err != nil {
   772  			if awsErr, ok := err.(awserr.Error); ok {
   773  				if awsErr.Code() == "ResourceNotFoundException" {
   774  					return 42, "DESTROYED", nil
   775  				}
   776  				return nil, awsErr.Code(), err
   777  			}
   778  			return nil, "failed", err
   779  		}
   780  
   781  		return resp.DeliveryStreamDescription, *resp.DeliveryStreamDescription.DeliveryStreamStatus, nil
   782  	}
   783  }