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..739b427 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/connect_step.go b/builder/amazon-windows/common/step_connect.go
similarity index 89%
rename from builder/amazon-windows/common/connect_step.go
rename to builder/amazon-windows/common/step_connect.go
index 028d690..af2566f 100644
--- a/builder/amazon-windows/common/connect_step.go
+++ b/builder/amazon-windows/common/step_connect.go
@@ -50,11 +50,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..313fe79
--- /dev/null
+++ b/builder/amazon-windows/common/step_generate_secure_vm_config.go
@@ -0,0 +1,154 @@
+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"
+)
+
+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..a220c2c
--- /dev/null
+++ b/builder/amazon-windows/common/step_get_password.go
@@ -0,0 +1,162 @@
+package common
+
+import (
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "log"
+ "time"
+
+ "code.google.com/p/gosshold/ssh/terminal"
+
+ "github.com/mitchellh/goamz/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):
+ }
+
+ resp, err := ec2conn.GetPasswordData(instance.InstanceId)
+ 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 {
+ fmt.Printf("Encrypted private key. Please enter passphrase: ")
+ password, err := terminal.ReadPassword(0)
+ fmt.Printf("\n")
+ if err != nil {
+ return "", err
+ }
+
+ asn1Bytes, err = x509.DecryptPEMBlock(block, password)
+ if err != nil {
+ return "", err
+ }
+ } else {
+ 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..80f03f2 100644
--- a/builder/amazon-windows/common/step_run_source_instance.go
+++ b/builder/amazon-windows/common/step_run_source_instance.go
@@ -9,25 +9,16 @@ import (
"github.com/mitchellh/goamz/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
@@ -39,9 +30,9 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
securityGroupIds := state.Get("securityGroupIds").([]string)
ui := state.Get("ui").(packer.Ui)
- userData := s.UserData
- if s.UserDataFile != "" {
- contents, err := ioutil.ReadFile(s.UserDataFile)
+ 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
@@ -56,14 +47,14 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
}
ui.Say("Launching a source AWS instance...")
- imageResp, err := ec2conn.Images([]string{s.SourceAMI}, ec2.NewFilter())
+ imageResp, err := ec2conn.Images([]string{s.RunConfig.SourceAmi}, ec2.NewFilter())
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
}
@@ -75,18 +66,18 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
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,
+ InstanceType: []string{s.RunConfig.InstanceType},
+ ProductDescription: []string{s.RunConfig.SpotPriceAutoProduct},
+ AvailabilityZone: s.RunConfig.AvailabilityZone,
StartTime: startTime,
})
if err != nil {
@@ -123,17 +114,17 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
if spotPrice == "" {
runOpts := &ec2.RunInstances{
KeyName: keyName,
- ImageId: s.SourceAMI,
- InstanceType: s.InstanceType,
+ ImageId: s.RunConfig.SourceAmi,
+ InstanceType: s.RunConfig.InstanceType,
UserData: []byte(userData),
MinCount: 0,
MaxCount: 0,
SecurityGroups: securityGroups,
- IamInstanceProfile: s.IamInstanceProfile,
- SubnetId: s.SubnetId,
- AssociatePublicIpAddress: s.AssociatePublicIpAddress,
+ IamInstanceProfile: s.RunConfig.IamInstanceProfile,
+ SubnetId: s.RunConfig.SubnetId,
+ AssociatePublicIpAddress: s.RunConfig.AssociatePublicIpAddress,
BlockDevices: s.BlockDevices.BuildLaunchDevices(),
- AvailZone: s.AvailabilityZone,
+ AvailZone: s.RunConfig.AvailabilityZone,
}
runResp, err := ec2conn.RunInstances(runOpts)
if err != nil {
@@ -146,20 +137,20 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
} else {
ui.Message(fmt.Sprintf(
"Requesting spot instance '%s' for: %s",
- s.InstanceType, spotPrice))
+ s.RunConfig.InstanceType, spotPrice))
runOpts := &ec2.RequestSpotInstances{
SpotPrice: spotPrice,
KeyName: keyName,
- ImageId: s.SourceAMI,
- InstanceType: s.InstanceType,
+ ImageId: s.RunConfig.SourceAmi,
+ InstanceType: s.RunConfig.InstanceType,
UserData: []byte(userData),
SecurityGroups: securityGroups,
- IamInstanceProfile: s.IamInstanceProfile,
- SubnetId: s.SubnetId,
- AssociatePublicIpAddress: s.AssociatePublicIpAddress,
+ IamInstanceProfile: s.RunConfig.IamInstanceProfile,
+ SubnetId: s.RunConfig.SubnetId,
+ AssociatePublicIpAddress: s.RunConfig.AssociatePublicIpAddress,
BlockDevices: s.BlockDevices.BuildLaunchDevices(),
- AvailZone: s.AvailabilityZone,
+ AvailZone: s.RunConfig.AvailabilityZone,
}
runSpotResp, err := ec2conn.RequestSpotInstances(runOpts)
if err != nil {
@@ -196,18 +187,26 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
instanceId = spotResp.SpotRequestResults[0].InstanceId
}
- ui.Message(fmt.Sprintf("Instance ID: %s", instanceId))
+ instanceResp, err := ec2conn.Instances([]string{instanceId}, nil)
+ 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,9 +214,9 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
s.instance = latestInstance.(*ec2.Instance)
- ec2Tags := make([]ec2.Tag, 1, len(s.Tags)+1)
+ ec2Tags := make([]ec2.Tag, 1, len(s.RunConfig.RunTags)+1)
ec2Tags[0] = ec2.Tag{"Name", "Packer Builder"}
- for k, v := range s.Tags {
+ for k, v := range s.RunConfig.RunTags {
ec2Tags = append(ec2Tags, ec2.Tag{k, v})
}
diff --git a/builder/amazon-windows/ebs/builder.go b/builder/amazon-windows/ebs/builder.go
index ff499d4..21ce95d 100644
--- a/builder/amazon-windows/ebs/builder.go
+++ b/builder/amazon-windows/ebs/builder.go
@@ -6,7 +6,9 @@
package ebs
import (
+ "fmt"
"log"
+ "time"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
@@ -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"`
@@ -83,38 +85,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/instance/builder.go b/builder/amazon-windows/instance/builder.go
index f1da772..8a1aab3 100644
--- a/builder/amazon-windows/instance/builder.go
+++ b/builder/amazon-windows/instance/builder.go
@@ -8,6 +8,7 @@ import (
"log"
"os"
"strings"
+ "time"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
@@ -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.
@@ -190,34 +191,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/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 == "" {