github.com/mohanarpit/terraform@v0.6.16-0.20160909104007-291f29853544/builtin/providers/aws/data_source_aws_ami.go (about)

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