github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/builtin/providers/aws/resource_aws_db_parameter_group.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "strings" 8 "time" 9 10 "github.com/hashicorp/terraform/helper/hashcode" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 14 "github.com/aws/aws-sdk-go/aws" 15 "github.com/aws/aws-sdk-go/aws/awserr" 16 "github.com/aws/aws-sdk-go/service/rds" 17 ) 18 19 func resourceAwsDbParameterGroup() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceAwsDbParameterGroupCreate, 22 Read: resourceAwsDbParameterGroupRead, 23 Update: resourceAwsDbParameterGroupUpdate, 24 Delete: resourceAwsDbParameterGroupDelete, 25 Importer: &schema.ResourceImporter{ 26 State: schema.ImportStatePassthrough, 27 }, 28 29 Schema: map[string]*schema.Schema{ 30 "arn": &schema.Schema{ 31 Type: schema.TypeString, 32 Computed: true, 33 }, 34 "name": &schema.Schema{ 35 Type: schema.TypeString, 36 ForceNew: true, 37 Required: true, 38 ValidateFunc: validateDbParamGroupName, 39 }, 40 "family": &schema.Schema{ 41 Type: schema.TypeString, 42 Required: true, 43 ForceNew: true, 44 }, 45 "description": &schema.Schema{ 46 Type: schema.TypeString, 47 Optional: true, 48 ForceNew: true, 49 Default: "Managed by Terraform", 50 }, 51 "parameter": &schema.Schema{ 52 Type: schema.TypeSet, 53 Optional: true, 54 ForceNew: false, 55 Elem: &schema.Resource{ 56 Schema: map[string]*schema.Schema{ 57 "name": &schema.Schema{ 58 Type: schema.TypeString, 59 Required: true, 60 }, 61 "value": &schema.Schema{ 62 Type: schema.TypeString, 63 Required: true, 64 }, 65 "apply_method": &schema.Schema{ 66 Type: schema.TypeString, 67 Optional: true, 68 Default: "immediate", 69 // this parameter is not actually state, but a 70 // meta-parameter describing how the RDS API call 71 // to modify the parameter group should be made. 72 // Future reads of the resource from AWS don't tell 73 // us what we used for apply_method previously, so 74 // by squashing state to an empty string we avoid 75 // needing to do an update for every future run. 76 StateFunc: func(interface{}) string { return "" }, 77 }, 78 }, 79 }, 80 Set: resourceAwsDbParameterHash, 81 }, 82 83 "tags": tagsSchema(), 84 }, 85 } 86 } 87 88 func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{}) error { 89 rdsconn := meta.(*AWSClient).rdsconn 90 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 91 92 createOpts := rds.CreateDBParameterGroupInput{ 93 DBParameterGroupName: aws.String(d.Get("name").(string)), 94 DBParameterGroupFamily: aws.String(d.Get("family").(string)), 95 Description: aws.String(d.Get("description").(string)), 96 Tags: tags, 97 } 98 99 log.Printf("[DEBUG] Create DB Parameter Group: %#v", createOpts) 100 _, err := rdsconn.CreateDBParameterGroup(&createOpts) 101 if err != nil { 102 return fmt.Errorf("Error creating DB Parameter Group: %s", err) 103 } 104 105 d.Partial(true) 106 d.SetPartial("name") 107 d.SetPartial("family") 108 d.SetPartial("description") 109 d.Partial(false) 110 111 d.SetId(*createOpts.DBParameterGroupName) 112 log.Printf("[INFO] DB Parameter Group ID: %s", d.Id()) 113 114 return resourceAwsDbParameterGroupUpdate(d, meta) 115 } 116 117 func resourceAwsDbParameterGroupRead(d *schema.ResourceData, meta interface{}) error { 118 rdsconn := meta.(*AWSClient).rdsconn 119 120 describeOpts := rds.DescribeDBParameterGroupsInput{ 121 DBParameterGroupName: aws.String(d.Id()), 122 } 123 124 describeResp, err := rdsconn.DescribeDBParameterGroups(&describeOpts) 125 if err != nil { 126 return err 127 } 128 129 if len(describeResp.DBParameterGroups) != 1 || 130 *describeResp.DBParameterGroups[0].DBParameterGroupName != d.Id() { 131 return fmt.Errorf("Unable to find Parameter Group: %#v", describeResp.DBParameterGroups) 132 } 133 134 d.Set("name", describeResp.DBParameterGroups[0].DBParameterGroupName) 135 d.Set("family", describeResp.DBParameterGroups[0].DBParameterGroupFamily) 136 d.Set("description", describeResp.DBParameterGroups[0].Description) 137 138 // Only include user customized parameters as there's hundreds of system/default ones 139 describeParametersOpts := rds.DescribeDBParametersInput{ 140 DBParameterGroupName: aws.String(d.Id()), 141 Source: aws.String("user"), 142 } 143 144 describeParametersResp, err := rdsconn.DescribeDBParameters(&describeParametersOpts) 145 if err != nil { 146 return err 147 } 148 149 d.Set("parameter", flattenParameters(describeParametersResp.Parameters)) 150 151 paramGroup := describeResp.DBParameterGroups[0] 152 arn, err := buildRDSPGARN(d.Id(), meta.(*AWSClient).accountid, meta.(*AWSClient).region) 153 if err != nil { 154 name := "<empty>" 155 if paramGroup.DBParameterGroupName != nil && *paramGroup.DBParameterGroupName != "" { 156 name = *paramGroup.DBParameterGroupName 157 } 158 log.Printf("[DEBUG] Error building ARN for DB Parameter Group, not setting Tags for Param Group %s", name) 159 } else { 160 d.Set("arn", arn) 161 resp, err := rdsconn.ListTagsForResource(&rds.ListTagsForResourceInput{ 162 ResourceName: aws.String(arn), 163 }) 164 165 if err != nil { 166 log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn) 167 } 168 169 var dt []*rds.Tag 170 if len(resp.TagList) > 0 { 171 dt = resp.TagList 172 } 173 d.Set("tags", tagsToMapRDS(dt)) 174 } 175 176 return nil 177 } 178 179 func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error { 180 rdsconn := meta.(*AWSClient).rdsconn 181 182 d.Partial(true) 183 184 if d.HasChange("parameter") { 185 o, n := d.GetChange("parameter") 186 if o == nil { 187 o = new(schema.Set) 188 } 189 if n == nil { 190 n = new(schema.Set) 191 } 192 193 os := o.(*schema.Set) 194 ns := n.(*schema.Set) 195 196 // Expand the "parameter" set to aws-sdk-go compat []rds.Parameter 197 parameters, err := expandParameters(ns.Difference(os).List()) 198 if err != nil { 199 return err 200 } 201 202 if len(parameters) > 0 { 203 // We can only modify 20 parameters at a time, so walk them until 204 // we've got them all. 205 maxParams := 20 206 for parameters != nil { 207 paramsToModify := make([]*rds.Parameter, 0) 208 if len(parameters) <= maxParams { 209 paramsToModify, parameters = parameters[:], nil 210 } else { 211 paramsToModify, parameters = parameters[:maxParams], parameters[maxParams:] 212 } 213 modifyOpts := rds.ModifyDBParameterGroupInput{ 214 DBParameterGroupName: aws.String(d.Get("name").(string)), 215 Parameters: paramsToModify, 216 } 217 218 log.Printf("[DEBUG] Modify DB Parameter Group: %s", modifyOpts) 219 _, err = rdsconn.ModifyDBParameterGroup(&modifyOpts) 220 if err != nil { 221 return fmt.Errorf("Error modifying DB Parameter Group: %s", err) 222 } 223 } 224 d.SetPartial("parameter") 225 } 226 } 227 228 if arn, err := buildRDSPGARN(d.Id(), meta.(*AWSClient).accountid, meta.(*AWSClient).region); err == nil { 229 if err := setTagsRDS(rdsconn, d, arn); err != nil { 230 return err 231 } else { 232 d.SetPartial("tags") 233 } 234 } 235 236 d.Partial(false) 237 238 return resourceAwsDbParameterGroupRead(d, meta) 239 } 240 241 func resourceAwsDbParameterGroupDelete(d *schema.ResourceData, meta interface{}) error { 242 stateConf := &resource.StateChangeConf{ 243 Pending: []string{"pending"}, 244 Target: []string{"destroyed"}, 245 Refresh: resourceAwsDbParameterGroupDeleteRefreshFunc(d, meta), 246 Timeout: 3 * time.Minute, 247 MinTimeout: 1 * time.Second, 248 } 249 _, err := stateConf.WaitForState() 250 return err 251 } 252 253 func resourceAwsDbParameterGroupDeleteRefreshFunc( 254 d *schema.ResourceData, 255 meta interface{}) resource.StateRefreshFunc { 256 rdsconn := meta.(*AWSClient).rdsconn 257 258 return func() (interface{}, string, error) { 259 260 deleteOpts := rds.DeleteDBParameterGroupInput{ 261 DBParameterGroupName: aws.String(d.Id()), 262 } 263 264 if _, err := rdsconn.DeleteDBParameterGroup(&deleteOpts); err != nil { 265 rdserr, ok := err.(awserr.Error) 266 if !ok { 267 return d, "error", err 268 } 269 270 if rdserr.Code() != "DBParameterGroupNotFoundFault" { 271 return d, "error", err 272 } 273 } 274 275 return d, "destroyed", nil 276 } 277 } 278 279 func resourceAwsDbParameterHash(v interface{}) int { 280 var buf bytes.Buffer 281 m := v.(map[string]interface{}) 282 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 283 // Store the value as a lower case string, to match how we store them in flattenParameters 284 buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["value"].(string)))) 285 286 return hashcode.String(buf.String()) 287 } 288 289 func buildRDSPGARN(identifier, accountid, region string) (string, error) { 290 if accountid == "" { 291 return "", fmt.Errorf("Unable to construct RDS ARN because of missing AWS Account ID") 292 } 293 arn := fmt.Sprintf("arn:aws:rds:%s:%s:pg:%s", region, accountid, identifier) 294 return arn, nil 295 296 }