2020-03-10 16:39:19 -04:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-03-12 22:26:38 -04:00
|
|
|
"encoding/json"
|
2020-03-10 16:39:19 -04:00
|
|
|
"fmt"
|
2020-03-12 22:26:38 -04:00
|
|
|
"strconv"
|
|
|
|
"time"
|
2020-03-10 16:39:19 -04:00
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
2020-03-12 22:26:38 -04:00
|
|
|
"github.com/aws/aws-sdk-go/service/ec2"
|
2020-03-10 16:39:19 -04:00
|
|
|
"github.com/aws/aws-sdk-go/service/ssm"
|
2020-03-30 07:47:31 -04:00
|
|
|
"github.com/hashicorp/packer/common/net"
|
2020-03-12 22:26:38 -04:00
|
|
|
"github.com/hashicorp/packer/common/retry"
|
2020-03-10 16:39:19 -04:00
|
|
|
"github.com/hashicorp/packer/helper/multistep"
|
|
|
|
"github.com/hashicorp/packer/packer"
|
|
|
|
)
|
|
|
|
|
|
|
|
type StepCreateSSMTunnel struct {
|
2020-04-01 17:33:44 -04:00
|
|
|
AWSSession *session.Session
|
|
|
|
DstPort int
|
|
|
|
SSMAgentEnabled bool
|
|
|
|
instanceId string
|
|
|
|
ssmSession *ssm.StartSessionOutput
|
2020-03-10 16:39:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
2020-04-01 17:33:44 -04:00
|
|
|
if !s.SSMAgentEnabled {
|
|
|
|
return multistep.ActionContinue
|
|
|
|
}
|
|
|
|
|
2020-03-10 16:39:19 -04:00
|
|
|
ui := state.Get("ui").(packer.Ui)
|
2020-03-30 07:47:31 -04:00
|
|
|
// Find an available TCP port for our HTTP server
|
|
|
|
l, err := net.ListenRangeConfig{
|
|
|
|
Min: 8000,
|
|
|
|
Max: 9000,
|
|
|
|
Addr: "0.0.0.0",
|
|
|
|
Network: "tcp",
|
|
|
|
}.Listen(ctx)
|
|
|
|
if err != nil {
|
|
|
|
err := fmt.Errorf("error finding an available port to initiate a session tunnel: %s", err)
|
|
|
|
state.Put("error", err)
|
|
|
|
ui.Error(err.Error())
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
|
|
|
dst, src := strconv.Itoa(s.DstPort), strconv.Itoa(l.Port)
|
2020-03-10 16:39:19 -04:00
|
|
|
params := map[string][]*string{
|
2020-03-30 07:47:31 -04:00
|
|
|
"portNumber": []*string{aws.String(dst)},
|
|
|
|
"localPortNumber": []*string{aws.String(src)},
|
2020-03-10 16:39:19 -04:00
|
|
|
}
|
2020-03-30 07:47:31 -04:00
|
|
|
l.Close()
|
|
|
|
|
2020-03-12 22:26:38 -04:00
|
|
|
instance, ok := state.Get("instance").(*ec2.Instance)
|
|
|
|
if !ok {
|
|
|
|
err := fmt.Errorf("error encountered in obtaining target instance id for SSM tunnel")
|
|
|
|
ui.Error(err.Error())
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2020-04-01 17:33:44 -04:00
|
|
|
s.instanceId = aws.StringValue(instance.InstanceId)
|
2020-03-10 16:39:19 -04:00
|
|
|
ssmconn := ssm.New(s.AWSSession)
|
|
|
|
input := ssm.StartSessionInput{
|
|
|
|
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
|
|
|
Parameters: params,
|
2020-04-01 17:33:44 -04:00
|
|
|
Target: aws.String(s.instanceId),
|
2020-03-10 16:39:19 -04:00
|
|
|
}
|
2020-03-30 07:47:31 -04:00
|
|
|
|
2020-04-01 17:33:44 -04:00
|
|
|
ui.Message(fmt.Sprintf("Starting PortForwarding session to instance %q on local port %q to remote port %q", s.instanceId, src, dst))
|
2020-03-12 22:26:38 -04:00
|
|
|
var output *ssm.StartSessionOutput
|
|
|
|
err = retry.Config{
|
|
|
|
ShouldRetry: func(err error) bool { return isAWSErr(err, "TargetNotConnected", "") },
|
2020-04-01 17:33:44 -04:00
|
|
|
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 60 * time.Second, Multiplier: 2}).Linear,
|
2020-03-12 22:26:38 -04:00
|
|
|
}.Run(ctx, func(ctx context.Context) error {
|
|
|
|
output, err = ssmconn.StartSessionWithContext(ctx, &input)
|
|
|
|
return err
|
|
|
|
})
|
2020-03-10 16:39:19 -04:00
|
|
|
|
|
|
|
if err != nil {
|
2020-04-01 17:33:44 -04:00
|
|
|
err = fmt.Errorf("error encountered in starting session for instance %q: %s", s.instanceId, err)
|
2020-03-10 16:39:19 -04:00
|
|
|
ui.Error(err.Error())
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
s.ssmSession = output
|
|
|
|
|
2020-03-30 07:47:31 -04:00
|
|
|
// AWS session-manager-plugin requires a valid session be passed in JSON
|
|
|
|
sessionDetails, err := json.Marshal(s.ssmSession)
|
2020-03-12 22:26:38 -04:00
|
|
|
if err != nil {
|
|
|
|
ui.Error(err.Error())
|
2020-03-30 07:47:31 -04:00
|
|
|
state.Put("error encountered in reading session details", err)
|
2020-03-12 22:26:38 -04:00
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2020-03-30 07:47:31 -04:00
|
|
|
sessionParameters, err := json.Marshal(input)
|
2020-03-12 22:26:38 -04:00
|
|
|
if err != nil {
|
|
|
|
ui.Error(err.Error())
|
2020-03-30 07:47:31 -04:00
|
|
|
state.Put("error encountered in reading session parameter details", err)
|
2020-03-12 22:26:38 -04:00
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
|
|
|
driver := SSMDriver{Ui: ui}
|
2020-03-30 07:47:31 -04:00
|
|
|
// sessionDetails, region, "StartSession", profile, paramJson, endpoint
|
|
|
|
region := aws.StringValue(s.AWSSession.Config.Region)
|
|
|
|
// how to best get Profile name
|
|
|
|
if err := driver.StartSession(string(sessionDetails), region, "default", string(sessionParameters), ssmconn.Endpoint); err != nil {
|
|
|
|
err = fmt.Errorf("error encountered in establishing a tunnel with the session-manager-plugin: %s", err)
|
2020-03-12 22:26:38 -04:00
|
|
|
ui.Error(err.Error())
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2020-04-01 17:33:44 -04:00
|
|
|
ui.Message(fmt.Sprintf("PortForwarding session to instance %q established!", s.instanceId))
|
2020-03-30 07:47:31 -04:00
|
|
|
state.Put("sessionPort", l.Port)
|
|
|
|
|
2020-03-10 16:39:19 -04:00
|
|
|
return multistep.ActionContinue
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StepCreateSSMTunnel) Cleanup(state multistep.StateBag) {
|
|
|
|
if s.ssmSession == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ui := state.Get("ui").(packer.Ui)
|
|
|
|
ssmconn := ssm.New(s.AWSSession)
|
|
|
|
_, err := ssmconn.TerminateSession(&ssm.TerminateSessionInput{SessionId: s.ssmSession.SessionId})
|
|
|
|
if err != nil {
|
|
|
|
msg := fmt.Sprintf("Error terminating SSM Session %q. Please terminate the session manually: %s",
|
|
|
|
aws.StringValue(s.ssmSession.SessionId), err)
|
|
|
|
ui.Error(msg)
|
|
|
|
}
|
2020-03-12 22:26:38 -04:00
|
|
|
|
2020-03-10 16:39:19 -04:00
|
|
|
}
|