diff --git a/builder/amazon/common/build_filter.go b/builder/amazon/common/build_filter.go new file mode 100644 index 000000000..8f621efc8 --- /dev/null +++ b/builder/amazon/common/build_filter.go @@ -0,0 +1,17 @@ +package common + +import ( + "github.com/aws/aws-sdk-go/service/ec2" +) + +// Build a slice of EC2 (AMI/Subnet/VPC) filter options from the filters provided. +func buildEc2Filters(input map[*string]*string) []*ec2.Filter { + var filters []*ec2.Filter + for k, v := range input { + filters = append(filters, &ec2.Filter{ + Name: k, + Values: []*string{v}, + }) + } + return filters +} diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index ee46cfea1..6973d6f35 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -29,33 +29,62 @@ func (d *AmiFilterOptions) NoOwner() bool { return len(d.Owners) == 0 } +type SubnetFilterOptions struct { + Filters map[*string]*string + MostFree bool `mapstructure:"most_free"` + Random bool `mapstructure:"random"` +} + +func (d *SubnetFilterOptions) Empty() bool { + return len(d.Filters) == 0 +} + +type VpcFilterOptions struct { + Filters map[*string]*string +} + +func (d *VpcFilterOptions) Empty() bool { + return len(d.Filters) == 0 +} + +type SecurityGroupFilterOptions struct { + Filters map[*string]*string +} + +func (d *SecurityGroupFilterOptions) Empty() bool { + return len(d.Filters) == 0 +} + // RunConfig contains configuration for running an instance from a source // AMI and details on how to access that launched image. type RunConfig struct { - AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"` - AvailabilityZone string `mapstructure:"availability_zone"` - BlockDurationMinutes int64 `mapstructure:"block_duration_minutes"` - DisableStopInstance bool `mapstructure:"disable_stop_instance"` - EbsOptimized bool `mapstructure:"ebs_optimized"` - EnableT2Unlimited bool `mapstructure:"enable_t2_unlimited"` - IamInstanceProfile string `mapstructure:"iam_instance_profile"` - InstanceInitiatedShutdownBehavior string `mapstructure:"shutdown_behavior"` - InstanceType string `mapstructure:"instance_type"` - RunTags map[string]string `mapstructure:"run_tags"` - SecurityGroupId string `mapstructure:"security_group_id"` - SecurityGroupIds []string `mapstructure:"security_group_ids"` - SourceAmi string `mapstructure:"source_ami"` - SourceAmiFilter AmiFilterOptions `mapstructure:"source_ami_filter"` - SpotPrice string `mapstructure:"spot_price"` - SpotPriceAutoProduct string `mapstructure:"spot_price_auto_product"` - SpotTags map[string]string `mapstructure:"spot_tags"` - SubnetId string `mapstructure:"subnet_id"` - TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"` - TemporarySGSourceCidr string `mapstructure:"temporary_security_group_source_cidr"` - UserData string `mapstructure:"user_data"` - UserDataFile string `mapstructure:"user_data_file"` - VpcId string `mapstructure:"vpc_id"` - WindowsPasswordTimeout time.Duration `mapstructure:"windows_password_timeout"` + AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"` + AvailabilityZone string `mapstructure:"availability_zone"` + BlockDurationMinutes int64 `mapstructure:"block_duration_minutes"` + DisableStopInstance bool `mapstructure:"disable_stop_instance"` + EbsOptimized bool `mapstructure:"ebs_optimized"` + EnableT2Unlimited bool `mapstructure:"enable_t2_unlimited"` + IamInstanceProfile string `mapstructure:"iam_instance_profile"` + InstanceInitiatedShutdownBehavior string `mapstructure:"shutdown_behavior"` + InstanceType string `mapstructure:"instance_type"` + SecurityGroupFilter SecurityGroupFilterOptions `mapstructure:"security_group_filter"` + RunTags map[string]string `mapstructure:"run_tags"` + SecurityGroupId string `mapstructure:"security_group_id"` + SecurityGroupIds []string `mapstructure:"security_group_ids"` + SourceAmi string `mapstructure:"source_ami"` + SourceAmiFilter AmiFilterOptions `mapstructure:"source_ami_filter"` + SpotPrice string `mapstructure:"spot_price"` + SpotPriceAutoProduct string `mapstructure:"spot_price_auto_product"` + SpotTags map[string]string `mapstructure:"spot_tags"` + SubnetFilter SubnetFilterOptions `mapstructure:"subnet_filter"` + SubnetId string `mapstructure:"subnet_id"` + TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"` + TemporarySGSourceCidr string `mapstructure:"temporary_security_group_source_cidr"` + UserData string `mapstructure:"user_data"` + UserDataFile string `mapstructure:"user_data_file"` + VpcFilter VpcFilterOptions `mapstructure:"vpc_filter"` + VpcId string `mapstructure:"vpc_id"` + WindowsPasswordTimeout time.Duration `mapstructure:"windows_password_timeout"` // Communicator settings Comm communicator.Config `mapstructure:",squash"` diff --git a/builder/amazon/common/step_network_info.go b/builder/amazon/common/step_network_info.go new file mode 100644 index 000000000..27cd99d73 --- /dev/null +++ b/builder/amazon/common/step_network_info.go @@ -0,0 +1,154 @@ +package common + +import ( + "context" + "fmt" + "log" + "math/rand" + "sort" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +// StepNetworkInfo queries AWS for information about +// VPC's and Subnets that is used throughout the AMI creation process. +// +// Produces (adding them to the state bag): +// vpc_id string - the VPC ID +// subnet_id string - the Subnet ID +// availability_zone string - the AZ name +type StepNetworkInfo struct { + VpcId string + VpcFilter VpcFilterOptions + SubnetId string + SubnetFilter SubnetFilterOptions + AvailabilityZone string + SecurityGroupIds []string + SecurityGroupFilter SecurityGroupFilterOptions +} + +type subnetsSort []*ec2.Subnet + +func (a subnetsSort) Len() int { return len(a) } +func (a subnetsSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a subnetsSort) Less(i, j int) bool { + return *a[i].AvailableIpAddressCount < *a[j].AvailableIpAddressCount +} + +// Returns the most recent AMI out of a slice of images. +func mostFreeSubnet(subnets []*ec2.Subnet) *ec2.Subnet { + sortedSubnets := subnets + sort.Sort(subnetsSort(sortedSubnets)) + return sortedSubnets[len(sortedSubnets)-1] +} + +func (s *StepNetworkInfo) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + ec2conn := state.Get("ec2").(*ec2.EC2) + ui := state.Get("ui").(packer.Ui) + + // VPC + if s.VpcId == "" && !s.VpcFilter.Empty() { + params := &ec2.DescribeVpcsInput{} + params.Filters = buildEc2Filters(s.VpcFilter.Filters) + s.VpcFilter.Filters[aws.String("state")] = aws.String("available") + + log.Printf("Using VPC Filters %v", params) + + vpcResp, err := ec2conn.DescribeVpcs(params) + if err != nil { + err := fmt.Errorf("Error querying VPCs: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + if len(vpcResp.Vpcs) != 1 { + err := fmt.Errorf("Exactly one VPC should match the filter, but %d VPC's was found matching filters: %v", len(vpcResp.Vpcs), params) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + s.VpcId = *vpcResp.Vpcs[0].VpcId + ui.Message(fmt.Sprintf("Found VPC ID: %s", s.VpcId)) + } + + // Subnet + if s.SubnetId == "" && !s.SubnetFilter.Empty() { + params := &ec2.DescribeSubnetsInput{} + s.SubnetFilter.Filters[aws.String("state")] = aws.String("available") + + if s.VpcId != "" { + s.SubnetFilter.Filters[aws.String("vpc-id")] = &s.VpcId + } + if s.AvailabilityZone != "" { + s.SubnetFilter.Filters[aws.String("availability-zone")] = &s.AvailabilityZone + } + params.Filters = buildEc2Filters(s.SubnetFilter.Filters) + log.Printf("Using Subnet Filters %v", params) + + subnetsResp, err := ec2conn.DescribeSubnets(params) + if err != nil { + err := fmt.Errorf("Error querying Subnets: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + if len(subnetsResp.Subnets) == 0 { + err := fmt.Errorf("No Subnets was found matching filters: %v", params) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + if len(subnetsResp.Subnets) > 1 && !s.SubnetFilter.Random && !s.SubnetFilter.MostFree { + err := fmt.Errorf("Your filter matched %d Subnets. Please try a more specific search, or set random or most_free to true.", len(subnetsResp.Subnets)) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + var subnet *ec2.Subnet + switch { + case s.SubnetFilter.MostFree: + subnet = mostFreeSubnet(subnetsResp.Subnets) + case s.SubnetFilter.Random: + subnet = subnetsResp.Subnets[rand.Intn(len(subnetsResp.Subnets))] + default: + subnet = subnetsResp.Subnets[0] + } + s.SubnetId = *subnet.SubnetId + ui.Message(fmt.Sprintf("Found Subnet ID: %s", s.SubnetId)) + } + + // Try to find AZ and VPC Id from Subnet if they are not yet found/given + if s.SubnetId != "" && (s.AvailabilityZone == "" || s.VpcId == "") { + log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", s.SubnetId) + resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&s.SubnetId}}) + if err != nil { + err := fmt.Errorf("Describing the subnet: %s returned error: %s.", s.SubnetId, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + if s.AvailabilityZone == "" { + s.AvailabilityZone = *resp.Subnets[0].AvailabilityZone + log.Printf("[INFO] AvailabilityZone found: '%s'", s.AvailabilityZone) + } + if s.VpcId == "" { + s.VpcId = *resp.Subnets[0].VpcId + log.Printf("[INFO] VpcId found: '%s'", s.VpcId) + } + } + + state.Put("vpc_id", s.VpcId) + state.Put("availability_zone", s.AvailabilityZone) + state.Put("subnet_id", s.SubnetId) + return multistep.ActionContinue +} + +func (s *StepNetworkInfo) Cleanup(multistep.StateBag) {} diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index a63ca1a47..8f06997f8 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -20,7 +20,6 @@ import ( type StepRunSourceInstance struct { AssociatePublicIpAddress bool - AvailabilityZone string BlockDevices BlockDevices Comm *communicator.Config Ctx interpolate.Context @@ -33,7 +32,6 @@ type StepRunSourceInstance struct { InstanceType string IsRestricted bool SourceAMI string - SubnetId string Tags TagMap UserData string UserDataFile string @@ -104,6 +102,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa return multistep.ActionHalt } + az := state.Get("availability_zone").(string) runOpts := &ec2.RunInstancesInput{ ImageId: &s.SourceAMI, InstanceType: &s.InstanceType, @@ -112,7 +111,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa MinCount: aws.Int64(1), IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: &s.IamInstanceProfile}, BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(), - Placement: &ec2.Placement{AvailabilityZone: &s.AvailabilityZone}, + Placement: &ec2.Placement{AvailabilityZone: &az}, EbsOptimized: &s.EbsOptimized, } @@ -153,18 +152,20 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa runOpts.KeyName = &s.Comm.SSHKeyPairName } - if s.SubnetId != "" && s.AssociatePublicIpAddress { + subnetId := state.Get("subnet_id").(string) + + if subnetId != "" && s.AssociatePublicIpAddress { runOpts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{ { DeviceIndex: aws.Int64(0), AssociatePublicIpAddress: &s.AssociatePublicIpAddress, - SubnetId: &s.SubnetId, + SubnetId: aws.String(subnetId), Groups: securityGroupIds, DeleteOnTermination: aws.Bool(true), }, } } else { - runOpts.SubnetId = &s.SubnetId + runOpts.SubnetId = aws.String(subnetId) runOpts.SecurityGroupIds = securityGroupIds } diff --git a/builder/amazon/common/step_run_spot_instance.go b/builder/amazon/common/step_run_spot_instance.go index c70863d8e..f36615073 100644 --- a/builder/amazon/common/step_run_spot_instance.go +++ b/builder/amazon/common/step_run_spot_instance.go @@ -22,7 +22,6 @@ import ( type StepRunSpotInstance struct { AssociatePublicIpAddress bool - AvailabilityZone string BlockDevices BlockDevices BlockDurationMinutes int64 Debug bool @@ -36,7 +35,6 @@ type StepRunSpotInstance struct { SpotPrice string SpotPriceProduct string SpotTags TagMap - SubnetId string Tags TagMap VolumeTags TagMap UserData string @@ -86,7 +84,12 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) } spotPrice := s.SpotPrice - availabilityZone := s.AvailabilityZone + azConfig := "" + if azRaw, ok := state.GetOk("availability_zone"); ok { + azConfig = azRaw.(string) + } + az := azConfig + if spotPrice == "auto" { ui.Message(fmt.Sprintf( "Finding spot price for %s %s...", @@ -97,7 +100,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) resp, err := ec2conn.DescribeSpotPriceHistory(&ec2.DescribeSpotPriceHistoryInput{ InstanceTypes: []*string{&s.InstanceType}, ProductDescriptions: []*string{&s.SpotPriceProduct}, - AvailabilityZone: &s.AvailabilityZone, + AvailabilityZone: &az, StartTime: &startTime, }) if err != nil { @@ -117,8 +120,8 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) } if price == 0 || current < price { price = current - if s.AvailabilityZone == "" { - availabilityZone = *history.AvailabilityZone + if azConfig == "" { + az = *history.AvailabilityZone } } } @@ -162,24 +165,26 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) UserData: &userData, IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: &s.IamInstanceProfile}, Placement: &ec2.SpotPlacement{ - AvailabilityZone: &availabilityZone, + AvailabilityZone: &az, }, BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(), EbsOptimized: &s.EbsOptimized, } - if s.SubnetId != "" && s.AssociatePublicIpAddress { + subnetId := state.Get("subnet_id").(string) + + if subnetId != "" && s.AssociatePublicIpAddress { runOpts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{ { DeviceIndex: aws.Int64(0), AssociatePublicIpAddress: &s.AssociatePublicIpAddress, - SubnetId: &s.SubnetId, + SubnetId: &subnetId, Groups: securityGroupIds, DeleteOnTermination: aws.Bool(true), }, } } else { - runOpts.SubnetId = &s.SubnetId + runOpts.SubnetId = &subnetId runOpts.SecurityGroupIds = securityGroupIds } diff --git a/builder/amazon/common/step_security_group.go b/builder/amazon/common/step_security_group.go index 78e3dab51..97bf556f6 100644 --- a/builder/amazon/common/step_security_group.go +++ b/builder/amazon/common/step_security_group.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "strings" "time" "github.com/aws/aws-sdk-go/aws" @@ -17,8 +18,8 @@ import ( type StepSecurityGroup struct { CommConfig *communicator.Config + SecurityGroupFilter SecurityGroupFilterOptions SecurityGroupIds []string - VpcId string TemporarySGSourceCidr string createdGroupId string @@ -27,6 +28,7 @@ type StepSecurityGroup struct { func (s *StepSecurityGroup) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) + vpcId := state.Get("vpc_id").(string) if len(s.SecurityGroupIds) > 0 { _, err := ec2conn.DescribeSecurityGroups( @@ -45,6 +47,35 @@ func (s *StepSecurityGroup) Run(_ context.Context, state multistep.StateBag) mul return multistep.ActionContinue } + if !s.SecurityGroupFilter.Empty() { + + params := &ec2.DescribeSecurityGroupsInput{} + if vpcId != "" { + s.SecurityGroupFilter.Filters[aws.String("vpc-id")] = &vpcId + } + params.Filters = buildEc2Filters(s.SecurityGroupFilter.Filters) + + log.Printf("Using SecurityGroup Filters %v", params) + + sgResp, err := ec2conn.DescribeSecurityGroups(params) + if err != nil { + err := fmt.Errorf("Couldn't find security groups for filter: %s", err) + log.Printf("[DEBUG] %s", err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + + securityGroupIds := []string{} + for _, sg := range sgResp.SecurityGroups { + securityGroupIds = append(securityGroupIds, *sg.GroupId) + } + + ui.Message(fmt.Sprintf("Found Security Group(s): %s", strings.Join(securityGroupIds, ", "))) + state.Put("securityGroupIds", securityGroupIds) + + return multistep.ActionContinue + } + port := s.CommConfig.Port() if port == 0 { if s.CommConfig.Type != "none" { @@ -60,9 +91,7 @@ func (s *StepSecurityGroup) Run(_ context.Context, state multistep.StateBag) mul Description: aws.String("Temporary group for Packer"), } - if s.VpcId != "" { - group.VpcId = &s.VpcId - } + group.VpcId = &vpcId groupResp, err := ec2conn.CreateSecurityGroup(group) if err != nil { diff --git a/builder/amazon/common/step_source_ami_info.go b/builder/amazon/common/step_source_ami_info.go index cb7a08b51..0486e6d45 100644 --- a/builder/amazon/common/step_source_ami_info.go +++ b/builder/amazon/common/step_source_ami_info.go @@ -25,18 +25,6 @@ type StepSourceAMIInfo struct { AmiFilters AmiFilterOptions } -// Build a slice of AMI filter options from the filters provided. -func buildAmiFilters(input map[*string]*string) []*ec2.Filter { - var filters []*ec2.Filter - for k, v := range input { - filters = append(filters, &ec2.Filter{ - Name: k, - Values: []*string{v}, - }) - } - return filters -} - type imageSort []*ec2.Image func (a imageSort) Len() int { return len(a) } @@ -66,7 +54,7 @@ func (s *StepSourceAMIInfo) Run(_ context.Context, state multistep.StateBag) mul // We have filters to apply if len(s.AmiFilters.Filters) > 0 { - params.Filters = buildAmiFilters(s.AmiFilters.Filters) + params.Filters = buildEc2Filters(s.AmiFilters.Filters) } if len(s.AmiFilters.Owners) > 0 { params.Owners = s.AmiFilters.Owners diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 1980a770d..7557abc8d 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -93,23 +93,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } ec2conn := ec2.New(session) - // If the subnet is specified but not the VpcId or AZ, try to determine them automatically - if b.config.SubnetId != "" && (b.config.AvailabilityZone == "" || b.config.VpcId == "") { - log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", b.config.SubnetId) - resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}}) - if err != nil { - return nil, err - } - if b.config.AvailabilityZone == "" { - b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone - log.Printf("[INFO] AvailabilityZone found: '%s'", b.config.AvailabilityZone) - } - if b.config.VpcId == "" { - b.config.VpcId = *resp.Subnets[0].VpcId - log.Printf("[INFO] VpcId found: '%s'", b.config.VpcId) - } - } - // Setup the state bag and initial state for the steps state := new(multistep.BasicStateBag) state.Put("config", &b.config) @@ -123,7 +106,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.BlockDevices, BlockDurationMinutes: b.config.BlockDurationMinutes, Ctx: b.config.ctx, @@ -138,7 +120,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SpotPrice: b.config.SpotPrice, SpotPriceProduct: b.config.SpotPriceAutoProduct, SpotTags: b.config.SpotTags, - SubnetId: b.config.SubnetId, Tags: b.config.RunTags, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, @@ -147,7 +128,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } else { instanceStep = &awscommon.StepRunSourceInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.BlockDevices, Comm: &b.config.RunConfig.Comm, Ctx: b.config.ctx, @@ -160,7 +140,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe InstanceType: b.config.InstanceType, IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(), SourceAMI: b.config.SourceAmi, - SubnetId: b.config.SubnetId, Tags: b.config.RunTags, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, @@ -181,15 +160,24 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe AmiFilters: b.config.SourceAmiFilter, AMIVirtType: b.config.AMIVirtType, }, + &awscommon.StepNetworkInfo{ + VpcId: b.config.VpcId, + VpcFilter: b.config.VpcFilter, + SecurityGroupIds: b.config.SecurityGroupIds, + SecurityGroupFilter: b.config.SecurityGroupFilter, + SubnetId: b.config.SubnetId, + SubnetFilter: b.config.SubnetFilter, + AvailabilityZone: b.config.AvailabilityZone, + }, &awscommon.StepKeyPair{ Debug: b.config.PackerDebug, Comm: &b.config.RunConfig.Comm, DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), }, &awscommon.StepSecurityGroup{ + SecurityGroupFilter: b.config.SecurityGroupFilter, SecurityGroupIds: b.config.SecurityGroupIds, CommConfig: &b.config.RunConfig.Comm, - VpcId: b.config.VpcId, TemporarySGSourceCidr: b.config.TemporarySGSourceCidr, }, &awscommon.StepCleanupVolumes{ diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index 59537572b..755d79139 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -107,23 +107,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } ec2conn := ec2.New(session) - // If the subnet is specified but not the VpcId or AZ, try to determine them automatically - if b.config.SubnetId != "" && (b.config.AvailabilityZone == "" || b.config.VpcId == "") { - log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", b.config.SubnetId) - resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}}) - if err != nil { - return nil, err - } - if b.config.AvailabilityZone == "" { - b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone - log.Printf("[INFO] AvailabilityZone found: '%s'", b.config.AvailabilityZone) - } - if b.config.VpcId == "" { - b.config.VpcId = *resp.Subnets[0].VpcId - log.Printf("[INFO] VpcId found: '%s'", b.config.VpcId) - } - } - // Setup the state bag and initial state for the steps state := new(multistep.BasicStateBag) state.Put("config", &b.config) @@ -137,7 +120,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.BlockDevices, BlockDurationMinutes: b.config.BlockDurationMinutes, Ctx: b.config.ctx, @@ -152,7 +134,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SpotPrice: b.config.SpotPrice, SpotPriceProduct: b.config.SpotPriceAutoProduct, SpotTags: b.config.SpotTags, - SubnetId: b.config.SubnetId, Tags: b.config.RunTags, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, @@ -161,7 +142,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } else { instanceStep = &awscommon.StepRunSourceInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.BlockDevices, Comm: &b.config.RunConfig.Comm, Ctx: b.config.ctx, @@ -174,7 +154,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe InstanceType: b.config.InstanceType, IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(), SourceAMI: b.config.SourceAmi, - SubnetId: b.config.SubnetId, Tags: b.config.RunTags, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, @@ -198,15 +177,24 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe AmiFilters: b.config.SourceAmiFilter, AMIVirtType: b.config.AMIVirtType, }, + &awscommon.StepNetworkInfo{ + VpcId: b.config.VpcId, + VpcFilter: b.config.VpcFilter, + SecurityGroupIds: b.config.SecurityGroupIds, + SecurityGroupFilter: b.config.SecurityGroupFilter, + SubnetId: b.config.SubnetId, + SubnetFilter: b.config.SubnetFilter, + AvailabilityZone: b.config.AvailabilityZone, + }, &awscommon.StepKeyPair{ Debug: b.config.PackerDebug, Comm: &b.config.RunConfig.Comm, DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), }, &awscommon.StepSecurityGroup{ + SecurityGroupFilter: b.config.SecurityGroupFilter, SecurityGroupIds: b.config.SecurityGroupIds, CommConfig: &b.config.RunConfig.Comm, - VpcId: b.config.VpcId, TemporarySGSourceCidr: b.config.TemporarySGSourceCidr, }, &awscommon.StepCleanupVolumes{ diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index 643b55a2a..0cc7f0754 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -92,23 +92,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } ec2conn := ec2.New(session) - // If the subnet is specified but not the VpcId or AZ, try to determine them automatically - if b.config.SubnetId != "" && (b.config.AvailabilityZone == "" || b.config.VpcId == "") { - log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", b.config.SubnetId) - resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}}) - if err != nil { - return nil, err - } - if b.config.AvailabilityZone == "" { - b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone - log.Printf("[INFO] AvailabilityZone found: '%s'", b.config.AvailabilityZone) - } - if b.config.VpcId == "" { - b.config.VpcId = *resp.Subnets[0].VpcId - log.Printf("[INFO] VpcId found: '%s'", b.config.VpcId) - } - } - // Setup the state bag and initial state for the steps state := new(multistep.BasicStateBag) state.Put("config", &b.config) @@ -121,7 +104,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.launchBlockDevices, BlockDurationMinutes: b.config.BlockDurationMinutes, Ctx: b.config.ctx, @@ -136,7 +118,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SpotPrice: b.config.SpotPrice, SpotPriceProduct: b.config.SpotPriceAutoProduct, SpotTags: b.config.SpotTags, - SubnetId: b.config.SubnetId, Tags: b.config.RunTags, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, @@ -144,7 +125,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } else { instanceStep = &awscommon.StepRunSourceInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.launchBlockDevices, Comm: &b.config.RunConfig.Comm, Ctx: b.config.ctx, @@ -157,7 +137,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe InstanceType: b.config.InstanceType, IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(), SourceAMI: b.config.SourceAmi, - SubnetId: b.config.SubnetId, Tags: b.config.RunTags, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, @@ -172,15 +151,24 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe EnableAMIENASupport: b.config.AMIENASupport, AmiFilters: b.config.SourceAmiFilter, }, + &awscommon.StepNetworkInfo{ + VpcId: b.config.VpcId, + VpcFilter: b.config.VpcFilter, + SecurityGroupIds: b.config.SecurityGroupIds, + SecurityGroupFilter: b.config.SecurityGroupFilter, + SubnetId: b.config.SubnetId, + SubnetFilter: b.config.SubnetFilter, + AvailabilityZone: b.config.AvailabilityZone, + }, &awscommon.StepKeyPair{ Debug: b.config.PackerDebug, Comm: &b.config.RunConfig.Comm, DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), }, &awscommon.StepSecurityGroup{ + SecurityGroupFilter: b.config.SecurityGroupFilter, SecurityGroupIds: b.config.SecurityGroupIds, CommConfig: &b.config.RunConfig.Comm, - VpcId: b.config.VpcId, TemporarySGSourceCidr: b.config.TemporarySGSourceCidr, }, instanceStep, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 6872f3883..68d06d7b2 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -177,23 +177,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } ec2conn := ec2.New(session) - // If the subnet is specified but not the VpcId or AZ, try to determine them automatically - if b.config.SubnetId != "" && (b.config.AvailabilityZone == "" || b.config.VpcId == "") { - log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", b.config.SubnetId) - resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}}) - if err != nil { - return nil, err - } - if b.config.AvailabilityZone == "" { - b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone - log.Printf("[INFO] AvailabilityZone found: '%s'", b.config.AvailabilityZone) - } - if b.config.VpcId == "" { - b.config.VpcId = *resp.Subnets[0].VpcId - log.Printf("[INFO] VpcId found: '%s'", b.config.VpcId) - } - } - // Setup the state bag and initial state for the steps state := new(multistep.BasicStateBag) state.Put("config", &b.config) @@ -207,7 +190,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.BlockDevices, BlockDurationMinutes: b.config.BlockDurationMinutes, Ctx: b.config.ctx, @@ -219,7 +201,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SourceAMI: b.config.SourceAmi, SpotPrice: b.config.SpotPrice, SpotPriceProduct: b.config.SpotPriceAutoProduct, - SubnetId: b.config.SubnetId, Tags: b.config.RunTags, SpotTags: b.config.SpotTags, UserData: b.config.UserData, @@ -228,7 +209,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } else { instanceStep = &awscommon.StepRunSourceInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.BlockDevices, Comm: &b.config.RunConfig.Comm, Ctx: b.config.ctx, @@ -239,7 +219,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe InstanceType: b.config.InstanceType, IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(), SourceAMI: b.config.SourceAmi, - SubnetId: b.config.SubnetId, Tags: b.config.RunTags, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, @@ -259,6 +238,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe AmiFilters: b.config.SourceAmiFilter, AMIVirtType: b.config.AMIVirtType, }, + &awscommon.StepNetworkInfo{ + VpcId: b.config.VpcId, + VpcFilter: b.config.VpcFilter, + SecurityGroupIds: b.config.SecurityGroupIds, + SecurityGroupFilter: b.config.SecurityGroupFilter, + SubnetId: b.config.SubnetId, + SubnetFilter: b.config.SubnetFilter, + AvailabilityZone: b.config.AvailabilityZone, + }, &awscommon.StepKeyPair{ Debug: b.config.PackerDebug, Comm: &b.config.RunConfig.Comm, @@ -266,8 +254,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &awscommon.StepSecurityGroup{ CommConfig: &b.config.RunConfig.Comm, + SecurityGroupFilter: b.config.SecurityGroupFilter, SecurityGroupIds: b.config.SecurityGroupIds, - VpcId: b.config.VpcId, TemporarySGSourceCidr: b.config.TemporarySGSourceCidr, }, instanceStep, diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index d54bd7dfd..13d95865d 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -278,6 +278,27 @@ builder. described above. Note that if this is specified, you must omit the `security_group_id`. +- `security_group_filter` (object) - Filters used to populate the `security_group_ids` field. + Example: + + ``` json + { + "security_group_filter": { + "filters": { + "tag:Class": "packer" + } + } + } + ``` + + This selects the SG's with tag `Class` with the value `packer`. + + - `filters` (map of strings) - filters used to select a `security_group_ids`. + Any filter described in the docs for [DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html) + is valid. + + `security_group_ids` take precendense over this. + - `temporary_security_group_source_cidr` (string) - An IPv4 CIDR block to be authorized access to the instance, when packer is creating a temporary security group. The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used @@ -401,6 +422,39 @@ builder. `subnet-12345def`, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC. +- `subnet_filter` (object) - Filters used to populate the `subnet_id` field. + Example: + + ``` json + { + "subnet_filter": { + "filters": { + "tag:Class": "build" + }, + "most_free": true, + "random": false + } + } + ``` + + This selects the Subnet with tag `Class` with the value `build`, which has + the most free IP addresses. + NOTE: This will fail unless *exactly* one Subnet is returned. By using + `most_free` or `random` one will be selected from those matching the filter. + + - `filters` (map of strings) - filters used to select a `subnet_id`. + NOTE: This will fail unless *exactly* one Subnet is returned. + Any filter described in the docs for [DescribeSubnets](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html) + is valid. + + - `most_free` (boolean) - The Subnet with the most free IPv4 addresses + will be used if multiple Subnets matches the filter. + + - `random` (boolean) - A random Subnet will be used if multiple Subnets + matches the filter. `most_free` have precendence over this. + + `subnet_id` take precedence over this. + - `tags` (object of key/value strings) - Tags applied to the AMI and relevant snapshots. This is a [template engine](/docs/templates/engine.html), @@ -427,6 +481,32 @@ builder. to be set. If this field is left blank, Packer will try to get the VPC ID from the `subnet_id`. +- `vpc_filter` (object) - Filters used to populate the `vpc_id` field. + Example: + + ``` json + { + "vpc_filter": { + "filters": { + "tag:Class": "build", + "isDefault": "false", + "cidr": "/24" + } + } + } + ``` + + This selects the VPC with tag `Class` with the value `build`, which is not the + default VPC, and have a IPv4 CIDR block of `/24`. + NOTE: This will fail unless *exactly* one VPC is returned. + + - `filters` (map of strings) - filters used to select a `vpc_id`. + NOTE: This will fail unless *exactly* one VPC is returned. + Any filter described in the docs for [DescribeVpcs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html) + is valid. + + `vpc_id` take precedence over this. + - `windows_password_timeout` (string) - The timeout for waiting for a Windows password for Windows instances. Defaults to 20 minutes. Example value: `10m` diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index a60609326..c3ec73da8 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -271,6 +271,27 @@ builder. described above. Note that if this is specified, you must omit the `security_group_id`. +- `security_group_filter` (object) - Filters used to populate the `security_group_ids` field. + Example: + + ``` json + { + "security_group_filter": { + "filters": { + "tag:Class": "packer" + } + } + } + ``` + + This selects the SG's with tag `Class` with the value `packer`. + + - `filters` (map of strings) - filters used to select a `security_group_ids`. + Any filter described in the docs for [DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html) + is valid. + + `security_group_ids` take precendense over this. + - `temporary_security_group_source_cidr` (string) - An IPv4 CIDR block to be authorized access to the instance, when packer is creating a temporary security group. The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used @@ -395,6 +416,39 @@ builder. `subnet-12345def`, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC. +- `subnet_filter` (object) - Filters used to populate the `subnet_id` field. + Example: + + ``` json + { + "subnet_filter": { + "filters": { + "tag:Class": "build" + }, + "most_free": true, + "random": false + } + } + ``` + + This selects the Subnet with tag `Class` with the value `build`, which has + the most free IP addresses. + NOTE: This will fail unless *exactly* one Subnet is returned. By using + `most_free` or `random` one will be selected from those matching the filter. + + - `filters` (map of strings) - filters used to select a `subnet_id`. + NOTE: This will fail unless *exactly* one Subnet is returned. + Any filter described in the docs for [DescribeSubnets](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html) + is valid. + + - `most_free` (boolean) - The Subnet with the most free IPv4 addresses + will be used if multiple Subnets matches the filter. + + - `random` (boolean) - A random Subnet will be used if multiple Subnets + matches the filter. `most_free` have precendence over this. + + `subnet_id` take precedence over this. + - `tags` (object of key/value strings) - Tags applied to the AMI and relevant snapshots. This is a [template engine](/docs/templates/engine.html), @@ -420,6 +474,32 @@ builder. to be set. If this field is left blank, Packer will try to get the VPC ID from the `subnet_id`. +- `vpc_filter` (object) - Filters used to populate the `vpc_id` field. + Example: + + ``` json + { + "vpc_filter": { + "filters": { + "tag:Class": "build", + "isDefault": "false", + "cidr": "/24" + } + } + } + ``` + + This selects the VPC with tag `Class` with the value `build`, which is not the + default VPC, and have a IPv4 CIDR block of `/24`. + NOTE: This will fail unless *exactly* one VPC is returned. + + - `filters` (map of strings) - filters used to select a `vpc_id`. + NOTE: This will fail unless *exactly* one VPC is returned. + Any filter described in the docs for [DescribeVpcs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html) + is valid. + + `vpc_id` take precedence over this. + - `windows_password_timeout` (string) - The timeout for waiting for a Windows password for Windows instances. Defaults to 20 minutes. Example value: `10m` diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index a52304529..4042f1ab6 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -204,6 +204,27 @@ builder. described above. Note that if this is specified, you must omit the `security_group_id`. +- `security_group_filter` (object) - Filters used to populate the `security_group_ids` field. + Example: + + ``` json + { + "security_group_filter": { + "filters": { + "tag:Class": "packer" + } + } + } + ``` + + This selects the SG's with tag `Class` with the value `packer`. + + - `filters` (map of strings) - filters used to select a `security_group_ids`. + Any filter described in the docs for [DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html) + is valid. + + `security_group_ids` take precendense over this. + - `temporary_security_group_source_cidr` (string) - An IPv4 CIDR block to be authorized access to the instance, when packer is creating a temporary security group. The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used @@ -313,6 +334,39 @@ builder. `subnet-12345def`, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC. +- `subnet_filter` (object) - Filters used to populate the `subnet_id` field. + Example: + + ``` json + { + "subnet_filter": { + "filters": { + "tag:Class": "build" + }, + "most_free": true, + "random": false + } + } + ``` + + This selects the Subnet with tag `Class` with the value `build`, which has + the most free IP addresses. + NOTE: This will fail unless *exactly* one Subnet is returned. By using + `most_free` or `random` one will be selected from those matching the filter. + + - `filters` (map of strings) - filters used to select a `subnet_id`. + NOTE: This will fail unless *exactly* one Subnet is returned. + Any filter described in the docs for [DescribeSubnets](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html) + is valid. + + - `most_free` (boolean) - The Subnet with the most free IPv4 addresses + will be used if multiple Subnets matches the filter. + + - `random` (boolean) - A random Subnet will be used if multiple Subnets + matches the filter. `most_free` have precendence over this. + + `subnet_id` take precedence over this. + - `temporary_key_pair_name` (string) - The name of the temporary key pair to generate. By default, Packer generates a name that looks like `packer_`, where <UUID> is a 36 character unique identifier. @@ -334,6 +388,32 @@ builder. to be set. If this field is left blank, Packer will try to get the VPC ID from the `subnet_id`. +- `vpc_filter` (object) - Filters used to populate the `vpc_id` field. + Example: + + ``` json + { + "vpc_filter": { + "filters": { + "tag:Class": "build", + "isDefault": "false", + "cidr": "/24" + } + } + } + ``` + + This selects the VPC with tag `Class` with the value `build`, which is not the + default VPC, and have a IPv4 CIDR block of `/24`. + NOTE: This will fail unless *exactly* one VPC is returned. + + - `filters` (map of strings) - filters used to select a `vpc_id`. + NOTE: This will fail unless *exactly* one VPC is returned. + Any filter described in the docs for [DescribeVpcs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html) + is valid. + + `vpc_id` take precedence over this. + - `windows_password_timeout` (string) - The timeout for waiting for a Windows password for Windows instances. Defaults to 20 minutes. Example value: `10m` diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index ee6f31195..136af3cb9 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -280,6 +280,27 @@ builder. described above. Note that if this is specified, you must omit the `security_group_id`. +- `security_group_filter` (object) - Filters used to populate the `security_group_ids` field. + Example: + + ``` json + { + "security_group_filter": { + "filters": { + "tag:Class": "packer" + } + } + } + ``` + + This selects the SG's with tag `Class` with the value `packer`. + + - `filters` (map of strings) - filters used to select a `security_group_ids`. + Any filter described in the docs for [DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html) + is valid. + + `security_group_ids` take precendense over this. + - `temporary_security_group_source_cidr` (string) - An IPv4 CIDR block to be authorized access to the instance, when packer is creating a temporary security group. The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used @@ -397,6 +418,39 @@ builder. `subnet-12345def`, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC. +- `subnet_filter` (object) - Filters used to populate the `subnet_id` field. + Example: + + ``` json + { + "subnet_filter": { + "filters": { + "tag:Class": "build" + }, + "most_free": true, + "random": false + } + } + ``` + + This selects the Subnet with tag `Class` with the value `build`, which has + the most free IP addresses. + NOTE: This will fail unless *exactly* one Subnet is returned. By using + `most_free` or `random` one will be selected from those matching the filter. + + - `filters` (map of strings) - filters used to select a `subnet_id`. + NOTE: This will fail unless *exactly* one Subnet is returned. + Any filter described in the docs for [DescribeSubnets](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html) + is valid. + + - `most_free` (boolean) - The Subnet with the most free IPv4 addresses + will be used if multiple Subnets matches the filter. + + - `random` (boolean) - A random Subnet will be used if multiple Subnets + matches the filter. `most_free` have precendence over this. + + `subnet_id` take precedence over this. + - `tags` (object of key/value strings) - Tags applied to the AMI. This is a [template engine](/docs/templates/engine.html), see [Build template data](#build-template-data) for more information. @@ -417,6 +471,32 @@ builder. to be set. If this field is left blank, Packer will try to get the VPC ID from the `subnet_id`. +- `vpc_filter` (object) - Filters used to populate the `vpc_id` field. + Example: + + ``` json + { + "vpc_filter": { + "filters": { + "tag:Class": "build", + "isDefault": "false", + "cidr": "/24" + } + } + } + ``` + + This selects the VPC with tag `Class` with the value `build`, which is not the + default VPC, and have a IPv4 CIDR block of `/24`. + NOTE: This will fail unless *exactly* one VPC is returned. + + - `filters` (map of strings) - filters used to select a `vpc_id`. + NOTE: This will fail unless *exactly* one VPC is returned. + Any filter described in the docs for [DescribeVpcs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html) + is valid. + + `vpc_id` take precedence over this. + - `x509_upload_path` (string) - The path on the remote machine where the X509 certificate will be uploaded. This path must already exist and be writable. X509 certificates are uploaded after provisioning is run, so it is perfectly