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