2017-02-02 11:30:05 +01:00
|
|
|
package aws
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
2017-04-05 07:48:37 +02:00
|
|
|
"time"
|
2017-02-02 11:30:05 +01:00
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
|
|
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
|
2017-04-05 07:48:37 +02:00
|
|
|
"github.com/hashicorp/terraform/helper/resource"
|
2017-02-02 11:30:05 +01:00
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
|
|
)
|
|
|
|
|
|
|
|
func resourceAwsDmsReplicationInstance() *schema.Resource {
|
|
|
|
return &schema.Resource{
|
|
|
|
Create: resourceAwsDmsReplicationInstanceCreate,
|
|
|
|
Read: resourceAwsDmsReplicationInstanceRead,
|
|
|
|
Update: resourceAwsDmsReplicationInstanceUpdate,
|
|
|
|
Delete: resourceAwsDmsReplicationInstanceDelete,
|
|
|
|
|
2017-05-23 20:31:26 +02:00
|
|
|
Timeouts: &schema.ResourceTimeout{
|
|
|
|
Create: schema.DefaultTimeout(30 * time.Minute),
|
|
|
|
Update: schema.DefaultTimeout(30 * time.Minute),
|
|
|
|
Delete: schema.DefaultTimeout(30 * time.Minute),
|
|
|
|
},
|
|
|
|
|
2017-02-02 11:30:05 +01:00
|
|
|
Importer: &schema.ResourceImporter{
|
|
|
|
State: schema.ImportStatePassthrough,
|
|
|
|
},
|
|
|
|
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"allocated_storage": {
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
ValidateFunc: validateIntegerInRange(5, 6144),
|
|
|
|
},
|
|
|
|
"apply_immediately": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"auto_minor_version_upgrade": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"availability_zone": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
"engine_version": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"kms_key_arn": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
ValidateFunc: validateArn,
|
|
|
|
},
|
|
|
|
"multi_az": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"preferred_maintenance_window": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
ValidateFunc: validateOnceAWeekWindowFormat,
|
|
|
|
},
|
|
|
|
"publicly_accessible": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
"replication_instance_arn": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"replication_instance_class": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
// Valid Values: dms.t2.micro | dms.t2.small | dms.t2.medium | dms.t2.large | dms.c4.large |
|
|
|
|
// dms.c4.xlarge | dms.c4.2xlarge | dms.c4.4xlarge
|
|
|
|
},
|
|
|
|
"replication_instance_id": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
ValidateFunc: validateDmsReplicationInstanceId,
|
|
|
|
},
|
|
|
|
"replication_instance_private_ips": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"replication_instance_public_ips": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"replication_subnet_group_id": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
"tags": {
|
|
|
|
Type: schema.TypeMap,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"vpc_security_group_ids": {
|
|
|
|
Type: schema.TypeSet,
|
|
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
|
|
Set: schema.HashString,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceAwsDmsReplicationInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*AWSClient).dmsconn
|
|
|
|
|
|
|
|
request := &dms.CreateReplicationInstanceInput{
|
|
|
|
AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)),
|
|
|
|
PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)),
|
|
|
|
ReplicationInstanceClass: aws.String(d.Get("replication_instance_class").(string)),
|
|
|
|
ReplicationInstanceIdentifier: aws.String(d.Get("replication_instance_id").(string)),
|
|
|
|
Tags: dmsTagsFromMap(d.Get("tags").(map[string]interface{})),
|
|
|
|
}
|
|
|
|
|
|
|
|
// WARNING: GetOk returns the zero value for the type if the key is omitted in config. This means for optional
|
|
|
|
// keys that the zero value is valid we cannot know if the zero value was in the config and cannot allow the API
|
|
|
|
// to set the default value. See GitHub Issue #5694 https://github.com/hashicorp/terraform/issues/5694
|
|
|
|
|
|
|
|
if v, ok := d.GetOk("allocated_storage"); ok {
|
|
|
|
request.AllocatedStorage = aws.Int64(int64(v.(int)))
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("engine_version"); ok {
|
|
|
|
request.EngineVersion = aws.String(v.(string))
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("kms_key_arn"); ok {
|
|
|
|
request.KmsKeyId = aws.String(v.(string))
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("preferred_maintenance_window"); ok {
|
|
|
|
request.PreferredMaintenanceWindow = aws.String(v.(string))
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("replication_subnet_group_id"); ok {
|
|
|
|
request.ReplicationSubnetGroupIdentifier = aws.String(v.(string))
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("vpc_security_group_ids"); ok {
|
|
|
|
request.VpcSecurityGroupIds = expandStringList(v.(*schema.Set).List())
|
|
|
|
}
|
|
|
|
|
|
|
|
az, azSet := d.GetOk("availability_zone")
|
|
|
|
if azSet {
|
|
|
|
request.AvailabilityZone = aws.String(az.(string))
|
|
|
|
}
|
|
|
|
|
|
|
|
if multiAz, ok := d.GetOk("multi_az"); ok {
|
|
|
|
request.MultiAZ = aws.Bool(multiAz.(bool))
|
|
|
|
|
|
|
|
if multiAz.(bool) && azSet {
|
|
|
|
return fmt.Errorf("Cannot set availability_zone if multi_az is set to true")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Println("[DEBUG] DMS create replication instance:", request)
|
|
|
|
|
|
|
|
_, err := conn.CreateReplicationInstance(request)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-04-05 07:48:37 +02:00
|
|
|
d.SetId(d.Get("replication_instance_id").(string))
|
|
|
|
|
|
|
|
stateConf := &resource.StateChangeConf{
|
|
|
|
Pending: []string{"creating"},
|
|
|
|
Target: []string{"available"},
|
|
|
|
Refresh: resourceAwsDmsReplicationInstanceStateRefreshFunc(d, meta),
|
|
|
|
Timeout: d.Timeout(schema.TimeoutCreate),
|
|
|
|
MinTimeout: 10 * time.Second,
|
|
|
|
Delay: 30 * time.Second, // Wait 30 secs before starting
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait, catching any errors
|
|
|
|
_, err = stateConf.WaitForState()
|
2017-02-02 11:30:05 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return resourceAwsDmsReplicationInstanceRead(d, meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceAwsDmsReplicationInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*AWSClient).dmsconn
|
|
|
|
|
|
|
|
response, err := conn.DescribeReplicationInstances(&dms.DescribeReplicationInstancesInput{
|
|
|
|
Filters: []*dms.Filter{
|
|
|
|
{
|
|
|
|
Name: aws.String("replication-instance-id"),
|
|
|
|
Values: []*string{aws.String(d.Id())}, // Must use d.Id() to work with import.
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
|
2017-04-05 07:48:37 +02:00
|
|
|
log.Printf("[DEBUG] DMS Replication Instance %q Not Found", d.Id())
|
2017-02-02 11:30:05 +01:00
|
|
|
d.SetId("")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = resourceAwsDmsReplicationInstanceSetState(d, response.ReplicationInstances[0])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
tagsResp, err := conn.ListTagsForResource(&dms.ListTagsForResourceInput{
|
|
|
|
ResourceArn: aws.String(d.Get("replication_instance_arn").(string)),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
d.Set("tags", dmsTagsToMap(tagsResp.TagList))
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceAwsDmsReplicationInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
request := &dms.ModifyReplicationInstanceInput{
|
|
|
|
ApplyImmediately: aws.Bool(d.Get("apply_immediately").(bool)),
|
|
|
|
ReplicationInstanceArn: aws.String(d.Get("replication_instance_arn").(string)),
|
|
|
|
}
|
|
|
|
hasChanges := false
|
|
|
|
|
|
|
|
if d.HasChange("auto_minor_version_upgrade") {
|
|
|
|
request.AutoMinorVersionUpgrade = aws.Bool(d.Get("auto_minor_version_upgrade").(bool))
|
|
|
|
hasChanges = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("allocated_storage") {
|
|
|
|
if v, ok := d.GetOk("allocated_storage"); ok {
|
|
|
|
request.AllocatedStorage = aws.Int64(int64(v.(int)))
|
|
|
|
hasChanges = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("engine_version") {
|
|
|
|
if v, ok := d.GetOk("engine_version"); ok {
|
|
|
|
request.ReplicationInstanceClass = aws.String(v.(string))
|
|
|
|
hasChanges = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("multi_az") {
|
|
|
|
if v, ok := d.GetOk("multi_az"); ok {
|
|
|
|
request.MultiAZ = aws.Bool(v.(bool))
|
|
|
|
hasChanges = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("preferred_maintenance_window") {
|
|
|
|
if v, ok := d.GetOk("preferred_maintenance_window"); ok {
|
|
|
|
request.PreferredMaintenanceWindow = aws.String(v.(string))
|
|
|
|
hasChanges = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("replication_instance_class") {
|
|
|
|
if v, ok := d.GetOk("replication_instance_class"); ok {
|
|
|
|
request.ReplicationInstanceClass = aws.String(v.(string))
|
|
|
|
hasChanges = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("vpc_security_group_ids") {
|
|
|
|
if v, ok := d.GetOk("vpc_security_group_ids"); ok {
|
|
|
|
request.VpcSecurityGroupIds = expandStringList(v.(*schema.Set).List())
|
|
|
|
hasChanges = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("tags") {
|
|
|
|
err := dmsSetTags(d.Get("replication_instance_arn").(string), d, meta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if hasChanges {
|
|
|
|
conn := meta.(*AWSClient).dmsconn
|
|
|
|
|
|
|
|
_, err := conn.ModifyReplicationInstance(request)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-04-05 07:48:37 +02:00
|
|
|
stateConf := &resource.StateChangeConf{
|
|
|
|
Pending: []string{"modifying"},
|
|
|
|
Target: []string{"available"},
|
|
|
|
Refresh: resourceAwsDmsReplicationInstanceStateRefreshFunc(d, meta),
|
2017-05-23 20:31:26 +02:00
|
|
|
Timeout: d.Timeout(schema.TimeoutUpdate),
|
2017-04-05 07:48:37 +02:00
|
|
|
MinTimeout: 10 * time.Second,
|
|
|
|
Delay: 30 * time.Second, // Wait 30 secs before starting
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait, catching any errors
|
|
|
|
_, err = stateConf.WaitForState()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-02 11:30:05 +01:00
|
|
|
return resourceAwsDmsReplicationInstanceRead(d, meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceAwsDmsReplicationInstanceDelete(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*AWSClient).dmsconn
|
|
|
|
|
|
|
|
request := &dms.DeleteReplicationInstanceInput{
|
|
|
|
ReplicationInstanceArn: aws.String(d.Get("replication_instance_arn").(string)),
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] DMS delete replication instance: %#v", request)
|
|
|
|
|
|
|
|
_, err := conn.DeleteReplicationInstance(request)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-04-05 07:48:37 +02:00
|
|
|
stateConf := &resource.StateChangeConf{
|
|
|
|
Pending: []string{"deleting"},
|
|
|
|
Target: []string{},
|
|
|
|
Refresh: resourceAwsDmsReplicationInstanceStateRefreshFunc(d, meta),
|
2017-05-23 20:31:26 +02:00
|
|
|
Timeout: d.Timeout(schema.TimeoutDelete),
|
2017-04-05 07:48:37 +02:00
|
|
|
MinTimeout: 10 * time.Second,
|
|
|
|
Delay: 30 * time.Second, // Wait 30 secs before starting
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait, catching any errors
|
|
|
|
_, err = stateConf.WaitForState()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-02-02 11:30:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceAwsDmsReplicationInstanceSetState(d *schema.ResourceData, instance *dms.ReplicationInstance) error {
|
|
|
|
d.SetId(*instance.ReplicationInstanceIdentifier)
|
|
|
|
|
|
|
|
d.Set("replication_instance_id", instance.ReplicationInstanceIdentifier)
|
|
|
|
d.Set("allocated_storage", instance.AllocatedStorage)
|
|
|
|
d.Set("auto_minor_version_upgrade", instance.AutoMinorVersionUpgrade)
|
|
|
|
d.Set("availability_zone", instance.AvailabilityZone)
|
|
|
|
d.Set("engine_version", instance.EngineVersion)
|
|
|
|
d.Set("kms_key_arn", instance.KmsKeyId)
|
|
|
|
d.Set("multi_az", instance.MultiAZ)
|
|
|
|
d.Set("preferred_maintenance_window", instance.PreferredMaintenanceWindow)
|
|
|
|
d.Set("publicly_accessible", instance.PubliclyAccessible)
|
|
|
|
d.Set("replication_instance_arn", instance.ReplicationInstanceArn)
|
|
|
|
d.Set("replication_instance_class", instance.ReplicationInstanceClass)
|
|
|
|
d.Set("replication_subnet_group_id", instance.ReplicationSubnetGroup.ReplicationSubnetGroupIdentifier)
|
|
|
|
|
|
|
|
vpc_security_group_ids := []string{}
|
|
|
|
for _, sg := range instance.VpcSecurityGroups {
|
|
|
|
vpc_security_group_ids = append(vpc_security_group_ids, aws.StringValue(sg.VpcSecurityGroupId))
|
|
|
|
}
|
|
|
|
|
|
|
|
d.Set("vpc_security_group_ids", vpc_security_group_ids)
|
|
|
|
|
|
|
|
private_ip_addresses := []string{}
|
|
|
|
for _, ip := range instance.ReplicationInstancePrivateIpAddresses {
|
|
|
|
private_ip_addresses = append(private_ip_addresses, aws.StringValue(ip))
|
|
|
|
}
|
|
|
|
|
|
|
|
d.Set("replication_instance_private_ips", private_ip_addresses)
|
|
|
|
|
|
|
|
public_ip_addresses := []string{}
|
|
|
|
for _, ip := range instance.ReplicationInstancePublicIpAddresses {
|
|
|
|
public_ip_addresses = append(public_ip_addresses, aws.StringValue(ip))
|
|
|
|
}
|
|
|
|
|
|
|
|
d.Set("replication_instance_public_ips", public_ip_addresses)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-04-05 07:48:37 +02:00
|
|
|
func resourceAwsDmsReplicationInstanceStateRefreshFunc(
|
|
|
|
d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
|
|
|
|
return func() (interface{}, string, error) {
|
|
|
|
conn := meta.(*AWSClient).dmsconn
|
2017-02-02 11:30:05 +01:00
|
|
|
|
2017-04-05 07:48:37 +02:00
|
|
|
v, err := conn.DescribeReplicationInstances(&dms.DescribeReplicationInstancesInput{
|
|
|
|
Filters: []*dms.Filter{
|
|
|
|
{
|
|
|
|
Name: aws.String("replication-instance-id"),
|
|
|
|
Values: []*string{aws.String(d.Id())}, // Must use d.Id() to work with import.
|
|
|
|
},
|
2017-02-02 11:30:05 +01:00
|
|
|
},
|
2017-04-05 07:48:37 +02:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
|
|
|
|
return nil, "", nil
|
|
|
|
}
|
|
|
|
log.Printf("Error on retrieving DMS Replication Instance when waiting: %s", err)
|
|
|
|
return nil, "", err
|
|
|
|
}
|
2017-02-02 11:30:05 +01:00
|
|
|
|
2017-04-05 07:48:37 +02:00
|
|
|
if v == nil {
|
|
|
|
return nil, "", nil
|
|
|
|
}
|
2017-02-02 11:30:05 +01:00
|
|
|
|
2017-04-05 07:48:37 +02:00
|
|
|
if v.ReplicationInstances == nil {
|
|
|
|
return nil, "", fmt.Errorf("Error on retrieving DMS Replication Instance when waiting for State")
|
|
|
|
}
|
2017-02-02 11:30:05 +01:00
|
|
|
|
2017-04-05 07:48:37 +02:00
|
|
|
return v, *v.ReplicationInstances[0].ReplicationInstanceStatus, nil
|
2017-02-02 11:30:05 +01:00
|
|
|
}
|
|
|
|
}
|