github.com/shvar/terraform@v0.6.9-0.20151215234924-3365cd2231df/builtin/providers/aws/resource_aws_db_subnet_group.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/iam" 13 "github.com/aws/aws-sdk-go/service/rds" 14 "github.com/hashicorp/terraform/helper/resource" 15 "github.com/hashicorp/terraform/helper/schema" 16 ) 17 18 func resourceAwsDbSubnetGroup() *schema.Resource { 19 return &schema.Resource{ 20 Create: resourceAwsDbSubnetGroupCreate, 21 Read: resourceAwsDbSubnetGroupRead, 22 Update: resourceAwsDbSubnetGroupUpdate, 23 Delete: resourceAwsDbSubnetGroupDelete, 24 25 Schema: map[string]*schema.Schema{ 26 "arn": &schema.Schema{ 27 Type: schema.TypeString, 28 Computed: true, 29 }, 30 31 "name": &schema.Schema{ 32 Type: schema.TypeString, 33 ForceNew: true, 34 Required: true, 35 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 36 value := v.(string) 37 if !regexp.MustCompile(`^[ .0-9A-Za-z-_]+$`).MatchString(value) { 38 errors = append(errors, fmt.Errorf( 39 "only alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k)) 40 } 41 if len(value) > 255 { 42 errors = append(errors, fmt.Errorf( 43 "%q cannot be longer than 255 characters", k)) 44 } 45 if regexp.MustCompile(`(?i)^default$`).MatchString(value) { 46 errors = append(errors, fmt.Errorf( 47 "%q is not allowed as %q", "Default", k)) 48 } 49 return 50 }, 51 }, 52 53 "description": &schema.Schema{ 54 Type: schema.TypeString, 55 Required: true, 56 ForceNew: true, 57 }, 58 59 "subnet_ids": &schema.Schema{ 60 Type: schema.TypeSet, 61 Required: true, 62 Elem: &schema.Schema{Type: schema.TypeString}, 63 Set: schema.HashString, 64 }, 65 66 "tags": tagsSchema(), 67 }, 68 } 69 } 70 71 func resourceAwsDbSubnetGroupCreate(d *schema.ResourceData, meta interface{}) error { 72 rdsconn := meta.(*AWSClient).rdsconn 73 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 74 75 subnetIdsSet := d.Get("subnet_ids").(*schema.Set) 76 subnetIds := make([]*string, subnetIdsSet.Len()) 77 for i, subnetId := range subnetIdsSet.List() { 78 subnetIds[i] = aws.String(subnetId.(string)) 79 } 80 81 createOpts := rds.CreateDBSubnetGroupInput{ 82 DBSubnetGroupName: aws.String(d.Get("name").(string)), 83 DBSubnetGroupDescription: aws.String(d.Get("description").(string)), 84 SubnetIds: subnetIds, 85 Tags: tags, 86 } 87 88 log.Printf("[DEBUG] Create DB Subnet Group: %#v", createOpts) 89 _, err := rdsconn.CreateDBSubnetGroup(&createOpts) 90 if err != nil { 91 return fmt.Errorf("Error creating DB Subnet Group: %s", err) 92 } 93 94 d.SetId(*createOpts.DBSubnetGroupName) 95 log.Printf("[INFO] DB Subnet Group ID: %s", d.Id()) 96 return resourceAwsDbSubnetGroupRead(d, meta) 97 } 98 99 func resourceAwsDbSubnetGroupRead(d *schema.ResourceData, meta interface{}) error { 100 rdsconn := meta.(*AWSClient).rdsconn 101 102 describeOpts := rds.DescribeDBSubnetGroupsInput{ 103 DBSubnetGroupName: aws.String(d.Id()), 104 } 105 106 describeResp, err := rdsconn.DescribeDBSubnetGroups(&describeOpts) 107 if err != nil { 108 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "DBSubnetGroupNotFoundFault" { 109 // Update state to indicate the db subnet no longer exists. 110 d.SetId("") 111 return nil 112 } 113 return err 114 } 115 116 if len(describeResp.DBSubnetGroups) == 0 { 117 return fmt.Errorf("Unable to find DB Subnet Group: %#v", describeResp.DBSubnetGroups) 118 } 119 120 var subnetGroup *rds.DBSubnetGroup 121 for _, s := range describeResp.DBSubnetGroups { 122 // AWS is down casing the name provided, so we compare lower case versions 123 // of the names. We lower case both our name and their name in the check, 124 // incase they change that someday. 125 if strings.ToLower(d.Id()) == strings.ToLower(*s.DBSubnetGroupName) { 126 subnetGroup = describeResp.DBSubnetGroups[0] 127 } 128 } 129 130 if subnetGroup.DBSubnetGroupName == nil { 131 return fmt.Errorf("Unable to find DB Subnet Group: %#v", describeResp.DBSubnetGroups) 132 } 133 134 d.Set("name", d.Id()) 135 d.Set("description", *subnetGroup.DBSubnetGroupDescription) 136 137 subnets := make([]string, 0, len(subnetGroup.Subnets)) 138 for _, s := range subnetGroup.Subnets { 139 subnets = append(subnets, *s.SubnetIdentifier) 140 } 141 d.Set("subnet_ids", subnets) 142 143 // list tags for resource 144 // set tags 145 conn := meta.(*AWSClient).rdsconn 146 arn, err := buildRDSsubgrpARN(d, meta) 147 if err != nil { 148 log.Printf("[DEBUG] Error building ARN for DB Subnet Group, not setting Tags for group %s", *subnetGroup.DBSubnetGroupName) 149 } else { 150 d.Set("arn", arn) 151 resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{ 152 ResourceName: aws.String(arn), 153 }) 154 155 if err != nil { 156 log.Printf("[DEBUG] Error retreiving tags for ARN: %s", arn) 157 } 158 159 var dt []*rds.Tag 160 if len(resp.TagList) > 0 { 161 dt = resp.TagList 162 } 163 d.Set("tags", tagsToMapRDS(dt)) 164 } 165 166 return nil 167 } 168 169 func resourceAwsDbSubnetGroupUpdate(d *schema.ResourceData, meta interface{}) error { 170 conn := meta.(*AWSClient).rdsconn 171 if d.HasChange("subnet_ids") { 172 _, n := d.GetChange("subnet_ids") 173 if n == nil { 174 n = new(schema.Set) 175 } 176 ns := n.(*schema.Set) 177 178 var sIds []*string 179 for _, s := range ns.List() { 180 sIds = append(sIds, aws.String(s.(string))) 181 } 182 183 _, err := conn.ModifyDBSubnetGroup(&rds.ModifyDBSubnetGroupInput{ 184 DBSubnetGroupName: aws.String(d.Id()), 185 SubnetIds: sIds, 186 }) 187 188 if err != nil { 189 return err 190 } 191 } 192 193 if arn, err := buildRDSsubgrpARN(d, meta); err == nil { 194 if err := setTagsRDS(conn, d, arn); err != nil { 195 return err 196 } else { 197 d.SetPartial("tags") 198 } 199 } 200 201 return resourceAwsDbSubnetGroupRead(d, meta) 202 } 203 204 func resourceAwsDbSubnetGroupDelete(d *schema.ResourceData, meta interface{}) error { 205 stateConf := &resource.StateChangeConf{ 206 Pending: []string{"pending"}, 207 Target: "destroyed", 208 Refresh: resourceAwsDbSubnetGroupDeleteRefreshFunc(d, meta), 209 Timeout: 3 * time.Minute, 210 MinTimeout: 1 * time.Second, 211 } 212 _, err := stateConf.WaitForState() 213 return err 214 } 215 216 func resourceAwsDbSubnetGroupDeleteRefreshFunc( 217 d *schema.ResourceData, 218 meta interface{}) resource.StateRefreshFunc { 219 rdsconn := meta.(*AWSClient).rdsconn 220 221 return func() (interface{}, string, error) { 222 223 deleteOpts := rds.DeleteDBSubnetGroupInput{ 224 DBSubnetGroupName: aws.String(d.Id()), 225 } 226 227 if _, err := rdsconn.DeleteDBSubnetGroup(&deleteOpts); err != nil { 228 rdserr, ok := err.(awserr.Error) 229 if !ok { 230 return d, "error", err 231 } 232 233 if rdserr.Code() != "DBSubnetGroupNotFoundFault" { 234 return d, "error", err 235 } 236 } 237 238 return d, "destroyed", nil 239 } 240 } 241 242 func buildRDSsubgrpARN(d *schema.ResourceData, meta interface{}) (string, error) { 243 iamconn := meta.(*AWSClient).iamconn 244 region := meta.(*AWSClient).region 245 // An zero value GetUserInput{} defers to the currently logged in user 246 resp, err := iamconn.GetUser(&iam.GetUserInput{}) 247 if err != nil { 248 return "", err 249 } 250 userARN := *resp.User.Arn 251 accountID := strings.Split(userARN, ":")[4] 252 arn := fmt.Sprintf("arn:aws:rds:%s:%s:subgrp:%s", region, accountID, d.Id()) 253 return arn, nil 254 }