github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_launch_configuration.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha1"
     6  	"encoding/base64"
     7  	"encoding/hex"
     8  	"fmt"
     9  	"log"
    10  	"strings"
    11  	"time"
    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/autoscaling"
    16  	"github.com/aws/aws-sdk-go/service/ec2"
    17  	"github.com/hashicorp/terraform/helper/hashcode"
    18  	"github.com/hashicorp/terraform/helper/resource"
    19  	"github.com/hashicorp/terraform/helper/schema"
    20  )
    21  
    22  func resourceAwsLaunchConfiguration() *schema.Resource {
    23  	return &schema.Resource{
    24  		Create: resourceAwsLaunchConfigurationCreate,
    25  		Read:   resourceAwsLaunchConfigurationRead,
    26  		Delete: resourceAwsLaunchConfigurationDelete,
    27  		Importer: &schema.ResourceImporter{
    28  			State: schema.ImportStatePassthrough,
    29  		},
    30  
    31  		Schema: map[string]*schema.Schema{
    32  			"name": &schema.Schema{
    33  				Type:          schema.TypeString,
    34  				Optional:      true,
    35  				Computed:      true,
    36  				ForceNew:      true,
    37  				ConflictsWith: []string{"name_prefix"},
    38  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    39  					// https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1932-L1939
    40  					value := v.(string)
    41  					if len(value) > 255 {
    42  						errors = append(errors, fmt.Errorf(
    43  							"%q cannot be longer than 255 characters", k))
    44  					}
    45  					return
    46  				},
    47  			},
    48  
    49  			"name_prefix": &schema.Schema{
    50  				Type:     schema.TypeString,
    51  				Optional: true,
    52  				ForceNew: true,
    53  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    54  					// https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1932-L1939
    55  					// uuid is 26 characters, limit the prefix to 229.
    56  					value := v.(string)
    57  					if len(value) > 229 {
    58  						errors = append(errors, fmt.Errorf(
    59  							"%q cannot be longer than 229 characters, name is limited to 255", k))
    60  					}
    61  					return
    62  				},
    63  			},
    64  
    65  			"image_id": &schema.Schema{
    66  				Type:     schema.TypeString,
    67  				Required: true,
    68  				ForceNew: true,
    69  			},
    70  
    71  			"instance_type": &schema.Schema{
    72  				Type:     schema.TypeString,
    73  				Required: true,
    74  				ForceNew: true,
    75  			},
    76  
    77  			"iam_instance_profile": &schema.Schema{
    78  				Type:     schema.TypeString,
    79  				Optional: true,
    80  				ForceNew: true,
    81  			},
    82  
    83  			"key_name": &schema.Schema{
    84  				Type:     schema.TypeString,
    85  				Optional: true,
    86  				Computed: true,
    87  				ForceNew: true,
    88  			},
    89  
    90  			"user_data": &schema.Schema{
    91  				Type:     schema.TypeString,
    92  				Optional: true,
    93  				ForceNew: true,
    94  				StateFunc: func(v interface{}) string {
    95  					switch v.(type) {
    96  					case string:
    97  						hash := sha1.Sum([]byte(v.(string)))
    98  						return hex.EncodeToString(hash[:])
    99  					default:
   100  						return ""
   101  					}
   102  				},
   103  			},
   104  
   105  			"security_groups": &schema.Schema{
   106  				Type:     schema.TypeSet,
   107  				Optional: true,
   108  				ForceNew: true,
   109  				Elem:     &schema.Schema{Type: schema.TypeString},
   110  				Set:      schema.HashString,
   111  			},
   112  
   113  			"vpc_classic_link_id": &schema.Schema{
   114  				Type:     schema.TypeString,
   115  				Optional: true,
   116  				ForceNew: true,
   117  			},
   118  
   119  			"vpc_classic_link_security_groups": &schema.Schema{
   120  				Type:     schema.TypeSet,
   121  				Optional: true,
   122  				ForceNew: true,
   123  				Elem:     &schema.Schema{Type: schema.TypeString},
   124  				Set:      schema.HashString,
   125  			},
   126  
   127  			"associate_public_ip_address": &schema.Schema{
   128  				Type:     schema.TypeBool,
   129  				Optional: true,
   130  				ForceNew: true,
   131  				Default:  false,
   132  			},
   133  
   134  			"spot_price": &schema.Schema{
   135  				Type:     schema.TypeString,
   136  				Optional: true,
   137  				ForceNew: true,
   138  			},
   139  
   140  			"ebs_optimized": &schema.Schema{
   141  				Type:     schema.TypeBool,
   142  				Optional: true,
   143  				ForceNew: true,
   144  				Computed: true,
   145  			},
   146  
   147  			"placement_tenancy": &schema.Schema{
   148  				Type:     schema.TypeString,
   149  				Optional: true,
   150  				ForceNew: true,
   151  			},
   152  
   153  			"enable_monitoring": &schema.Schema{
   154  				Type:     schema.TypeBool,
   155  				Optional: true,
   156  				ForceNew: true,
   157  				Default:  true,
   158  			},
   159  
   160  			"ebs_block_device": &schema.Schema{
   161  				Type:     schema.TypeSet,
   162  				Optional: true,
   163  				Computed: true,
   164  				Elem: &schema.Resource{
   165  					Schema: map[string]*schema.Schema{
   166  						"delete_on_termination": &schema.Schema{
   167  							Type:     schema.TypeBool,
   168  							Optional: true,
   169  							Default:  true,
   170  							ForceNew: true,
   171  						},
   172  
   173  						"device_name": &schema.Schema{
   174  							Type:     schema.TypeString,
   175  							Required: true,
   176  							ForceNew: true,
   177  						},
   178  
   179  						"iops": &schema.Schema{
   180  							Type:     schema.TypeInt,
   181  							Optional: true,
   182  							Computed: true,
   183  							ForceNew: true,
   184  						},
   185  
   186  						"snapshot_id": &schema.Schema{
   187  							Type:     schema.TypeString,
   188  							Optional: true,
   189  							Computed: true,
   190  							ForceNew: true,
   191  						},
   192  
   193  						"volume_size": &schema.Schema{
   194  							Type:     schema.TypeInt,
   195  							Optional: true,
   196  							Computed: true,
   197  							ForceNew: true,
   198  						},
   199  
   200  						"volume_type": &schema.Schema{
   201  							Type:     schema.TypeString,
   202  							Optional: true,
   203  							Computed: true,
   204  							ForceNew: true,
   205  						},
   206  
   207  						"encrypted": &schema.Schema{
   208  							Type:     schema.TypeBool,
   209  							Optional: true,
   210  							Computed: true,
   211  							ForceNew: true,
   212  						},
   213  					},
   214  				},
   215  				Set: func(v interface{}) int {
   216  					var buf bytes.Buffer
   217  					m := v.(map[string]interface{})
   218  					buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
   219  					buf.WriteString(fmt.Sprintf("%s-", m["snapshot_id"].(string)))
   220  					return hashcode.String(buf.String())
   221  				},
   222  			},
   223  
   224  			"ephemeral_block_device": &schema.Schema{
   225  				Type:     schema.TypeSet,
   226  				Optional: true,
   227  				ForceNew: true,
   228  				Elem: &schema.Resource{
   229  					Schema: map[string]*schema.Schema{
   230  						"device_name": &schema.Schema{
   231  							Type:     schema.TypeString,
   232  							Required: true,
   233  						},
   234  
   235  						"virtual_name": &schema.Schema{
   236  							Type:     schema.TypeString,
   237  							Required: true,
   238  						},
   239  					},
   240  				},
   241  				Set: func(v interface{}) int {
   242  					var buf bytes.Buffer
   243  					m := v.(map[string]interface{})
   244  					buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
   245  					buf.WriteString(fmt.Sprintf("%s-", m["virtual_name"].(string)))
   246  					return hashcode.String(buf.String())
   247  				},
   248  			},
   249  
   250  			"root_block_device": &schema.Schema{
   251  				// TODO: This is a set because we don't support singleton
   252  				//       sub-resources today. We'll enforce that the set only ever has
   253  				//       length zero or one below. When TF gains support for
   254  				//       sub-resources this can be converted.
   255  				Type:     schema.TypeSet,
   256  				Optional: true,
   257  				Computed: true,
   258  				Elem: &schema.Resource{
   259  					// "You can only modify the volume size, volume type, and Delete on
   260  					// Termination flag on the block device mapping entry for the root
   261  					// device volume." - bit.ly/ec2bdmap
   262  					Schema: map[string]*schema.Schema{
   263  						"delete_on_termination": &schema.Schema{
   264  							Type:     schema.TypeBool,
   265  							Optional: true,
   266  							Default:  true,
   267  							ForceNew: true,
   268  						},
   269  
   270  						"iops": &schema.Schema{
   271  							Type:     schema.TypeInt,
   272  							Optional: true,
   273  							Computed: true,
   274  							ForceNew: true,
   275  						},
   276  
   277  						"volume_size": &schema.Schema{
   278  							Type:     schema.TypeInt,
   279  							Optional: true,
   280  							Computed: true,
   281  							ForceNew: true,
   282  						},
   283  
   284  						"volume_type": &schema.Schema{
   285  							Type:     schema.TypeString,
   286  							Optional: true,
   287  							Computed: true,
   288  							ForceNew: true,
   289  						},
   290  					},
   291  				},
   292  				Set: func(v interface{}) int {
   293  					// there can be only one root device; no need to hash anything
   294  					return 0
   295  				},
   296  			},
   297  		},
   298  	}
   299  }
   300  
   301  func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface{}) error {
   302  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   303  	ec2conn := meta.(*AWSClient).ec2conn
   304  
   305  	createLaunchConfigurationOpts := autoscaling.CreateLaunchConfigurationInput{
   306  		LaunchConfigurationName: aws.String(d.Get("name").(string)),
   307  		ImageId:                 aws.String(d.Get("image_id").(string)),
   308  		InstanceType:            aws.String(d.Get("instance_type").(string)),
   309  		EbsOptimized:            aws.Bool(d.Get("ebs_optimized").(bool)),
   310  	}
   311  
   312  	if v, ok := d.GetOk("user_data"); ok {
   313  		userData := base64.StdEncoding.EncodeToString([]byte(v.(string)))
   314  		createLaunchConfigurationOpts.UserData = aws.String(userData)
   315  	}
   316  
   317  	createLaunchConfigurationOpts.InstanceMonitoring = &autoscaling.InstanceMonitoring{
   318  		Enabled: aws.Bool(d.Get("enable_monitoring").(bool)),
   319  	}
   320  
   321  	if v, ok := d.GetOk("iam_instance_profile"); ok {
   322  		createLaunchConfigurationOpts.IamInstanceProfile = aws.String(v.(string))
   323  	}
   324  
   325  	if v, ok := d.GetOk("placement_tenancy"); ok {
   326  		createLaunchConfigurationOpts.PlacementTenancy = aws.String(v.(string))
   327  	}
   328  
   329  	if v, ok := d.GetOk("associate_public_ip_address"); ok {
   330  		createLaunchConfigurationOpts.AssociatePublicIpAddress = aws.Bool(v.(bool))
   331  	}
   332  
   333  	if v, ok := d.GetOk("key_name"); ok {
   334  		createLaunchConfigurationOpts.KeyName = aws.String(v.(string))
   335  	}
   336  	if v, ok := d.GetOk("spot_price"); ok {
   337  		createLaunchConfigurationOpts.SpotPrice = aws.String(v.(string))
   338  	}
   339  
   340  	if v, ok := d.GetOk("security_groups"); ok {
   341  		createLaunchConfigurationOpts.SecurityGroups = expandStringList(
   342  			v.(*schema.Set).List(),
   343  		)
   344  	}
   345  
   346  	if v, ok := d.GetOk("vpc_classic_link_id"); ok {
   347  		createLaunchConfigurationOpts.ClassicLinkVPCId = aws.String(v.(string))
   348  	}
   349  
   350  	if v, ok := d.GetOk("vpc_classic_link_security_groups"); ok {
   351  		createLaunchConfigurationOpts.ClassicLinkVPCSecurityGroups = expandStringList(
   352  			v.(*schema.Set).List(),
   353  		)
   354  	}
   355  
   356  	var blockDevices []*autoscaling.BlockDeviceMapping
   357  
   358  	// We'll use this to detect if we're declaring it incorrectly as an ebs_block_device.
   359  	rootDeviceName, err := fetchRootDeviceName(d.Get("image_id").(string), ec2conn)
   360  	if err != nil {
   361  		return err
   362  	}
   363  	if rootDeviceName == nil {
   364  		// We do this so the value is empty so we don't have to do nil checks later
   365  		var blank string
   366  		rootDeviceName = &blank
   367  	}
   368  
   369  	if v, ok := d.GetOk("ebs_block_device"); ok {
   370  		vL := v.(*schema.Set).List()
   371  		for _, v := range vL {
   372  			bd := v.(map[string]interface{})
   373  			ebs := &autoscaling.Ebs{
   374  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
   375  			}
   376  
   377  			if v, ok := bd["snapshot_id"].(string); ok && v != "" {
   378  				ebs.SnapshotId = aws.String(v)
   379  			}
   380  
   381  			if v, ok := bd["encrypted"].(bool); ok && v {
   382  				ebs.Encrypted = aws.Bool(v)
   383  			}
   384  
   385  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
   386  				ebs.VolumeSize = aws.Int64(int64(v))
   387  			}
   388  
   389  			if v, ok := bd["volume_type"].(string); ok && v != "" {
   390  				ebs.VolumeType = aws.String(v)
   391  			}
   392  
   393  			if v, ok := bd["iops"].(int); ok && v > 0 {
   394  				ebs.Iops = aws.Int64(int64(v))
   395  			}
   396  
   397  			if *aws.String(bd["device_name"].(string)) == *rootDeviceName {
   398  				return fmt.Errorf("Root device (%s) declared as an 'ebs_block_device'.  Use 'root_block_device' keyword.", *rootDeviceName)
   399  			}
   400  
   401  			blockDevices = append(blockDevices, &autoscaling.BlockDeviceMapping{
   402  				DeviceName: aws.String(bd["device_name"].(string)),
   403  				Ebs:        ebs,
   404  			})
   405  		}
   406  	}
   407  
   408  	if v, ok := d.GetOk("ephemeral_block_device"); ok {
   409  		vL := v.(*schema.Set).List()
   410  		for _, v := range vL {
   411  			bd := v.(map[string]interface{})
   412  			blockDevices = append(blockDevices, &autoscaling.BlockDeviceMapping{
   413  				DeviceName:  aws.String(bd["device_name"].(string)),
   414  				VirtualName: aws.String(bd["virtual_name"].(string)),
   415  			})
   416  		}
   417  	}
   418  
   419  	if v, ok := d.GetOk("root_block_device"); ok {
   420  		vL := v.(*schema.Set).List()
   421  		if len(vL) > 1 {
   422  			return fmt.Errorf("Cannot specify more than one root_block_device.")
   423  		}
   424  		for _, v := range vL {
   425  			bd := v.(map[string]interface{})
   426  			ebs := &autoscaling.Ebs{
   427  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
   428  			}
   429  
   430  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
   431  				ebs.VolumeSize = aws.Int64(int64(v))
   432  			}
   433  
   434  			if v, ok := bd["volume_type"].(string); ok && v != "" {
   435  				ebs.VolumeType = aws.String(v)
   436  			}
   437  
   438  			if v, ok := bd["iops"].(int); ok && v > 0 {
   439  				ebs.Iops = aws.Int64(int64(v))
   440  			}
   441  
   442  			if dn, err := fetchRootDeviceName(d.Get("image_id").(string), ec2conn); err == nil {
   443  				if dn == nil {
   444  					return fmt.Errorf(
   445  						"Expected to find a Root Device name for AMI (%s), but got none",
   446  						d.Get("image_id").(string))
   447  				}
   448  				blockDevices = append(blockDevices, &autoscaling.BlockDeviceMapping{
   449  					DeviceName: dn,
   450  					Ebs:        ebs,
   451  				})
   452  			} else {
   453  				return err
   454  			}
   455  		}
   456  	}
   457  
   458  	if len(blockDevices) > 0 {
   459  		createLaunchConfigurationOpts.BlockDeviceMappings = blockDevices
   460  	}
   461  
   462  	var lcName string
   463  	if v, ok := d.GetOk("name"); ok {
   464  		lcName = v.(string)
   465  	} else if v, ok := d.GetOk("name_prefix"); ok {
   466  		lcName = resource.PrefixedUniqueId(v.(string))
   467  	} else {
   468  		lcName = resource.UniqueId()
   469  	}
   470  	createLaunchConfigurationOpts.LaunchConfigurationName = aws.String(lcName)
   471  
   472  	log.Printf(
   473  		"[DEBUG] autoscaling create launch configuration: %s", createLaunchConfigurationOpts)
   474  
   475  	// IAM profiles can take ~10 seconds to propagate in AWS:
   476  	// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
   477  	err = resource.Retry(30*time.Second, func() *resource.RetryError {
   478  		_, err := autoscalingconn.CreateLaunchConfiguration(&createLaunchConfigurationOpts)
   479  		if err != nil {
   480  			if awsErr, ok := err.(awserr.Error); ok {
   481  				if strings.Contains(awsErr.Message(), "Invalid IamInstanceProfile") {
   482  					return resource.RetryableError(err)
   483  				}
   484  				if strings.Contains(awsErr.Message(), "You are not authorized to perform this operation") {
   485  					return resource.RetryableError(err)
   486  				}
   487  			}
   488  			return resource.NonRetryableError(err)
   489  		}
   490  		return nil
   491  	})
   492  
   493  	if err != nil {
   494  		return fmt.Errorf("Error creating launch configuration: %s", err)
   495  	}
   496  
   497  	d.SetId(lcName)
   498  	log.Printf("[INFO] launch configuration ID: %s", d.Id())
   499  
   500  	// We put a Retry here since sometimes eventual consistency bites
   501  	// us and we need to retry a few times to get the LC to load properly
   502  	return resource.Retry(30*time.Second, func() *resource.RetryError {
   503  		err := resourceAwsLaunchConfigurationRead(d, meta)
   504  		if err != nil {
   505  			return resource.RetryableError(err)
   506  		}
   507  		return nil
   508  	})
   509  }
   510  
   511  func resourceAwsLaunchConfigurationRead(d *schema.ResourceData, meta interface{}) error {
   512  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   513  	ec2conn := meta.(*AWSClient).ec2conn
   514  
   515  	describeOpts := autoscaling.DescribeLaunchConfigurationsInput{
   516  		LaunchConfigurationNames: []*string{aws.String(d.Id())},
   517  	}
   518  
   519  	log.Printf("[DEBUG] launch configuration describe configuration: %s", describeOpts)
   520  	describConfs, err := autoscalingconn.DescribeLaunchConfigurations(&describeOpts)
   521  	if err != nil {
   522  		return fmt.Errorf("Error retrieving launch configuration: %s", err)
   523  	}
   524  	if len(describConfs.LaunchConfigurations) == 0 {
   525  		d.SetId("")
   526  		return nil
   527  	}
   528  
   529  	// Verify AWS returned our launch configuration
   530  	if *describConfs.LaunchConfigurations[0].LaunchConfigurationName != d.Id() {
   531  		return fmt.Errorf(
   532  			"Unable to find launch configuration: %#v",
   533  			describConfs.LaunchConfigurations)
   534  	}
   535  
   536  	lc := describConfs.LaunchConfigurations[0]
   537  
   538  	d.Set("key_name", lc.KeyName)
   539  	d.Set("image_id", lc.ImageId)
   540  	d.Set("instance_type", lc.InstanceType)
   541  	d.Set("name", lc.LaunchConfigurationName)
   542  
   543  	d.Set("iam_instance_profile", lc.IamInstanceProfile)
   544  	d.Set("ebs_optimized", lc.EbsOptimized)
   545  	d.Set("spot_price", lc.SpotPrice)
   546  	d.Set("enable_monitoring", lc.InstanceMonitoring.Enabled)
   547  	d.Set("security_groups", lc.SecurityGroups)
   548  	d.Set("associate_public_ip_address", lc.AssociatePublicIpAddress)
   549  
   550  	d.Set("vpc_classic_link_id", lc.ClassicLinkVPCId)
   551  	d.Set("vpc_classic_link_security_groups", lc.ClassicLinkVPCSecurityGroups)
   552  
   553  	if err := readLCBlockDevices(d, lc, ec2conn); err != nil {
   554  		return err
   555  	}
   556  
   557  	return nil
   558  }
   559  
   560  func resourceAwsLaunchConfigurationDelete(d *schema.ResourceData, meta interface{}) error {
   561  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   562  
   563  	log.Printf("[DEBUG] Launch Configuration destroy: %v", d.Id())
   564  	_, err := autoscalingconn.DeleteLaunchConfiguration(
   565  		&autoscaling.DeleteLaunchConfigurationInput{
   566  			LaunchConfigurationName: aws.String(d.Id()),
   567  		})
   568  	if err != nil {
   569  		autoscalingerr, ok := err.(awserr.Error)
   570  		if ok && (autoscalingerr.Code() == "InvalidConfiguration.NotFound" || autoscalingerr.Code() == "ValidationError") {
   571  			log.Printf("[DEBUG] Launch configuration (%s) not found", d.Id())
   572  			return nil
   573  		}
   574  
   575  		return err
   576  	}
   577  
   578  	return nil
   579  }
   580  
   581  func readLCBlockDevices(d *schema.ResourceData, lc *autoscaling.LaunchConfiguration, ec2conn *ec2.EC2) error {
   582  	ibds, err := readBlockDevicesFromLaunchConfiguration(d, lc, ec2conn)
   583  	if err != nil {
   584  		return err
   585  	}
   586  
   587  	if err := d.Set("ebs_block_device", ibds["ebs"]); err != nil {
   588  		return err
   589  	}
   590  	if err := d.Set("ephemeral_block_device", ibds["ephemeral"]); err != nil {
   591  		return err
   592  	}
   593  	if ibds["root"] != nil {
   594  		if err := d.Set("root_block_device", []interface{}{ibds["root"]}); err != nil {
   595  			return err
   596  		}
   597  	} else {
   598  		d.Set("root_block_device", []interface{}{})
   599  	}
   600  
   601  	return nil
   602  }
   603  
   604  func readBlockDevicesFromLaunchConfiguration(d *schema.ResourceData, lc *autoscaling.LaunchConfiguration, ec2conn *ec2.EC2) (
   605  	map[string]interface{}, error) {
   606  	blockDevices := make(map[string]interface{})
   607  	blockDevices["ebs"] = make([]map[string]interface{}, 0)
   608  	blockDevices["ephemeral"] = make([]map[string]interface{}, 0)
   609  	blockDevices["root"] = nil
   610  	if len(lc.BlockDeviceMappings) == 0 {
   611  		return nil, nil
   612  	}
   613  	rootDeviceName, err := fetchRootDeviceName(d.Get("image_id").(string), ec2conn)
   614  	if err != nil {
   615  		return nil, err
   616  	}
   617  	if rootDeviceName == nil {
   618  		// We do this so the value is empty so we don't have to do nil checks later
   619  		var blank string
   620  		rootDeviceName = &blank
   621  	}
   622  	for _, bdm := range lc.BlockDeviceMappings {
   623  		bd := make(map[string]interface{})
   624  		if bdm.Ebs != nil && bdm.Ebs.DeleteOnTermination != nil {
   625  			bd["delete_on_termination"] = *bdm.Ebs.DeleteOnTermination
   626  		}
   627  		if bdm.Ebs != nil && bdm.Ebs.VolumeSize != nil {
   628  			bd["volume_size"] = *bdm.Ebs.VolumeSize
   629  		}
   630  		if bdm.Ebs != nil && bdm.Ebs.VolumeType != nil {
   631  			bd["volume_type"] = *bdm.Ebs.VolumeType
   632  		}
   633  		if bdm.Ebs != nil && bdm.Ebs.Iops != nil {
   634  			bd["iops"] = *bdm.Ebs.Iops
   635  		}
   636  
   637  		if bdm.DeviceName != nil && *bdm.DeviceName == *rootDeviceName {
   638  			blockDevices["root"] = bd
   639  		} else {
   640  			if bdm.Ebs != nil && bdm.Ebs.Encrypted != nil {
   641  				bd["encrypted"] = *bdm.Ebs.Encrypted
   642  			}
   643  			if bdm.DeviceName != nil {
   644  				bd["device_name"] = *bdm.DeviceName
   645  			}
   646  			if bdm.VirtualName != nil {
   647  				bd["virtual_name"] = *bdm.VirtualName
   648  				blockDevices["ephemeral"] = append(blockDevices["ephemeral"].([]map[string]interface{}), bd)
   649  			} else {
   650  				if bdm.Ebs != nil && bdm.Ebs.SnapshotId != nil {
   651  					bd["snapshot_id"] = *bdm.Ebs.SnapshotId
   652  				}
   653  				blockDevices["ebs"] = append(blockDevices["ebs"].([]map[string]interface{}), bd)
   654  			}
   655  		}
   656  	}
   657  	return blockDevices, nil
   658  }