diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 54985cac8..932e416ad 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -376,6 +376,12 @@ type RunConfig struct { // subnet-12345def, where Packer will launch the EC2 instance. This field is // required if you are using an non-default VPC. SubnetId string `mapstructure:"subnet_id" required:"false"` + // [Tenancy](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-instance.html) used + // when Packer launches the EC2 instance, allowing it to be launched on dedicated hardware. + // + // The default is "default", meaning shared tenancy. Allowed values are "default", + // "dedicated" and "host". + Tenancy string `mapstructure:"tenancy" required:"false"` // 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. @@ -631,6 +637,13 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { } } + if c.Tenancy != "" && + c.Tenancy != "default" && + c.Tenancy != "dedicated" && + c.Tenancy != "host" { + errs = append(errs, fmt.Errorf("Error: Unknown tenancy type %s", c.Tenancy)) + } + return errs } diff --git a/builder/amazon/common/run_config_test.go b/builder/amazon/common/run_config_test.go index 23e28adf4..452cdab22 100644 --- a/builder/amazon/common/run_config_test.go +++ b/builder/amazon/common/run_config_test.go @@ -232,3 +232,23 @@ func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) { t.Fatal("keypair name does not match") } } + +func TestRunConfigPrepare_TenancyBad(t *testing.T) { + c := testConfig() + c.Tenancy = "not_real" + + if err := c.Prepare(nil); len(err) != 1 { + t.Fatal("Should error if tenancy is set to an invalid type") + } +} + +func TestRunConfigPrepare_TenancyGood(t *testing.T) { + validTenancy := []string{"", "default", "dedicated", "host"} + for _, vt := range validTenancy { + c := testConfig() + c.Tenancy = vt + if err := c.Prepare(nil); len(err) != 0 { + t.Fatalf("Should not error if tenancy is set to %s", vt) + } + } +} diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index 76939768e..71e42c261 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -33,6 +33,7 @@ type StepRunSourceInstance struct { IsRestricted bool SourceAMI string Tags map[string]string + Tenancy string UserData string UserDataFile string VolumeTags map[string]string @@ -195,6 +196,10 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa runOpts.InstanceInitiatedShutdownBehavior = &s.InstanceInitiatedShutdownBehavior } + if s.Tenancy != "" { + runOpts.Placement.Tenancy = aws.String(s.Tenancy) + } + var runResp *ec2.Reservation err = retry.Config{ Tries: 11, diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 56f48c49d..7d99facf1 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -206,6 +206,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(), SourceAMI: b.config.SourceAmi, Tags: b.config.RunTags, + Tenancy: b.config.Tenancy, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, VolumeTags: b.config.VolumeRunTags, diff --git a/builder/amazon/ebs/builder.hcl2spec.go b/builder/amazon/ebs/builder.hcl2spec.go index 571d93717..ac5fec820 100644 --- a/builder/amazon/ebs/builder.hcl2spec.go +++ b/builder/amazon/ebs/builder.hcl2spec.go @@ -81,6 +81,7 @@ type FlatConfig struct { SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"` SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"` SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"` + Tenancy *string `mapstructure:"tenancy" required:"false" cty:"tenancy" hcl:"tenancy"` TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"` UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"` @@ -226,6 +227,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())}, "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "tenancy": &hcldec.AttrSpec{Name: "tenancy", Type: cty.String, Required: false}, "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index d1620854f..d938cdd46 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -228,6 +228,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(), SourceAMI: b.config.SourceAmi, Tags: b.config.RunTags, + Tenancy: b.config.Tenancy, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, VolumeTags: b.config.VolumeRunTags, diff --git a/builder/amazon/ebssurrogate/builder.hcl2spec.go b/builder/amazon/ebssurrogate/builder.hcl2spec.go index deac89fac..3c6dad9a2 100644 --- a/builder/amazon/ebssurrogate/builder.hcl2spec.go +++ b/builder/amazon/ebssurrogate/builder.hcl2spec.go @@ -103,6 +103,7 @@ type FlatConfig struct { SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"` SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"` SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"` + Tenancy *string `mapstructure:"tenancy" required:"false" cty:"tenancy" hcl:"tenancy"` TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"` UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"` @@ -249,6 +250,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())}, "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "tenancy": &hcldec.AttrSpec{Name: "tenancy", Type: cty.String, Required: false}, "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index a1ed2a935..3ff801907 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -209,6 +209,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(), SourceAMI: b.config.SourceAmi, Tags: b.config.RunTags, + Tenancy: b.config.Tenancy, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, VolumeTags: b.config.VolumeRunTags, diff --git a/builder/amazon/ebsvolume/builder.hcl2spec.go b/builder/amazon/ebsvolume/builder.hcl2spec.go index fa5aad217..dde662e16 100644 --- a/builder/amazon/ebsvolume/builder.hcl2spec.go +++ b/builder/amazon/ebsvolume/builder.hcl2spec.go @@ -105,6 +105,7 @@ type FlatConfig struct { SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"` SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"` SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"` + Tenancy *string `mapstructure:"tenancy" required:"false" cty:"tenancy" hcl:"tenancy"` TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"` UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"` @@ -229,6 +230,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())}, "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "tenancy": &hcldec.AttrSpec{Name: "tenancy", Type: cty.String, Required: false}, "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index bc8680c5b..7e3256d22 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -286,6 +286,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(), SourceAMI: b.config.SourceAmi, Tags: b.config.RunTags, + Tenancy: b.config.Tenancy, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, } diff --git a/builder/amazon/instance/builder.hcl2spec.go b/builder/amazon/instance/builder.hcl2spec.go index fa967cdb5..2dbb722d5 100644 --- a/builder/amazon/instance/builder.hcl2spec.go +++ b/builder/amazon/instance/builder.hcl2spec.go @@ -81,6 +81,7 @@ type FlatConfig struct { SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"` SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"` SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"` + Tenancy *string `mapstructure:"tenancy" required:"false" cty:"tenancy" hcl:"tenancy"` TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"` UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"` @@ -232,6 +233,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())}, "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "tenancy": &hcldec.AttrSpec{Name: "tenancy", Type: cty.String, Required: false}, "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, diff --git a/website/pages/partials/builder/amazon/common/RunConfig-not-required.mdx b/website/pages/partials/builder/amazon/common/RunConfig-not-required.mdx index 4d66cb0e1..083e499eb 100644 --- a/website/pages/partials/builder/amazon/common/RunConfig-not-required.mdx +++ b/website/pages/partials/builder/amazon/common/RunConfig-not-required.mdx @@ -291,6 +291,12 @@ subnet-12345def, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC. +- `tenancy` (string) - [Tenancy](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-instance.html) used + when Packer launches the EC2 instance, allowing it to be launched on dedicated hardware. + + The default is "default", meaning shared tenancy. Allowed values are "default", + "dedicated" and "host". + - `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.