github.com/keshavdv/terraform@v0.7.0-rc2.0.20160711232630-d69256dcb425/builtin/providers/aws/data_source_aws_ami.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"sort"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/service/ec2"
    12  	"github.com/hashicorp/terraform/helper/hashcode"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func dataSourceAwsAmi() *schema.Resource {
    17  	return &schema.Resource{
    18  		Read: dataSourceAwsAmiRead,
    19  
    20  		Schema: map[string]*schema.Schema{
    21  			"executable_users": &schema.Schema{
    22  				Type:     schema.TypeList,
    23  				Optional: true,
    24  				ForceNew: true,
    25  				Elem:     &schema.Schema{Type: schema.TypeString},
    26  			},
    27  			"filter": &schema.Schema{
    28  				Type:     schema.TypeSet,
    29  				Optional: true,
    30  				ForceNew: true,
    31  				Elem: &schema.Resource{
    32  					Schema: map[string]*schema.Schema{
    33  						"name": &schema.Schema{
    34  							Type:     schema.TypeString,
    35  							Required: true,
    36  						},
    37  
    38  						"values": &schema.Schema{
    39  							Type:     schema.TypeList,
    40  							Required: true,
    41  							Elem:     &schema.Schema{Type: schema.TypeString},
    42  						},
    43  					},
    44  				},
    45  			},
    46  			"most_recent": &schema.Schema{
    47  				Type:     schema.TypeBool,
    48  				Optional: true,
    49  				Default:  false,
    50  				ForceNew: true,
    51  			},
    52  			"owners": &schema.Schema{
    53  				Type:     schema.TypeList,
    54  				Optional: true,
    55  				ForceNew: true,
    56  				Elem:     &schema.Schema{Type: schema.TypeString},
    57  			},
    58  			// Computed values.
    59  			"architecture": &schema.Schema{
    60  				Type:     schema.TypeString,
    61  				Computed: true,
    62  			},
    63  			"creation_date": &schema.Schema{
    64  				Type:     schema.TypeString,
    65  				Computed: true,
    66  			},
    67  			"description": &schema.Schema{
    68  				Type:     schema.TypeString,
    69  				Computed: true,
    70  			},
    71  			"hypervisor": &schema.Schema{
    72  				Type:     schema.TypeString,
    73  				Computed: true,
    74  			},
    75  			"image_id": &schema.Schema{
    76  				Type:     schema.TypeString,
    77  				Computed: true,
    78  			},
    79  			"image_location": &schema.Schema{
    80  				Type:     schema.TypeString,
    81  				Computed: true,
    82  			},
    83  			"image_owner_alias": &schema.Schema{
    84  				Type:     schema.TypeString,
    85  				Computed: true,
    86  			},
    87  			"image_type": &schema.Schema{
    88  				Type:     schema.TypeString,
    89  				Computed: true,
    90  			},
    91  			"kernel_id": &schema.Schema{
    92  				Type:     schema.TypeString,
    93  				Computed: true,
    94  			},
    95  			"name": &schema.Schema{
    96  				Type:     schema.TypeString,
    97  				Computed: true,
    98  			},
    99  			"owner_id": &schema.Schema{
   100  				Type:     schema.TypeString,
   101  				Computed: true,
   102  			},
   103  			"platform": &schema.Schema{
   104  				Type:     schema.TypeString,
   105  				Computed: true,
   106  			},
   107  			"public": &schema.Schema{
   108  				Type:     schema.TypeBool,
   109  				Computed: true,
   110  			},
   111  			"ramdisk_id": &schema.Schema{
   112  				Type:     schema.TypeString,
   113  				Computed: true,
   114  			},
   115  			"root_device_name": &schema.Schema{
   116  				Type:     schema.TypeString,
   117  				Computed: true,
   118  			},
   119  			"root_device_type": &schema.Schema{
   120  				Type:     schema.TypeString,
   121  				Computed: true,
   122  			},
   123  			"sriov_net_support": &schema.Schema{
   124  				Type:     schema.TypeString,
   125  				Computed: true,
   126  			},
   127  			"state": &schema.Schema{
   128  				Type:     schema.TypeString,
   129  				Computed: true,
   130  			},
   131  			"virtualization_type": &schema.Schema{
   132  				Type:     schema.TypeString,
   133  				Computed: true,
   134  			},
   135  			// Complex computed values
   136  			"block_device_mappings": &schema.Schema{
   137  				Type:     schema.TypeSet,
   138  				Computed: true,
   139  				Set:      amiBlockDeviceMappingHash,
   140  				Elem: &schema.Resource{
   141  					Schema: map[string]*schema.Schema{
   142  						"device_name": &schema.Schema{
   143  							Type:     schema.TypeString,
   144  							Computed: true,
   145  						},
   146  						"no_device": &schema.Schema{
   147  							Type:     schema.TypeString,
   148  							Computed: true,
   149  						},
   150  						"virtual_name": &schema.Schema{
   151  							Type:     schema.TypeString,
   152  							Computed: true,
   153  						},
   154  						"ebs": &schema.Schema{
   155  							Type:     schema.TypeMap,
   156  							Computed: true,
   157  						},
   158  					},
   159  				},
   160  			},
   161  			"product_codes": &schema.Schema{
   162  				Type:     schema.TypeSet,
   163  				Computed: true,
   164  				Set:      amiProductCodesHash,
   165  				Elem: &schema.Resource{
   166  					Schema: map[string]*schema.Schema{
   167  						"product_code_id": &schema.Schema{
   168  							Type:     schema.TypeString,
   169  							Computed: true,
   170  						},
   171  						"product_code_type": &schema.Schema{
   172  							Type:     schema.TypeString,
   173  							Computed: true,
   174  						},
   175  					},
   176  				},
   177  			},
   178  			"state_reason": &schema.Schema{
   179  				Type:     schema.TypeMap,
   180  				Computed: true,
   181  			},
   182  			"tags": &schema.Schema{
   183  				Type:     schema.TypeSet,
   184  				Computed: true,
   185  				Set:      amiTagsHash,
   186  				Elem: &schema.Resource{
   187  					Schema: map[string]*schema.Schema{
   188  						"key": &schema.Schema{
   189  							Type:     schema.TypeString,
   190  							Computed: true,
   191  						},
   192  						"value": &schema.Schema{
   193  							Type:     schema.TypeString,
   194  							Computed: true,
   195  						},
   196  					},
   197  				},
   198  			},
   199  		},
   200  	}
   201  }
   202  
   203  // dataSourceAwsAmiDescriptionRead performs the AMI lookup.
   204  func dataSourceAwsAmiRead(d *schema.ResourceData, meta interface{}) error {
   205  	conn := meta.(*AWSClient).ec2conn
   206  
   207  	executableUsers, executableUsersOk := d.GetOk("executable_users")
   208  	filters, filtersOk := d.GetOk("filter")
   209  	owners, ownersOk := d.GetOk("owners")
   210  
   211  	if executableUsersOk == false && filtersOk == false && ownersOk == false {
   212  		return fmt.Errorf("One of executable_users, filters, or owners must be assigned")
   213  	}
   214  
   215  	params := &ec2.DescribeImagesInput{}
   216  	if executableUsersOk {
   217  		params.ExecutableUsers = expandStringList(executableUsers.([]interface{}))
   218  	}
   219  	if filtersOk {
   220  		params.Filters = buildAmiFilters(filters.(*schema.Set))
   221  	}
   222  	if ownersOk {
   223  		params.Owners = expandStringList(owners.([]interface{}))
   224  	}
   225  
   226  	resp, err := conn.DescribeImages(params)
   227  	if err != nil {
   228  		return err
   229  	}
   230  	var image *ec2.Image
   231  	if len(resp.Images) < 1 {
   232  		return fmt.Errorf("Your query returned no results. Please change your filters and try again.")
   233  	} else if len(resp.Images) > 1 {
   234  		if (d.Get("most_recent").(bool)) == true {
   235  			log.Printf("[DEBUG] aws_ami - multiple results found and most_recent is set")
   236  			image = mostRecentAmi(resp.Images)
   237  		} else {
   238  			log.Printf("[DEBUG] aws_ami - multiple results found and most_recent not set")
   239  			return fmt.Errorf("Your query returned more than one result. Please try a more specific search, or set most_recent to true.")
   240  		}
   241  	} else {
   242  		log.Printf("[DEBUG] aws_ami - Single AMI found: %s", *resp.Images[0].ImageId)
   243  		image = resp.Images[0]
   244  	}
   245  	return amiDescriptionAttributes(d, image)
   246  }
   247  
   248  // Build a slice of AMI filter options from the filters provided.
   249  func buildAmiFilters(set *schema.Set) []*ec2.Filter {
   250  	var filters []*ec2.Filter
   251  	for _, v := range set.List() {
   252  		m := v.(map[string]interface{})
   253  		var filterValues []*string
   254  		for _, e := range m["values"].([]interface{}) {
   255  			filterValues = append(filterValues, aws.String(e.(string)))
   256  		}
   257  		filters = append(filters, &ec2.Filter{
   258  			Name:   aws.String(m["name"].(string)),
   259  			Values: filterValues,
   260  		})
   261  	}
   262  	return filters
   263  }
   264  
   265  type imageSort []*ec2.Image
   266  
   267  func (a imageSort) Len() int      { return len(a) }
   268  func (a imageSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   269  func (a imageSort) Less(i, j int) bool {
   270  	itime, _ := time.Parse(time.RFC3339, *a[i].CreationDate)
   271  	jtime, _ := time.Parse(time.RFC3339, *a[j].CreationDate)
   272  	return itime.Unix() < jtime.Unix()
   273  }
   274  
   275  // Returns the most recent AMI out of a slice of images.
   276  func mostRecentAmi(images []*ec2.Image) *ec2.Image {
   277  	sortedImages := images
   278  	sort.Sort(imageSort(sortedImages))
   279  	return sortedImages[len(sortedImages)-1]
   280  }
   281  
   282  // populate the numerous fields that the image description returns.
   283  func amiDescriptionAttributes(d *schema.ResourceData, image *ec2.Image) error {
   284  	// Simple attributes first
   285  	d.SetId(*image.ImageId)
   286  	d.Set("architecture", *image.Architecture)
   287  	d.Set("creation_date", *image.CreationDate)
   288  	if image.Description != nil {
   289  		d.Set("description", *image.Description)
   290  	}
   291  	d.Set("hypervisor", *image.Hypervisor)
   292  	d.Set("image_id", *image.ImageId)
   293  	d.Set("image_location", *image.ImageLocation)
   294  	if image.ImageOwnerAlias != nil {
   295  		d.Set("image_owner_alias", *image.ImageOwnerAlias)
   296  	}
   297  	d.Set("image_type", *image.ImageType)
   298  	if image.KernelId != nil {
   299  		d.Set("kernel_id", *image.KernelId)
   300  	}
   301  	d.Set("name", *image.Name)
   302  	d.Set("owner_id", *image.OwnerId)
   303  	if image.Platform != nil {
   304  		d.Set("platform", *image.Platform)
   305  	}
   306  	d.Set("public", *image.Public)
   307  	if image.RamdiskId != nil {
   308  		d.Set("ramdisk_id", *image.RamdiskId)
   309  	}
   310  	if image.RootDeviceName != nil {
   311  		d.Set("root_device_name", *image.RootDeviceName)
   312  	}
   313  	d.Set("root_device_type", *image.RootDeviceType)
   314  	if image.SriovNetSupport != nil {
   315  		d.Set("sriov_net_support", *image.SriovNetSupport)
   316  	}
   317  	d.Set("state", *image.State)
   318  	d.Set("virtualization_type", *image.VirtualizationType)
   319  	// Complex types get their own functions
   320  	if err := d.Set("block_device_mappings", amiBlockDeviceMappings(image.BlockDeviceMappings)); err != nil {
   321  		return err
   322  	}
   323  	if err := d.Set("product_codes", amiProductCodes(image.ProductCodes)); err != nil {
   324  		return err
   325  	}
   326  	if err := d.Set("state_reason", amiStateReason(image.StateReason)); err != nil {
   327  		return err
   328  	}
   329  	if err := d.Set("tags", amiTags(image.Tags)); err != nil {
   330  		return err
   331  	}
   332  	return nil
   333  }
   334  
   335  // Returns a set of block device mappings.
   336  func amiBlockDeviceMappings(m []*ec2.BlockDeviceMapping) *schema.Set {
   337  	s := &schema.Set{
   338  		F: amiBlockDeviceMappingHash,
   339  	}
   340  	for _, v := range m {
   341  		mapping := map[string]interface{}{
   342  			"device_name": *v.DeviceName,
   343  		}
   344  		if v.Ebs != nil {
   345  			ebs := map[string]interface{}{
   346  				"delete_on_termination": fmt.Sprintf("%t", *v.Ebs.DeleteOnTermination),
   347  				"encrypted":             fmt.Sprintf("%t", *v.Ebs.Encrypted),
   348  				"volume_size":           fmt.Sprintf("%d", *v.Ebs.VolumeSize),
   349  				"volume_type":           *v.Ebs.VolumeType,
   350  			}
   351  			// Iops is not always set
   352  			if v.Ebs.Iops != nil {
   353  				ebs["iops"] = fmt.Sprintf("%d", *v.Ebs.Iops)
   354  			} else {
   355  				ebs["iops"] = "0"
   356  			}
   357  			// snapshot id may not be set
   358  			if v.Ebs.SnapshotId != nil {
   359  				ebs["snapshot_id"] = *v.Ebs.SnapshotId
   360  			}
   361  
   362  			mapping["ebs"] = ebs
   363  		}
   364  		if v.VirtualName != nil {
   365  			mapping["virtual_name"] = *v.VirtualName
   366  		}
   367  		log.Printf("[DEBUG] aws_ami - adding block device mapping: %v", mapping)
   368  		s.Add(mapping)
   369  	}
   370  	return s
   371  }
   372  
   373  // Returns a set of product codes.
   374  func amiProductCodes(m []*ec2.ProductCode) *schema.Set {
   375  	s := &schema.Set{
   376  		F: amiProductCodesHash,
   377  	}
   378  	for _, v := range m {
   379  		code := map[string]interface{}{
   380  			"product_code_id":   *v.ProductCodeId,
   381  			"product_code_type": *v.ProductCodeType,
   382  		}
   383  		s.Add(code)
   384  	}
   385  	return s
   386  }
   387  
   388  // Returns the state reason.
   389  func amiStateReason(m *ec2.StateReason) map[string]interface{} {
   390  	s := make(map[string]interface{})
   391  	if m != nil {
   392  		s["code"] = *m.Code
   393  		s["message"] = *m.Message
   394  	} else {
   395  		s["code"] = "UNSET"
   396  		s["message"] = "UNSET"
   397  	}
   398  	return s
   399  }
   400  
   401  // Returns a set of tags.
   402  func amiTags(m []*ec2.Tag) *schema.Set {
   403  	s := &schema.Set{
   404  		F: amiTagsHash,
   405  	}
   406  	for _, v := range m {
   407  		tag := map[string]interface{}{
   408  			"key":   *v.Key,
   409  			"value": *v.Value,
   410  		}
   411  		s.Add(tag)
   412  	}
   413  	return s
   414  }
   415  
   416  // Generates a hash for the set hash function used by the block_device_mappings
   417  // attribute.
   418  func amiBlockDeviceMappingHash(v interface{}) int {
   419  	var buf bytes.Buffer
   420  	// All keys added in alphabetical order.
   421  	m := v.(map[string]interface{})
   422  	buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
   423  	if d, ok := m["ebs"]; ok {
   424  		if len(d.(map[string]interface{})) > 0 {
   425  			e := d.(map[string]interface{})
   426  			buf.WriteString(fmt.Sprintf("%s-", e["delete_on_termination"].(string)))
   427  			buf.WriteString(fmt.Sprintf("%s-", e["encrypted"].(string)))
   428  			buf.WriteString(fmt.Sprintf("%s-", e["iops"].(string)))
   429  			buf.WriteString(fmt.Sprintf("%s-", e["volume_size"].(string)))
   430  			buf.WriteString(fmt.Sprintf("%s-", e["volume_type"].(string)))
   431  		}
   432  	}
   433  	if d, ok := m["no_device"]; ok {
   434  		buf.WriteString(fmt.Sprintf("%s-", d.(string)))
   435  	}
   436  	if d, ok := m["virtual_name"]; ok {
   437  		buf.WriteString(fmt.Sprintf("%s-", d.(string)))
   438  	}
   439  	if d, ok := m["snapshot_id"]; ok {
   440  		buf.WriteString(fmt.Sprintf("%s-", d.(string)))
   441  	}
   442  	return hashcode.String(buf.String())
   443  }
   444  
   445  // Generates a hash for the set hash function used by the product_codes
   446  // attribute.
   447  func amiProductCodesHash(v interface{}) int {
   448  	var buf bytes.Buffer
   449  	m := v.(map[string]interface{})
   450  	// All keys added in alphabetical order.
   451  	buf.WriteString(fmt.Sprintf("%s-", m["product_code_id"].(string)))
   452  	buf.WriteString(fmt.Sprintf("%s-", m["product_code_type"].(string)))
   453  	return hashcode.String(buf.String())
   454  }
   455  
   456  // Generates a hash for the set hash function used by the tags
   457  // attribute.
   458  func amiTagsHash(v interface{}) int {
   459  	var buf bytes.Buffer
   460  	m := v.(map[string]interface{})
   461  	// All keys added in alphabetical order.
   462  	buf.WriteString(fmt.Sprintf("%s-", m["key"].(string)))
   463  	buf.WriteString(fmt.Sprintf("%s-", m["value"].(string)))
   464  	return hashcode.String(buf.String())
   465  }