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