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