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