github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/builtin/providers/aws/resource_aws_db_security_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/awslabs/aws-sdk-go/aws"
    10  	"github.com/awslabs/aws-sdk-go/service/rds"
    11  	"github.com/hashicorp/terraform/helper/hashcode"
    12  	"github.com/hashicorp/terraform/helper/multierror"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsDbSecurityGroup() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsDbSecurityGroupCreate,
    20  		Read:   resourceAwsDbSecurityGroupRead,
    21  		Delete: resourceAwsDbSecurityGroupDelete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"name": &schema.Schema{
    25  				Type:     schema.TypeString,
    26  				Required: true,
    27  				ForceNew: true,
    28  			},
    29  
    30  			"description": &schema.Schema{
    31  				Type:     schema.TypeString,
    32  				Required: true,
    33  				ForceNew: true,
    34  			},
    35  
    36  			"ingress": &schema.Schema{
    37  				Type:     schema.TypeSet,
    38  				Required: true,
    39  				ForceNew: true,
    40  				Elem: &schema.Resource{
    41  					Schema: map[string]*schema.Schema{
    42  						"cidr": &schema.Schema{
    43  							Type:     schema.TypeString,
    44  							Optional: true,
    45  						},
    46  
    47  						"security_group_name": &schema.Schema{
    48  							Type:     schema.TypeString,
    49  							Optional: true,
    50  							Computed: true,
    51  						},
    52  
    53  						"security_group_id": &schema.Schema{
    54  							Type:     schema.TypeString,
    55  							Optional: true,
    56  							Computed: true,
    57  						},
    58  
    59  						"security_group_owner_id": &schema.Schema{
    60  							Type:     schema.TypeString,
    61  							Optional: true,
    62  							Computed: true,
    63  						},
    64  					},
    65  				},
    66  				Set: resourceAwsDbSecurityGroupIngressHash,
    67  			},
    68  		},
    69  	}
    70  }
    71  
    72  func resourceAwsDbSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error {
    73  	conn := meta.(*AWSClient).rdsconn
    74  
    75  	var err error
    76  	var errs []error
    77  
    78  	opts := rds.CreateDBSecurityGroupInput{
    79  		DBSecurityGroupName:        aws.String(d.Get("name").(string)),
    80  		DBSecurityGroupDescription: aws.String(d.Get("description").(string)),
    81  	}
    82  
    83  	log.Printf("[DEBUG] DB Security Group create configuration: %#v", opts)
    84  	_, err = conn.CreateDBSecurityGroup(&opts)
    85  	if err != nil {
    86  		return fmt.Errorf("Error creating DB Security Group: %s", err)
    87  	}
    88  
    89  	d.SetId(d.Get("name").(string))
    90  
    91  	log.Printf("[INFO] DB Security Group ID: %s", d.Id())
    92  
    93  	sg, err := resourceAwsDbSecurityGroupRetrieve(d, meta)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	ingresses := d.Get("ingress").(*schema.Set)
    99  	for _, ing := range ingresses.List() {
   100  		err := resourceAwsDbSecurityGroupAuthorizeRule(ing, *sg.DBSecurityGroupName, conn)
   101  		if err != nil {
   102  			errs = append(errs, err)
   103  		}
   104  	}
   105  
   106  	if len(errs) > 0 {
   107  		return &multierror.Error{Errors: errs}
   108  	}
   109  
   110  	log.Println(
   111  		"[INFO] Waiting for Ingress Authorizations to be authorized")
   112  
   113  	stateConf := &resource.StateChangeConf{
   114  		Pending: []string{"authorizing"},
   115  		Target:  "authorized",
   116  		Refresh: resourceAwsDbSecurityGroupStateRefreshFunc(d, meta),
   117  		Timeout: 10 * time.Minute,
   118  	}
   119  
   120  	// Wait, catching any errors
   121  	_, err = stateConf.WaitForState()
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	return resourceAwsDbSecurityGroupRead(d, meta)
   127  }
   128  
   129  func resourceAwsDbSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
   130  	sg, err := resourceAwsDbSecurityGroupRetrieve(d, meta)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	d.Set("name", *sg.DBSecurityGroupName)
   136  	d.Set("description", *sg.DBSecurityGroupDescription)
   137  
   138  	// Create an empty schema.Set to hold all ingress rules
   139  	rules := &schema.Set{
   140  		F: resourceAwsDbSecurityGroupIngressHash,
   141  	}
   142  
   143  	for _, v := range sg.IPRanges {
   144  		rule := map[string]interface{}{"cidr": *v.CIDRIP}
   145  		rules.Add(rule)
   146  	}
   147  
   148  	for _, g := range sg.EC2SecurityGroups {
   149  		rule := map[string]interface{}{
   150  			"security_group_name":     *g.EC2SecurityGroupName,
   151  			"security_group_id":       *g.EC2SecurityGroupID,
   152  			"security_group_owner_id": *g.EC2SecurityGroupOwnerID,
   153  		}
   154  		rules.Add(rule)
   155  	}
   156  
   157  	d.Set("ingress", rules)
   158  
   159  	return nil
   160  }
   161  
   162  func resourceAwsDbSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error {
   163  	conn := meta.(*AWSClient).rdsconn
   164  
   165  	log.Printf("[DEBUG] DB Security Group destroy: %v", d.Id())
   166  
   167  	opts := rds.DeleteDBSecurityGroupInput{DBSecurityGroupName: aws.String(d.Id())}
   168  
   169  	log.Printf("[DEBUG] DB Security Group destroy configuration: %v", opts)
   170  	_, err := conn.DeleteDBSecurityGroup(&opts)
   171  
   172  	if err != nil {
   173  		newerr, ok := err.(aws.APIError)
   174  		if ok && newerr.Code == "InvalidDBSecurityGroup.NotFound" {
   175  			return nil
   176  		}
   177  		return err
   178  	}
   179  
   180  	return nil
   181  }
   182  
   183  func resourceAwsDbSecurityGroupRetrieve(d *schema.ResourceData, meta interface{}) (*rds.DBSecurityGroup, error) {
   184  	conn := meta.(*AWSClient).rdsconn
   185  
   186  	opts := rds.DescribeDBSecurityGroupsInput{
   187  		DBSecurityGroupName: aws.String(d.Id()),
   188  	}
   189  
   190  	log.Printf("[DEBUG] DB Security Group describe configuration: %#v", opts)
   191  
   192  	resp, err := conn.DescribeDBSecurityGroups(&opts)
   193  
   194  	if err != nil {
   195  		return nil, fmt.Errorf("Error retrieving DB Security Groups: %s", err)
   196  	}
   197  
   198  	if len(resp.DBSecurityGroups) != 1 ||
   199  		*resp.DBSecurityGroups[0].DBSecurityGroupName != d.Id() {
   200  		return nil, fmt.Errorf("Unable to find DB Security Group: %#v", resp.DBSecurityGroups)
   201  	}
   202  
   203  	return resp.DBSecurityGroups[0], nil
   204  }
   205  
   206  // Authorizes the ingress rule on the db security group
   207  func resourceAwsDbSecurityGroupAuthorizeRule(ingress interface{}, dbSecurityGroupName string, conn *rds.RDS) error {
   208  	ing := ingress.(map[string]interface{})
   209  
   210  	opts := rds.AuthorizeDBSecurityGroupIngressInput{
   211  		DBSecurityGroupName: aws.String(dbSecurityGroupName),
   212  	}
   213  
   214  	if attr, ok := ing["cidr"]; ok && attr != "" {
   215  		opts.CIDRIP = aws.String(attr.(string))
   216  	}
   217  
   218  	if attr, ok := ing["security_group_name"]; ok && attr != "" {
   219  		opts.EC2SecurityGroupName = aws.String(attr.(string))
   220  	}
   221  
   222  	if attr, ok := ing["security_group_id"]; ok && attr != "" {
   223  		opts.EC2SecurityGroupID = aws.String(attr.(string))
   224  	}
   225  
   226  	if attr, ok := ing["security_group_owner_id"]; ok && attr != "" {
   227  		opts.EC2SecurityGroupOwnerID = aws.String(attr.(string))
   228  	}
   229  
   230  	log.Printf("[DEBUG] Authorize ingress rule configuration: %#v", opts)
   231  
   232  	_, err := conn.AuthorizeDBSecurityGroupIngress(&opts)
   233  
   234  	if err != nil {
   235  		return fmt.Errorf("Error authorizing security group ingress: %s", err)
   236  	}
   237  
   238  	return nil
   239  }
   240  
   241  func resourceAwsDbSecurityGroupIngressHash(v interface{}) int {
   242  	var buf bytes.Buffer
   243  	m := v.(map[string]interface{})
   244  
   245  	if v, ok := m["cidr"]; ok {
   246  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   247  	}
   248  
   249  	if v, ok := m["security_group_name"]; ok {
   250  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   251  	}
   252  
   253  	if v, ok := m["security_group_id"]; ok {
   254  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   255  	}
   256  
   257  	if v, ok := m["security_group_owner_id"]; ok {
   258  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   259  	}
   260  
   261  	return hashcode.String(buf.String())
   262  }
   263  
   264  func resourceAwsDbSecurityGroupStateRefreshFunc(
   265  	d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   266  	return func() (interface{}, string, error) {
   267  		v, err := resourceAwsDbSecurityGroupRetrieve(d, meta)
   268  
   269  		if err != nil {
   270  			log.Printf("Error on retrieving DB Security Group when waiting: %s", err)
   271  			return nil, "", err
   272  		}
   273  
   274  		statuses := make([]string, 0, len(v.EC2SecurityGroups)+len(v.IPRanges))
   275  		for _, ec2g := range v.EC2SecurityGroups {
   276  			statuses = append(statuses, *ec2g.Status)
   277  		}
   278  		for _, ips := range v.IPRanges {
   279  			statuses = append(statuses, *ips.Status)
   280  		}
   281  
   282  		for _, stat := range statuses {
   283  			// Not done
   284  			if stat != "authorized" {
   285  				return nil, "authorizing", nil
   286  			}
   287  		}
   288  
   289  		return v, "authorized", nil
   290  	}
   291  }