github.com/jrperritt/terraform@v0.1.1-0.20170525065507-96f391dafc38/builtin/providers/aws/resource_aws_rds_cluster_instance.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/aws/aws-sdk-go/aws" 10 "github.com/aws/aws-sdk-go/service/rds" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsRDSClusterInstance() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsRDSClusterInstanceCreate, 18 Read: resourceAwsRDSClusterInstanceRead, 19 Update: resourceAwsRDSClusterInstanceUpdate, 20 Delete: resourceAwsRDSClusterInstanceDelete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 25 Timeouts: &schema.ResourceTimeout{ 26 Create: schema.DefaultTimeout(90 * time.Minute), 27 Update: schema.DefaultTimeout(90 * time.Minute), 28 Delete: schema.DefaultTimeout(90 * time.Minute), 29 }, 30 31 Schema: map[string]*schema.Schema{ 32 "identifier": { 33 Type: schema.TypeString, 34 Optional: true, 35 Computed: true, 36 ForceNew: true, 37 ConflictsWith: []string{"identifier_prefix"}, 38 ValidateFunc: validateRdsIdentifier, 39 }, 40 "identifier_prefix": { 41 Type: schema.TypeString, 42 Optional: true, 43 Computed: true, 44 ForceNew: true, 45 ValidateFunc: validateRdsIdentifierPrefix, 46 }, 47 48 "db_subnet_group_name": { 49 Type: schema.TypeString, 50 Optional: true, 51 ForceNew: true, 52 Computed: true, 53 }, 54 55 "writer": { 56 Type: schema.TypeBool, 57 Computed: true, 58 }, 59 60 "cluster_identifier": { 61 Type: schema.TypeString, 62 Required: true, 63 ForceNew: true, 64 }, 65 66 "endpoint": { 67 Type: schema.TypeString, 68 Computed: true, 69 }, 70 71 "port": { 72 Type: schema.TypeInt, 73 Computed: true, 74 }, 75 76 "publicly_accessible": { 77 Type: schema.TypeBool, 78 Optional: true, 79 Default: false, 80 }, 81 82 "instance_class": { 83 Type: schema.TypeString, 84 Required: true, 85 }, 86 87 "db_parameter_group_name": { 88 Type: schema.TypeString, 89 Optional: true, 90 Computed: true, 91 }, 92 93 // apply_immediately is used to determine when the update modifications 94 // take place. 95 // See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html 96 "apply_immediately": { 97 Type: schema.TypeBool, 98 Optional: true, 99 Computed: true, 100 }, 101 102 "kms_key_id": { 103 Type: schema.TypeString, 104 Computed: true, 105 }, 106 107 "storage_encrypted": { 108 Type: schema.TypeBool, 109 Computed: true, 110 }, 111 112 "auto_minor_version_upgrade": { 113 Type: schema.TypeBool, 114 Optional: true, 115 Default: true, 116 }, 117 118 "monitoring_role_arn": { 119 Type: schema.TypeString, 120 Optional: true, 121 Computed: true, 122 }, 123 124 "preferred_maintenance_window": { 125 Type: schema.TypeString, 126 Optional: true, 127 Computed: true, 128 StateFunc: func(v interface{}) string { 129 if v != nil { 130 value := v.(string) 131 return strings.ToLower(value) 132 } 133 return "" 134 }, 135 ValidateFunc: validateOnceAWeekWindowFormat, 136 }, 137 138 "preferred_backup_window": { 139 Type: schema.TypeString, 140 Optional: true, 141 Computed: true, 142 ValidateFunc: validateOnceADayWindowFormat, 143 }, 144 145 "monitoring_interval": { 146 Type: schema.TypeInt, 147 Optional: true, 148 Default: 0, 149 }, 150 151 "promotion_tier": { 152 Type: schema.TypeInt, 153 Optional: true, 154 Default: 0, 155 }, 156 157 "tags": tagsSchema(), 158 }, 159 } 160 } 161 162 func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{}) error { 163 conn := meta.(*AWSClient).rdsconn 164 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 165 166 createOpts := &rds.CreateDBInstanceInput{ 167 DBInstanceClass: aws.String(d.Get("instance_class").(string)), 168 DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)), 169 Engine: aws.String("aurora"), 170 PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)), 171 PromotionTier: aws.Int64(int64(d.Get("promotion_tier").(int))), 172 AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)), 173 Tags: tags, 174 } 175 176 if attr, ok := d.GetOk("db_parameter_group_name"); ok { 177 createOpts.DBParameterGroupName = aws.String(attr.(string)) 178 } 179 180 if v, ok := d.GetOk("identifier"); ok { 181 createOpts.DBInstanceIdentifier = aws.String(v.(string)) 182 } else { 183 if v, ok := d.GetOk("identifier_prefix"); ok { 184 createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId(v.(string))) 185 } else { 186 createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId("tf-")) 187 } 188 } 189 190 if attr, ok := d.GetOk("db_subnet_group_name"); ok { 191 createOpts.DBSubnetGroupName = aws.String(attr.(string)) 192 } 193 194 if attr, ok := d.GetOk("monitoring_role_arn"); ok { 195 createOpts.MonitoringRoleArn = aws.String(attr.(string)) 196 } 197 198 if attr, ok := d.GetOk("preferred_backup_window"); ok { 199 createOpts.PreferredBackupWindow = aws.String(attr.(string)) 200 } 201 202 if attr, ok := d.GetOk("preferred_maintenance_window"); ok { 203 createOpts.PreferredMaintenanceWindow = aws.String(attr.(string)) 204 } 205 206 if attr, ok := d.GetOk("monitoring_interval"); ok { 207 createOpts.MonitoringInterval = aws.Int64(int64(attr.(int))) 208 } 209 210 log.Printf("[DEBUG] Creating RDS DB Instance opts: %s", createOpts) 211 resp, err := conn.CreateDBInstance(createOpts) 212 if err != nil { 213 return err 214 } 215 216 d.SetId(*resp.DBInstance.DBInstanceIdentifier) 217 218 // reuse db_instance refresh func 219 stateConf := &resource.StateChangeConf{ 220 Pending: []string{"creating", "backing-up", "modifying"}, 221 Target: []string{"available"}, 222 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 223 Timeout: d.Timeout(schema.TimeoutCreate), 224 MinTimeout: 10 * time.Second, 225 Delay: 30 * time.Second, 226 } 227 228 // Wait, catching any errors 229 _, err = stateConf.WaitForState() 230 if err != nil { 231 return err 232 } 233 234 return resourceAwsRDSClusterInstanceRead(d, meta) 235 } 236 237 func resourceAwsRDSClusterInstanceRead(d *schema.ResourceData, meta interface{}) error { 238 db, err := resourceAwsDbInstanceRetrieve(d, meta) 239 // Errors from this helper are always reportable 240 if err != nil { 241 return fmt.Errorf("[WARN] Error on retrieving RDS Cluster Instance (%s): %s", d.Id(), err) 242 } 243 // A nil response means "not found" 244 if db == nil { 245 log.Printf("[WARN] RDS Cluster Instance (%s): not found, removing from state.", d.Id()) 246 d.SetId("") 247 return nil 248 } 249 250 // Retrieve DB Cluster information, to determine if this Instance is a writer 251 conn := meta.(*AWSClient).rdsconn 252 resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{ 253 DBClusterIdentifier: db.DBClusterIdentifier, 254 }) 255 256 var dbc *rds.DBCluster 257 for _, c := range resp.DBClusters { 258 if *c.DBClusterIdentifier == *db.DBClusterIdentifier { 259 dbc = c 260 } 261 } 262 263 if dbc == nil { 264 return fmt.Errorf("[WARN] Error finding RDS Cluster (%s) for Cluster Instance (%s): %s", 265 *db.DBClusterIdentifier, *db.DBInstanceIdentifier, err) 266 } 267 268 for _, m := range dbc.DBClusterMembers { 269 if *db.DBInstanceIdentifier == *m.DBInstanceIdentifier { 270 if *m.IsClusterWriter == true { 271 d.Set("writer", true) 272 } else { 273 d.Set("writer", false) 274 } 275 } 276 } 277 278 if db.Endpoint != nil { 279 d.Set("endpoint", db.Endpoint.Address) 280 d.Set("port", db.Endpoint.Port) 281 } 282 283 d.Set("publicly_accessible", db.PubliclyAccessible) 284 d.Set("cluster_identifier", db.DBClusterIdentifier) 285 d.Set("instance_class", db.DBInstanceClass) 286 d.Set("identifier", db.DBInstanceIdentifier) 287 d.Set("storage_encrypted", db.StorageEncrypted) 288 d.Set("kms_key_id", db.KmsKeyId) 289 d.Set("auto_minor_version_upgrade", db.AutoMinorVersionUpgrade) 290 d.Set("promotion_tier", db.PromotionTier) 291 d.Set("preferred_backup_window", db.PreferredBackupWindow) 292 d.Set("preferred_maintenance_window", db.PreferredMaintenanceWindow) 293 294 if db.MonitoringInterval != nil { 295 d.Set("monitoring_interval", db.MonitoringInterval) 296 } 297 298 if db.MonitoringRoleArn != nil { 299 d.Set("monitoring_role_arn", db.MonitoringRoleArn) 300 } 301 302 if len(db.DBParameterGroups) > 0 { 303 d.Set("db_parameter_group_name", db.DBParameterGroups[0].DBParameterGroupName) 304 } 305 306 // Fetch and save tags 307 arn, err := buildRDSARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region) 308 if err != nil { 309 log.Printf("[DEBUG] Error building ARN for RDS Cluster Instance (%s), not setting Tags", *db.DBInstanceIdentifier) 310 } else { 311 if err := saveTagsRDS(conn, d, arn); err != nil { 312 log.Printf("[WARN] Failed to save tags for RDS Cluster Instance (%s): %s", *db.DBClusterIdentifier, err) 313 } 314 } 315 316 return nil 317 } 318 319 func resourceAwsRDSClusterInstanceUpdate(d *schema.ResourceData, meta interface{}) error { 320 conn := meta.(*AWSClient).rdsconn 321 requestUpdate := false 322 323 req := &rds.ModifyDBInstanceInput{ 324 ApplyImmediately: aws.Bool(d.Get("apply_immediately").(bool)), 325 DBInstanceIdentifier: aws.String(d.Id()), 326 } 327 328 if d.HasChange("db_parameter_group_name") { 329 req.DBParameterGroupName = aws.String(d.Get("db_parameter_group_name").(string)) 330 requestUpdate = true 331 } 332 333 if d.HasChange("instance_class") { 334 req.DBInstanceClass = aws.String(d.Get("instance_class").(string)) 335 requestUpdate = true 336 } 337 338 if d.HasChange("monitoring_role_arn") { 339 d.SetPartial("monitoring_role_arn") 340 req.MonitoringRoleArn = aws.String(d.Get("monitoring_role_arn").(string)) 341 requestUpdate = true 342 } 343 344 if d.HasChange("preferred_backup_window") { 345 d.SetPartial("preferred_backup_window") 346 req.PreferredBackupWindow = aws.String(d.Get("preferred_backup_window").(string)) 347 requestUpdate = true 348 } 349 350 if d.HasChange("preferred_maintenance_window") { 351 d.SetPartial("preferred_maintenance_window") 352 req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintenance_window").(string)) 353 requestUpdate = true 354 } 355 356 if d.HasChange("monitoring_interval") { 357 d.SetPartial("monitoring_interval") 358 req.MonitoringInterval = aws.Int64(int64(d.Get("monitoring_interval").(int))) 359 requestUpdate = true 360 } 361 362 if d.HasChange("auto_minor_version_upgrade") { 363 d.SetPartial("auto_minor_version_upgrade") 364 req.AutoMinorVersionUpgrade = aws.Bool(d.Get("auto_minor_version_upgrade").(bool)) 365 requestUpdate = true 366 } 367 368 if d.HasChange("promotion_tier") { 369 d.SetPartial("promotion_tier") 370 req.PromotionTier = aws.Int64(int64(d.Get("promotion_tier").(int))) 371 requestUpdate = true 372 } 373 374 log.Printf("[DEBUG] Send DB Instance Modification request: %#v", requestUpdate) 375 if requestUpdate { 376 log.Printf("[DEBUG] DB Instance Modification request: %#v", req) 377 _, err := conn.ModifyDBInstance(req) 378 if err != nil { 379 return fmt.Errorf("Error modifying DB Instance %s: %s", d.Id(), err) 380 } 381 382 // reuse db_instance refresh func 383 stateConf := &resource.StateChangeConf{ 384 Pending: []string{"creating", "backing-up", "modifying"}, 385 Target: []string{"available"}, 386 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 387 Timeout: d.Timeout(schema.TimeoutUpdate), 388 MinTimeout: 10 * time.Second, 389 Delay: 30 * time.Second, // Wait 30 secs before starting 390 } 391 392 // Wait, catching any errors 393 _, err = stateConf.WaitForState() 394 if err != nil { 395 return err 396 } 397 398 } 399 400 if arn, err := buildRDSARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region); err == nil { 401 if err := setTagsRDS(conn, d, arn); err != nil { 402 return err 403 } 404 } 405 406 return resourceAwsRDSClusterInstanceRead(d, meta) 407 } 408 409 func resourceAwsRDSClusterInstanceDelete(d *schema.ResourceData, meta interface{}) error { 410 conn := meta.(*AWSClient).rdsconn 411 412 log.Printf("[DEBUG] RDS Cluster Instance destroy: %v", d.Id()) 413 414 opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())} 415 416 log.Printf("[DEBUG] RDS Cluster Instance destroy configuration: %s", opts) 417 if _, err := conn.DeleteDBInstance(&opts); err != nil { 418 return err 419 } 420 421 // re-uses db_instance refresh func 422 log.Println("[INFO] Waiting for RDS Cluster Instance to be destroyed") 423 stateConf := &resource.StateChangeConf{ 424 Pending: []string{"modifying", "deleting"}, 425 Target: []string{}, 426 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 427 Timeout: d.Timeout(schema.TimeoutDelete), 428 MinTimeout: 10 * time.Second, 429 Delay: 30 * time.Second, // Wait 30 secs before starting 430 } 431 432 if _, err := stateConf.WaitForState(); err != nil { 433 return err 434 } 435 436 return nil 437 438 }