github.com/kwoods/terraform@v0.6.11-0.20160809170336-13497db7138e/builtin/providers/aws/resource_aws_redshift_cluster.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"regexp"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/aws/awserr"
    12  	"github.com/aws/aws-sdk-go/service/redshift"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsRedshiftCluster() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsRedshiftClusterCreate,
    20  		Read:   resourceAwsRedshiftClusterRead,
    21  		Update: resourceAwsRedshiftClusterUpdate,
    22  		Delete: resourceAwsRedshiftClusterDelete,
    23  		Importer: &schema.ResourceImporter{
    24  			State: resourceAwsRedshiftClusterImport,
    25  		},
    26  
    27  		Schema: map[string]*schema.Schema{
    28  			"database_name": &schema.Schema{
    29  				Type:         schema.TypeString,
    30  				Optional:     true,
    31  				Computed:     true,
    32  				ValidateFunc: validateRedshiftClusterDbName,
    33  			},
    34  
    35  			"cluster_identifier": &schema.Schema{
    36  				Type:         schema.TypeString,
    37  				Required:     true,
    38  				ForceNew:     true,
    39  				ValidateFunc: validateRedshiftClusterIdentifier,
    40  			},
    41  			"cluster_type": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Optional: true,
    44  				Computed: true,
    45  			},
    46  
    47  			"node_type": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Required: true,
    50  			},
    51  
    52  			"master_username": &schema.Schema{
    53  				Type:         schema.TypeString,
    54  				Required:     true,
    55  				ValidateFunc: validateRedshiftClusterMasterUsername,
    56  			},
    57  
    58  			"master_password": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Required: true,
    61  			},
    62  
    63  			"cluster_security_groups": &schema.Schema{
    64  				Type:     schema.TypeSet,
    65  				Optional: true,
    66  				Computed: true,
    67  				Elem:     &schema.Schema{Type: schema.TypeString},
    68  				Set:      schema.HashString,
    69  			},
    70  
    71  			"vpc_security_group_ids": &schema.Schema{
    72  				Type:     schema.TypeSet,
    73  				Optional: true,
    74  				Computed: true,
    75  				Elem:     &schema.Schema{Type: schema.TypeString},
    76  				Set:      schema.HashString,
    77  			},
    78  
    79  			"cluster_subnet_group_name": &schema.Schema{
    80  				Type:     schema.TypeString,
    81  				Optional: true,
    82  				ForceNew: true,
    83  				Computed: true,
    84  			},
    85  
    86  			"availability_zone": &schema.Schema{
    87  				Type:     schema.TypeString,
    88  				Optional: true,
    89  				Computed: true,
    90  			},
    91  
    92  			"preferred_maintenance_window": &schema.Schema{
    93  				Type:     schema.TypeString,
    94  				Optional: true,
    95  				Computed: true,
    96  				StateFunc: func(val interface{}) string {
    97  					if val == nil {
    98  						return ""
    99  					}
   100  					return strings.ToLower(val.(string))
   101  				},
   102  			},
   103  
   104  			"cluster_parameter_group_name": &schema.Schema{
   105  				Type:     schema.TypeString,
   106  				Optional: true,
   107  				Computed: true,
   108  			},
   109  
   110  			"automated_snapshot_retention_period": &schema.Schema{
   111  				Type:     schema.TypeInt,
   112  				Optional: true,
   113  				Default:  1,
   114  				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   115  					value := v.(int)
   116  					if value > 35 {
   117  						es = append(es, fmt.Errorf(
   118  							"backup retention period cannot be more than 35 days"))
   119  					}
   120  					return
   121  				},
   122  			},
   123  
   124  			"port": &schema.Schema{
   125  				Type:     schema.TypeInt,
   126  				Optional: true,
   127  				Default:  5439,
   128  			},
   129  
   130  			"cluster_version": &schema.Schema{
   131  				Type:     schema.TypeString,
   132  				Optional: true,
   133  				Default:  "1.0",
   134  			},
   135  
   136  			"allow_version_upgrade": &schema.Schema{
   137  				Type:     schema.TypeBool,
   138  				Optional: true,
   139  				Default:  true,
   140  			},
   141  
   142  			"number_of_nodes": &schema.Schema{
   143  				Type:     schema.TypeInt,
   144  				Optional: true,
   145  				Default:  1,
   146  			},
   147  
   148  			"publicly_accessible": &schema.Schema{
   149  				Type:     schema.TypeBool,
   150  				Optional: true,
   151  				Default:  true,
   152  			},
   153  
   154  			"encrypted": &schema.Schema{
   155  				Type:     schema.TypeBool,
   156  				Optional: true,
   157  				Computed: true,
   158  			},
   159  
   160  			"kms_key_id": &schema.Schema{
   161  				Type:     schema.TypeString,
   162  				Optional: true,
   163  				Computed: true,
   164  				ForceNew: true,
   165  			},
   166  
   167  			"elastic_ip": &schema.Schema{
   168  				Type:     schema.TypeString,
   169  				Optional: true,
   170  			},
   171  
   172  			"final_snapshot_identifier": &schema.Schema{
   173  				Type:         schema.TypeString,
   174  				Optional:     true,
   175  				ValidateFunc: validateRedshiftClusterFinalSnapshotIdentifier,
   176  			},
   177  
   178  			"skip_final_snapshot": &schema.Schema{
   179  				Type:     schema.TypeBool,
   180  				Optional: true,
   181  				Default:  true,
   182  			},
   183  
   184  			"endpoint": &schema.Schema{
   185  				Type:     schema.TypeString,
   186  				Optional: true,
   187  				Computed: true,
   188  			},
   189  
   190  			"cluster_public_key": &schema.Schema{
   191  				Type:     schema.TypeString,
   192  				Optional: true,
   193  				Computed: true,
   194  			},
   195  
   196  			"cluster_revision_number": &schema.Schema{
   197  				Type:     schema.TypeString,
   198  				Optional: true,
   199  				Computed: true,
   200  			},
   201  
   202  			"iam_roles": &schema.Schema{
   203  				Type:     schema.TypeSet,
   204  				Optional: true,
   205  				Computed: true,
   206  				Elem:     &schema.Schema{Type: schema.TypeString},
   207  				Set:      schema.HashString,
   208  			},
   209  
   210  			"enable_logging": {
   211  				Type:     schema.TypeBool,
   212  				Optional: true,
   213  				Default:  false,
   214  			},
   215  
   216  			"bucket_name": {
   217  				Type:     schema.TypeString,
   218  				Optional: true,
   219  				Computed: true,
   220  			},
   221  
   222  			"s3_key_prefix": {
   223  				Type:     schema.TypeString,
   224  				Optional: true,
   225  				Computed: true,
   226  			},
   227  
   228  			"tags": tagsSchema(),
   229  		},
   230  	}
   231  }
   232  
   233  func resourceAwsRedshiftClusterImport(
   234  	d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
   235  	// Neither skip_final_snapshot nor final_snapshot_identifier can be fetched
   236  	// from any API call, so we need to default skip_final_snapshot to true so
   237  	// that final_snapshot_identifier is not required
   238  	d.Set("skip_final_snapshot", true)
   239  	return []*schema.ResourceData{d}, nil
   240  }
   241  
   242  func resourceAwsRedshiftClusterCreate(d *schema.ResourceData, meta interface{}) error {
   243  	conn := meta.(*AWSClient).redshiftconn
   244  
   245  	log.Printf("[INFO] Building Redshift Cluster Options")
   246  	tags := tagsFromMapRedshift(d.Get("tags").(map[string]interface{}))
   247  	createOpts := &redshift.CreateClusterInput{
   248  		ClusterIdentifier:                aws.String(d.Get("cluster_identifier").(string)),
   249  		Port:                             aws.Int64(int64(d.Get("port").(int))),
   250  		MasterUserPassword:               aws.String(d.Get("master_password").(string)),
   251  		MasterUsername:                   aws.String(d.Get("master_username").(string)),
   252  		ClusterVersion:                   aws.String(d.Get("cluster_version").(string)),
   253  		NodeType:                         aws.String(d.Get("node_type").(string)),
   254  		DBName:                           aws.String(d.Get("database_name").(string)),
   255  		AllowVersionUpgrade:              aws.Bool(d.Get("allow_version_upgrade").(bool)),
   256  		PubliclyAccessible:               aws.Bool(d.Get("publicly_accessible").(bool)),
   257  		AutomatedSnapshotRetentionPeriod: aws.Int64(int64(d.Get("automated_snapshot_retention_period").(int))),
   258  		Tags: tags,
   259  	}
   260  
   261  	if v := d.Get("number_of_nodes").(int); v > 1 {
   262  		createOpts.ClusterType = aws.String("multi-node")
   263  		createOpts.NumberOfNodes = aws.Int64(int64(d.Get("number_of_nodes").(int)))
   264  	} else {
   265  		createOpts.ClusterType = aws.String("single-node")
   266  	}
   267  
   268  	if v := d.Get("cluster_security_groups").(*schema.Set); v.Len() > 0 {
   269  		createOpts.ClusterSecurityGroups = expandStringList(v.List())
   270  	}
   271  
   272  	if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
   273  		createOpts.VpcSecurityGroupIds = expandStringList(v.List())
   274  	}
   275  
   276  	if v, ok := d.GetOk("cluster_subnet_group_name"); ok {
   277  		createOpts.ClusterSubnetGroupName = aws.String(v.(string))
   278  	}
   279  
   280  	if v, ok := d.GetOk("availability_zone"); ok {
   281  		createOpts.AvailabilityZone = aws.String(v.(string))
   282  	}
   283  
   284  	if v, ok := d.GetOk("preferred_maintenance_window"); ok {
   285  		createOpts.PreferredMaintenanceWindow = aws.String(v.(string))
   286  	}
   287  
   288  	if v, ok := d.GetOk("cluster_parameter_group_name"); ok {
   289  		createOpts.ClusterParameterGroupName = aws.String(v.(string))
   290  	}
   291  
   292  	if v, ok := d.GetOk("encrypted"); ok {
   293  		createOpts.Encrypted = aws.Bool(v.(bool))
   294  	}
   295  
   296  	if v, ok := d.GetOk("kms_key_id"); ok {
   297  		createOpts.KmsKeyId = aws.String(v.(string))
   298  	}
   299  
   300  	if v, ok := d.GetOk("elastic_ip"); ok {
   301  		createOpts.ElasticIp = aws.String(v.(string))
   302  	}
   303  
   304  	if v, ok := d.GetOk("iam_roles"); ok {
   305  		createOpts.IamRoles = expandStringList(v.(*schema.Set).List())
   306  	}
   307  
   308  	log.Printf("[DEBUG] Redshift Cluster create options: %s", createOpts)
   309  	resp, err := conn.CreateCluster(createOpts)
   310  	if err != nil {
   311  		log.Printf("[ERROR] Error creating Redshift Cluster: %s", err)
   312  		return err
   313  	}
   314  
   315  	log.Printf("[DEBUG]: Cluster create response: %s", resp)
   316  	d.SetId(*resp.Cluster.ClusterIdentifier)
   317  
   318  	stateConf := &resource.StateChangeConf{
   319  		Pending:    []string{"creating", "backing-up", "modifying"},
   320  		Target:     []string{"available"},
   321  		Refresh:    resourceAwsRedshiftClusterStateRefreshFunc(d, meta),
   322  		Timeout:    40 * time.Minute,
   323  		MinTimeout: 10 * time.Second,
   324  	}
   325  
   326  	_, err = stateConf.WaitForState()
   327  	if err != nil {
   328  		return fmt.Errorf("[WARN] Error waiting for Redshift Cluster state to be \"available\": %s", err)
   329  	}
   330  
   331  	if _, ok := d.GetOk("enable_logging"); ok {
   332  
   333  		loggingErr := enableRedshiftClusterLogging(d, conn)
   334  		if loggingErr != nil {
   335  			log.Printf("[ERROR] Error Enabling Logging on Redshift Cluster: %s", err)
   336  			return loggingErr
   337  		}
   338  
   339  	}
   340  
   341  	return resourceAwsRedshiftClusterRead(d, meta)
   342  }
   343  
   344  func resourceAwsRedshiftClusterRead(d *schema.ResourceData, meta interface{}) error {
   345  	conn := meta.(*AWSClient).redshiftconn
   346  
   347  	log.Printf("[INFO] Reading Redshift Cluster Information: %s", d.Id())
   348  	resp, err := conn.DescribeClusters(&redshift.DescribeClustersInput{
   349  		ClusterIdentifier: aws.String(d.Id()),
   350  	})
   351  
   352  	if err != nil {
   353  		if awsErr, ok := err.(awserr.Error); ok {
   354  			if "ClusterNotFound" == awsErr.Code() {
   355  				d.SetId("")
   356  				log.Printf("[DEBUG] Redshift Cluster (%s) not found", d.Id())
   357  				return nil
   358  			}
   359  		}
   360  		log.Printf("[DEBUG] Error describing Redshift Cluster (%s)", d.Id())
   361  		return err
   362  	}
   363  
   364  	var rsc *redshift.Cluster
   365  	for _, c := range resp.Clusters {
   366  		if *c.ClusterIdentifier == d.Id() {
   367  			rsc = c
   368  		}
   369  	}
   370  
   371  	if rsc == nil {
   372  		log.Printf("[WARN] Redshift Cluster (%s) not found", d.Id())
   373  		d.SetId("")
   374  		return nil
   375  	}
   376  
   377  	log.Printf("[INFO] Reading Redshift Cluster Logging Status: %s", d.Id())
   378  	loggingStatus, loggingErr := conn.DescribeLoggingStatus(&redshift.DescribeLoggingStatusInput{
   379  		ClusterIdentifier: aws.String(d.Id()),
   380  	})
   381  
   382  	if loggingErr != nil {
   383  		return loggingErr
   384  	}
   385  
   386  	d.Set("master_username", rsc.MasterUsername)
   387  	d.Set("node_type", rsc.NodeType)
   388  	d.Set("allow_version_upgrade", rsc.AllowVersionUpgrade)
   389  	d.Set("database_name", rsc.DBName)
   390  	d.Set("cluster_identifier", rsc.ClusterIdentifier)
   391  	d.Set("cluster_version", rsc.ClusterVersion)
   392  
   393  	d.Set("cluster_subnet_group_name", rsc.ClusterSubnetGroupName)
   394  	d.Set("availability_zone", rsc.AvailabilityZone)
   395  	d.Set("encrypted", rsc.Encrypted)
   396  	d.Set("kms_key_id", rsc.KmsKeyId)
   397  	d.Set("automated_snapshot_retention_period", rsc.AutomatedSnapshotRetentionPeriod)
   398  	d.Set("preferred_maintenance_window", rsc.PreferredMaintenanceWindow)
   399  	if rsc.Endpoint != nil && rsc.Endpoint.Address != nil {
   400  		endpoint := *rsc.Endpoint.Address
   401  		if rsc.Endpoint.Port != nil {
   402  			endpoint = fmt.Sprintf("%s:%d", endpoint, *rsc.Endpoint.Port)
   403  		}
   404  		d.Set("port", rsc.Endpoint.Port)
   405  		d.Set("endpoint", endpoint)
   406  	}
   407  	d.Set("cluster_parameter_group_name", rsc.ClusterParameterGroups[0].ParameterGroupName)
   408  	if len(rsc.ClusterNodes) > 1 {
   409  		d.Set("cluster_type", "multi-node")
   410  	} else {
   411  		d.Set("cluster_type", "single-node")
   412  	}
   413  	d.Set("number_of_nodes", rsc.NumberOfNodes)
   414  	d.Set("publicly_accessible", rsc.PubliclyAccessible)
   415  
   416  	var vpcg []string
   417  	for _, g := range rsc.VpcSecurityGroups {
   418  		vpcg = append(vpcg, *g.VpcSecurityGroupId)
   419  	}
   420  	if err := d.Set("vpc_security_group_ids", vpcg); err != nil {
   421  		return fmt.Errorf("[DEBUG] Error saving VPC Security Group IDs to state for Redshift Cluster (%s): %s", d.Id(), err)
   422  	}
   423  
   424  	var csg []string
   425  	for _, g := range rsc.ClusterSecurityGroups {
   426  		csg = append(csg, *g.ClusterSecurityGroupName)
   427  	}
   428  	if err := d.Set("cluster_security_groups", csg); err != nil {
   429  		return fmt.Errorf("[DEBUG] Error saving Cluster Security Group Names to state for Redshift Cluster (%s): %s", d.Id(), err)
   430  	}
   431  
   432  	var iamRoles []string
   433  	for _, i := range rsc.IamRoles {
   434  		iamRoles = append(iamRoles, *i.IamRoleArn)
   435  	}
   436  	if err := d.Set("iam_roles", iamRoles); err != nil {
   437  		return fmt.Errorf("[DEBUG] Error saving IAM Roles to state for Redshift Cluster (%s): %s", d.Id(), err)
   438  	}
   439  
   440  	d.Set("cluster_public_key", rsc.ClusterPublicKey)
   441  	d.Set("cluster_revision_number", rsc.ClusterRevisionNumber)
   442  	d.Set("tags", tagsToMapRedshift(rsc.Tags))
   443  
   444  	d.Set("bucket_name", loggingStatus.BucketName)
   445  	d.Set("enable_logging", loggingStatus.LoggingEnabled)
   446  	d.Set("s3_key_prefix", loggingStatus.S3KeyPrefix)
   447  
   448  	return nil
   449  }
   450  
   451  func resourceAwsRedshiftClusterUpdate(d *schema.ResourceData, meta interface{}) error {
   452  	conn := meta.(*AWSClient).redshiftconn
   453  	d.Partial(true)
   454  
   455  	arn, tagErr := buildRedshiftARN(d.Id(), meta.(*AWSClient).accountid, meta.(*AWSClient).region)
   456  	if tagErr != nil {
   457  		return fmt.Errorf("Error building ARN for Redshift Cluster, not updating Tags for cluster %s", d.Id())
   458  	} else {
   459  		if tagErr := setTagsRedshift(conn, d, arn); tagErr != nil {
   460  			return tagErr
   461  		} else {
   462  			d.SetPartial("tags")
   463  		}
   464  	}
   465  
   466  	requestUpdate := false
   467  	log.Printf("[INFO] Building Redshift Modify Cluster Options")
   468  	req := &redshift.ModifyClusterInput{
   469  		ClusterIdentifier: aws.String(d.Id()),
   470  	}
   471  
   472  	if d.HasChange("cluster_type") {
   473  		req.ClusterType = aws.String(d.Get("cluster_type").(string))
   474  		requestUpdate = true
   475  	}
   476  
   477  	if d.HasChange("node_type") {
   478  		req.NodeType = aws.String(d.Get("node_type").(string))
   479  		requestUpdate = true
   480  	}
   481  
   482  	if d.HasChange("number_of_nodes") {
   483  		if v := d.Get("number_of_nodes").(int); v > 1 {
   484  			req.ClusterType = aws.String("multi-node")
   485  			req.NumberOfNodes = aws.Int64(int64(d.Get("number_of_nodes").(int)))
   486  		} else {
   487  			req.ClusterType = aws.String("single-node")
   488  		}
   489  
   490  		req.NodeType = aws.String(d.Get("node_type").(string))
   491  		requestUpdate = true
   492  	}
   493  
   494  	if d.HasChange("cluster_security_groups") {
   495  		req.ClusterSecurityGroups = expandStringList(d.Get("cluster_security_groups").(*schema.Set).List())
   496  		requestUpdate = true
   497  	}
   498  
   499  	if d.HasChange("vpc_security_group_ips") {
   500  		req.VpcSecurityGroupIds = expandStringList(d.Get("vpc_security_group_ips").(*schema.Set).List())
   501  		requestUpdate = true
   502  	}
   503  
   504  	if d.HasChange("master_password") {
   505  		req.MasterUserPassword = aws.String(d.Get("master_password").(string))
   506  		requestUpdate = true
   507  	}
   508  
   509  	if d.HasChange("cluster_parameter_group_name") {
   510  		req.ClusterParameterGroupName = aws.String(d.Get("cluster_parameter_group_name").(string))
   511  		requestUpdate = true
   512  	}
   513  
   514  	if d.HasChange("automated_snapshot_retention_period") {
   515  		req.AutomatedSnapshotRetentionPeriod = aws.Int64(int64(d.Get("automated_snapshot_retention_period").(int)))
   516  		requestUpdate = true
   517  	}
   518  
   519  	if d.HasChange("preferred_maintenance_window") {
   520  		req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintenance_window").(string))
   521  		requestUpdate = true
   522  	}
   523  
   524  	if d.HasChange("cluster_version") {
   525  		req.ClusterVersion = aws.String(d.Get("cluster_version").(string))
   526  		requestUpdate = true
   527  	}
   528  
   529  	if d.HasChange("allow_version_upgrade") {
   530  		req.AllowVersionUpgrade = aws.Bool(d.Get("allow_version_upgrade").(bool))
   531  		requestUpdate = true
   532  	}
   533  
   534  	if d.HasChange("publicly_accessible") {
   535  		req.PubliclyAccessible = aws.Bool(d.Get("publicly_accessible").(bool))
   536  		requestUpdate = true
   537  	}
   538  
   539  	if requestUpdate {
   540  		log.Printf("[INFO] Modifying Redshift Cluster: %s", d.Id())
   541  		log.Printf("[DEBUG] Redshift Cluster Modify options: %s", req)
   542  		_, err := conn.ModifyCluster(req)
   543  		if err != nil {
   544  			return fmt.Errorf("[WARN] Error modifying Redshift Cluster (%s): %s", d.Id(), err)
   545  		}
   546  	}
   547  
   548  	if d.HasChange("iam_roles") {
   549  		o, n := d.GetChange("iam_roles")
   550  		if o == nil {
   551  			o = new(schema.Set)
   552  		}
   553  		if n == nil {
   554  			n = new(schema.Set)
   555  		}
   556  
   557  		os := o.(*schema.Set)
   558  		ns := n.(*schema.Set)
   559  
   560  		removeIams := os.Difference(ns).List()
   561  		addIams := ns.Difference(os).List()
   562  
   563  		log.Printf("[INFO] Building Redshift Modify Cluster IAM Role Options")
   564  		req := &redshift.ModifyClusterIamRolesInput{
   565  			ClusterIdentifier: aws.String(d.Id()),
   566  			AddIamRoles:       expandStringList(addIams),
   567  			RemoveIamRoles:    expandStringList(removeIams),
   568  		}
   569  
   570  		log.Printf("[INFO] Modifying Redshift Cluster IAM Roles: %s", d.Id())
   571  		log.Printf("[DEBUG] Redshift Cluster Modify IAM Role options: %s", req)
   572  		_, err := conn.ModifyClusterIamRoles(req)
   573  		if err != nil {
   574  			return fmt.Errorf("[WARN] Error modifying Redshift Cluster IAM Roles (%s): %s", d.Id(), err)
   575  		}
   576  
   577  		d.SetPartial("iam_roles")
   578  	}
   579  
   580  	if requestUpdate || d.HasChange("iam_roles") {
   581  
   582  		stateConf := &resource.StateChangeConf{
   583  			Pending:    []string{"creating", "deleting", "rebooting", "resizing", "renaming", "modifying"},
   584  			Target:     []string{"available"},
   585  			Refresh:    resourceAwsRedshiftClusterStateRefreshFunc(d, meta),
   586  			Timeout:    40 * time.Minute,
   587  			MinTimeout: 10 * time.Second,
   588  		}
   589  
   590  		// Wait, catching any errors
   591  		_, err := stateConf.WaitForState()
   592  		if err != nil {
   593  			return fmt.Errorf("[WARN] Error Modifying Redshift Cluster (%s): %s", d.Id(), err)
   594  		}
   595  	}
   596  
   597  	if d.HasChange("enable_logging") || d.HasChange("bucket_name") || d.HasChange("s3_key_prefix") {
   598  		var loggingErr error
   599  		if _, ok := d.GetOk("enable_logging"); ok {
   600  
   601  			log.Printf("[INFO] Enabling Logging for Redshift Cluster %q", d.Id())
   602  			loggingErr = enableRedshiftClusterLogging(d, conn)
   603  			if loggingErr != nil {
   604  				return loggingErr
   605  			}
   606  		} else {
   607  
   608  			log.Printf("[INFO] Disabling Logging for Redshift Cluster %q", d.Id())
   609  			_, loggingErr = conn.DisableLogging(&redshift.DisableLoggingInput{
   610  				ClusterIdentifier: aws.String(d.Id()),
   611  			})
   612  			if loggingErr != nil {
   613  				return loggingErr
   614  			}
   615  		}
   616  
   617  		d.SetPartial("enable_logging")
   618  	}
   619  
   620  	d.Partial(false)
   621  
   622  	return resourceAwsRedshiftClusterRead(d, meta)
   623  }
   624  
   625  func enableRedshiftClusterLogging(d *schema.ResourceData, conn *redshift.Redshift) error {
   626  	if _, ok := d.GetOk("bucket_name"); !ok {
   627  		return fmt.Errorf("bucket_name must be set when enabling logging for Redshift Clusters")
   628  	}
   629  
   630  	params := &redshift.EnableLoggingInput{
   631  		ClusterIdentifier: aws.String(d.Id()),
   632  		BucketName:        aws.String(d.Get("bucket_name").(string)),
   633  	}
   634  
   635  	if v, ok := d.GetOk("s3_key_prefix"); ok {
   636  		params.S3KeyPrefix = aws.String(v.(string))
   637  	}
   638  
   639  	_, loggingErr := conn.EnableLogging(params)
   640  	if loggingErr != nil {
   641  		log.Printf("[ERROR] Error Enabling Logging on Redshift Cluster: %s", loggingErr)
   642  		return loggingErr
   643  	}
   644  	return nil
   645  }
   646  
   647  func resourceAwsRedshiftClusterDelete(d *schema.ResourceData, meta interface{}) error {
   648  	conn := meta.(*AWSClient).redshiftconn
   649  	log.Printf("[DEBUG] Destroying Redshift Cluster (%s)", d.Id())
   650  
   651  	deleteOpts := redshift.DeleteClusterInput{
   652  		ClusterIdentifier: aws.String(d.Id()),
   653  	}
   654  
   655  	skipFinalSnapshot := d.Get("skip_final_snapshot").(bool)
   656  	deleteOpts.SkipFinalClusterSnapshot = aws.Bool(skipFinalSnapshot)
   657  
   658  	if skipFinalSnapshot == false {
   659  		if name, present := d.GetOk("final_snapshot_identifier"); present {
   660  			deleteOpts.FinalClusterSnapshotIdentifier = aws.String(name.(string))
   661  		} else {
   662  			return fmt.Errorf("Redshift Cluster Instance FinalSnapshotIdentifier is required when a final snapshot is required")
   663  		}
   664  	}
   665  
   666  	log.Printf("[DEBUG] Redshift Cluster delete options: %s", deleteOpts)
   667  	_, err := conn.DeleteCluster(&deleteOpts)
   668  	if err != nil {
   669  		return fmt.Errorf("[ERROR] Error deleting Redshift Cluster (%s): %s", d.Id(), err)
   670  	}
   671  
   672  	stateConf := &resource.StateChangeConf{
   673  		Pending:    []string{"available", "creating", "deleting", "rebooting", "resizing", "renaming"},
   674  		Target:     []string{"destroyed"},
   675  		Refresh:    resourceAwsRedshiftClusterStateRefreshFunc(d, meta),
   676  		Timeout:    40 * time.Minute,
   677  		MinTimeout: 5 * time.Second,
   678  	}
   679  
   680  	// Wait, catching any errors
   681  	_, err = stateConf.WaitForState()
   682  	if err != nil {
   683  		return fmt.Errorf("[ERROR] Error deleting Redshift Cluster (%s): %s", d.Id(), err)
   684  	}
   685  
   686  	log.Printf("[INFO] Redshift Cluster %s successfully deleted", d.Id())
   687  
   688  	return nil
   689  }
   690  
   691  func resourceAwsRedshiftClusterStateRefreshFunc(d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   692  	return func() (interface{}, string, error) {
   693  		conn := meta.(*AWSClient).redshiftconn
   694  
   695  		log.Printf("[INFO] Reading Redshift Cluster Information: %s", d.Id())
   696  		resp, err := conn.DescribeClusters(&redshift.DescribeClustersInput{
   697  			ClusterIdentifier: aws.String(d.Id()),
   698  		})
   699  
   700  		if err != nil {
   701  			if awsErr, ok := err.(awserr.Error); ok {
   702  				if "ClusterNotFound" == awsErr.Code() {
   703  					return 42, "destroyed", nil
   704  				}
   705  			}
   706  			log.Printf("[WARN] Error on retrieving Redshift Cluster (%s) when waiting: %s", d.Id(), err)
   707  			return nil, "", err
   708  		}
   709  
   710  		var rsc *redshift.Cluster
   711  
   712  		for _, c := range resp.Clusters {
   713  			if *c.ClusterIdentifier == d.Id() {
   714  				rsc = c
   715  			}
   716  		}
   717  
   718  		if rsc == nil {
   719  			return 42, "destroyed", nil
   720  		}
   721  
   722  		if rsc.ClusterStatus != nil {
   723  			log.Printf("[DEBUG] Redshift Cluster status (%s): %s", d.Id(), *rsc.ClusterStatus)
   724  		}
   725  
   726  		return rsc, *rsc.ClusterStatus, nil
   727  	}
   728  }
   729  
   730  func validateRedshiftClusterIdentifier(v interface{}, k string) (ws []string, errors []error) {
   731  	value := v.(string)
   732  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
   733  		errors = append(errors, fmt.Errorf(
   734  			"only lowercase alphanumeric characters and hyphens allowed in %q", k))
   735  	}
   736  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
   737  		errors = append(errors, fmt.Errorf(
   738  			"first character of %q must be a letter", k))
   739  	}
   740  	if regexp.MustCompile(`--`).MatchString(value) {
   741  		errors = append(errors, fmt.Errorf(
   742  			"%q cannot contain two consecutive hyphens", k))
   743  	}
   744  	if regexp.MustCompile(`-$`).MatchString(value) {
   745  		errors = append(errors, fmt.Errorf(
   746  			"%q cannot end with a hyphen", k))
   747  	}
   748  	return
   749  }
   750  
   751  func validateRedshiftClusterDbName(v interface{}, k string) (ws []string, errors []error) {
   752  	value := v.(string)
   753  	if !regexp.MustCompile(`^[a-z]+$`).MatchString(value) {
   754  		errors = append(errors, fmt.Errorf(
   755  			"only lowercase letters characters allowed in %q", k))
   756  	}
   757  	if len(value) > 64 {
   758  		errors = append(errors, fmt.Errorf(
   759  			"%q cannot be longer than 64 characters: %q", k, value))
   760  	}
   761  	if value == "" {
   762  		errors = append(errors, fmt.Errorf(
   763  			"%q cannot be an empty string", k))
   764  	}
   765  
   766  	return
   767  }
   768  
   769  func validateRedshiftClusterFinalSnapshotIdentifier(v interface{}, k string) (ws []string, errors []error) {
   770  	value := v.(string)
   771  	if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   772  		errors = append(errors, fmt.Errorf(
   773  			"only alphanumeric characters and hyphens allowed in %q", k))
   774  	}
   775  	if regexp.MustCompile(`--`).MatchString(value) {
   776  		errors = append(errors, fmt.Errorf("%q cannot contain two consecutive hyphens", k))
   777  	}
   778  	if regexp.MustCompile(`-$`).MatchString(value) {
   779  		errors = append(errors, fmt.Errorf("%q cannot end in a hyphen", k))
   780  	}
   781  	if len(value) > 255 {
   782  		errors = append(errors, fmt.Errorf("%q cannot be more than 255 characters", k))
   783  	}
   784  	return
   785  }
   786  
   787  func validateRedshiftClusterMasterUsername(v interface{}, k string) (ws []string, errors []error) {
   788  	value := v.(string)
   789  	if !regexp.MustCompile(`^\w+$`).MatchString(value) {
   790  		errors = append(errors, fmt.Errorf(
   791  			"only alphanumeric characters in %q", k))
   792  	}
   793  	if !regexp.MustCompile(`^[A-Za-z]`).MatchString(value) {
   794  		errors = append(errors, fmt.Errorf(
   795  			"first character of %q must be a letter", k))
   796  	}
   797  	if len(value) > 128 {
   798  		errors = append(errors, fmt.Errorf("%q cannot be more than 128 characters", k))
   799  	}
   800  	return
   801  }
   802  
   803  func buildRedshiftARN(identifier, accountid, region string) (string, error) {
   804  	if accountid == "" {
   805  		return "", fmt.Errorf("Unable to construct cluster ARN because of missing AWS Account ID")
   806  	}
   807  	arn := fmt.Sprintf("arn:aws:redshift:%s:%s:cluster:%s", region, accountid, identifier)
   808  	return arn, nil
   809  
   810  }