github.com/i0n/terraform@v0.4.3-0.20150506151324-010a39a58ec1/builtin/providers/aws/resource_aws_db_instance.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/awslabs/aws-sdk-go/aws" 9 "github.com/awslabs/aws-sdk-go/service/iam" 10 "github.com/awslabs/aws-sdk-go/service/rds" 11 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceAwsDbInstance() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceAwsDbInstanceCreate, 19 Read: resourceAwsDbInstanceRead, 20 Update: resourceAwsDbInstanceUpdate, 21 Delete: resourceAwsDbInstanceDelete, 22 23 Schema: map[string]*schema.Schema{ 24 "name": &schema.Schema{ 25 Type: schema.TypeString, 26 Optional: true, 27 ForceNew: true, 28 }, 29 30 "username": &schema.Schema{ 31 Type: schema.TypeString, 32 Required: true, 33 ForceNew: true, 34 }, 35 36 "password": &schema.Schema{ 37 Type: schema.TypeString, 38 Required: true, 39 }, 40 41 "engine": &schema.Schema{ 42 Type: schema.TypeString, 43 Required: true, 44 ForceNew: true, 45 }, 46 47 "engine_version": &schema.Schema{ 48 Type: schema.TypeString, 49 Required: true, 50 }, 51 52 "storage_encrypted": &schema.Schema{ 53 Type: schema.TypeBool, 54 Optional: true, 55 ForceNew: true, 56 }, 57 58 "allocated_storage": &schema.Schema{ 59 Type: schema.TypeInt, 60 Required: true, 61 }, 62 63 "storage_type": &schema.Schema{ 64 Type: schema.TypeString, 65 Optional: true, 66 Computed: true, 67 }, 68 69 "identifier": &schema.Schema{ 70 Type: schema.TypeString, 71 Required: true, 72 ForceNew: true, 73 }, 74 75 "instance_class": &schema.Schema{ 76 Type: schema.TypeString, 77 Required: true, 78 }, 79 80 "availability_zone": &schema.Schema{ 81 Type: schema.TypeString, 82 Optional: true, 83 Computed: true, 84 ForceNew: true, 85 }, 86 87 "backup_retention_period": &schema.Schema{ 88 Type: schema.TypeInt, 89 Optional: true, 90 Default: 1, 91 }, 92 93 "backup_window": &schema.Schema{ 94 Type: schema.TypeString, 95 Optional: true, 96 Computed: true, 97 }, 98 99 "iops": &schema.Schema{ 100 Type: schema.TypeInt, 101 Optional: true, 102 }, 103 104 "maintenance_window": &schema.Schema{ 105 Type: schema.TypeString, 106 Optional: true, 107 Computed: true, 108 }, 109 110 "multi_az": &schema.Schema{ 111 Type: schema.TypeBool, 112 Optional: true, 113 Computed: true, 114 }, 115 116 "port": &schema.Schema{ 117 Type: schema.TypeInt, 118 Optional: true, 119 Computed: true, 120 ForceNew: true, 121 }, 122 123 "publicly_accessible": &schema.Schema{ 124 Type: schema.TypeBool, 125 Optional: true, 126 ForceNew: true, 127 }, 128 129 "vpc_security_group_ids": &schema.Schema{ 130 Type: schema.TypeSet, 131 Optional: true, 132 Computed: true, 133 Elem: &schema.Schema{Type: schema.TypeString}, 134 Set: schema.HashString, 135 }, 136 137 "security_group_names": &schema.Schema{ 138 Type: schema.TypeSet, 139 Optional: true, 140 Elem: &schema.Schema{Type: schema.TypeString}, 141 Set: schema.HashString, 142 }, 143 144 "final_snapshot_identifier": &schema.Schema{ 145 Type: schema.TypeString, 146 Optional: true, 147 }, 148 149 "db_subnet_group_name": &schema.Schema{ 150 Type: schema.TypeString, 151 Optional: true, 152 ForceNew: true, 153 Computed: true, 154 }, 155 156 "parameter_group_name": &schema.Schema{ 157 Type: schema.TypeString, 158 Optional: true, 159 Computed: true, 160 }, 161 162 "address": &schema.Schema{ 163 Type: schema.TypeString, 164 Computed: true, 165 }, 166 167 "endpoint": &schema.Schema{ 168 Type: schema.TypeString, 169 Computed: true, 170 }, 171 172 "status": &schema.Schema{ 173 Type: schema.TypeString, 174 Computed: true, 175 }, 176 177 // apply_immediately is used to determine when the update modifications 178 // take place. 179 // See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html 180 "apply_immediately": &schema.Schema{ 181 Type: schema.TypeBool, 182 Optional: true, 183 Computed: true, 184 }, 185 186 "tags": tagsSchema(), 187 }, 188 } 189 } 190 191 func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error { 192 conn := meta.(*AWSClient).rdsconn 193 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 194 opts := rds.CreateDBInstanceInput{ 195 AllocatedStorage: aws.Long(int64(d.Get("allocated_storage").(int))), 196 DBInstanceClass: aws.String(d.Get("instance_class").(string)), 197 DBInstanceIdentifier: aws.String(d.Get("identifier").(string)), 198 DBName: aws.String(d.Get("name").(string)), 199 MasterUsername: aws.String(d.Get("username").(string)), 200 MasterUserPassword: aws.String(d.Get("password").(string)), 201 Engine: aws.String(d.Get("engine").(string)), 202 EngineVersion: aws.String(d.Get("engine_version").(string)), 203 StorageEncrypted: aws.Boolean(d.Get("storage_encrypted").(bool)), 204 Tags: tags, 205 } 206 207 if attr, ok := d.GetOk("storage_type"); ok { 208 opts.StorageType = aws.String(attr.(string)) 209 } 210 211 attr := d.Get("backup_retention_period") 212 opts.BackupRetentionPeriod = aws.Long(int64(attr.(int))) 213 214 if attr, ok := d.GetOk("iops"); ok { 215 opts.IOPS = aws.Long(int64(attr.(int))) 216 } 217 218 if attr, ok := d.GetOk("port"); ok { 219 opts.Port = aws.Long(int64(attr.(int))) 220 } 221 222 if attr, ok := d.GetOk("multi_az"); ok { 223 opts.MultiAZ = aws.Boolean(attr.(bool)) 224 } 225 226 if attr, ok := d.GetOk("availability_zone"); ok { 227 opts.AvailabilityZone = aws.String(attr.(string)) 228 } 229 230 if attr, ok := d.GetOk("maintenance_window"); ok { 231 opts.PreferredMaintenanceWindow = aws.String(attr.(string)) 232 } 233 234 if attr, ok := d.GetOk("backup_window"); ok { 235 opts.PreferredBackupWindow = aws.String(attr.(string)) 236 } 237 238 if attr, ok := d.GetOk("publicly_accessible"); ok { 239 opts.PubliclyAccessible = aws.Boolean(attr.(bool)) 240 } 241 242 if attr, ok := d.GetOk("db_subnet_group_name"); ok { 243 opts.DBSubnetGroupName = aws.String(attr.(string)) 244 } 245 246 if attr, ok := d.GetOk("parameter_group_name"); ok { 247 opts.DBParameterGroupName = aws.String(attr.(string)) 248 } 249 250 if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { 251 var s []*string 252 for _, v := range attr.List() { 253 s = append(s, aws.String(v.(string))) 254 } 255 opts.VPCSecurityGroupIDs = s 256 } 257 258 if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 { 259 var s []*string 260 for _, v := range attr.List() { 261 s = append(s, aws.String(v.(string))) 262 } 263 opts.DBSecurityGroups = s 264 } 265 266 log.Printf("[DEBUG] DB Instance create configuration: %#v", opts) 267 _, err := conn.CreateDBInstance(&opts) 268 if err != nil { 269 return fmt.Errorf("Error creating DB Instance: %s", err) 270 } 271 272 d.SetId(d.Get("identifier").(string)) 273 274 log.Printf("[INFO] DB Instance ID: %s", d.Id()) 275 276 log.Println( 277 "[INFO] Waiting for DB Instance to be available") 278 279 stateConf := &resource.StateChangeConf{ 280 Pending: []string{"creating", "backing-up", "modifying"}, 281 Target: "available", 282 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 283 Timeout: 40 * time.Minute, 284 MinTimeout: 10 * time.Second, 285 Delay: 30 * time.Second, // Wait 30 secs before starting 286 } 287 288 // Wait, catching any errors 289 _, err = stateConf.WaitForState() 290 if err != nil { 291 return err 292 } 293 294 return resourceAwsDbInstanceRead(d, meta) 295 } 296 297 func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error { 298 v, err := resourceAwsBbInstanceRetrieve(d, meta) 299 300 if err != nil { 301 return err 302 } 303 if v == nil { 304 d.SetId("") 305 return nil 306 } 307 308 d.Set("name", v.DBName) 309 d.Set("username", v.MasterUsername) 310 d.Set("engine", v.Engine) 311 d.Set("engine_version", v.EngineVersion) 312 d.Set("allocated_storage", v.AllocatedStorage) 313 d.Set("storage_type", v.StorageType) 314 d.Set("instance_class", v.DBInstanceClass) 315 d.Set("availability_zone", v.AvailabilityZone) 316 d.Set("backup_retention_period", v.BackupRetentionPeriod) 317 d.Set("backup_window", v.PreferredBackupWindow) 318 d.Set("maintenance_window", v.PreferredMaintenanceWindow) 319 d.Set("multi_az", v.MultiAZ) 320 if v.DBSubnetGroup != nil { 321 d.Set("db_subnet_group_name", v.DBSubnetGroup.DBSubnetGroupName) 322 } 323 324 if len(v.DBParameterGroups) > 0 { 325 d.Set("parameter_group_name", v.DBParameterGroups[0].DBParameterGroupName) 326 } 327 328 if v.Endpoint != nil { 329 d.Set("port", v.Endpoint.Port) 330 d.Set("address", v.Endpoint.Address) 331 332 if v.Endpoint.Address != nil && v.Endpoint.Port != nil { 333 d.Set("endpoint", 334 fmt.Sprintf("%s:%d", *v.Endpoint.Address, *v.Endpoint.Port)) 335 } 336 } 337 338 d.Set("status", v.DBInstanceStatus) 339 d.Set("storage_encrypted", v.StorageEncrypted) 340 341 // list tags for resource 342 // set tags 343 conn := meta.(*AWSClient).rdsconn 344 arn, err := buildRDSARN(d, meta) 345 if err != nil { 346 name := "<empty>" 347 if v.DBName != nil && *v.DBName != "" { 348 name = *v.DBName 349 } 350 351 log.Printf("[DEBUG] Error building ARN for DB Instance, not setting Tags for DB %s", name) 352 } else { 353 resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{ 354 ResourceName: aws.String(arn), 355 }) 356 357 if err != nil { 358 log.Printf("[DEBUG] Error retreiving tags for ARN: %s", arn) 359 } 360 361 var dt []*rds.Tag 362 if len(resp.TagList) > 0 { 363 dt = resp.TagList 364 } 365 d.Set("tags", tagsToMapRDS(dt)) 366 } 367 368 // Create an empty schema.Set to hold all vpc security group ids 369 ids := &schema.Set{ 370 F: schema.HashString, 371 } 372 for _, v := range v.VPCSecurityGroups { 373 ids.Add(*v.VPCSecurityGroupID) 374 } 375 d.Set("vpc_security_group_ids", ids) 376 377 // Create an empty schema.Set to hold all security group names 378 sgn := &schema.Set{ 379 F: schema.HashString, 380 } 381 for _, v := range v.DBSecurityGroups { 382 sgn.Add(*v.DBSecurityGroupName) 383 } 384 d.Set("security_group_names", sgn) 385 386 return nil 387 } 388 389 func resourceAwsDbInstanceDelete(d *schema.ResourceData, meta interface{}) error { 390 conn := meta.(*AWSClient).rdsconn 391 392 log.Printf("[DEBUG] DB Instance destroy: %v", d.Id()) 393 394 opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())} 395 396 finalSnapshot := d.Get("final_snapshot_identifier").(string) 397 if finalSnapshot == "" { 398 opts.SkipFinalSnapshot = aws.Boolean(true) 399 } else { 400 opts.FinalDBSnapshotIdentifier = aws.String(finalSnapshot) 401 } 402 403 log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts) 404 if _, err := conn.DeleteDBInstance(&opts); err != nil { 405 return err 406 } 407 408 log.Println( 409 "[INFO] Waiting for DB Instance to be destroyed") 410 stateConf := &resource.StateChangeConf{ 411 Pending: []string{"creating", "backing-up", 412 "modifying", "deleting", "available"}, 413 Target: "", 414 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 415 Timeout: 40 * time.Minute, 416 MinTimeout: 10 * time.Second, 417 Delay: 30 * time.Second, // Wait 30 secs before starting 418 } 419 if _, err := stateConf.WaitForState(); err != nil { 420 return err 421 } 422 423 return nil 424 } 425 426 func resourceAwsDbInstanceUpdate(d *schema.ResourceData, meta interface{}) error { 427 conn := meta.(*AWSClient).rdsconn 428 429 d.Partial(true) 430 431 req := &rds.ModifyDBInstanceInput{ 432 ApplyImmediately: aws.Boolean(d.Get("apply_immediately").(bool)), 433 DBInstanceIdentifier: aws.String(d.Id()), 434 } 435 d.SetPartial("apply_immediately") 436 437 if d.HasChange("allocated_storage") { 438 d.SetPartial("allocated_storage") 439 req.AllocatedStorage = aws.Long(int64(d.Get("allocated_storage").(int))) 440 } 441 if d.HasChange("backup_retention_period") { 442 d.SetPartial("backup_retention_period") 443 req.BackupRetentionPeriod = aws.Long(int64(d.Get("backup_retention_period").(int))) 444 } 445 if d.HasChange("instance_class") { 446 d.SetPartial("instance_class") 447 req.DBInstanceClass = aws.String(d.Get("instance_class").(string)) 448 } 449 if d.HasChange("parameter_group_name") { 450 d.SetPartial("parameter_group_name") 451 req.DBParameterGroupName = aws.String(d.Get("parameter_group_name").(string)) 452 } 453 if d.HasChange("engine_version") { 454 d.SetPartial("engine_version") 455 req.EngineVersion = aws.String(d.Get("engine_version").(string)) 456 } 457 if d.HasChange("iops") { 458 d.SetPartial("iops") 459 req.IOPS = aws.Long(int64(d.Get("iops").(int))) 460 } 461 if d.HasChange("backup_window") { 462 d.SetPartial("backup_window") 463 req.PreferredBackupWindow = aws.String(d.Get("backup_window").(string)) 464 } 465 if d.HasChange("maintenance_window") { 466 d.SetPartial("maintenance_window") 467 req.PreferredMaintenanceWindow = aws.String(d.Get("maintenance_window").(string)) 468 } 469 if d.HasChange("password") { 470 d.SetPartial("password") 471 req.MasterUserPassword = aws.String(d.Get("password").(string)) 472 } 473 if d.HasChange("multi_az") { 474 d.SetPartial("multi_az") 475 req.MultiAZ = aws.Boolean(d.Get("multi_az").(bool)) 476 } 477 if d.HasChange("storage_type") { 478 d.SetPartial("storage_type") 479 req.StorageType = aws.String(d.Get("storage_type").(string)) 480 } 481 482 if d.HasChange("vpc_security_group_ids") { 483 if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { 484 var s []*string 485 for _, v := range attr.List() { 486 s = append(s, aws.String(v.(string))) 487 } 488 req.VPCSecurityGroupIDs = s 489 } 490 } 491 492 if d.HasChange("vpc_security_group_ids") { 493 if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 { 494 var s []*string 495 for _, v := range attr.List() { 496 s = append(s, aws.String(v.(string))) 497 } 498 req.DBSecurityGroups = s 499 } 500 } 501 502 log.Printf("[DEBUG] DB Instance Modification request: %#v", req) 503 _, err := conn.ModifyDBInstance(req) 504 if err != nil { 505 return fmt.Errorf("Error modifying DB Instance %s: %s", d.Id(), err) 506 } 507 508 if arn, err := buildRDSARN(d, meta); err == nil { 509 if err := setTagsRDS(conn, d, arn); err != nil { 510 return err 511 } else { 512 d.SetPartial("tags") 513 } 514 } 515 d.Partial(false) 516 return resourceAwsDbInstanceRead(d, meta) 517 } 518 519 func resourceAwsBbInstanceRetrieve( 520 d *schema.ResourceData, meta interface{}) (*rds.DBInstance, error) { 521 conn := meta.(*AWSClient).rdsconn 522 523 opts := rds.DescribeDBInstancesInput{ 524 DBInstanceIdentifier: aws.String(d.Id()), 525 } 526 527 log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts) 528 529 resp, err := conn.DescribeDBInstances(&opts) 530 531 if err != nil { 532 dbinstanceerr, ok := err.(aws.APIError) 533 if ok && dbinstanceerr.Code == "DBInstanceNotFound" { 534 return nil, nil 535 } 536 return nil, fmt.Errorf("Error retrieving DB Instances: %s", err) 537 } 538 539 if len(resp.DBInstances) != 1 || 540 *resp.DBInstances[0].DBInstanceIdentifier != d.Id() { 541 if err != nil { 542 return nil, nil 543 } 544 } 545 546 return resp.DBInstances[0], nil 547 } 548 549 func resourceAwsDbInstanceStateRefreshFunc( 550 d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { 551 return func() (interface{}, string, error) { 552 v, err := resourceAwsBbInstanceRetrieve(d, meta) 553 554 if err != nil { 555 log.Printf("Error on retrieving DB Instance when waiting: %s", err) 556 return nil, "", err 557 } 558 559 if v == nil { 560 return nil, "", nil 561 } 562 563 return v, *v.DBInstanceStatus, nil 564 } 565 } 566 567 func buildRDSARN(d *schema.ResourceData, meta interface{}) (string, error) { 568 iamconn := meta.(*AWSClient).iamconn 569 region := meta.(*AWSClient).region 570 // An zero value GetUserInput{} defers to the currently logged in user 571 resp, err := iamconn.GetUser(&iam.GetUserInput{}) 572 if err != nil { 573 return "", err 574 } 575 user := resp.User 576 arn := fmt.Sprintf("arn:aws:rds:%s:%s:db:%s", region, *user.UserID, d.Id()) 577 return arn, nil 578 }