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