github.phpd.cn/hashicorp/packer@v1.3.2/builder/amazon/common/ami_config.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/packer/template/interpolate" 8 ) 9 10 // AMIConfig is for common configuration related to creating AMIs. 11 type AMIConfig struct { 12 AMIName string `mapstructure:"ami_name"` 13 AMIDescription string `mapstructure:"ami_description"` 14 AMIVirtType string `mapstructure:"ami_virtualization_type"` 15 AMIUsers []string `mapstructure:"ami_users"` 16 AMIGroups []string `mapstructure:"ami_groups"` 17 AMIProductCodes []string `mapstructure:"ami_product_codes"` 18 AMIRegions []string `mapstructure:"ami_regions"` 19 AMISkipRegionValidation bool `mapstructure:"skip_region_validation"` 20 AMITags TagMap `mapstructure:"tags"` 21 AMIENASupport *bool `mapstructure:"ena_support"` 22 AMISriovNetSupport bool `mapstructure:"sriov_support"` 23 AMIForceDeregister bool `mapstructure:"force_deregister"` 24 AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"` 25 AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"` 26 AMIKmsKeyId string `mapstructure:"kms_key_id"` 27 AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids"` 28 SnapshotTags TagMap `mapstructure:"snapshot_tags"` 29 SnapshotUsers []string `mapstructure:"snapshot_users"` 30 SnapshotGroups []string `mapstructure:"snapshot_groups"` 31 } 32 33 func stringInSlice(s []string, searchstr string) bool { 34 for _, item := range s { 35 if item == searchstr { 36 return true 37 } 38 } 39 return false 40 } 41 42 func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context) []error { 43 var errs []error 44 45 if c.AMIName == "" { 46 errs = append(errs, fmt.Errorf("ami_name must be specified")) 47 } 48 49 // Make sure that if we have region_kms_key_ids defined, 50 // the regions in region_kms_key_ids are also in ami_regions 51 if len(c.AMIRegionKMSKeyIDs) > 0 { 52 for kmsKeyRegion := range c.AMIRegionKMSKeyIDs { 53 if !stringInSlice(c.AMIRegions, kmsKeyRegion) { 54 errs = append(errs, fmt.Errorf("Region %s is in region_kms_key_ids but not in ami_regions", kmsKeyRegion)) 55 } 56 } 57 } 58 59 errs = append(errs, c.prepareRegions(accessConfig)...) 60 61 if len(c.AMIUsers) > 0 && c.AMIEncryptBootVolume { 62 errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume")) 63 } 64 65 if len(c.SnapshotUsers) > 0 { 66 if len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume { 67 errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key")) 68 } 69 if len(c.AMIRegionKMSKeyIDs) > 0 { 70 for _, kmsKey := range c.AMIRegionKMSKeyIDs { 71 if len(kmsKey) == 0 { 72 errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key")) 73 } 74 } 75 } 76 } 77 78 if len(c.AMIName) < 3 || len(c.AMIName) > 128 { 79 errs = append(errs, fmt.Errorf("ami_name must be between 3 and 128 characters long")) 80 } 81 82 if c.AMIName != templateCleanAMIName(c.AMIName) { 83 errs = append(errs, fmt.Errorf("AMIName should only contain "+ 84 "alphanumeric characters, parentheses (()), square brackets ([]), spaces "+ 85 "( ), periods (.), slashes (/), dashes (-), single quotes ('), at-signs "+ 86 "(@), or underscores(_). You can use the `clean_ami_name` template "+ 87 "filter to automatically clean your ami name.")) 88 } 89 90 if len(errs) > 0 { 91 return errs 92 } 93 94 return nil 95 } 96 97 func (c *AMIConfig) prepareRegions(accessConfig *AccessConfig) (errs []error) { 98 if len(c.AMIRegions) > 0 { 99 if !c.AMISkipRegionValidation { 100 // Verify the regions are real 101 err := accessConfig.ValidateRegion(c.AMIRegions...) 102 if err != nil { 103 errs = append(errs, fmt.Errorf("error validating regions: %v", err)) 104 } 105 } 106 107 regionSet := make(map[string]struct{}) 108 regions := make([]string, 0, len(c.AMIRegions)) 109 110 for _, region := range c.AMIRegions { 111 // If we already saw the region, then don't look again 112 if _, ok := regionSet[region]; ok { 113 continue 114 } 115 116 // Mark that we saw the region 117 regionSet[region] = struct{}{} 118 119 // Make sure that if we have region_kms_key_ids defined, 120 // the regions in ami_regions are also in region_kms_key_ids 121 if len(c.AMIRegionKMSKeyIDs) > 0 { 122 if _, ok := c.AMIRegionKMSKeyIDs[region]; !ok { 123 errs = append(errs, fmt.Errorf("Region %s is in ami_regions but not in region_kms_key_ids", region)) 124 } 125 } 126 if (accessConfig != nil) && (region == accessConfig.RawRegion) { 127 // make sure we don't try to copy to the region we originally 128 // create the AMI in. 129 log.Printf("Cannot copy AMI to AWS session region '%s', deleting it from `ami_regions`.", region) 130 continue 131 } 132 regions = append(regions, region) 133 } 134 135 c.AMIRegions = regions 136 } 137 return errs 138 }