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