diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 73970e59e..4f20ed3b4 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -54,6 +54,7 @@ type RunConfig struct { Comm communicator.Config `mapstructure:",squash"` SSHKeyPairName string `mapstructure:"ssh_keypair_name"` SSHPrivateIp bool `mapstructure:"ssh_private_ip"` + SSHInterface string `mapstructure:"ssh_interface"` } func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { @@ -77,6 +78,24 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) + if c.SSHPrivateIp && c.SSHInterface != "" { + errs = append(errs, errors.New("ssh_interface and ssh_private_ip should not both be specified")) + } + + // Legacy configurable + if c.SSHPrivateIp { + c.SSHInterface = "private_ip" + } + + // Valadating ssh_interface + if c.SSHInterface != "public_ip" && + c.SSHInterface != "private_ip" && + c.SSHInterface != "public_dns" && + c.SSHInterface != "private_dns" && + c.SSHInterface != "" { + errs = append(errs, errors.New(fmt.Sprintf("Unknown interface type: %s", c.SSHInterface))) + } + if c.SSHKeyPairName != "" { if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKey == "" { errs = append(errs, errors.New("A private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name.")) diff --git a/builder/amazon/common/ssh.go b/builder/amazon/common/ssh.go index c451bbf96..be414a8a0 100644 --- a/builder/amazon/common/ssh.go +++ b/builder/amazon/common/ssh.go @@ -25,21 +25,40 @@ var ( // SSHHost returns a function that can be given to the SSH communicator // for determining the SSH address based on the instance DNS name. -func SSHHost(e ec2Describer, private bool) func(multistep.StateBag) (string, error) { +func SSHHost(e ec2Describer, sshInterface string) func(multistep.StateBag) (string, error) { return func(state multistep.StateBag) (string, error) { const tries = 2 // <= with current structure to check result of describing `tries` times for j := 0; j <= tries; j++ { var host string i := state.Get("instance").(*ec2.Instance) - if i.VpcId != nil && *i.VpcId != "" { - if i.PublicIpAddress != nil && *i.PublicIpAddress != "" && !private { + if sshInterface != "" { + switch sshInterface { + case "public_ip": + if i.PublicIpAddress != nil { + host = *i.PublicIpAddress + } + case "private_ip": + if i.PrivateIpAddress != nil { + host = *i.PrivateIpAddress + } + case "public_dns": + if i.PublicDnsName != nil { + host = *i.PublicDnsName + } + case "private_dns": + if i.PrivateDnsName != nil { + host = *i.PrivateDnsName + } + default: + panic(fmt.Sprintf("Unknown interface type: %s", sshInterface)) + } + } else if i.VpcId != nil && *i.VpcId != "" { + if i.PublicIpAddress != nil && *i.PublicIpAddress != "" { host = *i.PublicIpAddress } else if i.PrivateIpAddress != nil && *i.PrivateIpAddress != "" { host = *i.PrivateIpAddress } - } else if private && i.PrivateIpAddress != nil && *i.PrivateIpAddress != "" { - host = *i.PrivateIpAddress } else if i.PublicDnsName != nil && *i.PublicDnsName != "" { host = *i.PublicDnsName } @@ -63,7 +82,7 @@ func SSHHost(e ec2Describer, private bool) func(multistep.StateBag) (string, err time.Sleep(sshHostSleepDuration) } - return "", errors.New("couldn't determine IP address for instance") + return "", errors.New("couldn't determine address for instance") } } diff --git a/builder/amazon/common/ssh_test.go b/builder/amazon/common/ssh_test.go index 9f1d4a53e..e39088e67 100644 --- a/builder/amazon/common/ssh_test.go +++ b/builder/amazon/common/ssh_test.go @@ -9,9 +9,10 @@ import ( ) const ( - privateIP = "10.0.0.1" - publicIP = "192.168.1.1" - publicDNS = "public.dns.test" + privateIP = "10.0.0.1" + publicIP = "192.168.1.1" + privateDNS = "private.dns.test" + publicDNS = "public.dns.test" ) func TestSSHHost(t *testing.T) { @@ -20,44 +21,54 @@ func TestSSHHost(t *testing.T) { sshHostSleepDuration = 0 var cases = []struct { - allowTries int - vpcId string - private bool + allowTries int + vpcId string + sshInterface string ok bool wantHost string }{ - {1, "", false, true, publicDNS}, - {1, "", true, true, privateIP}, - {1, "vpc-id", false, true, publicIP}, - {1, "vpc-id", true, true, privateIP}, - {2, "", false, true, publicDNS}, - {2, "", true, true, privateIP}, - {2, "vpc-id", false, true, publicIP}, - {2, "vpc-id", true, true, privateIP}, - {3, "", false, false, ""}, - {3, "", true, false, ""}, - {3, "vpc-id", false, false, ""}, - {3, "vpc-id", true, false, ""}, + {1, "", "", true, publicDNS}, + {1, "", "private_ip", true, privateIP}, + {1, "vpc-id", "", true, publicIP}, + {1, "vpc-id", "private_ip", true, privateIP}, + {1, "vpc-id", "private_dns", true, privateDNS}, + {1, "vpc-id", "public_dns", true, publicDNS}, + {1, "vpc-id", "public_ip", true, publicIP}, + {2, "", "", true, publicDNS}, + {2, "", "private_ip", true, privateIP}, + {2, "vpc-id", "", true, publicIP}, + {2, "vpc-id", "private_ip", true, privateIP}, + {2, "vpc-id", "private_dns", true, privateDNS}, + {2, "vpc-id", "public_dns", true, publicDNS}, + {2, "vpc-id", "public_ip", true, publicIP}, + {3, "", "", false, ""}, + {3, "", "private_ip", false, ""}, + {3, "vpc-id", "", false, ""}, + {3, "vpc-id", "private_ip", false, ""}, + {3, "vpc-id", "private_dns", false, ""}, + {3, "vpc-id", "public_dns", false, ""}, + {3, "vpc-id", "public_ip", false, ""}, } for _, c := range cases { - testSSHHost(t, c.allowTries, c.vpcId, c.private, c.ok, c.wantHost) + testSSHHost(t, c.allowTries, c.vpcId, c.sshInterface, c.ok, c.wantHost) } } -func testSSHHost(t *testing.T, allowTries int, vpcId string, private, ok bool, wantHost string) { - t.Logf("allowTries=%d vpcId=%s private=%t ok=%t wantHost=%q", allowTries, vpcId, private, ok, wantHost) +func testSSHHost(t *testing.T, allowTries int, vpcId string, sshInterface string, ok bool, wantHost string) { + t.Logf("allowTries=%d vpcId=%s sshInterface=%s ok=%t wantHost=%q", allowTries, vpcId, sshInterface, ok, wantHost) e := &fakeEC2Describer{ allowTries: allowTries, vpcId: vpcId, privateIP: privateIP, publicIP: publicIP, + privateDNS: privateDNS, publicDNS: publicDNS, } - f := SSHHost(e, private) + f := SSHHost(e, sshInterface) st := &multistep.BasicStateBag{} st.Put("instance", &ec2.Instance{ InstanceId: aws.String("instance-id"), @@ -85,8 +96,8 @@ type fakeEC2Describer struct { allowTries int tries int - vpcId string - privateIP, publicIP, publicDNS string + vpcId string + privateIP, publicIP, privateDNS, publicDNS string } func (d *fakeEC2Describer) DescribeInstances(in *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { @@ -104,6 +115,7 @@ func (d *fakeEC2Describer) DescribeInstances(in *ec2.DescribeInstancesInput) (*e instance.PublicIpAddress = aws.String(d.publicIP) instance.PrivateIpAddress = aws.String(d.privateIP) instance.PublicDnsName = aws.String(d.publicDNS) + instance.PrivateDnsName = aws.String(d.privateDNS) } out := &ec2.DescribeInstancesOutput{ diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 1cc346472..56757b826 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -193,7 +193,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.RunConfig.Comm, Host: awscommon.SSHHost( ec2conn, - b.config.SSHPrivateIp), + b.config.SSHInterface), SSHConfig: awscommon.SSHConfig( b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index 16e8367da..fec997816 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -204,7 +204,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.RunConfig.Comm, Host: awscommon.SSHHost( ec2conn, - b.config.SSHPrivateIp), + b.config.SSHInterface), SSHConfig: awscommon.SSHConfig( b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index ea3f74b61..6bd8927ee 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -181,7 +181,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.RunConfig.Comm, Host: awscommon.SSHHost( ec2conn, - b.config.SSHPrivateIp), + b.config.SSHInterface), SSHConfig: awscommon.SSHConfig( b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 7cee44c09..220ee7b10 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -268,7 +268,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.RunConfig.Comm, Host: awscommon.SSHHost( ec2conn, - b.config.SSHPrivateIp), + b.config.SSHInterface), SSHConfig: awscommon.SSHConfig( b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index df165ea4b..c050cd070 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -328,8 +328,19 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - If true, then SSH will always use the private - IP if available. Also works for WinRM. +- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, + then SSH will always use the private IP if available. Also works for WinRM. + +- `ssh_interface` (string) - One of `public_ip`, `private_ip`, + `public_dns` or `private_dns`. If set, either the public IP address, + private IP address, public DNS name or private DNS name will used as the host for SSH. + The default behaviour if inside a VPC is to use the public IP address if available, + otherwise the private IP address will be used. If not in a VPC the public DNS name + will be used. + + Where Packer is configured for an outbound proxy but WinRM traffic should be direct + `ssh_interface` must be set to `private_dns` and `.compute.internal` included + in the `NO_PROXY` environment variable. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index c044fc350..640710230 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -321,8 +321,19 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - If true, then SSH will always use the private - IP if available. +- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, + then SSH will always use the private IP if available. Also works for WinRM. + +- `ssh_interface` (string) - One of `public_ip`, `private_ip`, + `public_dns` or `private_dns`. If set, either the public IP address, + private IP address, public DNS name or private DNS name will used as the host for SSH. + The default behaviour if inside a VPC is to use the public IP address if available, + otherwise the private IP address will be used. If not in a VPC the public DNS name + will be used. + + Where Packer is configured for an outbound proxy but WinRM traffic should be direct + `ssh_interface` must be set to `private_dns` and `.compute.internal` included + in the `NO_PROXY` environment variable. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index 125064626..2b92c6b44 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -225,8 +225,19 @@ builder. [`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file) must be specified with this. -- `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private - IP if available. Also works for WinRM. +- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, + then SSH will always use the private IP if available. Also works for WinRM. + +- `ssh_interface` (string) - One of `public_ip`, `private_ip`, + `public_dns` or `private_dns`. If set, either the public IP address, + private IP address, public DNS name or private DNS name will used as the host for SSH. + The default behaviour if inside a VPC is to use the public IP address if available, + otherwise the private IP address will be used. If not in a VPC the public DNS name + will be used. + + Where Packer is configured for an outbound proxy but WinRM traffic should be direct + `ssh_interface` must be set to `private_dns` and `.compute.internal` included + in the `NO_PROXY` environment variable. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 95a1bc007..198345ba7 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -329,8 +329,19 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - If true, then SSH will always use the private - IP if available. Also works for WinRM. +- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, + then SSH will always use the private IP if available. Also works for WinRM. + +- `ssh_interface` (string) - One of `public_ip`, `private_ip`, + `public_dns` or `private_dns`. If set, either the public IP address, + private IP address, public DNS name or private DNS name will used as the host for SSH. + The default behaviour if inside a VPC is to use the public IP address if available, + otherwise the private IP address will be used. If not in a VPC the public DNS name + will be used. + + Where Packer is configured for an outbound proxy but WinRM traffic should be direct + `ssh_interface` must be set to `private_dns` and `.compute.internal` included + in the `NO_PROXY` environment variable. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is