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