diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 7905eeef1..54985cac8 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -473,6 +473,13 @@ type RunConfig struct { // terminating the tunnel it will automatically terminate itself after 20 minutes of inactivity. SSHInterface string `mapstructure:"ssh_interface"` + // The time to wait before establishing the Session Manager session. + // The value of this should be a duration. Examples are + // `5s` and `1m30s` which will cause Packer to wait five seconds and one + // minute 30 seconds, respectively. If no set, defaults to 10 seconds. + // This option is useful when the remote port takes longer to become available. + PauseBeforeSSM time.Duration `mapstructure:"pause_before_ssm"` + // Which port to connect the local end of the session tunnel to. If // left blank, Packer will choose a port for you from available ports. // This option is only used when `ssh_interface` is set `session_manager`. @@ -535,6 +542,10 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { msg := fmt.Errorf(`no iam_instance_profile defined; session_manager connectivity requires a valid instance profile with AmazonSSMManagedInstanceCore permissions. Alternatively a temporary_iam_instance_profile_policy_document can be used.`) errs = append(errs, msg) } + + if c.PauseBeforeSSM == 0 { + c.PauseBeforeSSM = 10 * time.Second + } } if c.Comm.SSHKeyPairName != "" { diff --git a/builder/amazon/common/step_create_ssm_tunnel.go b/builder/amazon/common/step_create_ssm_tunnel.go index 72f6b2ced..c29a4dc5b 100644 --- a/builder/amazon/common/step_create_ssm_tunnel.go +++ b/builder/amazon/common/step_create_ssm_tunnel.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strconv" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" @@ -21,6 +22,7 @@ type StepCreateSSMTunnel struct { RemotePortNumber int SSMAgentEnabled bool instanceId string + PauseBeforeSSM time.Duration driver *SSMDriver } @@ -32,6 +34,17 @@ func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionContinue } + // Wait for the remote port to become available + if s.PauseBeforeSSM > 0 { + ui.Say(fmt.Sprintf("Waiting %s for establishing the SSM session...", s.PauseBeforeSSM)) + select { + case <-time.After(s.PauseBeforeSSM): + break + case <-ctx.Done(): + return multistep.ActionHalt + } + } + // Configure local port number if err := s.ConfigureLocalHostPort(ctx); err != nil { err := fmt.Errorf("error finding an available port to initiate a session tunnel: %s", err) diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index a19fbc423..56f48c49d 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -269,6 +269,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack &awscommon.StepCreateSSMTunnel{ AWSSession: session, Region: *ec2conn.Config.Region, + PauseBeforeSSM: b.config.PauseBeforeSSM, LocalPortNumber: b.config.SessionManagerPort, RemotePortNumber: b.config.Comm.Port(), SSMAgentEnabled: b.config.SSMAgentEnabled(), diff --git a/builder/amazon/ebs/builder.hcl2spec.go b/builder/amazon/ebs/builder.hcl2spec.go index ad606ee35..c712eaf5d 100644 --- a/builder/amazon/ebs/builder.hcl2spec.go +++ b/builder/amazon/ebs/builder.hcl2spec.go @@ -134,6 +134,7 @@ type FlatConfig struct { WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"` + PauseBeforeSSM *string `mapstructure:"pause_before_ssm" cty:"pause_before_ssm" hcl:"pause_before_ssm"` SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"` AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings" hcl:"ami_block_device_mappings"` LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings" hcl:"launch_block_device_mappings"` @@ -277,6 +278,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "pause_before_ssm": &hcldec.AttrSpec{Name: "pause_before_ssm", Type: cty.String, Required: false}, "session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false}, "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index f53124128..d1620854f 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -293,6 +293,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack &awscommon.StepCreateSSMTunnel{ AWSSession: session, Region: *ec2conn.Config.Region, + PauseBeforeSSM: b.config.PauseBeforeSSM, LocalPortNumber: b.config.SessionManagerPort, RemotePortNumber: b.config.Comm.Port(), SSMAgentEnabled: b.config.SSMAgentEnabled(), diff --git a/builder/amazon/ebssurrogate/builder.hcl2spec.go b/builder/amazon/ebssurrogate/builder.hcl2spec.go index da81891e5..654833c99 100644 --- a/builder/amazon/ebssurrogate/builder.hcl2spec.go +++ b/builder/amazon/ebssurrogate/builder.hcl2spec.go @@ -156,6 +156,7 @@ type FlatConfig struct { WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"` + PauseBeforeSSM *string `mapstructure:"pause_before_ssm" cty:"pause_before_ssm" hcl:"pause_before_ssm"` SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"` AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name" hcl:"ami_name"` AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description" hcl:"ami_description"` @@ -300,6 +301,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "pause_before_ssm": &hcldec.AttrSpec{Name: "pause_before_ssm", Type: cty.String, Required: false}, "session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false}, "ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false}, "ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false}, diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index f6aa47b2f..a1ed2a935 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -263,6 +263,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack &awscommon.StepCreateSSMTunnel{ AWSSession: session, Region: *ec2conn.Config.Region, + PauseBeforeSSM: b.config.PauseBeforeSSM, LocalPortNumber: b.config.SessionManagerPort, RemotePortNumber: b.config.Comm.Port(), SSMAgentEnabled: b.config.SSMAgentEnabled(), diff --git a/builder/amazon/ebsvolume/builder.hcl2spec.go b/builder/amazon/ebsvolume/builder.hcl2spec.go index 861cb005a..6188a7945 100644 --- a/builder/amazon/ebsvolume/builder.hcl2spec.go +++ b/builder/amazon/ebsvolume/builder.hcl2spec.go @@ -158,6 +158,7 @@ type FlatConfig struct { WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"` + PauseBeforeSSM *string `mapstructure:"pause_before_ssm" cty:"pause_before_ssm" hcl:"pause_before_ssm"` SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"` AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support" hcl:"ena_support"` AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support" hcl:"sriov_support"` @@ -280,6 +281,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "pause_before_ssm": &hcldec.AttrSpec{Name: "pause_before_ssm", Type: cty.String, Required: false}, "session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false}, "ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false}, "sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false}, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index b408ecf7d..bc8680c5b 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -343,6 +343,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack &awscommon.StepCreateSSMTunnel{ AWSSession: session, Region: *ec2conn.Config.Region, + PauseBeforeSSM: b.config.PauseBeforeSSM, LocalPortNumber: b.config.SessionManagerPort, RemotePortNumber: b.config.Comm.Port(), SSMAgentEnabled: b.config.SSMAgentEnabled(), diff --git a/builder/amazon/instance/builder.hcl2spec.go b/builder/amazon/instance/builder.hcl2spec.go index 30f3926f0..218ac08ed 100644 --- a/builder/amazon/instance/builder.hcl2spec.go +++ b/builder/amazon/instance/builder.hcl2spec.go @@ -134,6 +134,7 @@ type FlatConfig struct { WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"` + PauseBeforeSSM *string `mapstructure:"pause_before_ssm" cty:"pause_before_ssm" hcl:"pause_before_ssm"` SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"` AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings" hcl:"ami_block_device_mappings"` LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings" hcl:"launch_block_device_mappings"` @@ -283,6 +284,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "pause_before_ssm": &hcldec.AttrSpec{Name: "pause_before_ssm", Type: cty.String, Required: false}, "session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false}, "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, 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 6c3a3afd6..4d66cb0e1 100644 --- a/website/pages/partials/builder/amazon/common/RunConfig-not-required.mdx +++ b/website/pages/partials/builder/amazon/common/RunConfig-not-required.mdx @@ -383,6 +383,12 @@ - Upon termination the secure tunnel will be terminated automatically, if however there is a failure in terminating the tunnel it will automatically terminate itself after 20 minutes of inactivity. +- `pause_before_ssm` (duration string | ex: "1h5m2s") - The time to wait before establishing the Session Manager session. + The value of this should be a duration. Examples are + `5s` and `1m30s` which will cause Packer to wait five seconds and one + minute 30 seconds, respectively. If no set, defaults to 10 seconds. + This option is useful when the remote port takes longer to become available. + - `session_manager_port` (int) - Which port to connect the local end of the session tunnel to. If left blank, Packer will choose a port for you from available ports. This option is only used when `ssh_interface` is set `session_manager`.