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 }