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