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 }