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