github.com/cbroglie/terraform@v0.7.0-rc3.0.20170410193827-735dfc416d46/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 layerIds, err = sortListBasedonTFFile(layerIds, d, "layer_ids") 569 if err != nil { 570 return fmt.Errorf("[DEBUG] Error sorting layer_ids attribute: %#v", err) 571 } 572 if err := d.Set("layer_ids", layerIds); err != nil { 573 return fmt.Errorf("[DEBUG] Error setting layer_ids attribute: %#v, error: %#v", layerIds, err) 574 } 575 d.Set("os", instance.Os) 576 d.Set("platform", instance.Platform) 577 d.Set("private_dns", instance.PrivateDns) 578 d.Set("private_ip", instance.PrivateIp) 579 d.Set("public_dns", instance.PublicDns) 580 d.Set("public_ip", instance.PublicIp) 581 d.Set("registered_by", instance.RegisteredBy) 582 d.Set("reported_agent_version", instance.ReportedAgentVersion) 583 d.Set("reported_os_family", instance.ReportedOs.Family) 584 d.Set("reported_os_name", instance.ReportedOs.Name) 585 d.Set("reported_os_version", instance.ReportedOs.Version) 586 d.Set("root_device_type", instance.RootDeviceType) 587 d.Set("root_device_volume_id", instance.RootDeviceVolumeId) 588 d.Set("ssh_host_dsa_key_fingerprint", instance.SshHostDsaKeyFingerprint) 589 d.Set("ssh_host_rsa_key_fingerprint", instance.SshHostRsaKeyFingerprint) 590 d.Set("ssh_key_name", instance.SshKeyName) 591 d.Set("stack_id", instance.StackId) 592 d.Set("status", instance.Status) 593 d.Set("subnet_id", instance.SubnetId) 594 d.Set("tenancy", instance.Tenancy) 595 d.Set("virtualization_type", instance.VirtualizationType) 596 597 // Read BlockDeviceMapping 598 ibds, err := readOpsworksBlockDevices(d, instance, meta) 599 if err != nil { 600 return err 601 } 602 603 if err := d.Set("ebs_block_device", ibds["ebs"]); err != nil { 604 return err 605 } 606 if err := d.Set("ephemeral_block_device", ibds["ephemeral"]); err != nil { 607 return err 608 } 609 if ibds["root"] != nil { 610 if err := d.Set("root_block_device", []interface{}{ibds["root"]}); err != nil { 611 return err 612 } 613 } else { 614 d.Set("root_block_device", []interface{}{}) 615 } 616 617 // Read Security Groups 618 sgs := make([]string, 0, len(instance.SecurityGroupIds)) 619 for _, sg := range instance.SecurityGroupIds { 620 sgs = append(sgs, *sg) 621 } 622 if err := d.Set("security_group_ids", sgs); err != nil { 623 return err 624 } 625 626 return nil 627 } 628 629 func resourceAwsOpsworksInstanceCreate(d *schema.ResourceData, meta interface{}) error { 630 client := meta.(*AWSClient).opsworksconn 631 632 err := resourceAwsOpsworksInstanceValidate(d) 633 if err != nil { 634 return err 635 } 636 637 req := &opsworks.CreateInstanceInput{ 638 AgentVersion: aws.String(d.Get("agent_version").(string)), 639 Architecture: aws.String(d.Get("architecture").(string)), 640 EbsOptimized: aws.Bool(d.Get("ebs_optimized").(bool)), 641 InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)), 642 InstanceType: aws.String(d.Get("instance_type").(string)), 643 LayerIds: expandStringList(d.Get("layer_ids").([]interface{})), 644 StackId: aws.String(d.Get("stack_id").(string)), 645 } 646 647 if v, ok := d.GetOk("ami_id"); ok { 648 req.AmiId = aws.String(v.(string)) 649 req.Os = aws.String("Custom") 650 } 651 652 if v, ok := d.GetOk("auto_scaling_type"); ok { 653 req.AutoScalingType = aws.String(v.(string)) 654 } 655 656 if v, ok := d.GetOk("availability_zone"); ok { 657 req.AvailabilityZone = aws.String(v.(string)) 658 } 659 660 if v, ok := d.GetOk("hostname"); ok { 661 req.Hostname = aws.String(v.(string)) 662 } 663 664 if v, ok := d.GetOk("os"); ok { 665 req.Os = aws.String(v.(string)) 666 } 667 668 if v, ok := d.GetOk("root_device_type"); ok { 669 req.RootDeviceType = aws.String(v.(string)) 670 } 671 672 if v, ok := d.GetOk("ssh_key_name"); ok { 673 req.SshKeyName = aws.String(v.(string)) 674 } 675 676 if v, ok := d.GetOk("subnet_id"); ok { 677 req.SubnetId = aws.String(v.(string)) 678 } 679 680 if v, ok := d.GetOk("tenancy"); ok { 681 req.Tenancy = aws.String(v.(string)) 682 } 683 684 if v, ok := d.GetOk("virtualization_type"); ok { 685 req.VirtualizationType = aws.String(v.(string)) 686 } 687 688 var blockDevices []*opsworks.BlockDeviceMapping 689 690 if v, ok := d.GetOk("ebs_block_device"); ok { 691 vL := v.(*schema.Set).List() 692 for _, v := range vL { 693 bd := v.(map[string]interface{}) 694 ebs := &opsworks.EbsBlockDevice{ 695 DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)), 696 } 697 698 if v, ok := bd["snapshot_id"].(string); ok && v != "" { 699 ebs.SnapshotId = aws.String(v) 700 } 701 702 if v, ok := bd["volume_size"].(int); ok && v != 0 { 703 ebs.VolumeSize = aws.Int64(int64(v)) 704 } 705 706 if v, ok := bd["volume_type"].(string); ok && v != "" { 707 ebs.VolumeType = aws.String(v) 708 } 709 710 if v, ok := bd["iops"].(int); ok && v > 0 { 711 ebs.Iops = aws.Int64(int64(v)) 712 } 713 714 blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{ 715 DeviceName: aws.String(bd["device_name"].(string)), 716 Ebs: ebs, 717 }) 718 } 719 } 720 721 if v, ok := d.GetOk("ephemeral_block_device"); ok { 722 vL := v.(*schema.Set).List() 723 for _, v := range vL { 724 bd := v.(map[string]interface{}) 725 blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{ 726 DeviceName: aws.String(bd["device_name"].(string)), 727 VirtualName: aws.String(bd["virtual_name"].(string)), 728 }) 729 } 730 } 731 732 if v, ok := d.GetOk("root_block_device"); ok { 733 vL := v.(*schema.Set).List() 734 if len(vL) > 1 { 735 return fmt.Errorf("Cannot specify more than one root_block_device.") 736 } 737 for _, v := range vL { 738 bd := v.(map[string]interface{}) 739 ebs := &opsworks.EbsBlockDevice{ 740 DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)), 741 } 742 743 if v, ok := bd["volume_size"].(int); ok && v != 0 { 744 ebs.VolumeSize = aws.Int64(int64(v)) 745 } 746 747 if v, ok := bd["volume_type"].(string); ok && v != "" { 748 ebs.VolumeType = aws.String(v) 749 } 750 751 if v, ok := bd["iops"].(int); ok && v > 0 { 752 ebs.Iops = aws.Int64(int64(v)) 753 } 754 755 blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{ 756 DeviceName: aws.String("ROOT_DEVICE"), 757 Ebs: ebs, 758 }) 759 } 760 } 761 762 if len(blockDevices) > 0 { 763 req.BlockDeviceMappings = blockDevices 764 } 765 766 log.Printf("[DEBUG] Creating OpsWorks instance") 767 768 var resp *opsworks.CreateInstanceOutput 769 770 resp, err = client.CreateInstance(req) 771 if err != nil { 772 return err 773 } 774 775 if resp.InstanceId == nil { 776 return fmt.Errorf("Error launching instance: no instance returned in response") 777 } 778 779 instanceId := *resp.InstanceId 780 d.SetId(instanceId) 781 d.Set("id", instanceId) 782 783 if v, ok := d.GetOk("state"); ok && v.(string) == "running" { 784 err := startOpsworksInstance(d, meta, true) 785 if err != nil { 786 return err 787 } 788 } 789 790 return resourceAwsOpsworksInstanceRead(d, meta) 791 } 792 793 func resourceAwsOpsworksInstanceUpdate(d *schema.ResourceData, meta interface{}) error { 794 client := meta.(*AWSClient).opsworksconn 795 796 err := resourceAwsOpsworksInstanceValidate(d) 797 if err != nil { 798 return err 799 } 800 801 req := &opsworks.UpdateInstanceInput{ 802 AgentVersion: aws.String(d.Get("agent_version").(string)), 803 Architecture: aws.String(d.Get("architecture").(string)), 804 InstanceId: aws.String(d.Get("id").(string)), 805 InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)), 806 } 807 808 if v, ok := d.GetOk("ami_id"); ok { 809 req.AmiId = aws.String(v.(string)) 810 req.Os = aws.String("Custom") 811 } 812 813 if v, ok := d.GetOk("auto_scaling_type"); ok { 814 req.AutoScalingType = aws.String(v.(string)) 815 } 816 817 if v, ok := d.GetOk("hostname"); ok { 818 req.Hostname = aws.String(v.(string)) 819 } 820 821 if v, ok := d.GetOk("instance_type"); ok { 822 req.InstanceType = aws.String(v.(string)) 823 } 824 825 if v, ok := d.GetOk("layer_ids"); ok { 826 req.LayerIds = expandStringList(v.([]interface{})) 827 } 828 829 if v, ok := d.GetOk("os"); ok { 830 req.Os = aws.String(v.(string)) 831 } 832 833 if v, ok := d.GetOk("ssh_key_name"); ok { 834 req.SshKeyName = aws.String(v.(string)) 835 } 836 837 log.Printf("[DEBUG] Updating OpsWorks instance: %s", d.Id()) 838 839 _, err = client.UpdateInstance(req) 840 if err != nil { 841 return err 842 } 843 844 var status string 845 846 if v, ok := d.GetOk("status"); ok { 847 status = v.(string) 848 } else { 849 status = "stopped" 850 } 851 852 if v, ok := d.GetOk("state"); ok { 853 state := v.(string) 854 if state == "running" { 855 if status == "stopped" || status == "stopping" || status == "shutting_down" { 856 err := startOpsworksInstance(d, meta, false) 857 if err != nil { 858 return err 859 } 860 } 861 } else { 862 if status != "stopped" && status != "stopping" && status != "shutting_down" { 863 err := stopOpsworksInstance(d, meta, true) 864 if err != nil { 865 return err 866 } 867 } 868 } 869 } 870 871 return resourceAwsOpsworksInstanceRead(d, meta) 872 } 873 874 func resourceAwsOpsworksInstanceDelete(d *schema.ResourceData, meta interface{}) error { 875 client := meta.(*AWSClient).opsworksconn 876 877 if v, ok := d.GetOk("status"); ok && v.(string) != "stopped" { 878 err := stopOpsworksInstance(d, meta, true) 879 if err != nil { 880 return err 881 } 882 } 883 884 req := &opsworks.DeleteInstanceInput{ 885 InstanceId: aws.String(d.Id()), 886 DeleteElasticIp: aws.Bool(d.Get("delete_eip").(bool)), 887 DeleteVolumes: aws.Bool(d.Get("delete_ebs").(bool)), 888 } 889 890 log.Printf("[DEBUG] Deleting OpsWorks instance: %s", d.Id()) 891 892 _, err := client.DeleteInstance(req) 893 if err != nil { 894 return err 895 } 896 897 d.SetId("") 898 return nil 899 } 900 901 func resourceAwsOpsworksInstanceImport( 902 d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { 903 // Neither delete_eip nor delete_ebs can be fetched 904 // from any API call, so we need to default to the values 905 // we set in the schema by default 906 d.Set("delete_ebs", true) 907 d.Set("delete_eip", true) 908 return []*schema.ResourceData{d}, nil 909 } 910 911 func startOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error { 912 client := meta.(*AWSClient).opsworksconn 913 914 instanceId := d.Get("id").(string) 915 916 req := &opsworks.StartInstanceInput{ 917 InstanceId: aws.String(instanceId), 918 } 919 920 log.Printf("[DEBUG] Starting OpsWorks instance: %s", instanceId) 921 922 _, err := client.StartInstance(req) 923 924 if err != nil { 925 return err 926 } 927 928 if wait { 929 log.Printf("[DEBUG] Waiting for instance (%s) to become running", instanceId) 930 931 stateConf := &resource.StateChangeConf{ 932 Pending: []string{"requested", "pending", "booting", "running_setup"}, 933 Target: []string{"online"}, 934 Refresh: OpsworksInstanceStateRefreshFunc(client, instanceId), 935 Timeout: 10 * time.Minute, 936 Delay: 10 * time.Second, 937 MinTimeout: 3 * time.Second, 938 } 939 _, err = stateConf.WaitForState() 940 if err != nil { 941 return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s", 942 instanceId, err) 943 } 944 } 945 946 return nil 947 } 948 949 func stopOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error { 950 client := meta.(*AWSClient).opsworksconn 951 952 instanceId := d.Get("id").(string) 953 954 req := &opsworks.StopInstanceInput{ 955 InstanceId: aws.String(instanceId), 956 } 957 958 log.Printf("[DEBUG] Stopping OpsWorks instance: %s", instanceId) 959 960 _, err := client.StopInstance(req) 961 962 if err != nil { 963 return err 964 } 965 966 if wait { 967 log.Printf("[DEBUG] Waiting for instance (%s) to become stopped", instanceId) 968 969 stateConf := &resource.StateChangeConf{ 970 Pending: []string{"stopping", "terminating", "shutting_down", "terminated"}, 971 Target: []string{"stopped"}, 972 Refresh: OpsworksInstanceStateRefreshFunc(client, instanceId), 973 Timeout: 10 * time.Minute, 974 Delay: 10 * time.Second, 975 MinTimeout: 3 * time.Second, 976 } 977 _, err = stateConf.WaitForState() 978 if err != nil { 979 return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s", 980 instanceId, err) 981 } 982 } 983 984 return nil 985 } 986 987 func readOpsworksBlockDevices(d *schema.ResourceData, instance *opsworks.Instance, meta interface{}) ( 988 map[string]interface{}, error) { 989 990 blockDevices := make(map[string]interface{}) 991 blockDevices["ebs"] = make([]map[string]interface{}, 0) 992 blockDevices["ephemeral"] = make([]map[string]interface{}, 0) 993 blockDevices["root"] = nil 994 995 if len(instance.BlockDeviceMappings) == 0 { 996 return nil, nil 997 } 998 999 for _, bdm := range instance.BlockDeviceMappings { 1000 bd := make(map[string]interface{}) 1001 if bdm.Ebs != nil && bdm.Ebs.DeleteOnTermination != nil { 1002 bd["delete_on_termination"] = *bdm.Ebs.DeleteOnTermination 1003 } 1004 if bdm.Ebs != nil && bdm.Ebs.VolumeSize != nil { 1005 bd["volume_size"] = *bdm.Ebs.VolumeSize 1006 } 1007 if bdm.Ebs != nil && bdm.Ebs.VolumeType != nil { 1008 bd["volume_type"] = *bdm.Ebs.VolumeType 1009 } 1010 if bdm.Ebs != nil && bdm.Ebs.Iops != nil { 1011 bd["iops"] = *bdm.Ebs.Iops 1012 } 1013 if bdm.DeviceName != nil && *bdm.DeviceName == "ROOT_DEVICE" { 1014 blockDevices["root"] = bd 1015 } else { 1016 if bdm.DeviceName != nil { 1017 bd["device_name"] = *bdm.DeviceName 1018 } 1019 if bdm.VirtualName != nil { 1020 bd["virtual_name"] = *bdm.VirtualName 1021 blockDevices["ephemeral"] = append(blockDevices["ephemeral"].([]map[string]interface{}), bd) 1022 } else { 1023 if bdm.Ebs != nil && bdm.Ebs.SnapshotId != nil { 1024 bd["snapshot_id"] = *bdm.Ebs.SnapshotId 1025 } 1026 blockDevices["ebs"] = append(blockDevices["ebs"].([]map[string]interface{}), bd) 1027 } 1028 } 1029 } 1030 return blockDevices, nil 1031 } 1032 1033 func OpsworksInstanceStateRefreshFunc(conn *opsworks.OpsWorks, instanceID string) resource.StateRefreshFunc { 1034 return func() (interface{}, string, error) { 1035 resp, err := conn.DescribeInstances(&opsworks.DescribeInstancesInput{ 1036 InstanceIds: []*string{aws.String(instanceID)}, 1037 }) 1038 if err != nil { 1039 if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" { 1040 // Set this to nil as if we didn't find anything. 1041 resp = nil 1042 } else { 1043 log.Printf("Error on OpsworksInstanceStateRefresh: %s", err) 1044 return nil, "", err 1045 } 1046 } 1047 1048 if resp == nil || len(resp.Instances) == 0 { 1049 // Sometimes AWS just has consistency issues and doesn't see 1050 // our instance yet. Return an empty state. 1051 return nil, "", nil 1052 } 1053 1054 i := resp.Instances[0] 1055 return i, *i.Status, nil 1056 } 1057 }