diff --git a/.gitignore b/.gitignore
index 0206537..41fb989 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,7 @@ _testmain.go
*.cov
bin
pkg
+
+#IntelliJ Go Project Files
+.idea
+*.iml
diff --git a/builder/amazon-windows/common/run_config.go b/builder/amazon-windows/common/run_config.go
index cccf898..6106468 100644
--- a/builder/amazon-windows/common/run_config.go
+++ b/builder/amazon-windows/common/run_config.go
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
+ "github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
)
@@ -13,19 +14,24 @@ import (
type RunConfig struct {
AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"`
AvailabilityZone string `mapstructure:"availability_zone"`
+ ConfigureSecureWinRM bool `mapstructure:"winrm_autoconfigure"`
IamInstanceProfile string `mapstructure:"iam_instance_profile"`
InstanceType string `mapstructure:"instance_type"`
+ KeyPairPrivateKeyFile string `mapstructure:"key_pair_private_key_file"`
+ NewAdministratorPassword string `mapstructure:"new_administrator_password"`
RunTags map[string]string `mapstructure:"run_tags"`
SourceAmi string `mapstructure:"source_ami"`
SpotPrice string `mapstructure:"spot_price"`
SpotPriceAutoProduct string `mapstructure:"spot_price_auto_product"`
- WinRMPrivateIp bool `mapstructure:"winrm_private_ip"`
SecurityGroupId string `mapstructure:"security_group_id"`
SecurityGroupIds []string `mapstructure:"security_group_ids"`
SubnetId string `mapstructure:"subnet_id"`
+ TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
UserData string `mapstructure:"user_data"`
UserDataFile string `mapstructure:"user_data_file"`
VpcId string `mapstructure:"vpc_id"`
+ WinRMPrivateIp bool `mapstructure:"winrm_private_ip"`
+ WinRMCertificateFile string `mapstructure:"winrm_certificate_file"`
}
func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
@@ -38,17 +44,19 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
}
templates := map[string]*string{
- "iam_instance_profile": &c.IamInstanceProfile,
- "instance_type": &c.InstanceType,
- "spot_price": &c.SpotPrice,
- "spot_price_auto_product": &c.SpotPriceAutoProduct,
- "source_ami": &c.SourceAmi,
- "subnet_id": &c.SubnetId,
- "vpc_id": &c.VpcId,
- "availability_zone": &c.AvailabilityZone,
- "user_data": &c.UserData,
- "user_data_file": &c.UserDataFile,
- "security_group_id": &c.SecurityGroupId,
+ "iam_instance_profile": &c.IamInstanceProfile,
+ "instance_type": &c.InstanceType,
+ "key_pair_private_key_file": &c.KeyPairPrivateKeyFile,
+ "spot_price": &c.SpotPrice,
+ "spot_price_auto_product": &c.SpotPriceAutoProduct,
+ "source_ami": &c.SourceAmi,
+ "subnet_id": &c.SubnetId,
+ "temporary_key_pair_name": &c.TemporaryKeyPairName,
+ "vpc_id": &c.VpcId,
+ "availability_zone": &c.AvailabilityZone,
+ "user_data": &c.UserData,
+ "user_data_file": &c.UserDataFile,
+ "security_group_id": &c.SecurityGroupId,
}
errs := make([]error, 0)
@@ -70,6 +78,11 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
errs = append(errs, errors.New("An instance_type must be specified"))
}
+ if c.TemporaryKeyPairName == "" {
+ c.TemporaryKeyPairName = fmt.Sprintf(
+ "packer %s", uuid.TimeOrderedUUID())
+ }
+
if c.SpotPrice == "auto" {
if c.SpotPriceAutoProduct == "" {
errs = append(errs, errors.New(
@@ -77,11 +90,21 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
}
}
- if c.UserData != "" && c.UserDataFile != "" {
- errs = append(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified."))
- } else if c.UserDataFile != "" {
- if _, err := os.Stat(c.UserDataFile); err != nil {
- errs = append(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
+ if c.ConfigureSecureWinRM {
+ if c.UserData != "" || c.UserDataFile != "" {
+ errs = append(errs, fmt.Errorf("winrm_autoconfigure cannot be used in conjunction with user_data or user_data_file"))
+ }
+
+ if c.WinRMCertificateFile == "" {
+ errs = append(errs, fmt.Errorf("winrm_certificate_file must be set to the path of a PFX container holding the certificate to be used for WinRM."))
+ }
+ } else {
+ if c.UserData != "" && c.UserDataFile != "" {
+ errs = append(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified."))
+ } else if c.UserDataFile != "" {
+ if _, err := os.Stat(c.UserDataFile); err != nil {
+ errs = append(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
+ }
}
}
diff --git a/builder/amazon-windows/common/state.go b/builder/amazon-windows/common/state.go
index a3abd6e..439c6ea 100644
--- a/builder/amazon-windows/common/state.go
+++ b/builder/amazon-windows/common/state.go
@@ -3,13 +3,15 @@ package common
import (
"errors"
"fmt"
- "github.com/mitchellh/goamz/ec2"
- "github.com/mitchellh/multistep"
"log"
"net"
"os"
"strconv"
"time"
+
+ "github.com/awslabs/aws-sdk-go/aws"
+ "github.com/awslabs/aws-sdk-go/service/ec2"
+ "github.com/mitchellh/multistep"
)
// StateRefreshFunc is a function type used for StateChangeConf that is
@@ -36,9 +38,13 @@ type StateChangeConf struct {
// an AMI for state changes.
func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc {
return func() (interface{}, string, error) {
- resp, err := conn.Images([]string{imageId}, ec2.NewFilter())
+ input := &ec2.DescribeImagesInput{
+ ImageIDs: []*string{&imageId},
+ }
+ resp, err := conn.DescribeImages(input)
+
if err != nil {
- if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidAMIID.NotFound" {
+ if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidAMIID.NotFound" {
// Set this to nil as if we didn't find anything.
resp = nil
} else if isTransientNetworkError(err) {
@@ -57,17 +63,20 @@ func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc {
}
i := resp.Images[0]
- return i, i.State, nil
+ return i, *i.State, nil
}
}
// InstanceStateRefreshFunc returns a StateRefreshFunc that is used to watch
// an EC2 instance.
-func InstanceStateRefreshFunc(conn *ec2.EC2, instanceId string) StateRefreshFunc {
+func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc {
return func() (interface{}, string, error) {
- resp, err := conn.Instances([]string{instanceId}, ec2.NewFilter())
+ input := &ec2.DescribeInstancesInput{
+ InstanceIDs: []*string{i.InstanceID},
+ }
+ resp, err := conn.DescribeInstances(input)
if err != nil {
- if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
+ if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
// Set this to nil as if we didn't find anything.
resp = nil
} else if isTransientNetworkError(err) {
@@ -85,8 +94,8 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, instanceId string) StateRefreshFunc
return nil, "", nil
}
- i := &resp.Reservations[0].Instances[0]
- return i, i.State.Name, nil
+ i := resp.Reservations[0].Instances[0]
+ return i, *i.State.Name, nil
}
}
@@ -94,9 +103,12 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, instanceId string) StateRefreshFunc
// a spot request for state changes.
func SpotRequestStateRefreshFunc(conn *ec2.EC2, spotRequestId string) StateRefreshFunc {
return func() (interface{}, string, error) {
- resp, err := conn.DescribeSpotRequests([]string{spotRequestId}, ec2.NewFilter())
+ input := &ec2.DescribeSpotInstanceRequestsInput{
+ SpotInstanceRequestIDs: []*string{&spotRequestId},
+ }
+ resp, err := conn.DescribeSpotInstanceRequests(input)
if err != nil {
- if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidSpotInstanceRequestID.NotFound" {
+ if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidSpotInstanceRequestID.NotFound" {
// Set this to nil as if we didn't find anything.
resp = nil
} else if isTransientNetworkError(err) {
@@ -108,14 +120,14 @@ func SpotRequestStateRefreshFunc(conn *ec2.EC2, spotRequestId string) StateRefre
}
}
- if resp == nil || len(resp.SpotRequestResults) == 0 {
+ if resp == nil || len(resp.SpotInstanceRequests) == 0 {
// Sometimes AWS has consistency issues and doesn't see the
// SpotRequest. Return an empty state.
return nil, "", nil
}
- i := resp.SpotRequestResults[0]
- return i, i.State, nil
+ i := resp.SpotInstanceRequests[0]
+ return i, *i.State, nil
}
}
diff --git a/builder/amazon-windows/common/connect_step.go b/builder/amazon-windows/common/step_connect.go
similarity index 66%
rename from builder/amazon-windows/common/connect_step.go
rename to builder/amazon-windows/common/step_connect.go
index 028d690..49db9f0 100644
--- a/builder/amazon-windows/common/connect_step.go
+++ b/builder/amazon-windows/common/step_connect.go
@@ -1,14 +1,14 @@
package common
import (
- "fmt"
- "github.com/mitchellh/goamz/ec2"
- "github.com/mitchellh/multistep"
- //"github.com/mitchellh/packer/common"
"errors"
- wincommon "github.com/packer-community/packer-windows-plugins/common"
+ "fmt"
"log"
"time"
+
+ "github.com/awslabs/aws-sdk-go/service/ec2"
+ "github.com/mitchellh/multistep"
+ wincommon "github.com/packer-community/packer-windows-plugins/common"
)
// Returns an Endpoint suitable for the WinRM communicator
@@ -17,13 +17,13 @@ func WinRMAddress(e *ec2.EC2, port uint, private bool) func(multistep.StateBag)
for j := 0; j < 2; j++ {
var host string
i := state.Get("instance").(*ec2.Instance)
- if i.DNSName != "" {
- host = i.DNSName
- } else if i.VpcId != "" {
- if i.PublicIpAddress != "" && !private {
- host = i.PublicIpAddress
+ if *i.PublicDNSName != "" {
+ host = *i.PublicDNSName
+ } else if *i.VPCID != "" {
+ if *i.PublicIPAddress != "" && !private {
+ host = *i.PublicIPAddress
} else {
- host = i.PrivateIpAddress
+ host = *i.PrivateIPAddress
}
}
@@ -32,13 +32,16 @@ func WinRMAddress(e *ec2.EC2, port uint, private bool) func(multistep.StateBag)
return fmt.Sprintf("%s:%d", host, port), nil
}
- r, err := e.Instances([]string{i.InstanceId}, ec2.NewFilter())
+ input := &ec2.DescribeInstancesInput{
+ InstanceIDs: []*string{i.InstanceID},
+ }
+ r, err := e.DescribeInstances(input)
if err != nil {
return "", err
}
if len(r.Reservations) == 0 || len(r.Reservations[0].Instances) == 0 {
- return "", fmt.Errorf("instance not found: %s", i.InstanceId)
+ return "", fmt.Errorf("instance not found: %s", i.InstanceID)
}
state.Put("instance", &r.Reservations[0].Instances[0])
@@ -50,11 +53,10 @@ func WinRMAddress(e *ec2.EC2, port uint, private bool) func(multistep.StateBag)
}
// Creates a WinRM connect step for an EC2 instance
-func NewConnectStep(ec2 *ec2.EC2, private bool, winrmConfig wincommon.WinRMConfig) multistep.Step {
+func NewConnectStep(ec2 *ec2.EC2, private bool, winrmConfig *wincommon.WinRMConfig) multistep.Step {
return &wincommon.StepConnectWinRM{
WinRMAddress: WinRMAddress(ec2, winrmConfig.WinRMPort, private),
- WinRMUser: winrmConfig.WinRMUser,
- WinRMPassword: winrmConfig.WinRMPassword,
+ WinRMConfig: winrmConfig,
WinRMWaitTimeout: winrmConfig.WinRMWaitTimeout,
}
}
diff --git a/builder/amazon-windows/common/step_generate_secure_vm_config.go b/builder/amazon-windows/common/step_generate_secure_vm_config.go
new file mode 100644
index 0000000..c5d43e1
--- /dev/null
+++ b/builder/amazon-windows/common/step_generate_secure_vm_config.go
@@ -0,0 +1,157 @@
+package common
+
+import (
+ "bytes"
+ "encoding/base64"
+ "fmt"
+ "io/ioutil"
+ "text/template"
+
+ "github.com/mitchellh/multistep"
+ "github.com/mitchellh/packer/packer"
+
+ wincommon "github.com/packer-community/packer-windows-plugins/common"
+)
+
+//A StepGenerateSecureWinRMUserData contains the state necessary to run the step to
+//generate EC2 user-data for the given RunConfig.
+type StepGenerateSecureWinRMUserData struct {
+ WinRMConfig *wincommon.WinRMConfig
+ WinRMCertificateFile string
+ RunConfig *RunConfig
+}
+
+func (s *StepGenerateSecureWinRMUserData) Run(state multistep.StateBag) multistep.StepAction {
+ ui := state.Get("ui").(packer.Ui)
+
+ if !s.RunConfig.ConfigureSecureWinRM {
+ return multistep.ActionContinue
+ }
+
+ ui.Say("Generating user data for configuring WinRM over TLS...")
+
+ certBytes, err := ioutil.ReadFile(s.WinRMCertificateFile)
+ if err != nil {
+ ui.Error(fmt.Sprintf("Error reading WinRM certificate file: %s", err))
+ return multistep.ActionHalt
+ }
+
+ encodedCert := base64.StdEncoding.EncodeToString(certBytes)
+
+ var adminPasswordBuffer bytes.Buffer
+ if s.RunConfig.NewAdministratorPassword != "" {
+ ui.Say("Configuring user data to change Administrator password...")
+ err = changeAdministratorPasswordTemplate.Execute(&adminPasswordBuffer, changeAdministratorPasswordOptions{
+ NewAdministratorPassword: s.RunConfig.NewAdministratorPassword,
+ })
+ if err != nil {
+ ui.Error(fmt.Sprintf("Error executing Change Administrator Password template: %s", err))
+ return multistep.ActionHalt
+ }
+ }
+
+ var buffer bytes.Buffer
+ err = configureSecureWinRMTemplate.Execute(&buffer, configureSecureWinRMOptions{
+ CertificatePfxBase64Encoded: encodedCert,
+ InstallListenerCommand: installListenerCommand,
+ AllowBasicCommand: allowBasicCommand,
+ AllowUnencryptedCommand: allowUnencryptedCommand,
+ AllowCredSSPCommand: allowCredSSPCommand,
+ MaxMemoryPerShellCommand: maxMemoryPerShellCommand,
+ MaxTimeoutMsCommand: maxTimeoutMsCommand,
+
+ ChangeAdministratorPasswordCommand: adminPasswordBuffer.String(),
+ })
+ if err != nil {
+ ui.Error(fmt.Sprintf("Error executing Secure WinRM User Data template: %s", err))
+ return multistep.ActionHalt
+ }
+
+ s.RunConfig.UserData = buffer.String()
+ return multistep.ActionContinue
+}
+
+func (s *StepGenerateSecureWinRMUserData) Cleanup(multistep.StateBag) {
+ // No cleanup...
+}
+
+type changeAdministratorPasswordOptions struct {
+ NewAdministratorPassword string
+}
+
+var changeAdministratorPasswordTemplate = template.Must(template.New("ChangeAdministratorPassword").Parse(`$user = [adsi]"WinNT://localhost/Administrator,user"
+$user.SetPassword("{{.NewAdministratorPassword}}")
+$user.SetInfo()`))
+
+type configureSecureWinRMOptions struct {
+ CertificatePfxBase64Encoded string
+ InstallListenerCommand string
+ AllowBasicCommand string
+ AllowUnencryptedCommand string
+ AllowCredSSPCommand string
+ MaxMemoryPerShellCommand string
+ MaxTimeoutMsCommand string
+ ChangeAdministratorPasswordCommand string
+}
+
+//This is needed to because Powershell uses ` for escapes and there's no straightforward way of constructing
+// the necessary escaping in the hash otherwise.
+const (
+ installListenerCommand = "Start-Process -FilePath winrm -ArgumentList \"create winrm/config/listener?Address=*+Transport=HTTPS @{Hostname=`\"$certSubjectName`\";CertificateThumbprint=`\"$certThumbprint`\";Port=`\"5986`\"}\" -NoNewWindow -Wait"
+
+ allowBasicCommand = "Start-Process -FilePath winrm -ArgumentList \"set winrm/config/service/auth @{Basic=`\"true`\"}\" -NoNewWindow -Wait"
+ allowUnencryptedCommand = "Start-Process -FilePath winrm -ArgumentList \"set winrm/config/service @{AllowUnencrypted=`\"false`\"}\" -NoNewWindow -Wait"
+ allowCredSSPCommand = "Start-Process -FilePath winrm -ArgumentList \"set winrm/config/service/auth @{CredSSP=`\"true`\"}\" -NoNewWindow -Wait"
+ maxMemoryPerShellCommand = "Start-Process -FilePath winrm -ArgumentList \"set winrm/config/winrs @{MaxMemoryPerShellMB=`\"1024`\"}\" -NoNewWindow -Wait"
+ maxTimeoutMsCommand = "Start-Process -FilePath winrm -ArgumentList \"set winrm/config @{MaxTimeoutms=`\"1800000`\"}\" -NoNewWindow -Wait"
+)
+
+var configureSecureWinRMTemplate = template.Must(template.New("ConfigureSecureWinRM").Parse(`
+Write-Host "Disabling WinRM over HTTP..."
+Disable-NetFirewallRule -Name "WINRM-HTTP-In-TCP"
+Disable-NetFirewallRule -Name "WINRM-HTTP-In-TCP-PUBLIC"
+
+Start-Process -FilePath winrm -ArgumentList "delete winrm/config/listener?Address=*+Transport=HTTP" -NoNewWindow -Wait
+
+Write-Host "Configuring WinRM for HTTPS..."
+
+{{.MaxTimeoutMsCommand}}
+
+{{.MaxMemoryPerShellCommand}}
+
+{{.AllowUnencryptedCommand}}
+
+{{.AllowBasicCommand}}
+
+{{.AllowCredSSPCommand}}
+
+New-NetFirewallRule -Name "WINRM-HTTPS-In-TCP" -DisplayName "Windows Remote Management (HTTPS-In)" -Description "Inbound rule for Windows Remote Management via WS-Management. [TCP 5986]" -Group "Windows Remote Management" -Program "System" -Protocol TCP -LocalPort "5986" -Action Allow -Profile Domain,Private
+
+New-NetFirewallRule -Name "WINRM-HTTPS-In-TCP-PUBLIC" -DisplayName "Windows Remote Management (HTTPS-In)" -Description "Inbound rule for Windows Remote Management via WS-Management. [TCP 5986]" -Group "Windows Remote Management" -Program "System" -Protocol TCP -LocalPort "5986" -Action Allow -Profile Public
+
+$certContent = "{{.CertificatePfxBase64Encoded}}"
+
+$certBytes = [System.Convert]::FromBase64String($certContent)
+$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
+$pfx.Import($certBytes, "", "Exportable,PersistKeySet,MachineKeySet")
+$certThumbprint = $pfx.Thumbprint
+$certSubjectName = $pfx.SubjectName.Name.TrimStart("CN = ").Trim()
+
+$store = new-object System.Security.Cryptography.X509Certificates.X509Store("My", "LocalMachine")
+try {
+ $store.Open("ReadWrite,MaxAllowed")
+ $store.Add($pfx)
+
+} finally {
+ $store.Close()
+}
+
+{{.InstallListenerCommand}}
+
+{{.ChangeAdministratorPasswordCommand}}
+
+Write-Host "Restarting WinRM Service..."
+Stop-Service winrm
+Set-Service winrm -StartupType "Automatic"
+Start-Service winrm
+`))
diff --git a/builder/amazon-windows/common/step_get_password.go b/builder/amazon-windows/common/step_get_password.go
new file mode 100644
index 0000000..a861705
--- /dev/null
+++ b/builder/amazon-windows/common/step_get_password.go
@@ -0,0 +1,153 @@
+package common
+
+import (
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/awslabs/aws-sdk-go/service/ec2"
+ "github.com/mitchellh/multistep"
+ "github.com/mitchellh/packer/packer"
+
+ wincommon "github.com/packer-community/packer-windows-plugins/common"
+)
+
+type StepGetPassword struct {
+ WinRMConfig *wincommon.WinRMConfig
+ RunConfig *RunConfig
+ GetPasswordTimeout time.Duration
+}
+
+func (s *StepGetPassword) Run(state multistep.StateBag) multistep.StepAction {
+ ui := state.Get("ui").(packer.Ui)
+ instance := state.Get("instance").(*ec2.Instance)
+
+ if s.RunConfig.NewAdministratorPassword != "" {
+ s.WinRMConfig.WinRMPassword = s.RunConfig.NewAdministratorPassword
+ return multistep.ActionContinue
+ }
+
+ var password string
+ var err error
+
+ cancel := make(chan struct{})
+ waitDone := make(chan bool, 1)
+ go func() {
+ ui.Say(fmt.Sprintf("Retrieving auto-generated password for instance %s...", *instance.InstanceID))
+
+ password, err = s.waitForPassword(state, cancel)
+ if err != nil {
+ waitDone <- false
+ return
+ }
+ waitDone <- true
+ }()
+
+ log.Printf("Waiting to retrieve instance %s password, up to timeout: %s", *instance.InstanceID, s.GetPasswordTimeout)
+ timeout := time.After(s.GetPasswordTimeout)
+
+WaitLoop:
+ for {
+ // Wait for one of: the password becoming available, a timeout occuring
+ // or an interrupt coming through.
+ select {
+ case <-waitDone:
+ if err != nil {
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+
+ s.WinRMConfig.WinRMPassword = password
+ break WaitLoop
+
+ case <-timeout:
+ err := fmt.Errorf(fmt.Sprintf("Timeout retrieving password for instance %s", *instance.InstanceID))
+ state.Put("error", err)
+ ui.Error(err.Error())
+ close(cancel)
+ return multistep.ActionHalt
+
+ case <-time.After(1 * time.Second):
+ if _, ok := state.GetOk(multistep.StateCancelled); ok {
+ // Build was cancelled.
+ close(cancel)
+ log.Println("Interrupt detected, cancelling password retrieval")
+ return multistep.ActionHalt
+ }
+ }
+ }
+
+ return multistep.ActionContinue
+
+}
+
+func (s *StepGetPassword) waitForPassword(state multistep.StateBag, cancel <-chan struct{}) (string, error) {
+ ec2conn := state.Get("ec2").(*ec2.EC2)
+ instance := state.Get("instance").(*ec2.Instance)
+ privateKey := state.Get("privateKey").(string)
+
+ for {
+ select {
+ case <-cancel:
+ log.Println("Retrieve password wait cancelled. Exiting loop.")
+ return "", errors.New("Retrieve password wait cancelled")
+
+ case <-time.After(20 * time.Second):
+ }
+
+ input := &ec2.GetPasswordDataInput{
+ InstanceID: instance.InstanceID,
+ }
+ resp, err := ec2conn.GetPasswordData(input)
+ if err != nil {
+ err := fmt.Errorf("Error retrieving auto-generated instance password: %s", err)
+ return "", err
+ }
+
+ if *resp.PasswordData != "" {
+ decryptedPassword, err := decryptPasswordDataWithPrivateKey(*resp.PasswordData, []byte(privateKey))
+ if err != nil {
+ err := fmt.Errorf("Error decrypting auto-generated instance password: %s", err)
+ return "", err
+ }
+ return decryptedPassword, nil
+ }
+ }
+}
+
+func (s *StepGetPassword) Cleanup(multistep.StateBag) {
+ // No cleanup...
+}
+
+func decryptPasswordDataWithPrivateKey(passwordData string, pemBytes []byte) (string, error) {
+ encryptedPasswd, err := base64.StdEncoding.DecodeString(passwordData)
+ if err != nil {
+ return "", err
+ }
+
+ block, _ := pem.Decode(pemBytes)
+ var asn1Bytes []byte
+ if _, ok := block.Headers["DEK-Info"]; ok {
+ return "", fmt.Errorf("Cannot decrypt instance password as the keypair is protected with a passphrase")
+ }
+
+ asn1Bytes = block.Bytes
+
+ key, err := x509.ParsePKCS1PrivateKey(asn1Bytes)
+ if err != nil {
+ return "", err
+ }
+
+ out, err := rsa.DecryptPKCS1v15(nil, key, encryptedPasswd)
+ if err != nil {
+ return "", err
+ }
+
+ return string(out), nil
+}
diff --git a/builder/amazon-windows/common/step_run_source_instance.go b/builder/amazon-windows/common/step_run_source_instance.go
index 96638c5..ced3e70 100644
--- a/builder/amazon-windows/common/step_run_source_instance.go
+++ b/builder/amazon-windows/common/step_run_source_instance.go
@@ -7,41 +7,38 @@ import (
"strconv"
"time"
- "github.com/mitchellh/goamz/ec2"
+ "github.com/awslabs/aws-sdk-go/aws"
+ "github.com/awslabs/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
- awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
+
+ awscommon "github.com/mitchellh/packer/builder/amazon/common"
)
type StepRunSourceInstance struct {
- AssociatePublicIpAddress bool
- AvailabilityZone string
- BlockDevices awscommon.BlockDevices
- Debug bool
- ExpectedRootDevice string
- InstanceType string
- IamInstanceProfile string
- SourceAMI string
- SpotPrice string
- SpotPriceProduct string
- SubnetId string
- Tags map[string]string
- UserData string
- UserDataFile string
+ RunConfig *RunConfig
+ BlockDevices *awscommon.BlockDevices
+ ExpectedRootDevice string
+ Debug bool
instance *ec2.Instance
- spotRequest *ec2.SpotRequestResult
+ spotRequest *ec2.SpotInstanceRequest
}
func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
keyName := state.Get("keyPair").(string)
- securityGroupIds := state.Get("securityGroupIds").([]string)
+ tempSecurityGroupIds := state.Get("securityGroupIds").([]string)
ui := state.Get("ui").(packer.Ui)
- userData := s.UserData
- if s.UserDataFile != "" {
- contents, err := ioutil.ReadFile(s.UserDataFile)
+ securityGroupIds := make([]*string, len(tempSecurityGroupIds))
+ for i, sg := range tempSecurityGroupIds {
+ securityGroupIds[i] = &sg
+ }
+
+ userData := s.RunConfig.UserData
+ if s.RunConfig.UserDataFile != "" {
+ contents, err := ioutil.ReadFile(s.RunConfig.UserDataFile)
if err != nil {
state.Put("error", fmt.Errorf("Problem reading user data file: %s", err))
return multistep.ActionHalt
@@ -50,44 +47,41 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
userData = string(contents)
}
- securityGroups := make([]ec2.SecurityGroup, len(securityGroupIds))
- for n, securityGroupId := range securityGroupIds {
- securityGroups[n] = ec2.SecurityGroup{Id: securityGroupId}
- }
-
ui.Say("Launching a source AWS instance...")
- imageResp, err := ec2conn.Images([]string{s.SourceAMI}, ec2.NewFilter())
+ imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
+ ImageIDs: []*string{&s.RunConfig.SourceAmi},
+ })
if err != nil {
state.Put("error", fmt.Errorf("There was a problem with the source AMI: %s", err))
return multistep.ActionHalt
}
if len(imageResp.Images) != 1 {
- state.Put("error", fmt.Errorf("The source AMI '%s' could not be found.", s.SourceAMI))
+ state.Put("error", fmt.Errorf("The source AMI '%s' could not be found.", s.RunConfig.SourceAmi))
return multistep.ActionHalt
}
- if s.ExpectedRootDevice != "" && imageResp.Images[0].RootDeviceType != s.ExpectedRootDevice {
+ if s.ExpectedRootDevice != "" && *imageResp.Images[0].RootDeviceType != s.ExpectedRootDevice {
state.Put("error", fmt.Errorf(
"The provided source AMI has an invalid root device type.\n"+
"Expected '%s', got '%s'.",
- s.ExpectedRootDevice, imageResp.Images[0].RootDeviceType))
+ s.ExpectedRootDevice, *imageResp.Images[0].RootDeviceType))
return multistep.ActionHalt
}
- spotPrice := s.SpotPrice
+ spotPrice := s.RunConfig.SpotPrice
if spotPrice == "auto" {
ui.Message(fmt.Sprintf(
"Finding spot price for %s %s...",
- s.SpotPriceProduct, s.InstanceType))
+ s.RunConfig.SpotPriceAutoProduct, s.RunConfig.InstanceType))
// Detect the spot price
startTime := time.Now().Add(-1 * time.Hour)
- resp, err := ec2conn.DescribeSpotPriceHistory(&ec2.DescribeSpotPriceHistory{
- InstanceType: []string{s.InstanceType},
- ProductDescription: []string{s.SpotPriceProduct},
- AvailabilityZone: s.AvailabilityZone,
- StartTime: startTime,
+ resp, err := ec2conn.DescribeSpotPriceHistory(&ec2.DescribeSpotPriceHistoryInput{
+ InstanceTypes: []*string{&s.RunConfig.InstanceType},
+ ProductDescriptions: []*string{&s.RunConfig.SpotPriceAutoProduct},
+ AvailabilityZone: &s.RunConfig.AvailabilityZone,
+ StartTime: &startTime,
})
if err != nil {
err := fmt.Errorf("Error finding spot price: %s", err)
@@ -97,9 +91,9 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
}
var price float64
- for _, history := range resp.History {
+ for _, history := range resp.SpotPriceHistory {
log.Printf("[INFO] Candidate spot price: %s", history.SpotPrice)
- current, err := strconv.ParseFloat(history.SpotPrice, 64)
+ current, err := strconv.ParseFloat(*history.SpotPrice, 64)
if err != nil {
log.Printf("[ERR] Error parsing spot price: %s", err)
continue
@@ -121,20 +115,33 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
var instanceId string
if spotPrice == "" {
- runOpts := &ec2.RunInstances{
- KeyName: keyName,
- ImageId: s.SourceAMI,
- InstanceType: s.InstanceType,
- UserData: []byte(userData),
- MinCount: 0,
- MaxCount: 0,
- SecurityGroups: securityGroups,
- IamInstanceProfile: s.IamInstanceProfile,
- SubnetId: s.SubnetId,
- AssociatePublicIpAddress: s.AssociatePublicIpAddress,
- BlockDevices: s.BlockDevices.BuildLaunchDevices(),
- AvailZone: s.AvailabilityZone,
+ runOpts := &ec2.RunInstancesInput{
+ KeyName: &keyName,
+ ImageID: &s.RunConfig.SourceAmi,
+ InstanceType: &s.RunConfig.InstanceType,
+ UserData: &userData,
+ MaxCount: aws.Long(1),
+ MinCount: aws.Long(1),
+ IAMInstanceProfile: &ec2.IAMInstanceProfileSpecification{Name: &s.RunConfig.IamInstanceProfile},
+ BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(),
+ Placement: &ec2.Placement{AvailabilityZone: &s.RunConfig.AvailabilityZone},
}
+
+ if s.RunConfig.SubnetId != "" && s.RunConfig.AssociatePublicIpAddress {
+ runOpts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{
+ &ec2.InstanceNetworkInterfaceSpecification{
+ DeviceIndex: aws.Long(0),
+ AssociatePublicIPAddress: &s.RunConfig.AssociatePublicIpAddress,
+ SubnetID: &s.RunConfig.SubnetId,
+ Groups: securityGroupIds,
+ DeleteOnTermination: aws.Boolean(true),
+ },
+ }
+ } else {
+ runOpts.SubnetID = &s.RunConfig.SubnetId
+ runOpts.SecurityGroupIDs = securityGroupIds
+ }
+
runResp, err := ec2conn.RunInstances(runOpts)
if err != nil {
err := fmt.Errorf("Error launching source instance: %s", err)
@@ -142,26 +149,29 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
ui.Error(err.Error())
return multistep.ActionHalt
}
- instanceId = runResp.Instances[0].InstanceId
+ instanceId = *runResp.Instances[0].InstanceID
} else {
ui.Message(fmt.Sprintf(
"Requesting spot instance '%s' for: %s",
- s.InstanceType, spotPrice))
-
- runOpts := &ec2.RequestSpotInstances{
- SpotPrice: spotPrice,
- KeyName: keyName,
- ImageId: s.SourceAMI,
- InstanceType: s.InstanceType,
- UserData: []byte(userData),
- SecurityGroups: securityGroups,
- IamInstanceProfile: s.IamInstanceProfile,
- SubnetId: s.SubnetId,
- AssociatePublicIpAddress: s.AssociatePublicIpAddress,
- BlockDevices: s.BlockDevices.BuildLaunchDevices(),
- AvailZone: s.AvailabilityZone,
- }
- runSpotResp, err := ec2conn.RequestSpotInstances(runOpts)
+ s.RunConfig.InstanceType, spotPrice))
+
+ runSpotResp, err := ec2conn.RequestSpotInstances(&ec2.RequestSpotInstancesInput{
+ SpotPrice: &spotPrice,
+ LaunchSpecification: &ec2.RequestSpotLaunchSpecification{
+ KeyName: &keyName,
+ ImageID: &s.RunConfig.SourceAmi,
+ InstanceType: &s.RunConfig.InstanceType,
+ UserData: &userData,
+ SecurityGroupIDs: securityGroupIds,
+ IAMInstanceProfile: &ec2.IAMInstanceProfileSpecification{Name: &s.RunConfig.IamInstanceProfile},
+ SubnetID: &s.RunConfig.SubnetId,
+ NetworkInterfaces: []*ec2.InstanceNetworkInterfaceSpecification{
+ &ec2.InstanceNetworkInterfaceSpecification{AssociatePublicIPAddress: &s.RunConfig.AssociatePublicIpAddress},
+ },
+ Placement: &ec2.SpotPlacement{AvailabilityZone: &s.RunConfig.AvailabilityZone},
+ BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(),
+ },
+ })
if err != nil {
err := fmt.Errorf("Error launching source spot instance: %s", err)
state.Put("error", err)
@@ -169,14 +179,14 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
return multistep.ActionHalt
}
- s.spotRequest = &runSpotResp.SpotRequestResults[0]
+ s.spotRequest = runSpotResp.SpotInstanceRequests[0]
- spotRequestId := s.spotRequest.SpotRequestId
+ spotRequestId := s.spotRequest.SpotInstanceRequestID
ui.Message(fmt.Sprintf("Waiting for spot request (%s) to become active...", spotRequestId))
stateChange := awscommon.StateChangeConf{
Pending: []string{"open"},
Target: "active",
- Refresh: awscommon.SpotRequestStateRefreshFunc(ec2conn, spotRequestId),
+ Refresh: awscommon.SpotRequestStateRefreshFunc(ec2conn, *spotRequestId),
StepState: state,
}
_, err = awscommon.WaitForState(&stateChange)
@@ -186,28 +196,38 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
ui.Error(err.Error())
return multistep.ActionHalt
}
- spotResp, err := ec2conn.DescribeSpotRequests([]string{spotRequestId}, nil)
+ spotResp, err := ec2conn.DescribeSpotInstanceRequests(&ec2.DescribeSpotInstanceRequestsInput{
+ SpotInstanceRequestIDs: []*string{spotRequestId},
+ })
if err != nil {
err := fmt.Errorf("Error finding spot request (%s): %s", spotRequestId, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
- instanceId = spotResp.SpotRequestResults[0].InstanceId
+ instanceId = *spotResp.SpotInstanceRequests[0].InstanceID
}
- ui.Message(fmt.Sprintf("Instance ID: %s", instanceId))
+ instanceResp, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesInput{InstanceIDs: []*string{&instanceId}})
+ if err != nil {
+ err := fmt.Errorf("Error finding source instance (%s): %s", instanceId, err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ s.instance = instanceResp.Reservations[0].Instances[0]
+ ui.Message(fmt.Sprintf("Instance ID: %s", *s.instance.InstanceID))
- ui.Say(fmt.Sprintf("Waiting for instance (%v) to become ready...", instanceId))
- stateChange := StateChangeConf{
+ ui.Say(fmt.Sprintf("Waiting for instance (%s) to become ready...", *s.instance.InstanceID))
+ stateChange := awscommon.StateChangeConf{
Pending: []string{"pending"},
Target: "running",
- Refresh: InstanceStateRefreshFunc(ec2conn, instanceId),
+ Refresh: awscommon.InstanceStateRefreshFunc(ec2conn, s.instance),
StepState: state,
}
- latestInstance, err := WaitForState(&stateChange)
+ latestInstance, err := awscommon.WaitForState(&stateChange)
if err != nil {
- err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", instanceId, err)
+ err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", *s.instance.InstanceID, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
@@ -215,29 +235,32 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
s.instance = latestInstance.(*ec2.Instance)
- ec2Tags := make([]ec2.Tag, 1, len(s.Tags)+1)
- ec2Tags[0] = ec2.Tag{"Name", "Packer Builder"}
- for k, v := range s.Tags {
- ec2Tags = append(ec2Tags, ec2.Tag{k, v})
+ ec2Tags := make([]*ec2.Tag, 1, len(s.RunConfig.RunTags)+1)
+ ec2Tags[0] = &ec2.Tag{Key: aws.String("Name"), Value: aws.String("Packer Builder")}
+ for k, v := range s.RunConfig.RunTags {
+ ec2Tags = append(ec2Tags, &ec2.Tag{Key: &k, Value: &v})
}
- _, err = ec2conn.CreateTags([]string{s.instance.InstanceId}, ec2Tags)
+ _, err = ec2conn.CreateTags(&ec2.CreateTagsInput{
+ Tags: ec2Tags,
+ Resources: []*string{s.instance.InstanceID},
+ })
if err != nil {
ui.Message(
fmt.Sprintf("Failed to tag a Name on the builder instance: %s", err))
}
if s.Debug {
- if s.instance.DNSName != "" {
- ui.Message(fmt.Sprintf("Public DNS: %s", s.instance.DNSName))
+ if s.instance.PublicDNSName != nil && *s.instance.PublicDNSName != "" {
+ ui.Message(fmt.Sprintf("Public DNS: %s", *s.instance.PublicDNSName))
}
- if s.instance.PublicIpAddress != "" {
- ui.Message(fmt.Sprintf("Public IP: %s", s.instance.PublicIpAddress))
+ if s.instance.PublicIPAddress != nil && *s.instance.PublicIPAddress != "" {
+ ui.Message(fmt.Sprintf("Public IP: %s", *s.instance.PublicIPAddress))
}
- if s.instance.PrivateIpAddress != "" {
- ui.Message(fmt.Sprintf("Private IP: %s", s.instance.PrivateIpAddress))
+ if s.instance.PrivateIPAddress != nil && *s.instance.PrivateIPAddress != "" {
+ ui.Message(fmt.Sprintf("Private IP: %s", *s.instance.PrivateIPAddress))
}
}
@@ -254,17 +277,20 @@ func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) {
// Cancel the spot request if it exists
if s.spotRequest != nil {
ui.Say("Cancelling the spot request...")
- if _, err := ec2conn.CancelSpotRequests([]string{s.spotRequest.SpotRequestId}); err != nil {
+ input := &ec2.CancelSpotInstanceRequestsInput{
+ SpotInstanceRequestIDs: []*string{s.spotRequest.InstanceID},
+ }
+ if _, err := ec2conn.CancelSpotInstanceRequests(input); err != nil {
ui.Error(fmt.Sprintf("Error cancelling the spot request, may still be around: %s", err))
return
}
- stateChange := awscommon.StateChangeConf{
+ stateChange := StateChangeConf{
Pending: []string{"active", "open"},
- Refresh: awscommon.SpotRequestStateRefreshFunc(ec2conn, s.spotRequest.SpotRequestId),
+ Refresh: SpotRequestStateRefreshFunc(ec2conn, *s.spotRequest.SpotInstanceRequestID),
Target: "cancelled",
}
- awscommon.WaitForState(&stateChange)
+ WaitForState(&stateChange)
}
@@ -272,16 +298,16 @@ func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) {
if s.instance != nil {
ui.Say("Terminating the source AWS instance...")
- if _, err := ec2conn.TerminateInstances([]string{s.instance.InstanceId}); err != nil {
+ if _, err := ec2conn.TerminateInstances(&ec2.TerminateInstancesInput{InstanceIDs: []*string{s.instance.InstanceID}}); err != nil {
ui.Error(fmt.Sprintf("Error terminating instance, may still be around: %s", err))
return
}
- stateChange := awscommon.StateChangeConf{
+ stateChange := StateChangeConf{
Pending: []string{"pending", "running", "shutting-down", "stopped", "stopping"},
- Refresh: awscommon.InstanceStateRefreshFunc(ec2conn, s.instance),
+ Refresh: InstanceStateRefreshFunc(ec2conn, s.instance),
Target: "terminated",
}
- awscommon.WaitForState(&stateChange)
+ WaitForState(&stateChange)
}
}
diff --git a/builder/amazon-windows/common/step_security_group.go b/builder/amazon-windows/common/step_security_group.go
index 6e19ce6..f4168ae 100644
--- a/builder/amazon-windows/common/step_security_group.go
+++ b/builder/amazon-windows/common/step_security_group.go
@@ -2,12 +2,14 @@ package common
import (
"fmt"
- "github.com/mitchellh/goamz/ec2"
+ "log"
+ "time"
+
+ "github.com/awslabs/aws-sdk-go/aws"
+ "github.com/awslabs/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
- "log"
- "time"
)
type StepSecurityGroup struct {
@@ -32,14 +34,15 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
panic("WinRMPort must be set to a non-zero value.")
}
+ // Create the group
// Create the group
ui.Say("Creating temporary security group for this instance...")
groupName := fmt.Sprintf("packer %s", uuid.TimeOrderedUUID())
log.Printf("Temporary group name: %s", groupName)
- group := ec2.SecurityGroup{
- Name: groupName,
- Description: "Temporary group for Packer",
- VpcId: s.VpcId,
+ group := &ec2.CreateSecurityGroupInput{
+ GroupName: &groupName,
+ Description: aws.String("Temporary group for Packer"),
+ VPCID: &s.VpcId,
}
groupResp, err := ec2conn.CreateSecurityGroup(group)
if err != nil {
@@ -49,24 +52,24 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
}
// Set the group ID so we can delete it later
- s.createdGroupId = groupResp.Id
+ s.createdGroupId = *groupResp.GroupID
// Authorize the WinRM access
- perms := []ec2.IPPerm{
- ec2.IPPerm{
- Protocol: "tcp",
- FromPort: int(s.WinRMPort),
- ToPort: int(s.WinRMPort),
- SourceIPs: []string{"0.0.0.0/0"},
- },
+ // Authorize the SSH access for the security group
+ req := &ec2.AuthorizeSecurityGroupIngressInput{
+ GroupID: groupResp.GroupID,
+ IPProtocol: aws.String("tcp"),
+ FromPort: aws.Long(int64(s.WinRMPort)),
+ ToPort: aws.Long(int64(s.WinRMPort)),
+ CIDRIP: aws.String("0.0.0.0/0"),
}
// We loop and retry this a few times because sometimes the security
// group isn't available immediately because AWS resources are eventaully
// consistent.
- ui.Say("Authorizing WinRM access on the temporary security group...")
+ ui.Say("Authorizing SSH access on the temporary security group...")
for i := 0; i < 5; i++ {
- _, err = ec2conn.AuthorizeSecurityGroup(groupResp.SecurityGroup, perms)
+ _, err = ec2conn.AuthorizeSecurityGroupIngress(req)
if err == nil {
break
}
@@ -100,7 +103,7 @@ func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) {
var err error
for i := 0; i < 5; i++ {
- _, err = ec2conn.DeleteSecurityGroup(ec2.SecurityGroup{Id: s.createdGroupId})
+ _, err = ec2conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{GroupID: &s.createdGroupId})
if err == nil {
break
}
diff --git a/builder/amazon-windows/ebs/builder.go b/builder/amazon-windows/ebs/builder.go
index ff499d4..429a4ce 100644
--- a/builder/amazon-windows/ebs/builder.go
+++ b/builder/amazon-windows/ebs/builder.go
@@ -6,9 +6,11 @@
package ebs
import (
+ "fmt"
"log"
+ "time"
- "github.com/mitchellh/goamz/ec2"
+ "github.com/awslabs/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/common"
@@ -18,7 +20,7 @@ import (
)
// The unique ID for this builder
-const BuilderId = "mitchellh.amazonebs"
+const BuilderId = "packercommunity.windows.amazon.ebs"
type config struct {
common.PackerConfig `mapstructure:",squash"`
@@ -65,17 +67,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
- region, err := b.config.Region()
+ config, err := b.config.Config()
if err != nil {
return nil, err
}
- auth, err := b.config.AccessConfig.Auth()
- if err != nil {
- return nil, err
- }
-
- ec2conn := ec2.New(auth, region)
+ ec2conn := ec2.New(config)
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
@@ -83,38 +80,42 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
state.Put("ec2", ec2conn)
state.Put("hook", hook)
state.Put("ui", ui)
- // Required by StepRunSourceInstance. Seems a better alternative
- // to duplicating ~300 lines of code just to remove it as a dependency
- state.Put("keyPair", "")
+ state.Put("keyPair", b.config.TemporaryKeyPairName)
// Build the steps
steps := []multistep.Step{
+ &winawscommon.StepGenerateSecureWinRMUserData{
+ RunConfig: &b.config.RunConfig,
+ WinRMConfig: &b.config.WinRMConfig,
+ WinRMCertificateFile: b.config.WinRMCertificateFile,
+ },
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking,
},
+ &awscommon.StepKeyPair{
+ Debug: b.config.PackerDebug,
+ DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
+ KeyPairName: b.config.TemporaryKeyPairName,
+ PrivateKeyFile: b.config.KeyPairPrivateKeyFile,
+ },
&winawscommon.StepSecurityGroup{
SecurityGroupIds: b.config.SecurityGroupIds,
WinRMPort: b.config.WinRMPort,
VpcId: b.config.VpcId,
},
&winawscommon.StepRunSourceInstance{
- Debug: b.config.PackerDebug,
- ExpectedRootDevice: "ebs",
- SpotPrice: b.config.SpotPrice,
- SpotPriceProduct: b.config.SpotPriceAutoProduct,
- InstanceType: b.config.InstanceType,
- UserData: b.config.UserData,
- UserDataFile: b.config.UserDataFile,
- SourceAMI: b.config.SourceAmi,
- IamInstanceProfile: b.config.IamInstanceProfile,
- SubnetId: b.config.SubnetId,
- AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
- AvailabilityZone: b.config.AvailabilityZone,
- BlockDevices: b.config.BlockDevices,
- Tags: b.config.RunTags,
+ Debug: b.config.PackerDebug,
+ ExpectedRootDevice: "ebs",
+ BlockDevices: &b.config.BlockDevices,
+ RunConfig: &b.config.RunConfig,
+ },
+ &winawscommon.StepGetPassword{
+ WinRMConfig: &b.config.WinRMConfig,
+ RunConfig: &b.config.RunConfig,
+ GetPasswordTimeout: 5 * time.Minute,
},
- winawscommon.NewConnectStep(ec2conn, b.config.WinRMPrivateIp, b.config.WinRMConfig),
+ winawscommon.NewConnectStep(ec2conn, b.config.WinRMPrivateIp, &b.config.WinRMConfig),
&common.StepProvision{},
&stepStopInstance{SpotPrice: b.config.SpotPrice},
// TODO(mitchellh): verify works with spots
diff --git a/builder/amazon-windows/ebs/step_create_ami.go b/builder/amazon-windows/ebs/step_create_ami.go
index 967ad2d..1c38374 100644
--- a/builder/amazon-windows/ebs/step_create_ami.go
+++ b/builder/amazon-windows/ebs/step_create_ami.go
@@ -2,10 +2,12 @@ package ebs
import (
"fmt"
- "github.com/mitchellh/goamz/ec2"
+
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
+
+ "github.com/awslabs/aws-sdk-go/service/ec2"
)
type stepCreateAMI struct {
@@ -20,10 +22,10 @@ func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction {
// Create the image
ui.Say(fmt.Sprintf("Creating the AMI: %s", config.AMIName))
- createOpts := &ec2.CreateImage{
- InstanceId: instance.InstanceId,
- Name: config.AMIName,
- BlockDevices: config.BlockDevices.BuildAMIDevices(),
+ createOpts := &ec2.CreateImageInput{
+ InstanceID: instance.InstanceID,
+ Name: &config.AMIName,
+ BlockDeviceMappings: config.BlockDevices.BuildAMIDevices(),
}
createResp, err := ec2conn.CreateImage(createOpts)
@@ -35,16 +37,16 @@ func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction {
}
// Set the AMI ID in the state
- ui.Message(fmt.Sprintf("AMI: %s", createResp.ImageId))
+ ui.Message(fmt.Sprintf("AMI: %s", *createResp.ImageID))
amis := make(map[string]string)
- amis[ec2conn.Region.Name] = createResp.ImageId
+ amis[ec2conn.Config.Region] = *createResp.ImageID
state.Put("amis", amis)
// Wait for the image to become ready
stateChange := awscommon.StateChangeConf{
Pending: []string{"pending"},
Target: "available",
- Refresh: awscommon.AMIStateRefreshFunc(ec2conn, createResp.ImageId),
+ Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *createResp.ImageID),
StepState: state,
}
@@ -56,14 +58,14 @@ func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
- imagesResp, err := ec2conn.Images([]string{createResp.ImageId}, nil)
+ imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIDs: []*string{createResp.ImageID}})
if err != nil {
err := fmt.Errorf("Error searching for AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
- s.image = &imagesResp.Images[0]
+ s.image = imagesResp.Images[0]
return multistep.ActionContinue
}
@@ -83,11 +85,9 @@ func (s *stepCreateAMI) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packer.Ui)
ui.Say("Deregistering the AMI because cancelation or error...")
- if resp, err := ec2conn.DeregisterImage(s.image.Id); err != nil {
+ deregisterOpts := &ec2.DeregisterImageInput{ImageID: s.image.ImageID}
+ if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
return
- } else if resp.Return == false {
- ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", resp.Return))
- return
}
}
diff --git a/builder/amazon-windows/ebs/step_modify_instance.go b/builder/amazon-windows/ebs/step_modify_instance.go
index 21c5e7d..f7d9c8d 100644
--- a/builder/amazon-windows/ebs/step_modify_instance.go
+++ b/builder/amazon-windows/ebs/step_modify_instance.go
@@ -3,7 +3,9 @@ package ebs
import (
"fmt"
- "github.com/mitchellh/goamz/ec2"
+ "github.com/awslabs/aws-sdk-go/aws"
+ "github.com/awslabs/aws-sdk-go/service/ec2"
+
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
@@ -19,12 +21,12 @@ func (s *stepModifyInstance) Run(state multistep.StateBag) multistep.StepAction
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
if config.AMIEnhancedNetworking {
ui.Say("Enabling Enhanced Networking...")
- _, err := ec2conn.ModifyInstance(
- instance.InstanceId,
- &ec2.ModifyInstance{SriovNetSupport: true},
- )
+ _, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
+ InstanceID: instance.InstanceID,
+ SRIOVNetSupport: &ec2.AttributeValue{Value: aws.String("simple")},
+ })
if err != nil {
- err := fmt.Errorf("Error enabling Enhanced Networking on %s: %s", instance.InstanceId, err)
+ err := fmt.Errorf("Error enabling Enhanced Networking on %s: %s", *instance.InstanceID, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
diff --git a/builder/amazon-windows/ebs/step_stop_instance.go b/builder/amazon-windows/ebs/step_stop_instance.go
index 09c19bd..1271b8b 100644
--- a/builder/amazon-windows/ebs/step_stop_instance.go
+++ b/builder/amazon-windows/ebs/step_stop_instance.go
@@ -2,7 +2,8 @@ package ebs
import (
"fmt"
- "github.com/mitchellh/goamz/ec2"
+
+ "github.com/awslabs/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
@@ -24,7 +25,7 @@ func (s *stepStopInstance) Run(state multistep.StateBag) multistep.StepAction {
// Stop the instance so we can create an AMI from it
ui.Say("Stopping the source instance...")
- _, err := ec2conn.StopInstances(instance.InstanceId)
+ _, err := ec2conn.StopInstances(&ec2.StopInstancesInput{InstanceIDs: []*string{instance.InstanceID}})
if err != nil {
err := fmt.Errorf("Error stopping instance: %s", err)
state.Put("error", err)
diff --git a/builder/amazon-windows/instance/builder.go b/builder/amazon-windows/instance/builder.go
index f1da772..2c52f29 100644
--- a/builder/amazon-windows/instance/builder.go
+++ b/builder/amazon-windows/instance/builder.go
@@ -8,8 +8,9 @@ import (
"log"
"os"
"strings"
+ "time"
- "github.com/mitchellh/goamz/ec2"
+ "github.com/awslabs/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
awsinstcommon "github.com/mitchellh/packer/builder/amazon/instance"
@@ -20,7 +21,7 @@ import (
)
// The unique ID for this builder
-const BuilderId = "mitchellh.amazon.instance"
+const BuilderId = "packercommunity.windows.amazon.instance"
// Config is the configuration that is chained through the steps and
// settable from the template.
@@ -172,17 +173,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
- region, err := b.config.Region()
+ config, err := b.config.Config()
if err != nil {
return nil, err
}
- auth, err := b.config.AccessConfig.Auth()
- if err != nil {
- return nil, err
- }
-
- ec2conn := ec2.New(auth, region)
+ ec2conn := ec2.New(config)
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
@@ -190,34 +186,41 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
state.Put("ec2", ec2conn)
state.Put("hook", hook)
state.Put("ui", ui)
+ state.Put("keyPair", b.config.TemporaryKeyPairName)
// Build the steps
steps := []multistep.Step{
+ &winawscommon.StepGenerateSecureWinRMUserData{
+ RunConfig: &b.config.RunConfig,
+ WinRMConfig: &b.config.WinRMConfig,
+ WinRMCertificateFile: b.config.WinRMCertificateFile,
+ },
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking,
},
+ &awscommon.StepKeyPair{
+ Debug: b.config.PackerDebug,
+ DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
+ KeyPairName: b.config.TemporaryKeyPairName,
+ PrivateKeyFile: b.config.KeyPairPrivateKeyFile,
+ },
&winawscommon.StepSecurityGroup{
SecurityGroupIds: b.config.SecurityGroupIds,
WinRMPort: b.config.WinRMPort,
VpcId: b.config.VpcId,
},
&winawscommon.StepRunSourceInstance{
- Debug: b.config.PackerDebug,
- SpotPrice: b.config.SpotPrice,
- SpotPriceProduct: b.config.SpotPriceAutoProduct,
- InstanceType: b.config.InstanceType,
- IamInstanceProfile: b.config.IamInstanceProfile,
- UserData: b.config.UserData,
- UserDataFile: b.config.UserDataFile,
- SourceAMI: b.config.SourceAmi,
- SubnetId: b.config.SubnetId,
- AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
- AvailabilityZone: b.config.AvailabilityZone,
- BlockDevices: b.config.BlockDevices,
- Tags: b.config.RunTags,
+ Debug: b.config.PackerDebug,
+ BlockDevices: &b.config.BlockDevices,
+ RunConfig: &b.config.RunConfig,
+ },
+ &winawscommon.StepGetPassword{
+ WinRMConfig: &b.config.WinRMConfig,
+ RunConfig: &b.config.RunConfig,
+ GetPasswordTimeout: 5 * time.Minute,
},
- winawscommon.NewConnectStep(ec2conn, b.config.WinRMPrivateIp, b.config.WinRMConfig),
+ winawscommon.NewConnectStep(ec2conn, b.config.WinRMPrivateIp, &b.config.WinRMConfig),
&common.StepProvision{},
&awsinstcommon.StepUploadX509Cert{},
&awsinstcommon.StepBundleVolume{
diff --git a/builder/amazon-windows/instance/builder_test.go b/builder/amazon-windows/instance/builder_test.go
index d5ce5f2..eb23586 100644
--- a/builder/amazon-windows/instance/builder_test.go
+++ b/builder/amazon-windows/instance/builder_test.go
@@ -1,10 +1,11 @@
package instance
import (
- "github.com/mitchellh/packer/packer"
"io/ioutil"
"os"
"testing"
+
+ "github.com/mitchellh/packer/packer"
)
func testConfig() map[string]interface{} {
diff --git a/builder/parallels-windows/common/connect_step.go b/builder/parallels-windows/common/connect_step.go
index ddbcf2b..1b69342 100644
--- a/builder/parallels-windows/common/connect_step.go
+++ b/builder/parallels-windows/common/connect_step.go
@@ -9,7 +9,7 @@ import (
wincommon "github.com/packer-community/packer-windows-plugins/common"
)
-func WinRMAddressFunc(config wincommon.WinRMConfig) func(state multistep.StateBag) (string, error) {
+func WinRMAddressFunc(config *wincommon.WinRMConfig) func(state multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
log.Printf("Determining WinRM remote IP address...")
@@ -37,11 +37,10 @@ func WinRMAddressFunc(config wincommon.WinRMConfig) func(state multistep.StateBa
}
// Creates a generic WinRM connect step from a Parallels builder config
-func NewConnectStep(winrmConfig wincommon.WinRMConfig) multistep.Step {
+func NewConnectStep(winrmConfig *wincommon.WinRMConfig) multistep.Step {
return &wincommon.StepConnectWinRM{
WinRMAddress: WinRMAddressFunc(winrmConfig),
- WinRMUser: winrmConfig.WinRMUser,
- WinRMPassword: winrmConfig.WinRMPassword,
+ WinRMConfig: winrmConfig,
WinRMWaitTimeout: winrmConfig.WinRMWaitTimeout,
}
}
diff --git a/builder/parallels-windows/common/connect_step_test.go b/builder/parallels-windows/common/connect_step_test.go
index 9f3ef21..8674a05 100644
--- a/builder/parallels-windows/common/connect_step_test.go
+++ b/builder/parallels-windows/common/connect_step_test.go
@@ -20,7 +20,7 @@ func TestWinRMAddressFunc_UsesPortForwardingFail(t *testing.T) {
state.Put("driver", ¶llelscommon.DriverMock{IpAddressError: errors.New("Invalid machine state"), MacReturn: "01cd123"})
state.Put("vmName", "myvmname")
- f := WinRMAddressFunc(config)
+ f := WinRMAddressFunc(&config)
_, err := f(state)
if err == nil {
@@ -39,7 +39,7 @@ func TestWinRMAddressFunc_UsesPortForwarding(t *testing.T) {
state.Put("driver", ¶llelscommon.DriverMock{IpAddressReturn: "172.17.4.13", MacReturn: "01cd123"})
state.Put("vmName", "myvmname")
- f := WinRMAddressFunc(config)
+ f := WinRMAddressFunc(&config)
address, err := f(state)
if err != nil {
diff --git a/builder/parallels-windows/iso/builder.go b/builder/parallels-windows/iso/builder.go
index d1a5b2d..6d7be88 100644
--- a/builder/parallels-windows/iso/builder.go
+++ b/builder/parallels-windows/iso/builder.go
@@ -278,7 +278,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
VMName: b.config.VMName,
Tpl: b.config.tpl,
},
- winparallelscommon.NewConnectStep(b.config.WinRMConfig),
+ winparallelscommon.NewConnectStep(&b.config.WinRMConfig),
¶llelscommon.StepUploadVersion{
Path: b.config.PrlctlVersionFile,
},
diff --git a/builder/parallels-windows/pvm/builder.go b/builder/parallels-windows/pvm/builder.go
index b720235..a7b2026 100644
--- a/builder/parallels-windows/pvm/builder.go
+++ b/builder/parallels-windows/pvm/builder.go
@@ -81,7 +81,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
VMName: b.config.VMName,
Tpl: b.config.tpl,
},
- winparallelscommon.NewConnectStep(b.config.WinRMConfig),
+ winparallelscommon.NewConnectStep(&b.config.WinRMConfig),
¶llelscommon.StepUploadVersion{
Path: b.config.PrlctlVersionFile,
},
diff --git a/builder/virtualbox-windows/common/connect_step.go b/builder/virtualbox-windows/common/connect_step.go
index b3bf892..6b6d8ff 100644
--- a/builder/virtualbox-windows/common/connect_step.go
+++ b/builder/virtualbox-windows/common/connect_step.go
@@ -8,7 +8,7 @@ import (
wincommon "github.com/packer-community/packer-windows-plugins/common"
)
-func WinRMAddressFunc(config wincommon.WinRMConfig) func(state multistep.StateBag) (string, error) {
+func WinRMAddressFunc(config *wincommon.WinRMConfig) func(state multistep.StateBag) (string, error) {
if config.WinRMHost == "" {
log.Printf("No WinRM Host provided, using default host 127.0.0.1")
config.WinRMHost = "127.0.0.1"
@@ -27,11 +27,10 @@ func WinRMAddressFunc(config wincommon.WinRMConfig) func(state multistep.StateBa
}
// Creates a generic WinRM connect step from a Virtualbox builder config
-func NewConnectStep(winrmConfig wincommon.WinRMConfig) multistep.Step {
+func NewConnectStep(winrmConfig *wincommon.WinRMConfig) multistep.Step {
return &wincommon.StepConnectWinRM{
WinRMAddress: WinRMAddressFunc(winrmConfig),
- WinRMUser: winrmConfig.WinRMUser,
- WinRMPassword: winrmConfig.WinRMPassword,
+ WinRMConfig: winrmConfig,
WinRMWaitTimeout: winrmConfig.WinRMWaitTimeout,
}
}
diff --git a/builder/virtualbox-windows/common/connect_step_test.go b/builder/virtualbox-windows/common/connect_step_test.go
index 3bba71f..e429d34 100644
--- a/builder/virtualbox-windows/common/connect_step_test.go
+++ b/builder/virtualbox-windows/common/connect_step_test.go
@@ -16,7 +16,7 @@ func TestWinRMAddressFunc_UsesPortForwarding(t *testing.T) {
state := new(multistep.BasicStateBag)
state.Put("winrmHostPort", uint(123))
- f := WinRMAddressFunc(config)
+ f := WinRMAddressFunc(&config)
address, err := f(state)
if err != nil {
diff --git a/builder/virtualbox-windows/iso/builder.go b/builder/virtualbox-windows/iso/builder.go
index 171b636..7950ae8 100644
--- a/builder/virtualbox-windows/iso/builder.go
+++ b/builder/virtualbox-windows/iso/builder.go
@@ -16,7 +16,7 @@ import (
wincommon "github.com/packer-community/packer-windows-plugins/common"
)
-const BuilderId = "mitchellh.virtualbox"
+const BuilderId = "packercommunity.windows.virtualbox.iso"
type Builder struct {
config config
@@ -314,7 +314,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
VMName: b.config.VMName,
Tpl: b.config.tpl,
},
- winvboxcommon.NewConnectStep(b.config.WinRMConfig),
+ winvboxcommon.NewConnectStep(&b.config.WinRMConfig),
&vboxcommon.StepUploadVersion{
Path: b.config.VBoxVersionFile,
},
diff --git a/builder/virtualbox-windows/ovf/builder.go b/builder/virtualbox-windows/ovf/builder.go
index c03beae..826b339 100644
--- a/builder/virtualbox-windows/ovf/builder.go
+++ b/builder/virtualbox-windows/ovf/builder.go
@@ -101,7 +101,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
VMName: b.config.VMName,
Tpl: b.config.tpl,
},
- winvboxcommon.NewConnectStep(b.config.WinRMConfig),
+ winvboxcommon.NewConnectStep(&b.config.WinRMConfig),
&vboxcommon.StepUploadVersion{
Path: b.config.VBoxVersionFile,
},
diff --git a/builder/vmware-windows/common/connect_step.go b/builder/vmware-windows/common/connect_step.go
index 410a80d..64bf0c4 100644
--- a/builder/vmware-windows/common/connect_step.go
+++ b/builder/vmware-windows/common/connect_step.go
@@ -1,19 +1,18 @@
package common
import (
- wincommon "github.com/packer-community/packer-windows-plugins/common"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common"
+ wincommon "github.com/packer-community/packer-windows-plugins/common"
)
// Creates a generic SSH or WinRM connect step from a VMWare builder config
-func NewConnectStep(communicatorType string, driver Driver, sshConfig *SSHConfig, winrmConfig *WinRMConfig) multistep.Step {
+func NewConnectStep(communicatorType string, driver Driver, sshConfig *SSHConfig, winrmConfig *wincommon.WinRMConfig) multistep.Step {
//if communicatorType == packer.WinRMCommunicatorType {
if communicatorType == "winrm" {
return &wincommon.StepConnectWinRM{
WinRMAddress: WinRMAddressFunc(winrmConfig, driver),
- WinRMUser: winrmConfig.WinRMUser,
- WinRMPassword: winrmConfig.WinRMPassword,
+ WinRMConfig: winrmConfig,
WinRMWaitTimeout: winrmConfig.WinRMWaitTimeout,
}
} else {
diff --git a/builder/vmware-windows/common/winrm.go b/builder/vmware-windows/common/winrm.go
index 275ed7e..421153c 100644
--- a/builder/vmware-windows/common/winrm.go
+++ b/builder/vmware-windows/common/winrm.go
@@ -4,9 +4,10 @@ import (
"fmt"
"github.com/mitchellh/multistep"
+ wincommon "github.com/packer-community/packer-windows-plugins/common"
)
-func WinRMAddressFunc(config *WinRMConfig, driver Driver) func(multistep.StateBag) (string, error) {
+func WinRMAddressFunc(config *wincommon.WinRMConfig, driver Driver) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
if config.WinRMHost != "" {
return fmt.Sprintf("%s:%d", config.WinRMHost, config.WinRMPort), nil
diff --git a/builder/vmware-windows/common/winrm_config.go b/builder/vmware-windows/common/winrm_config.go
deleted file mode 100644
index d4fba28..0000000
--- a/builder/vmware-windows/common/winrm_config.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package common
-
-import (
- "errors"
- "fmt"
- "net"
- "time"
-
- "github.com/mitchellh/packer/packer"
-)
-
-type WinRMConfig struct {
- WinRMUser string `mapstructure:"winrm_username"`
- WinRMPassword string `mapstructure:"winrm_password"`
- WinRMHost string `mapstructure:"winrm_host"`
- WinRMPort uint `mapstructure:"winrm_port"`
- RawWinRMWaitTimeout string `mapstructure:"winrm_wait_timeout"`
-
- WinRMWaitTimeout time.Duration
-}
-
-func (c *WinRMConfig) Prepare(t *packer.ConfigTemplate) []error {
- if c.WinRMPort == 0 {
- c.WinRMPort = 5985
- }
-
- if c.RawWinRMWaitTimeout == "" {
- c.RawWinRMWaitTimeout = "20m"
- }
-
- templates := map[string]*string{
- "winrm_password": &c.WinRMPassword,
- "winrm_username": &c.WinRMUser,
- "winrm_wait_timeout": &c.RawWinRMWaitTimeout,
- }
-
- errs := make([]error, 0)
- for n, ptr := range templates {
- var err error
- *ptr, err = t.Process(*ptr, nil)
- if err != nil {
- errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err))
- }
- }
-
- if c.WinRMHost != "" {
- if ip := net.ParseIP(c.WinRMHost); ip == nil {
- if _, err := net.LookupHost(c.WinRMHost); err != nil {
- errs = append(errs, errors.New("winrm_host is an invalid IP or hostname"))
- }
- }
- }
-
- if c.WinRMUser == "" {
- errs = append(errs, errors.New("winrm_username must be specified."))
- }
-
- var err error
- c.WinRMWaitTimeout, err = time.ParseDuration(c.RawWinRMWaitTimeout)
- if err != nil {
- errs = append(errs, fmt.Errorf("Failed parsing winrm_wait_timeout: %s", err))
- }
-
- return errs
-}
diff --git a/builder/vmware-windows/common/winrm_config_test.go b/builder/vmware-windows/common/winrm_config_test.go
deleted file mode 100644
index 332d1df..0000000
--- a/builder/vmware-windows/common/winrm_config_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package common
-
-import (
- "testing"
-)
-
-func testWinRMConfig() *WinRMConfig {
- return &WinRMConfig{
- WinRMUser: "admin",
- }
-}
-
-func TestWinRMConfigPrepare(t *testing.T) {
- c := testWinRMConfig()
- errs := c.Prepare(testConfigTemplate(t))
- if len(errs) > 0 {
- t.Fatalf("err: %#v", errs)
- }
-
- if c.WinRMPort != 5985 {
- t.Errorf("bad winrm port: %d", c.WinRMPort)
- }
-}
-
-func TestWinRMConfigPrepare_WinRMUser(t *testing.T) {
- var c *WinRMConfig
- var errs []error
-
- c = testWinRMConfig()
- c.WinRMUser = ""
- errs = c.Prepare(testConfigTemplate(t))
- if len(errs) == 0 {
- t.Fatalf("should have error")
- }
-
- c = testWinRMConfig()
- c.WinRMUser = "exists"
- errs = c.Prepare(testConfigTemplate(t))
- if len(errs) > 0 {
- t.Fatalf("should not have error: %#v", errs)
- }
-}
-
-func TestWinRMConfigPrepare_WinRMWaitTimeout(t *testing.T) {
- var c *WinRMConfig
- var errs []error
-
- // Defaults
- c = testWinRMConfig()
- c.RawWinRMWaitTimeout = ""
- errs = c.Prepare(testConfigTemplate(t))
- if len(errs) > 0 {
- t.Fatalf("should not have error: %#v", errs)
- }
- if c.RawWinRMWaitTimeout != "20m" {
- t.Fatalf("bad value: %s", c.RawWinRMWaitTimeout)
- }
-
- // Test with a bad value
- c = testWinRMConfig()
- c.RawWinRMWaitTimeout = "this is not good"
- errs = c.Prepare(testConfigTemplate(t))
- if len(errs) == 0 {
- t.Fatal("should have error")
- }
-
- // Test with a good one
- c = testWinRMConfig()
- c.RawWinRMWaitTimeout = "5s"
- errs = c.Prepare(testConfigTemplate(t))
- if len(errs) > 0 {
- t.Fatalf("should not have error: %#v", errs)
- }
-}
diff --git a/builder/vmware-windows/iso/builder.go b/builder/vmware-windows/iso/builder.go
index 79224be..68eeeef 100644
--- a/builder/vmware-windows/iso/builder.go
+++ b/builder/vmware-windows/iso/builder.go
@@ -14,9 +14,10 @@ import (
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
vmwcommon "github.com/packer-community/packer-windows-plugins/builder/vmware-windows/common"
+ wincommon "github.com/packer-community/packer-windows-plugins/common"
)
-const BuilderIdESX = "mitchellh.vmware-esx"
+const BuilderIdESX = "packercommunity.windows.vmware.iso"
type Builder struct {
config config
@@ -32,7 +33,7 @@ type config struct {
vmwcommon.SSHConfig `mapstructure:",squash"`
vmwcommon.ToolsConfig `mapstructure:",squash"`
vmwcommon.VMXConfig `mapstructure:",squash"`
- vmwcommon.WinRMConfig `mapstructure:",squash"`
+ wincommon.WinRMConfig `mapstructure:",squash"`
AdditionalDiskSize []uint `mapstructure:"additionaldisk_size"`
DiskName string `mapstructure:"vmdk_name"`
diff --git a/builder/vmware-windows/vmx/config.go b/builder/vmware-windows/vmx/config.go
index c13df14..a4e3f84 100644
--- a/builder/vmware-windows/vmx/config.go
+++ b/builder/vmware-windows/vmx/config.go
@@ -7,6 +7,7 @@ import (
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
vmwcommon "github.com/packer-community/packer-windows-plugins/builder/vmware-windows/common"
+ wincommon "github.com/packer-community/packer-windows-plugins/common"
)
// Config is the configuration structure for the builder.
@@ -19,7 +20,7 @@ type Config struct {
vmwcommon.SSHConfig `mapstructure:",squash"`
vmwcommon.ToolsConfig `mapstructure:",squash"`
vmwcommon.VMXConfig `mapstructure:",squash"`
- vmwcommon.WinRMConfig `mapstructure:",squash"`
+ wincommon.WinRMConfig `mapstructure:",squash"`
BootCommand []string `mapstructure:"boot_command"`
FloppyFiles []string `mapstructure:"floppy_files"`
diff --git a/common/step_connect_winrm.go b/common/step_connect_winrm.go
index 9251f45..0b58516 100644
--- a/common/step_connect_winrm.go
+++ b/common/step_connect_winrm.go
@@ -29,11 +29,8 @@ type StepConnectWinRM struct {
// if necessary for this address.
WinRMAddress func(multistep.StateBag) (string, error)
- // The user name to connect to WinRM as
- WinRMUser string
-
- // The user password
- WinRMPassword string
+ // WinRMConfig contains the configuration for WinRM.
+ WinRMConfig *WinRMConfig
// WinRMWaitTimeout is the total timeout to wait for WinRM to become available.
WinRMWaitTimeout time.Duration
@@ -111,7 +108,7 @@ func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan
continue
}
- endpoint, err := newEndpoint(address)
+ endpoint, err := newEndpoint(address, s.WinRMConfig)
if err != nil {
log.Printf("Incorrect format for WinRM address: %s", err)
continue
@@ -119,7 +116,7 @@ func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan
log.Printf("Attempting WinRM connection (timeout: %s)", s.WinRMWaitTimeout)
- comm, err = plugin.New(endpoint, s.WinRMUser, s.WinRMPassword, s.WinRMWaitTimeout)
+ comm, err = plugin.New(endpoint, s.WinRMConfig.WinRMUser, s.WinRMConfig.WinRMPassword, s.WinRMWaitTimeout)
if err != nil {
log.Printf("WinRM connection err: %s", err)
continue
@@ -131,7 +128,7 @@ func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan
return comm, nil
}
-func newEndpoint(address string) (*winrm.Endpoint, error) {
+func newEndpoint(address string, config *WinRMConfig) (*winrm.Endpoint, error) {
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, err
@@ -141,5 +138,12 @@ func newEndpoint(address string) (*winrm.Endpoint, error) {
if err != nil {
return nil, err
}
- return &winrm.Endpoint{Host: host, Port: iport}, nil
+
+ return &winrm.Endpoint{
+ Host: host,
+ Port: iport,
+ HTTPS: !config.WinRMHttp,
+ Insecure: config.WinRMIgnoreCertChain,
+ CACert: &config.WinRMCACertBytes,
+ }, nil
}
diff --git a/common/winrm_config.go b/common/winrm_config.go
index 23abac2..b89a4dd 100644
--- a/common/winrm_config.go
+++ b/common/winrm_config.go
@@ -10,18 +10,22 @@ import (
)
type WinRMConfig struct {
- WinRMUser string `mapstructure:"winrm_username"`
- WinRMPassword string `mapstructure:"winrm_password"`
- WinRMHost string `mapstructure:"winrm_host"`
- WinRMPort uint `mapstructure:"winrm_port"`
- RawWinRMWaitTimeout string `mapstructure:"winrm_wait_timeout"`
+ WinRMUser string `mapstructure:"winrm_username"`
+ WinRMPassword string `mapstructure:"winrm_password"`
+ WinRMHost string `mapstructure:"winrm_host"`
+ WinRMPort uint `mapstructure:"winrm_port"`
+ RawWinRMWaitTimeout string `mapstructure:"winrm_wait_timeout"`
+ WinRMHttp bool `mapstructure:"winrm_http"`
+ WinRMIgnoreCertChain bool `mapstructure:"winrm_ignore_cert_chain"`
+ WinRMCACertFile string `mapstructure:"winrm_ca_cert_file"`
WinRMWaitTimeout time.Duration
+ WinRMCACertBytes []byte
}
func (c *WinRMConfig) Prepare(t *packer.ConfigTemplate) []error {
if c.WinRMPort == 0 {
- c.WinRMPort = 5985
+ c.WinRMPort = 5986
}
if c.RawWinRMWaitTimeout == "" {
diff --git a/common/winrm_config_test.go b/common/winrm_config_test.go
index 27088b0..45e5eef 100644
--- a/common/winrm_config_test.go
+++ b/common/winrm_config_test.go
@@ -27,7 +27,7 @@ func TestWinRMConfigPrepare(t *testing.T) {
t.Fatalf("err: %#v", errs)
}
- if c.WinRMPort != 5985 {
+ if c.WinRMPort != 5986 {
t.Errorf("bad winrm port: %d", c.WinRMPort)
}
}
diff --git a/communicator/winrm/communicator.go b/communicator/winrm/communicator.go
index f64003c..b872472 100644
--- a/communicator/winrm/communicator.go
+++ b/communicator/winrm/communicator.go
@@ -111,6 +111,9 @@ func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) {
User: c.user,
Password: c.password,
},
+ Https: c.endpoint.HTTPS,
+ Insecure: c.endpoint.Insecure,
+ CACertBytes: *c.endpoint.CACert,
OperationTimeout: time.Minute * 5,
MaxOperationsPerShell: 15, // lowest common denominator
})
diff --git a/communicator/winrm/communicator_test.go b/communicator/winrm/communicator_test.go
index ed1b904..143897e 100644
--- a/communicator/winrm/communicator_test.go
+++ b/communicator/winrm/communicator_test.go
@@ -24,7 +24,14 @@ func TestStart(t *testing.T) {
// You can comment this line out temporarily during development
t.Skip()
- comm, err := New(&winrm.Endpoint{"localhost", 5985}, "vagrant", "vagrant", time.Duration(1)*time.Minute)
+ endpoint := &winrm.Endpoint{
+ Host: "localhost",
+ Port: 5985,
+ HTTPS: false,
+ Insecure: false,
+ CACert: nil,
+ }
+ comm, err := New(endpoint, "vagrant", "vagrant", time.Duration(1)*time.Minute)
if err != nil {
t.Fatalf("error connecting to WinRM: %s", err)
}
@@ -59,7 +66,14 @@ func TestUpload(t *testing.T) {
// You can comment this line out temporarily during development
t.Skip()
- comm, err := New(&winrm.Endpoint{"localhost", 5985}, "vagrant", "vagrant", time.Duration(1)*time.Minute)
+ endpoint := &winrm.Endpoint{
+ Host: "localhost",
+ Port: 5985,
+ HTTPS: false,
+ Insecure: false,
+ CACert: nil,
+ }
+ comm, err := New(endpoint, "vagrant", "vagrant", time.Duration(1)*time.Minute)
if err != nil {
t.Fatalf("error connecting to WinRM: %s", err)
}
@@ -81,7 +95,14 @@ func TestUploadDir(t *testing.T) {
// You can comment this line out temporarily during development
t.Skip()
- comm, err := New(&winrm.Endpoint{"localhost", 5985}, "vagrant", "vagrant", time.Duration(1)*time.Minute)
+ endpoint := &winrm.Endpoint{
+ Host: "localhost",
+ Port: 5985,
+ HTTPS: false,
+ Insecure: false,
+ CACert: nil,
+ }
+ comm, err := New(endpoint, "vagrant", "vagrant", time.Duration(1)*time.Minute)
if err != nil {
t.Fatalf("error connecting to WinRM: %s", err)
}
@@ -91,4 +112,3 @@ func TestUploadDir(t *testing.T) {
t.Fatalf("error uploading dir: %s", err)
}
}
-
diff --git a/plugin/builder-amazon-windows-instance/main.go b/plugin/builder-amazon-windows-instance/main.go
new file mode 100644
index 0000000..8917822
--- /dev/null
+++ b/plugin/builder-amazon-windows-instance/main.go
@@ -0,0 +1,15 @@
+package main
+
+import (
+ "github.com/mitchellh/packer/packer/plugin"
+ "github.com/packer-community/packer-windows-plugins/builder/amazon-windows/instance"
+)
+
+func main() {
+ server, err := plugin.Server()
+ if err != nil {
+ panic(err)
+ }
+ server.RegisterBuilder(new(instance.Builder))
+ server.Serve()
+}
diff --git a/plugin/communicator-winrm/main.go b/plugin/communicator-winrm/main.go
index ea8fd9e..b7ae7ed 100644
--- a/plugin/communicator-winrm/main.go
+++ b/plugin/communicator-winrm/main.go
@@ -2,18 +2,21 @@ package main
import (
"flag"
- plugin "github.com/packer-community/packer-windows-plugins/communicator/winrm"
+ "log"
+ "os"
+ "time"
+
"github.com/masterzen/winrm/winrm"
"github.com/mitchellh/packer/packer"
rpc "github.com/mitchellh/packer/packer/plugin"
+ plugin "github.com/packer-community/packer-windows-plugins/communicator/winrm"
"github.com/rakyll/command"
- "log"
- "os"
- "time"
)
var host = flag.String("host", "localhost", "host machine")
var port = flag.Int("port", 5985, "host port")
+var http = flag.Bool("http", false, "use http")
+var ignoreCertChain = flag.Bool("ignorecertchain", false, "ignore certificate chain")
var user = flag.String("user", "vagrant", "user to run as")
var pass = flag.String("pass", "vagrant", "user's password")
var timeout = flag.Duration("timeout", 60*time.Second, "connection timeout")
@@ -49,7 +52,12 @@ func (r *RunCommand) Flags(fs *flag.FlagSet) *flag.FlagSet {
func (r *RunCommand) Run(args []string) {
command := args[0]
- endpoint := &winrm.Endpoint{Host: *host, Port: *port}
+ endpoint := &winrm.Endpoint{
+ Host: *host,
+ Port: *port,
+ HTTPS: !*http,
+ Insecure: *ignoreCertChain,
+ }
communicator, err := plugin.New(endpoint, *user, *pass, *timeout)
rc := &packer.RemoteCmd{
Command: command,
diff --git a/provisioner/powershell/elevated.go b/provisioner/powershell/elevated.go
index 04ebaf8..83fe938 100644
--- a/provisioner/powershell/elevated.go
+++ b/provisioner/powershell/elevated.go
@@ -53,7 +53,7 @@ $t.XmlText = @'
cmd
- /c powershell.exe -EncodedCommand {{.EncodedCommand}} > %TEMP%\{{.TaskName}}.out 2>&1
+ /c powershell.exe -ExecutionPolicy Bypass -EncodedCommand {{.EncodedCommand}} > %TEMP%\{{.TaskName}}.out 2>&1
diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go
index c6fa28d..2fb4eea 100644
--- a/provisioner/powershell/provisioner.go
+++ b/provisioner/powershell/provisioner.go
@@ -120,7 +120,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
}
if p.config.ExecuteCommand == "" {
- p.config.ExecuteCommand = `powershell "& { {{.Vars}}{{.Path}}; exit $LastExitCode}"`
+ p.config.ExecuteCommand = `powershell -ExecutionPolicy Bypass "& { {{.Vars}}{{.Path}} }"`
}
if p.config.ElevatedExecuteCommand == "" {