github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_opsworks_instance.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/hashcode" 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 13 "github.com/aws/aws-sdk-go/aws" 14 "github.com/aws/aws-sdk-go/aws/awserr" 15 "github.com/aws/aws-sdk-go/service/opsworks" 16 ) 17 18 func resourceAwsOpsworksInstance() *schema.Resource { 19 return &schema.Resource{ 20 Create: resourceAwsOpsworksInstanceCreate, 21 Read: resourceAwsOpsworksInstanceRead, 22 Update: resourceAwsOpsworksInstanceUpdate, 23 Delete: resourceAwsOpsworksInstanceDelete, 24 Importer: &schema.ResourceImporter{ 25 State: resourceAwsOpsworksInstanceImport, 26 }, 27 28 Schema: map[string]*schema.Schema{ 29 "id": { 30 Type: schema.TypeString, 31 Computed: true, 32 }, 33 34 "agent_version": { 35 Type: schema.TypeString, 36 Optional: true, 37 Default: "INHERIT", 38 }, 39 40 "ami_id": { 41 Type: schema.TypeString, 42 Optional: true, 43 Computed: true, 44 ForceNew: true, 45 }, 46 47 "architecture": { 48 Type: schema.TypeString, 49 Optional: true, 50 Default: "x86_64", 51 ValidateFunc: validateArchitecture, 52 }, 53 54 "auto_scaling_type": { 55 Type: schema.TypeString, 56 Optional: true, 57 ValidateFunc: validateAutoScalingType, 58 }, 59 60 "availability_zone": { 61 Type: schema.TypeString, 62 Optional: true, 63 Computed: true, 64 ForceNew: true, 65 }, 66 67 "created_at": { 68 Type: schema.TypeString, 69 Optional: true, 70 Computed: true, 71 }, 72 73 "delete_ebs": { 74 Type: schema.TypeBool, 75 Optional: true, 76 Default: true, 77 }, 78 79 "delete_eip": { 80 Type: schema.TypeBool, 81 Optional: true, 82 Default: true, 83 }, 84 85 "ebs_optimized": { 86 Type: schema.TypeBool, 87 Optional: true, 88 Default: false, 89 ForceNew: true, 90 }, 91 92 "ec2_instance_id": { 93 Type: schema.TypeString, 94 Optional: true, 95 Computed: true, 96 }, 97 98 "ecs_cluster_arn": { 99 Type: schema.TypeString, 100 Optional: true, 101 Computed: true, 102 }, 103 104 "elastic_ip": { 105 Type: schema.TypeString, 106 Optional: true, 107 Computed: true, 108 }, 109 110 "hostname": { 111 Type: schema.TypeString, 112 Optional: true, 113 Computed: true, 114 ForceNew: true, 115 }, 116 117 "infrastructure_class": { 118 Type: schema.TypeString, 119 Optional: true, 120 Computed: true, 121 }, 122 123 "install_updates_on_boot": { 124 Type: schema.TypeBool, 125 Optional: true, 126 Default: true, 127 }, 128 129 "instance_profile_arn": { 130 Type: schema.TypeString, 131 Optional: true, 132 Computed: true, 133 }, 134 135 "instance_type": { 136 Type: schema.TypeString, 137 Optional: true, 138 }, 139 140 "last_service_error_id": { 141 Type: schema.TypeString, 142 Optional: true, 143 Computed: true, 144 }, 145 146 "layer_ids": { 147 Type: schema.TypeList, 148 Required: true, 149 Elem: &schema.Schema{Type: schema.TypeString}, 150 }, 151 152 "os": { 153 Type: schema.TypeString, 154 Optional: true, 155 Computed: true, 156 ForceNew: true, 157 }, 158 159 "platform": { 160 Type: schema.TypeString, 161 Optional: true, 162 Computed: true, 163 }, 164 165 "private_dns": { 166 Type: schema.TypeString, 167 Optional: true, 168 Computed: true, 169 }, 170 171 "private_ip": { 172 Type: schema.TypeString, 173 Optional: true, 174 Computed: true, 175 }, 176 177 "public_dns": { 178 Type: schema.TypeString, 179 Optional: true, 180 Computed: true, 181 }, 182 183 "public_ip": { 184 Type: schema.TypeString, 185 Optional: true, 186 Computed: true, 187 }, 188 189 "registered_by": { 190 Type: schema.TypeString, 191 Optional: true, 192 Computed: true, 193 }, 194 195 "reported_agent_version": { 196 Type: schema.TypeString, 197 Optional: true, 198 Computed: true, 199 }, 200 201 "reported_os_family": { 202 Type: schema.TypeString, 203 Optional: true, 204 Computed: true, 205 }, 206 207 "reported_os_name": { 208 Type: schema.TypeString, 209 Optional: true, 210 Computed: true, 211 }, 212 213 "reported_os_version": { 214 Type: schema.TypeString, 215 Optional: true, 216 Computed: true, 217 }, 218 219 "root_device_type": { 220 Type: schema.TypeString, 221 Optional: true, 222 ForceNew: true, 223 Computed: true, 224 ValidateFunc: validateRootDeviceType, 225 }, 226 227 "root_device_volume_id": { 228 Type: schema.TypeString, 229 Optional: true, 230 Computed: true, 231 }, 232 233 "security_group_ids": { 234 Type: schema.TypeList, 235 Optional: true, 236 Computed: true, 237 Elem: &schema.Schema{Type: schema.TypeString}, 238 }, 239 240 "ssh_host_dsa_key_fingerprint": { 241 Type: schema.TypeString, 242 Optional: true, 243 Computed: true, 244 }, 245 246 "ssh_host_rsa_key_fingerprint": { 247 Type: schema.TypeString, 248 Optional: true, 249 Computed: true, 250 }, 251 252 "ssh_key_name": { 253 Type: schema.TypeString, 254 Optional: true, 255 Computed: true, 256 }, 257 258 "stack_id": { 259 Type: schema.TypeString, 260 Required: true, 261 ForceNew: true, 262 }, 263 264 "state": { 265 Type: schema.TypeString, 266 Optional: true, 267 ValidateFunc: validateState, 268 }, 269 270 "status": { 271 Type: schema.TypeString, 272 Optional: true, 273 Computed: true, 274 }, 275 276 "subnet_id": { 277 Type: schema.TypeString, 278 Optional: true, 279 Computed: true, 280 ForceNew: true, 281 }, 282 283 "tenancy": { 284 Type: schema.TypeString, 285 Optional: true, 286 Computed: true, 287 ForceNew: true, 288 ValidateFunc: validateTenancy, 289 }, 290 291 "virtualization_type": { 292 Type: schema.TypeString, 293 Optional: true, 294 Computed: true, 295 ForceNew: true, 296 ValidateFunc: validateVirtualizationType, 297 }, 298 299 "ebs_block_device": { 300 Type: schema.TypeSet, 301 Optional: true, 302 Computed: true, 303 ForceNew: true, 304 Elem: &schema.Resource{ 305 Schema: map[string]*schema.Schema{ 306 "delete_on_termination": { 307 Type: schema.TypeBool, 308 Optional: true, 309 Default: true, 310 ForceNew: true, 311 }, 312 313 "device_name": { 314 Type: schema.TypeString, 315 Required: true, 316 ForceNew: true, 317 }, 318 319 "iops": { 320 Type: schema.TypeInt, 321 Optional: true, 322 Computed: true, 323 ForceNew: true, 324 }, 325 326 "snapshot_id": { 327 Type: schema.TypeString, 328 Optional: true, 329 Computed: true, 330 ForceNew: true, 331 }, 332 333 "volume_size": { 334 Type: schema.TypeInt, 335 Optional: true, 336 Computed: true, 337 ForceNew: true, 338 }, 339 340 "volume_type": { 341 Type: schema.TypeString, 342 Optional: true, 343 Computed: true, 344 ForceNew: true, 345 }, 346 }, 347 }, 348 Set: func(v interface{}) int { 349 var buf bytes.Buffer 350 m := v.(map[string]interface{}) 351 buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) 352 buf.WriteString(fmt.Sprintf("%s-", m["snapshot_id"].(string))) 353 return hashcode.String(buf.String()) 354 }, 355 }, 356 "ephemeral_block_device": { 357 Type: schema.TypeSet, 358 Optional: true, 359 Computed: true, 360 ForceNew: true, 361 Elem: &schema.Resource{ 362 Schema: map[string]*schema.Schema{ 363 "device_name": { 364 Type: schema.TypeString, 365 Required: true, 366 }, 367 368 "virtual_name": { 369 Type: schema.TypeString, 370 Required: true, 371 }, 372 }, 373 }, 374 Set: func(v interface{}) int { 375 var buf bytes.Buffer 376 m := v.(map[string]interface{}) 377 buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) 378 buf.WriteString(fmt.Sprintf("%s-", m["virtual_name"].(string))) 379 return hashcode.String(buf.String()) 380 }, 381 }, 382 383 "root_block_device": { 384 // TODO: This is a set because we don't support singleton 385 // sub-resources today. We'll enforce that the set only ever has 386 // length zero or one below. When TF gains support for 387 // sub-resources this can be converted. 388 Type: schema.TypeSet, 389 Optional: true, 390 Computed: true, 391 ForceNew: true, 392 Elem: &schema.Resource{ 393 // "You can only modify the volume size, volume type, and Delete on 394 // Termination flag on the block device mapping entry for the root 395 // device volume." - bit.ly/ec2bdmap 396 Schema: map[string]*schema.Schema{ 397 "delete_on_termination": { 398 Type: schema.TypeBool, 399 Optional: true, 400 Default: true, 401 ForceNew: true, 402 }, 403 404 "iops": { 405 Type: schema.TypeInt, 406 Optional: true, 407 Computed: true, 408 ForceNew: true, 409 }, 410 411 "volume_size": { 412 Type: schema.TypeInt, 413 Optional: true, 414 Computed: true, 415 ForceNew: true, 416 }, 417 418 "volume_type": { 419 Type: schema.TypeString, 420 Optional: true, 421 Computed: true, 422 ForceNew: true, 423 }, 424 }, 425 }, 426 Set: func(v interface{}) int { 427 // there can be only one root device; no need to hash anything 428 return 0 429 }, 430 }, 431 }, 432 } 433 } 434 435 func validateArchitecture(v interface{}, k string) (ws []string, errors []error) { 436 value := v.(string) 437 if value != "x86_64" && value != "i386" { 438 errors = append(errors, fmt.Errorf( 439 "%q must be one of \"x86_64\" or \"i386\"", k)) 440 } 441 return 442 } 443 444 func validateTenancy(v interface{}, k string) (ws []string, errors []error) { 445 value := v.(string) 446 if value != "dedicated" && value != "default" && value != "host" { 447 errors = append(errors, fmt.Errorf( 448 "%q must be one of \"dedicated\", \"default\" or \"host\"", k)) 449 } 450 return 451 } 452 453 func validateAutoScalingType(v interface{}, k string) (ws []string, errors []error) { 454 value := v.(string) 455 if value != "load" && value != "timer" { 456 errors = append(errors, fmt.Errorf( 457 "%q must be one of \"load\" or \"timer\"", k)) 458 } 459 return 460 } 461 462 func validateRootDeviceType(v interface{}, k string) (ws []string, errors []error) { 463 value := v.(string) 464 if value != "ebs" && value != "instance-store" { 465 errors = append(errors, fmt.Errorf( 466 "%q must be one of \"ebs\" or \"instance-store\"", k)) 467 } 468 return 469 } 470 471 func validateState(v interface{}, k string) (ws []string, errors []error) { 472 value := v.(string) 473 if value != "running" && value != "stopped" { 474 errors = append(errors, fmt.Errorf( 475 "%q must be one of \"running\" or \"stopped\"", k)) 476 } 477 return 478 } 479 480 func validateVirtualizationType(v interface{}, k string) (ws []string, errors []error) { 481 value := v.(string) 482 if value != "paravirtual" && value != "hvm" { 483 errors = append(errors, fmt.Errorf( 484 "%q must be one of \"paravirtual\" or \"hvm\"", k)) 485 } 486 return 487 } 488 489 func resourceAwsOpsworksInstanceValidate(d *schema.ResourceData) error { 490 if d.HasChange("ami_id") { 491 if v, ok := d.GetOk("os"); ok { 492 if v.(string) != "Custom" { 493 return fmt.Errorf("OS must be \"Custom\" when using using a custom ami_id") 494 } 495 } 496 497 if _, ok := d.GetOk("root_block_device"); ok { 498 return fmt.Errorf("Cannot specify root_block_device when using a custom ami_id.") 499 } 500 501 if _, ok := d.GetOk("ebs_block_device"); ok { 502 return fmt.Errorf("Cannot specify ebs_block_device when using a custom ami_id.") 503 } 504 505 if _, ok := d.GetOk("ephemeral_block_device"); ok { 506 return fmt.Errorf("Cannot specify ephemeral_block_device when using a custom ami_id.") 507 } 508 } 509 return nil 510 } 511 512 func resourceAwsOpsworksInstanceRead(d *schema.ResourceData, meta interface{}) error { 513 client := meta.(*AWSClient).opsworksconn 514 515 req := &opsworks.DescribeInstancesInput{ 516 InstanceIds: []*string{ 517 aws.String(d.Id()), 518 }, 519 } 520 521 log.Printf("[DEBUG] Reading OpsWorks instance: %s", d.Id()) 522 523 resp, err := client.DescribeInstances(req) 524 if err != nil { 525 if awserr, ok := err.(awserr.Error); ok { 526 if awserr.Code() == "ResourceNotFoundException" { 527 d.SetId("") 528 return nil 529 } 530 } 531 return err 532 } 533 534 // If nothing was found, then return no state 535 if len(resp.Instances) == 0 { 536 d.SetId("") 537 return nil 538 } 539 instance := resp.Instances[0] 540 541 if instance.InstanceId == nil { 542 d.SetId("") 543 return nil 544 } 545 instanceId := *instance.InstanceId 546 547 d.SetId(instanceId) 548 d.Set("agent_version", instance.AgentVersion) 549 d.Set("ami_id", instance.AmiId) 550 d.Set("architecture", instance.Architecture) 551 d.Set("auto_scaling_type", instance.AutoScalingType) 552 d.Set("availability_zone", instance.AvailabilityZone) 553 d.Set("created_at", instance.CreatedAt) 554 d.Set("ebs_optimized", instance.EbsOptimized) 555 d.Set("ec2_instance_id", instance.Ec2InstanceId) 556 d.Set("ecs_cluster_arn", instance.EcsClusterArn) 557 d.Set("elastic_ip", instance.ElasticIp) 558 d.Set("hostname", instance.Hostname) 559 d.Set("infrastructure_class", instance.InfrastructureClass) 560 d.Set("install_updates_on_boot", instance.InstallUpdatesOnBoot) 561 d.Set("id", instanceId) 562 d.Set("instance_profile_arn", instance.InstanceProfileArn) 563 d.Set("instance_type", instance.InstanceType) 564 d.Set("last_service_error_id", instance.LastServiceErrorId) 565 var layerIds []string 566 for _, v := range instance.LayerIds { 567 layerIds = append(layerIds, *v) 568 } 569 layerIds, err = sortListBasedonTFFile(layerIds, d, "layer_ids") 570 if err != nil { 571 return fmt.Errorf("[DEBUG] Error sorting layer_ids attribute: %#v", err) 572 } 573 if err := d.Set("layer_ids", layerIds); err != nil { 574 return fmt.Errorf("[DEBUG] Error setting layer_ids attribute: %#v, error: %#v", layerIds, err) 575 } 576 d.Set("os", instance.Os) 577 d.Set("platform", instance.Platform) 578 d.Set("private_dns", instance.PrivateDns) 579 d.Set("private_ip", instance.PrivateIp) 580 d.Set("public_dns", instance.PublicDns) 581 d.Set("public_ip", instance.PublicIp) 582 d.Set("registered_by", instance.RegisteredBy) 583 d.Set("reported_agent_version", instance.ReportedAgentVersion) 584 d.Set("reported_os_family", instance.ReportedOs.Family) 585 d.Set("reported_os_name", instance.ReportedOs.Name) 586 d.Set("reported_os_version", instance.ReportedOs.Version) 587 d.Set("root_device_type", instance.RootDeviceType) 588 d.Set("root_device_volume_id", instance.RootDeviceVolumeId) 589 d.Set("ssh_host_dsa_key_fingerprint", instance.SshHostDsaKeyFingerprint) 590 d.Set("ssh_host_rsa_key_fingerprint", instance.SshHostRsaKeyFingerprint) 591 d.Set("ssh_key_name", instance.SshKeyName) 592 d.Set("stack_id", instance.StackId) 593 d.Set("status", instance.Status) 594 d.Set("subnet_id", instance.SubnetId) 595 d.Set("tenancy", instance.Tenancy) 596 d.Set("virtualization_type", instance.VirtualizationType) 597 598 // Read BlockDeviceMapping 599 ibds, err := readOpsworksBlockDevices(d, instance, meta) 600 if err != nil { 601 return err 602 } 603 604 if err := d.Set("ebs_block_device", ibds["ebs"]); err != nil { 605 return err 606 } 607 if err := d.Set("ephemeral_block_device", ibds["ephemeral"]); err != nil { 608 return err 609 } 610 if ibds["root"] != nil { 611 if err := d.Set("root_block_device", []interface{}{ibds["root"]}); err != nil { 612 return err 613 } 614 } else { 615 d.Set("root_block_device", []interface{}{}) 616 } 617 618 // Read Security Groups 619 sgs := make([]string, 0, len(instance.SecurityGroupIds)) 620 for _, sg := range instance.SecurityGroupIds { 621 sgs = append(sgs, *sg) 622 } 623 if err := d.Set("security_group_ids", sgs); err != nil { 624 return err 625 } 626 627 return nil 628 } 629 630 func resourceAwsOpsworksInstanceCreate(d *schema.ResourceData, meta interface{}) error { 631 client := meta.(*AWSClient).opsworksconn 632 633 err := resourceAwsOpsworksInstanceValidate(d) 634 if err != nil { 635 return err 636 } 637 638 req := &opsworks.CreateInstanceInput{ 639 AgentVersion: aws.String(d.Get("agent_version").(string)), 640 Architecture: aws.String(d.Get("architecture").(string)), 641 EbsOptimized: aws.Bool(d.Get("ebs_optimized").(bool)), 642 InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)), 643 InstanceType: aws.String(d.Get("instance_type").(string)), 644 LayerIds: expandStringList(d.Get("layer_ids").([]interface{})), 645 StackId: aws.String(d.Get("stack_id").(string)), 646 } 647 648 if v, ok := d.GetOk("ami_id"); ok { 649 req.AmiId = aws.String(v.(string)) 650 req.Os = aws.String("Custom") 651 } 652 653 if v, ok := d.GetOk("auto_scaling_type"); ok { 654 req.AutoScalingType = aws.String(v.(string)) 655 } 656 657 if v, ok := d.GetOk("availability_zone"); ok { 658 req.AvailabilityZone = aws.String(v.(string)) 659 } 660 661 if v, ok := d.GetOk("hostname"); ok { 662 req.Hostname = aws.String(v.(string)) 663 } 664 665 if v, ok := d.GetOk("os"); ok { 666 req.Os = aws.String(v.(string)) 667 } 668 669 if v, ok := d.GetOk("root_device_type"); ok { 670 req.RootDeviceType = aws.String(v.(string)) 671 } 672 673 if v, ok := d.GetOk("ssh_key_name"); ok { 674 req.SshKeyName = aws.String(v.(string)) 675 } 676 677 if v, ok := d.GetOk("subnet_id"); ok { 678 req.SubnetId = aws.String(v.(string)) 679 } 680 681 if v, ok := d.GetOk("tenancy"); ok { 682 req.Tenancy = aws.String(v.(string)) 683 } 684 685 if v, ok := d.GetOk("virtualization_type"); ok { 686 req.VirtualizationType = aws.String(v.(string)) 687 } 688 689 var blockDevices []*opsworks.BlockDeviceMapping 690 691 if v, ok := d.GetOk("ebs_block_device"); ok { 692 vL := v.(*schema.Set).List() 693 for _, v := range vL { 694 bd := v.(map[string]interface{}) 695 ebs := &opsworks.EbsBlockDevice{ 696 DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)), 697 } 698 699 if v, ok := bd["snapshot_id"].(string); ok && v != "" { 700 ebs.SnapshotId = aws.String(v) 701 } 702 703 if v, ok := bd["volume_size"].(int); ok && v != 0 { 704 ebs.VolumeSize = aws.Int64(int64(v)) 705 } 706 707 if v, ok := bd["volume_type"].(string); ok && v != "" { 708 ebs.VolumeType = aws.String(v) 709 } 710 711 if v, ok := bd["iops"].(int); ok && v > 0 { 712 ebs.Iops = aws.Int64(int64(v)) 713 } 714 715 blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{ 716 DeviceName: aws.String(bd["device_name"].(string)), 717 Ebs: ebs, 718 }) 719 } 720 } 721 722 if v, ok := d.GetOk("ephemeral_block_device"); ok { 723 vL := v.(*schema.Set).List() 724 for _, v := range vL { 725 bd := v.(map[string]interface{}) 726 blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{ 727 DeviceName: aws.String(bd["device_name"].(string)), 728 VirtualName: aws.String(bd["virtual_name"].(string)), 729 }) 730 } 731 } 732 733 if v, ok := d.GetOk("root_block_device"); ok { 734 vL := v.(*schema.Set).List() 735 if len(vL) > 1 { 736 return fmt.Errorf("Cannot specify more than one root_block_device.") 737 } 738 for _, v := range vL { 739 bd := v.(map[string]interface{}) 740 ebs := &opsworks.EbsBlockDevice{ 741 DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)), 742 } 743 744 if v, ok := bd["volume_size"].(int); ok && v != 0 { 745 ebs.VolumeSize = aws.Int64(int64(v)) 746 } 747 748 if v, ok := bd["volume_type"].(string); ok && v != "" { 749 ebs.VolumeType = aws.String(v) 750 } 751 752 if v, ok := bd["iops"].(int); ok && v > 0 { 753 ebs.Iops = aws.Int64(int64(v)) 754 } 755 756 blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{ 757 DeviceName: aws.String("ROOT_DEVICE"), 758 Ebs: ebs, 759 }) 760 } 761 } 762 763 if len(blockDevices) > 0 { 764 req.BlockDeviceMappings = blockDevices 765 } 766 767 log.Printf("[DEBUG] Creating OpsWorks instance") 768 769 var resp *opsworks.CreateInstanceOutput 770 771 resp, err = client.CreateInstance(req) 772 if err != nil { 773 return err 774 } 775 776 if resp.InstanceId == nil { 777 return fmt.Errorf("Error launching instance: no instance returned in response") 778 } 779 780 instanceId := *resp.InstanceId 781 d.SetId(instanceId) 782 d.Set("id", instanceId) 783 784 if v, ok := d.GetOk("state"); ok && v.(string) == "running" { 785 err := startOpsworksInstance(d, meta, true) 786 if err != nil { 787 return err 788 } 789 } 790 791 return resourceAwsOpsworksInstanceRead(d, meta) 792 } 793 794 func resourceAwsOpsworksInstanceUpdate(d *schema.ResourceData, meta interface{}) error { 795 client := meta.(*AWSClient).opsworksconn 796 797 err := resourceAwsOpsworksInstanceValidate(d) 798 if err != nil { 799 return err 800 } 801 802 req := &opsworks.UpdateInstanceInput{ 803 AgentVersion: aws.String(d.Get("agent_version").(string)), 804 Architecture: aws.String(d.Get("architecture").(string)), 805 InstanceId: aws.String(d.Get("id").(string)), 806 InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)), 807 } 808 809 if v, ok := d.GetOk("ami_id"); ok { 810 req.AmiId = aws.String(v.(string)) 811 req.Os = aws.String("Custom") 812 } 813 814 if v, ok := d.GetOk("auto_scaling_type"); ok { 815 req.AutoScalingType = aws.String(v.(string)) 816 } 817 818 if v, ok := d.GetOk("hostname"); ok { 819 req.Hostname = aws.String(v.(string)) 820 } 821 822 if v, ok := d.GetOk("instance_type"); ok { 823 req.InstanceType = aws.String(v.(string)) 824 } 825 826 if v, ok := d.GetOk("layer_ids"); ok { 827 req.LayerIds = expandStringList(v.([]interface{})) 828 } 829 830 if v, ok := d.GetOk("os"); ok { 831 req.Os = aws.String(v.(string)) 832 } 833 834 if v, ok := d.GetOk("ssh_key_name"); ok { 835 req.SshKeyName = aws.String(v.(string)) 836 } 837 838 log.Printf("[DEBUG] Updating OpsWorks instance: %s", d.Id()) 839 840 _, err = client.UpdateInstance(req) 841 if err != nil { 842 return err 843 } 844 845 var status string 846 847 if v, ok := d.GetOk("status"); ok { 848 status = v.(string) 849 } else { 850 status = "stopped" 851 } 852 853 if v, ok := d.GetOk("state"); ok { 854 state := v.(string) 855 if state == "running" { 856 if status == "stopped" || status == "stopping" || status == "shutting_down" { 857 err := startOpsworksInstance(d, meta, false) 858 if err != nil { 859 return err 860 } 861 } 862 } else { 863 if status != "stopped" && status != "stopping" && status != "shutting_down" { 864 err := stopOpsworksInstance(d, meta, true) 865 if err != nil { 866 return err 867 } 868 } 869 } 870 } 871 872 return resourceAwsOpsworksInstanceRead(d, meta) 873 } 874 875 func resourceAwsOpsworksInstanceDelete(d *schema.ResourceData, meta interface{}) error { 876 client := meta.(*AWSClient).opsworksconn 877 878 if v, ok := d.GetOk("status"); ok && v.(string) != "stopped" { 879 err := stopOpsworksInstance(d, meta, true) 880 if err != nil { 881 return err 882 } 883 } 884 885 req := &opsworks.DeleteInstanceInput{ 886 InstanceId: aws.String(d.Id()), 887 DeleteElasticIp: aws.Bool(d.Get("delete_eip").(bool)), 888 DeleteVolumes: aws.Bool(d.Get("delete_ebs").(bool)), 889 } 890 891 log.Printf("[DEBUG] Deleting OpsWorks instance: %s", d.Id()) 892 893 _, err := client.DeleteInstance(req) 894 if err != nil { 895 return err 896 } 897 898 d.SetId("") 899 return nil 900 } 901 902 func resourceAwsOpsworksInstanceImport( 903 d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { 904 // Neither delete_eip nor delete_ebs can be fetched 905 // from any API call, so we need to default to the values 906 // we set in the schema by default 907 d.Set("delete_ebs", true) 908 d.Set("delete_eip", true) 909 return []*schema.ResourceData{d}, nil 910 } 911 912 func startOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error { 913 client := meta.(*AWSClient).opsworksconn 914 915 instanceId := d.Get("id").(string) 916 917 req := &opsworks.StartInstanceInput{ 918 InstanceId: aws.String(instanceId), 919 } 920 921 log.Printf("[DEBUG] Starting OpsWorks instance: %s", instanceId) 922 923 _, err := client.StartInstance(req) 924 925 if err != nil { 926 return err 927 } 928 929 if wait { 930 log.Printf("[DEBUG] Waiting for instance (%s) to become running", instanceId) 931 932 stateConf := &resource.StateChangeConf{ 933 Pending: []string{"requested", "pending", "booting", "running_setup"}, 934 Target: []string{"online"}, 935 Refresh: OpsworksInstanceStateRefreshFunc(client, instanceId), 936 Timeout: 10 * time.Minute, 937 Delay: 10 * time.Second, 938 MinTimeout: 3 * time.Second, 939 } 940 _, err = stateConf.WaitForState() 941 if err != nil { 942 return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s", 943 instanceId, err) 944 } 945 } 946 947 return nil 948 } 949 950 func stopOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error { 951 client := meta.(*AWSClient).opsworksconn 952 953 instanceId := d.Get("id").(string) 954 955 req := &opsworks.StopInstanceInput{ 956 InstanceId: aws.String(instanceId), 957 } 958 959 log.Printf("[DEBUG] Stopping OpsWorks instance: %s", instanceId) 960 961 _, err := client.StopInstance(req) 962 963 if err != nil { 964 return err 965 } 966 967 if wait { 968 log.Printf("[DEBUG] Waiting for instance (%s) to become stopped", instanceId) 969 970 stateConf := &resource.StateChangeConf{ 971 Pending: []string{"stopping", "terminating", "shutting_down", "terminated"}, 972 Target: []string{"stopped"}, 973 Refresh: OpsworksInstanceStateRefreshFunc(client, instanceId), 974 Timeout: 10 * time.Minute, 975 Delay: 10 * time.Second, 976 MinTimeout: 3 * time.Second, 977 } 978 _, err = stateConf.WaitForState() 979 if err != nil { 980 return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s", 981 instanceId, err) 982 } 983 } 984 985 return nil 986 } 987 988 func readOpsworksBlockDevices(d *schema.ResourceData, instance *opsworks.Instance, meta interface{}) ( 989 map[string]interface{}, error) { 990 991 blockDevices := make(map[string]interface{}) 992 blockDevices["ebs"] = make([]map[string]interface{}, 0) 993 blockDevices["ephemeral"] = make([]map[string]interface{}, 0) 994 blockDevices["root"] = nil 995 996 if len(instance.BlockDeviceMappings) == 0 { 997 return nil, nil 998 } 999 1000 for _, bdm := range instance.BlockDeviceMappings { 1001 bd := make(map[string]interface{}) 1002 if bdm.Ebs != nil && bdm.Ebs.DeleteOnTermination != nil { 1003 bd["delete_on_termination"] = *bdm.Ebs.DeleteOnTermination 1004 } 1005 if bdm.Ebs != nil && bdm.Ebs.VolumeSize != nil { 1006 bd["volume_size"] = *bdm.Ebs.VolumeSize 1007 } 1008 if bdm.Ebs != nil && bdm.Ebs.VolumeType != nil { 1009 bd["volume_type"] = *bdm.Ebs.VolumeType 1010 } 1011 if bdm.Ebs != nil && bdm.Ebs.Iops != nil { 1012 bd["iops"] = *bdm.Ebs.Iops 1013 } 1014 if bdm.DeviceName != nil && *bdm.DeviceName == "ROOT_DEVICE" { 1015 blockDevices["root"] = bd 1016 } else { 1017 if bdm.DeviceName != nil { 1018 bd["device_name"] = *bdm.DeviceName 1019 } 1020 if bdm.VirtualName != nil { 1021 bd["virtual_name"] = *bdm.VirtualName 1022 blockDevices["ephemeral"] = append(blockDevices["ephemeral"].([]map[string]interface{}), bd) 1023 } else { 1024 if bdm.Ebs != nil && bdm.Ebs.SnapshotId != nil { 1025 bd["snapshot_id"] = *bdm.Ebs.SnapshotId 1026 } 1027 blockDevices["ebs"] = append(blockDevices["ebs"].([]map[string]interface{}), bd) 1028 } 1029 } 1030 } 1031 return blockDevices, nil 1032 } 1033 1034 func OpsworksInstanceStateRefreshFunc(conn *opsworks.OpsWorks, instanceID string) resource.StateRefreshFunc { 1035 return func() (interface{}, string, error) { 1036 resp, err := conn.DescribeInstances(&opsworks.DescribeInstancesInput{ 1037 InstanceIds: []*string{aws.String(instanceID)}, 1038 }) 1039 if err != nil { 1040 if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" { 1041 // Set this to nil as if we didn't find anything. 1042 resp = nil 1043 } else { 1044 log.Printf("Error on OpsworksInstanceStateRefresh: %s", err) 1045 return nil, "", err 1046 } 1047 } 1048 1049 if resp == nil || len(resp.Instances) == 0 { 1050 // Sometimes AWS just has consistency issues and doesn't see 1051 // our instance yet. Return an empty state. 1052 return nil, "", nil 1053 } 1054 1055 i := resp.Instances[0] 1056 return i, *i.Status, nil 1057 } 1058 }