github.com/gabrielperezs/terraform@v0.7.0-rc2.0.20160715084931-f7da2612946f/builtin/providers/aws/resource_aws_redshift_security_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"regexp"
     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/go-multierror"
    14  	"github.com/hashicorp/terraform/helper/hashcode"
    15  	"github.com/hashicorp/terraform/helper/resource"
    16  	"github.com/hashicorp/terraform/helper/schema"
    17  )
    18  
    19  func resourceAwsRedshiftSecurityGroup() *schema.Resource {
    20  	return &schema.Resource{
    21  		Create: resourceAwsRedshiftSecurityGroupCreate,
    22  		Read:   resourceAwsRedshiftSecurityGroupRead,
    23  		Update: resourceAwsRedshiftSecurityGroupUpdate,
    24  		Delete: resourceAwsRedshiftSecurityGroupDelete,
    25  
    26  		Schema: map[string]*schema.Schema{
    27  			"name": &schema.Schema{
    28  				Type:         schema.TypeString,
    29  				Required:     true,
    30  				ForceNew:     true,
    31  				ValidateFunc: validateRedshiftSecurityGroupName,
    32  			},
    33  
    34  			"description": &schema.Schema{
    35  				Type:     schema.TypeString,
    36  				Optional: true,
    37  				ForceNew: true,
    38  				Default:  "Managed by Terraform",
    39  			},
    40  
    41  			"ingress": &schema.Schema{
    42  				Type:     schema.TypeSet,
    43  				Required: true,
    44  				Elem: &schema.Resource{
    45  					Schema: map[string]*schema.Schema{
    46  						"cidr": &schema.Schema{
    47  							Type:     schema.TypeString,
    48  							Optional: true,
    49  						},
    50  
    51  						"security_group_name": &schema.Schema{
    52  							Type:     schema.TypeString,
    53  							Optional: true,
    54  							Computed: true,
    55  						},
    56  
    57  						"security_group_owner_id": &schema.Schema{
    58  							Type:     schema.TypeString,
    59  							Optional: true,
    60  							Computed: true,
    61  						},
    62  					},
    63  				},
    64  				Set: resourceAwsRedshiftSecurityGroupIngressHash,
    65  			},
    66  		},
    67  	}
    68  }
    69  
    70  func resourceAwsRedshiftSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error {
    71  	conn := meta.(*AWSClient).redshiftconn
    72  
    73  	var err error
    74  	var errs []error
    75  
    76  	name := d.Get("name").(string)
    77  	desc := d.Get("description").(string)
    78  	sgInput := &redshift.CreateClusterSecurityGroupInput{
    79  		ClusterSecurityGroupName: aws.String(name),
    80  		Description:              aws.String(desc),
    81  	}
    82  	log.Printf("[DEBUG] Redshift security group create: name: %s, description: %s", name, desc)
    83  	_, err = conn.CreateClusterSecurityGroup(sgInput)
    84  	if err != nil {
    85  		return fmt.Errorf("Error creating RedshiftSecurityGroup: %s", err)
    86  	}
    87  
    88  	d.SetId(d.Get("name").(string))
    89  
    90  	log.Printf("[INFO] Redshift Security Group ID: %s", d.Id())
    91  	sg, err := resourceAwsRedshiftSecurityGroupRetrieve(d, meta)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	ingresses := d.Get("ingress").(*schema.Set)
    97  	for _, ing := range ingresses.List() {
    98  		err := resourceAwsRedshiftSecurityGroupAuthorizeRule(ing, *sg.ClusterSecurityGroupName, conn)
    99  		if err != nil {
   100  			errs = append(errs, err)
   101  		}
   102  	}
   103  
   104  	if len(errs) > 0 {
   105  		return &multierror.Error{Errors: errs}
   106  	}
   107  
   108  	log.Println("[INFO] Waiting for Redshift Security Group Ingress Authorizations to be authorized")
   109  	stateConf := &resource.StateChangeConf{
   110  		Pending: []string{"authorizing"},
   111  		Target:  []string{"authorized"},
   112  		Refresh: resourceAwsRedshiftSecurityGroupStateRefreshFunc(d, meta),
   113  		Timeout: 10 * time.Minute,
   114  	}
   115  
   116  	_, err = stateConf.WaitForState()
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	return resourceAwsRedshiftSecurityGroupRead(d, meta)
   122  }
   123  
   124  func resourceAwsRedshiftSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
   125  	sg, err := resourceAwsRedshiftSecurityGroupRetrieve(d, meta)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	rules := &schema.Set{
   131  		F: resourceAwsRedshiftSecurityGroupIngressHash,
   132  	}
   133  
   134  	for _, v := range sg.IPRanges {
   135  		rule := map[string]interface{}{"cidr": *v.CIDRIP}
   136  		rules.Add(rule)
   137  	}
   138  
   139  	for _, g := range sg.EC2SecurityGroups {
   140  		rule := map[string]interface{}{
   141  			"security_group_name":     *g.EC2SecurityGroupName,
   142  			"security_group_owner_id": *g.EC2SecurityGroupOwnerId,
   143  		}
   144  		rules.Add(rule)
   145  	}
   146  
   147  	d.Set("ingress", rules)
   148  	d.Set("name", *sg.ClusterSecurityGroupName)
   149  	d.Set("description", *sg.Description)
   150  
   151  	return nil
   152  }
   153  
   154  func resourceAwsRedshiftSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   155  	conn := meta.(*AWSClient).redshiftconn
   156  
   157  	if d.HasChange("ingress") {
   158  		o, n := d.GetChange("ingress")
   159  		if o == nil {
   160  			o = new(schema.Set)
   161  		}
   162  		if n == nil {
   163  			n = new(schema.Set)
   164  		}
   165  
   166  		os := o.(*schema.Set)
   167  		ns := n.(*schema.Set)
   168  
   169  		removeIngressRules, err := expandRedshiftSGRevokeIngress(os.Difference(ns).List())
   170  		if err != nil {
   171  			return err
   172  		}
   173  		if len(removeIngressRules) > 0 {
   174  			for _, r := range removeIngressRules {
   175  				r.ClusterSecurityGroupName = aws.String(d.Id())
   176  
   177  				_, err := conn.RevokeClusterSecurityGroupIngress(&r)
   178  				if err != nil {
   179  					return err
   180  				}
   181  			}
   182  		}
   183  
   184  		addIngressRules, err := expandRedshiftSGAuthorizeIngress(ns.Difference(os).List())
   185  		if err != nil {
   186  			return err
   187  		}
   188  		if len(addIngressRules) > 0 {
   189  			for _, r := range addIngressRules {
   190  				r.ClusterSecurityGroupName = aws.String(d.Id())
   191  
   192  				_, err := conn.AuthorizeClusterSecurityGroupIngress(&r)
   193  				if err != nil {
   194  					return err
   195  				}
   196  			}
   197  		}
   198  
   199  	}
   200  	return resourceAwsRedshiftSecurityGroupRead(d, meta)
   201  }
   202  
   203  func resourceAwsRedshiftSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error {
   204  	conn := meta.(*AWSClient).redshiftconn
   205  
   206  	log.Printf("[DEBUG] Redshift Security Group destroy: %v", d.Id())
   207  	opts := redshift.DeleteClusterSecurityGroupInput{
   208  		ClusterSecurityGroupName: aws.String(d.Id()),
   209  	}
   210  
   211  	log.Printf("[DEBUG] Redshift Security Group destroy configuration: %v", opts)
   212  	_, err := conn.DeleteClusterSecurityGroup(&opts)
   213  
   214  	if err != nil {
   215  		newerr, ok := err.(awserr.Error)
   216  		if ok && newerr.Code() == "InvalidRedshiftSecurityGroup.NotFound" {
   217  			return nil
   218  		}
   219  		return err
   220  	}
   221  
   222  	return nil
   223  }
   224  
   225  func resourceAwsRedshiftSecurityGroupRetrieve(d *schema.ResourceData, meta interface{}) (*redshift.ClusterSecurityGroup, error) {
   226  	conn := meta.(*AWSClient).redshiftconn
   227  
   228  	opts := redshift.DescribeClusterSecurityGroupsInput{
   229  		ClusterSecurityGroupName: aws.String(d.Id()),
   230  	}
   231  
   232  	log.Printf("[DEBUG] Redshift Security Group describe configuration: %#v", opts)
   233  
   234  	resp, err := conn.DescribeClusterSecurityGroups(&opts)
   235  
   236  	if err != nil {
   237  		return nil, fmt.Errorf("Error retrieving Redshift Security Groups: %s", err)
   238  	}
   239  
   240  	if len(resp.ClusterSecurityGroups) != 1 ||
   241  		*resp.ClusterSecurityGroups[0].ClusterSecurityGroupName != d.Id() {
   242  		return nil, fmt.Errorf("Unable to find Redshift Security Group: %#v", resp.ClusterSecurityGroups)
   243  	}
   244  
   245  	return resp.ClusterSecurityGroups[0], nil
   246  }
   247  
   248  func validateRedshiftSecurityGroupName(v interface{}, k string) (ws []string, errors []error) {
   249  	value := v.(string)
   250  	if value == "default" {
   251  		errors = append(errors, fmt.Errorf("the Redshift Security Group name cannot be %q", value))
   252  	}
   253  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
   254  		errors = append(errors, fmt.Errorf(
   255  			"only lowercase alphanumeric characters and hyphens allowed in %q: %q",
   256  			k, value))
   257  	}
   258  	if len(value) > 255 {
   259  		errors = append(errors, fmt.Errorf(
   260  			"%q cannot be longer than 32 characters: %q", k, value))
   261  	}
   262  	return
   263  
   264  }
   265  
   266  func resourceAwsRedshiftSecurityGroupIngressHash(v interface{}) int {
   267  	var buf bytes.Buffer
   268  	m := v.(map[string]interface{})
   269  
   270  	if v, ok := m["cidr"]; ok {
   271  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   272  	}
   273  
   274  	if v, ok := m["security_group_name"]; ok {
   275  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   276  	}
   277  
   278  	if v, ok := m["security_group_owner_id"]; ok {
   279  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   280  	}
   281  
   282  	return hashcode.String(buf.String())
   283  }
   284  
   285  func resourceAwsRedshiftSecurityGroupAuthorizeRule(ingress interface{}, redshiftSecurityGroupName string, conn *redshift.Redshift) error {
   286  	ing := ingress.(map[string]interface{})
   287  
   288  	opts := redshift.AuthorizeClusterSecurityGroupIngressInput{
   289  		ClusterSecurityGroupName: aws.String(redshiftSecurityGroupName),
   290  	}
   291  
   292  	if attr, ok := ing["cidr"]; ok && attr != "" {
   293  		opts.CIDRIP = aws.String(attr.(string))
   294  	}
   295  
   296  	if attr, ok := ing["security_group_name"]; ok && attr != "" {
   297  		opts.EC2SecurityGroupName = aws.String(attr.(string))
   298  	}
   299  
   300  	if attr, ok := ing["security_group_owner_id"]; ok && attr != "" {
   301  		opts.EC2SecurityGroupOwnerId = aws.String(attr.(string))
   302  	}
   303  
   304  	log.Printf("[DEBUG] Authorize ingress rule configuration: %#v", opts)
   305  	_, err := conn.AuthorizeClusterSecurityGroupIngress(&opts)
   306  
   307  	if err != nil {
   308  		return fmt.Errorf("Error authorizing security group ingress: %s", err)
   309  	}
   310  
   311  	return nil
   312  }
   313  
   314  func resourceAwsRedshiftSecurityGroupStateRefreshFunc(
   315  	d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   316  	return func() (interface{}, string, error) {
   317  		v, err := resourceAwsRedshiftSecurityGroupRetrieve(d, meta)
   318  
   319  		if err != nil {
   320  			log.Printf("Error on retrieving Redshift Security Group when waiting: %s", err)
   321  			return nil, "", err
   322  		}
   323  
   324  		statuses := make([]string, 0, len(v.EC2SecurityGroups)+len(v.IPRanges))
   325  		for _, ec2g := range v.EC2SecurityGroups {
   326  			statuses = append(statuses, *ec2g.Status)
   327  		}
   328  		for _, ips := range v.IPRanges {
   329  			statuses = append(statuses, *ips.Status)
   330  		}
   331  
   332  		for _, stat := range statuses {
   333  			// Not done
   334  			if stat != "authorized" {
   335  				return nil, "authorizing", nil
   336  			}
   337  		}
   338  
   339  		return v, "authorized", nil
   340  	}
   341  }
   342  
   343  func expandRedshiftSGAuthorizeIngress(configured []interface{}) ([]redshift.AuthorizeClusterSecurityGroupIngressInput, error) {
   344  	var ingress []redshift.AuthorizeClusterSecurityGroupIngressInput
   345  
   346  	// Loop over our configured parameters and create
   347  	// an array of aws-sdk-go compatabile objects
   348  	for _, pRaw := range configured {
   349  		data := pRaw.(map[string]interface{})
   350  
   351  		i := redshift.AuthorizeClusterSecurityGroupIngressInput{}
   352  
   353  		if v, ok := data["cidr"]; ok {
   354  			i.CIDRIP = aws.String(v.(string))
   355  		}
   356  
   357  		if v, ok := data["security_group_name"]; ok {
   358  			i.EC2SecurityGroupName = aws.String(v.(string))
   359  		}
   360  
   361  		if v, ok := data["security_group_owner_id"]; ok {
   362  			i.EC2SecurityGroupOwnerId = aws.String(v.(string))
   363  		}
   364  
   365  		ingress = append(ingress, i)
   366  	}
   367  
   368  	return ingress, nil
   369  }
   370  
   371  func expandRedshiftSGRevokeIngress(configured []interface{}) ([]redshift.RevokeClusterSecurityGroupIngressInput, error) {
   372  	var ingress []redshift.RevokeClusterSecurityGroupIngressInput
   373  
   374  	// Loop over our configured parameters and create
   375  	// an array of aws-sdk-go compatabile objects
   376  	for _, pRaw := range configured {
   377  		data := pRaw.(map[string]interface{})
   378  
   379  		i := redshift.RevokeClusterSecurityGroupIngressInput{}
   380  
   381  		if v, ok := data["cidr"]; ok {
   382  			i.CIDRIP = aws.String(v.(string))
   383  		}
   384  
   385  		if v, ok := data["security_group_name"]; ok {
   386  			i.EC2SecurityGroupName = aws.String(v.(string))
   387  		}
   388  
   389  		if v, ok := data["security_group_owner_id"]; ok {
   390  			i.EC2SecurityGroupOwnerId = aws.String(v.(string))
   391  		}
   392  
   393  		ingress = append(ingress, i)
   394  	}
   395  
   396  	return ingress, nil
   397  }