github.com/gabrielperezs/terraform@v0.7.0-rc2.0.20160715084931-f7da2612946f/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: schema.ImportStatePassthrough,
    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  			"tags": tagsSchema(),
   211  		},
   212  	}
   213  }
   214  
   215  func resourceAwsRedshiftClusterCreate(d *schema.ResourceData, meta interface{}) error {
   216  	conn := meta.(*AWSClient).redshiftconn
   217  
   218  	log.Printf("[INFO] Building Redshift Cluster Options")
   219  	tags := tagsFromMapRedshift(d.Get("tags").(map[string]interface{}))
   220  	createOpts := &redshift.CreateClusterInput{
   221  		ClusterIdentifier:                aws.String(d.Get("cluster_identifier").(string)),
   222  		Port:                             aws.Int64(int64(d.Get("port").(int))),
   223  		MasterUserPassword:               aws.String(d.Get("master_password").(string)),
   224  		MasterUsername:                   aws.String(d.Get("master_username").(string)),
   225  		ClusterVersion:                   aws.String(d.Get("cluster_version").(string)),
   226  		NodeType:                         aws.String(d.Get("node_type").(string)),
   227  		DBName:                           aws.String(d.Get("database_name").(string)),
   228  		AllowVersionUpgrade:              aws.Bool(d.Get("allow_version_upgrade").(bool)),
   229  		PubliclyAccessible:               aws.Bool(d.Get("publicly_accessible").(bool)),
   230  		AutomatedSnapshotRetentionPeriod: aws.Int64(int64(d.Get("automated_snapshot_retention_period").(int))),
   231  		Tags: tags,
   232  	}
   233  
   234  	if v := d.Get("number_of_nodes").(int); v > 1 {
   235  		createOpts.ClusterType = aws.String("multi-node")
   236  		createOpts.NumberOfNodes = aws.Int64(int64(d.Get("number_of_nodes").(int)))
   237  	} else {
   238  		createOpts.ClusterType = aws.String("single-node")
   239  	}
   240  
   241  	if v := d.Get("cluster_security_groups").(*schema.Set); v.Len() > 0 {
   242  		createOpts.ClusterSecurityGroups = expandStringList(v.List())
   243  	}
   244  
   245  	if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
   246  		createOpts.VpcSecurityGroupIds = expandStringList(v.List())
   247  	}
   248  
   249  	if v, ok := d.GetOk("cluster_subnet_group_name"); ok {
   250  		createOpts.ClusterSubnetGroupName = aws.String(v.(string))
   251  	}
   252  
   253  	if v, ok := d.GetOk("availability_zone"); ok {
   254  		createOpts.AvailabilityZone = aws.String(v.(string))
   255  	}
   256  
   257  	if v, ok := d.GetOk("preferred_maintenance_window"); ok {
   258  		createOpts.PreferredMaintenanceWindow = aws.String(v.(string))
   259  	}
   260  
   261  	if v, ok := d.GetOk("cluster_parameter_group_name"); ok {
   262  		createOpts.ClusterParameterGroupName = aws.String(v.(string))
   263  	}
   264  
   265  	if v, ok := d.GetOk("encrypted"); ok {
   266  		createOpts.Encrypted = aws.Bool(v.(bool))
   267  	}
   268  
   269  	if v, ok := d.GetOk("kms_key_id"); ok {
   270  		createOpts.KmsKeyId = aws.String(v.(string))
   271  	}
   272  
   273  	if v, ok := d.GetOk("elastic_ip"); ok {
   274  		createOpts.ElasticIp = aws.String(v.(string))
   275  	}
   276  
   277  	if v, ok := d.GetOk("iam_roles"); ok {
   278  		createOpts.IamRoles = expandStringList(v.(*schema.Set).List())
   279  	}
   280  
   281  	log.Printf("[DEBUG] Redshift Cluster create options: %s", createOpts)
   282  	resp, err := conn.CreateCluster(createOpts)
   283  	if err != nil {
   284  		log.Printf("[ERROR] Error creating Redshift Cluster: %s", err)
   285  		return err
   286  	}
   287  
   288  	log.Printf("[DEBUG]: Cluster create response: %s", resp)
   289  	d.SetId(*resp.Cluster.ClusterIdentifier)
   290  
   291  	stateConf := &resource.StateChangeConf{
   292  		Pending:    []string{"creating", "backing-up", "modifying"},
   293  		Target:     []string{"available"},
   294  		Refresh:    resourceAwsRedshiftClusterStateRefreshFunc(d, meta),
   295  		Timeout:    40 * time.Minute,
   296  		MinTimeout: 10 * time.Second,
   297  	}
   298  
   299  	_, err = stateConf.WaitForState()
   300  	if err != nil {
   301  		return fmt.Errorf("[WARN] Error waiting for Redshift Cluster state to be \"available\": %s", err)
   302  	}
   303  
   304  	return resourceAwsRedshiftClusterRead(d, meta)
   305  }
   306  
   307  func resourceAwsRedshiftClusterRead(d *schema.ResourceData, meta interface{}) error {
   308  	conn := meta.(*AWSClient).redshiftconn
   309  
   310  	log.Printf("[INFO] Reading Redshift Cluster Information: %s", d.Id())
   311  	resp, err := conn.DescribeClusters(&redshift.DescribeClustersInput{
   312  		ClusterIdentifier: aws.String(d.Id()),
   313  	})
   314  
   315  	if err != nil {
   316  		if awsErr, ok := err.(awserr.Error); ok {
   317  			if "ClusterNotFound" == awsErr.Code() {
   318  				d.SetId("")
   319  				log.Printf("[DEBUG] Redshift Cluster (%s) not found", d.Id())
   320  				return nil
   321  			}
   322  		}
   323  		log.Printf("[DEBUG] Error describing Redshift Cluster (%s)", d.Id())
   324  		return err
   325  	}
   326  
   327  	var rsc *redshift.Cluster
   328  	for _, c := range resp.Clusters {
   329  		if *c.ClusterIdentifier == d.Id() {
   330  			rsc = c
   331  		}
   332  	}
   333  
   334  	if rsc == nil {
   335  		log.Printf("[WARN] Redshift Cluster (%s) not found", d.Id())
   336  		d.SetId("")
   337  		return nil
   338  	}
   339  
   340  	d.Set("master_username", rsc.MasterUsername)
   341  	d.Set("node_type", rsc.NodeType)
   342  	d.Set("allow_version_upgrade", rsc.AllowVersionUpgrade)
   343  	d.Set("database_name", rsc.DBName)
   344  	d.Set("cluster_identifier", rsc.ClusterIdentifier)
   345  	d.Set("cluster_version", rsc.ClusterVersion)
   346  
   347  	d.Set("cluster_subnet_group_name", rsc.ClusterSubnetGroupName)
   348  	d.Set("availability_zone", rsc.AvailabilityZone)
   349  	d.Set("encrypted", rsc.Encrypted)
   350  	d.Set("kms_key_id", rsc.KmsKeyId)
   351  	d.Set("automated_snapshot_retention_period", rsc.AutomatedSnapshotRetentionPeriod)
   352  	d.Set("preferred_maintenance_window", rsc.PreferredMaintenanceWindow)
   353  	if rsc.Endpoint != nil && rsc.Endpoint.Address != nil {
   354  		endpoint := *rsc.Endpoint.Address
   355  		if rsc.Endpoint.Port != nil {
   356  			endpoint = fmt.Sprintf("%s:%d", endpoint, *rsc.Endpoint.Port)
   357  		}
   358  		d.Set("port", rsc.Endpoint.Port)
   359  		d.Set("endpoint", endpoint)
   360  	}
   361  	d.Set("cluster_parameter_group_name", rsc.ClusterParameterGroups[0].ParameterGroupName)
   362  	if len(rsc.ClusterNodes) > 1 {
   363  		d.Set("cluster_type", "multi-node")
   364  	} else {
   365  		d.Set("cluster_type", "single-node")
   366  	}
   367  	d.Set("number_of_nodes", len(rsc.ClusterNodes))
   368  	d.Set("publicly_accessible", rsc.PubliclyAccessible)
   369  
   370  	var vpcg []string
   371  	for _, g := range rsc.VpcSecurityGroups {
   372  		vpcg = append(vpcg, *g.VpcSecurityGroupId)
   373  	}
   374  	if err := d.Set("vpc_security_group_ids", vpcg); err != nil {
   375  		return fmt.Errorf("[DEBUG] Error saving VPC Security Group IDs to state for Redshift Cluster (%s): %s", d.Id(), err)
   376  	}
   377  
   378  	var csg []string
   379  	for _, g := range rsc.ClusterSecurityGroups {
   380  		csg = append(csg, *g.ClusterSecurityGroupName)
   381  	}
   382  	if err := d.Set("cluster_security_groups", csg); err != nil {
   383  		return fmt.Errorf("[DEBUG] Error saving Cluster Security Group Names to state for Redshift Cluster (%s): %s", d.Id(), err)
   384  	}
   385  
   386  	var iamRoles []string
   387  	for _, i := range rsc.IamRoles {
   388  		iamRoles = append(iamRoles, *i.IamRoleArn)
   389  	}
   390  	if err := d.Set("iam_roles", iamRoles); err != nil {
   391  		return fmt.Errorf("[DEBUG] Error saving IAM Roles to state for Redshift Cluster (%s): %s", d.Id(), err)
   392  	}
   393  
   394  	d.Set("cluster_public_key", rsc.ClusterPublicKey)
   395  	d.Set("cluster_revision_number", rsc.ClusterRevisionNumber)
   396  	d.Set("tags", tagsToMapRedshift(rsc.Tags))
   397  
   398  	return nil
   399  }
   400  
   401  func resourceAwsRedshiftClusterUpdate(d *schema.ResourceData, meta interface{}) error {
   402  	conn := meta.(*AWSClient).redshiftconn
   403  	d.Partial(true)
   404  
   405  	arn, tagErr := buildRedshiftARN(d.Id(), meta.(*AWSClient).accountid, meta.(*AWSClient).region)
   406  	if tagErr != nil {
   407  		return fmt.Errorf("Error building ARN for Redshift Cluster, not updating Tags for cluster %s", d.Id())
   408  	} else {
   409  		if tagErr := setTagsRedshift(conn, d, arn); tagErr != nil {
   410  			return tagErr
   411  		} else {
   412  			d.SetPartial("tags")
   413  		}
   414  	}
   415  
   416  	requestUpdate := false
   417  	log.Printf("[INFO] Building Redshift Modify Cluster Options")
   418  	req := &redshift.ModifyClusterInput{
   419  		ClusterIdentifier: aws.String(d.Id()),
   420  	}
   421  
   422  	if d.HasChange("cluster_type") {
   423  		req.ClusterType = aws.String(d.Get("cluster_type").(string))
   424  		requestUpdate = true
   425  	}
   426  
   427  	if d.HasChange("node_type") {
   428  		req.NodeType = aws.String(d.Get("node_type").(string))
   429  		requestUpdate = true
   430  	}
   431  
   432  	if d.HasChange("number_of_nodes") {
   433  		if v := d.Get("number_of_nodes").(int); v > 1 {
   434  			req.ClusterType = aws.String("multi-node")
   435  			req.NumberOfNodes = aws.Int64(int64(d.Get("number_of_nodes").(int)))
   436  		} else {
   437  			req.ClusterType = aws.String("single-node")
   438  		}
   439  
   440  		req.NodeType = aws.String(d.Get("node_type").(string))
   441  		requestUpdate = true
   442  	}
   443  
   444  	if d.HasChange("cluster_security_groups") {
   445  		req.ClusterSecurityGroups = expandStringList(d.Get("cluster_security_groups").(*schema.Set).List())
   446  		requestUpdate = true
   447  	}
   448  
   449  	if d.HasChange("vpc_security_group_ips") {
   450  		req.VpcSecurityGroupIds = expandStringList(d.Get("vpc_security_group_ips").(*schema.Set).List())
   451  		requestUpdate = true
   452  	}
   453  
   454  	if d.HasChange("master_password") {
   455  		req.MasterUserPassword = aws.String(d.Get("master_password").(string))
   456  		requestUpdate = true
   457  	}
   458  
   459  	if d.HasChange("cluster_parameter_group_name") {
   460  		req.ClusterParameterGroupName = aws.String(d.Get("cluster_parameter_group_name").(string))
   461  		requestUpdate = true
   462  	}
   463  
   464  	if d.HasChange("automated_snapshot_retention_period") {
   465  		req.AutomatedSnapshotRetentionPeriod = aws.Int64(int64(d.Get("automated_snapshot_retention_period").(int)))
   466  		requestUpdate = true
   467  	}
   468  
   469  	if d.HasChange("preferred_maintenance_window") {
   470  		req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintenance_window").(string))
   471  		requestUpdate = true
   472  	}
   473  
   474  	if d.HasChange("cluster_version") {
   475  		req.ClusterVersion = aws.String(d.Get("cluster_version").(string))
   476  		requestUpdate = true
   477  	}
   478  
   479  	if d.HasChange("allow_version_upgrade") {
   480  		req.AllowVersionUpgrade = aws.Bool(d.Get("allow_version_upgrade").(bool))
   481  		requestUpdate = true
   482  	}
   483  
   484  	if d.HasChange("publicly_accessible") {
   485  		req.PubliclyAccessible = aws.Bool(d.Get("publicly_accessible").(bool))
   486  		requestUpdate = true
   487  	}
   488  
   489  	if requestUpdate {
   490  		log.Printf("[INFO] Modifying Redshift Cluster: %s", d.Id())
   491  		log.Printf("[DEBUG] Redshift Cluster Modify options: %s", req)
   492  		_, err := conn.ModifyCluster(req)
   493  		if err != nil {
   494  			return fmt.Errorf("[WARN] Error modifying Redshift Cluster (%s): %s", d.Id(), err)
   495  		}
   496  	}
   497  
   498  	if d.HasChange("iam_roles") {
   499  		o, n := d.GetChange("iam_roles")
   500  		if o == nil {
   501  			o = new(schema.Set)
   502  		}
   503  		if n == nil {
   504  			n = new(schema.Set)
   505  		}
   506  
   507  		os := o.(*schema.Set)
   508  		ns := n.(*schema.Set)
   509  
   510  		removeIams := os.Difference(ns).List()
   511  		addIams := ns.Difference(os).List()
   512  
   513  		log.Printf("[INFO] Building Redshift Modify Cluster IAM Role Options")
   514  		req := &redshift.ModifyClusterIamRolesInput{
   515  			ClusterIdentifier: aws.String(d.Id()),
   516  			AddIamRoles:       expandStringList(addIams),
   517  			RemoveIamRoles:    expandStringList(removeIams),
   518  		}
   519  
   520  		log.Printf("[INFO] Modifying Redshift Cluster IAM Roles: %s", d.Id())
   521  		log.Printf("[DEBUG] Redshift Cluster Modify IAM Role options: %s", req)
   522  		_, err := conn.ModifyClusterIamRoles(req)
   523  		if err != nil {
   524  			return fmt.Errorf("[WARN] Error modifying Redshift Cluster IAM Roles (%s): %s", d.Id(), err)
   525  		}
   526  
   527  		d.SetPartial("iam_roles")
   528  	}
   529  
   530  	if requestUpdate || d.HasChange("iam_roles") {
   531  
   532  		stateConf := &resource.StateChangeConf{
   533  			Pending:    []string{"creating", "deleting", "rebooting", "resizing", "renaming", "modifying"},
   534  			Target:     []string{"available"},
   535  			Refresh:    resourceAwsRedshiftClusterStateRefreshFunc(d, meta),
   536  			Timeout:    40 * time.Minute,
   537  			MinTimeout: 10 * time.Second,
   538  		}
   539  
   540  		// Wait, catching any errors
   541  		_, err := stateConf.WaitForState()
   542  		if err != nil {
   543  			return fmt.Errorf("[WARN] Error Modifying Redshift Cluster (%s): %s", d.Id(), err)
   544  		}
   545  	}
   546  
   547  	d.Partial(false)
   548  
   549  	return resourceAwsRedshiftClusterRead(d, meta)
   550  }
   551  
   552  func resourceAwsRedshiftClusterDelete(d *schema.ResourceData, meta interface{}) error {
   553  	conn := meta.(*AWSClient).redshiftconn
   554  	log.Printf("[DEBUG] Destroying Redshift Cluster (%s)", d.Id())
   555  
   556  	deleteOpts := redshift.DeleteClusterInput{
   557  		ClusterIdentifier: aws.String(d.Id()),
   558  	}
   559  
   560  	skipFinalSnapshot, exists := d.GetOk("skip_final_snapshot")
   561  	if !exists {
   562  		skipFinalSnapshot = true
   563  	}
   564  	deleteOpts.SkipFinalClusterSnapshot = aws.Bool(skipFinalSnapshot.(bool))
   565  
   566  	if skipFinalSnapshot == false {
   567  		if name, present := d.GetOk("final_snapshot_identifier"); present {
   568  			deleteOpts.FinalClusterSnapshotIdentifier = aws.String(name.(string))
   569  		} else {
   570  			return fmt.Errorf("Redshift Cluster Instance FinalSnapshotIdentifier is required when a final snapshot is required")
   571  		}
   572  	}
   573  
   574  	log.Printf("[DEBUG] Redshift Cluster delete options: %s", deleteOpts)
   575  	_, err := conn.DeleteCluster(&deleteOpts)
   576  	if err != nil {
   577  		return fmt.Errorf("[ERROR] Error deleting Redshift Cluster (%s): %s", d.Id(), err)
   578  	}
   579  
   580  	stateConf := &resource.StateChangeConf{
   581  		Pending:    []string{"available", "creating", "deleting", "rebooting", "resizing", "renaming"},
   582  		Target:     []string{"destroyed"},
   583  		Refresh:    resourceAwsRedshiftClusterStateRefreshFunc(d, meta),
   584  		Timeout:    40 * time.Minute,
   585  		MinTimeout: 5 * time.Second,
   586  	}
   587  
   588  	// Wait, catching any errors
   589  	_, err = stateConf.WaitForState()
   590  	if err != nil {
   591  		return fmt.Errorf("[ERROR] Error deleting Redshift Cluster (%s): %s", d.Id(), err)
   592  	}
   593  
   594  	log.Printf("[INFO] Redshift Cluster %s successfully deleted", d.Id())
   595  
   596  	return nil
   597  }
   598  
   599  func resourceAwsRedshiftClusterStateRefreshFunc(d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   600  	return func() (interface{}, string, error) {
   601  		conn := meta.(*AWSClient).redshiftconn
   602  
   603  		log.Printf("[INFO] Reading Redshift Cluster Information: %s", d.Id())
   604  		resp, err := conn.DescribeClusters(&redshift.DescribeClustersInput{
   605  			ClusterIdentifier: aws.String(d.Id()),
   606  		})
   607  
   608  		if err != nil {
   609  			if awsErr, ok := err.(awserr.Error); ok {
   610  				if "ClusterNotFound" == awsErr.Code() {
   611  					return 42, "destroyed", nil
   612  				}
   613  			}
   614  			log.Printf("[WARN] Error on retrieving Redshift Cluster (%s) when waiting: %s", d.Id(), err)
   615  			return nil, "", err
   616  		}
   617  
   618  		var rsc *redshift.Cluster
   619  
   620  		for _, c := range resp.Clusters {
   621  			if *c.ClusterIdentifier == d.Id() {
   622  				rsc = c
   623  			}
   624  		}
   625  
   626  		if rsc == nil {
   627  			return 42, "destroyed", nil
   628  		}
   629  
   630  		if rsc.ClusterStatus != nil {
   631  			log.Printf("[DEBUG] Redshift Cluster status (%s): %s", d.Id(), *rsc.ClusterStatus)
   632  		}
   633  
   634  		return rsc, *rsc.ClusterStatus, nil
   635  	}
   636  }
   637  
   638  func validateRedshiftClusterIdentifier(v interface{}, k string) (ws []string, errors []error) {
   639  	value := v.(string)
   640  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
   641  		errors = append(errors, fmt.Errorf(
   642  			"only lowercase alphanumeric characters and hyphens allowed in %q", k))
   643  	}
   644  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
   645  		errors = append(errors, fmt.Errorf(
   646  			"first character of %q must be a letter", k))
   647  	}
   648  	if regexp.MustCompile(`--`).MatchString(value) {
   649  		errors = append(errors, fmt.Errorf(
   650  			"%q cannot contain two consecutive hyphens", k))
   651  	}
   652  	if regexp.MustCompile(`-$`).MatchString(value) {
   653  		errors = append(errors, fmt.Errorf(
   654  			"%q cannot end with a hyphen", k))
   655  	}
   656  	return
   657  }
   658  
   659  func validateRedshiftClusterDbName(v interface{}, k string) (ws []string, errors []error) {
   660  	value := v.(string)
   661  	if !regexp.MustCompile(`^[a-z]+$`).MatchString(value) {
   662  		errors = append(errors, fmt.Errorf(
   663  			"only lowercase letters characters allowed in %q", k))
   664  	}
   665  	if len(value) > 64 {
   666  		errors = append(errors, fmt.Errorf(
   667  			"%q cannot be longer than 64 characters: %q", k, value))
   668  	}
   669  	if value == "" {
   670  		errors = append(errors, fmt.Errorf(
   671  			"%q cannot be an empty string", k))
   672  	}
   673  
   674  	return
   675  }
   676  
   677  func validateRedshiftClusterFinalSnapshotIdentifier(v interface{}, k string) (ws []string, errors []error) {
   678  	value := v.(string)
   679  	if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   680  		errors = append(errors, fmt.Errorf(
   681  			"only alphanumeric characters and hyphens allowed in %q", k))
   682  	}
   683  	if regexp.MustCompile(`--`).MatchString(value) {
   684  		errors = append(errors, fmt.Errorf("%q cannot contain two consecutive hyphens", k))
   685  	}
   686  	if regexp.MustCompile(`-$`).MatchString(value) {
   687  		errors = append(errors, fmt.Errorf("%q cannot end in a hyphen", k))
   688  	}
   689  	if len(value) > 255 {
   690  		errors = append(errors, fmt.Errorf("%q cannot be more than 255 characters", k))
   691  	}
   692  	return
   693  }
   694  
   695  func validateRedshiftClusterMasterUsername(v interface{}, k string) (ws []string, errors []error) {
   696  	value := v.(string)
   697  	if !regexp.MustCompile(`^\w+$`).MatchString(value) {
   698  		errors = append(errors, fmt.Errorf(
   699  			"only alphanumeric characters in %q", k))
   700  	}
   701  	if !regexp.MustCompile(`^[A-Za-z]`).MatchString(value) {
   702  		errors = append(errors, fmt.Errorf(
   703  			"first character of %q must be a letter", k))
   704  	}
   705  	if len(value) > 128 {
   706  		errors = append(errors, fmt.Errorf("%q cannot be more than 128 characters", k))
   707  	}
   708  	return
   709  }
   710  
   711  func buildRedshiftARN(identifier, accountid, region string) (string, error) {
   712  	if accountid == "" {
   713  		return "", fmt.Errorf("Unable to construct cluster ARN because of missing AWS Account ID")
   714  	}
   715  	arn := fmt.Sprintf("arn:aws:redshift:%s:%s:cluster:%s", region, accountid, identifier)
   716  	return arn, nil
   717  
   718  }