Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
packer
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kristopher Ruzic
packer
Commits
ccf20568
Commit
ccf20568
authored
Jun 14, 2015
by
Mitchell Hashimoto
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2240 from mitchellh/f-windows
Windows AWS instances
parents
b8d5bd37
b2e9277d
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
225 additions
and
9 deletions
+225
-9
builder/amazon/common/run_config.go
builder/amazon/common/run_config.go
+6
-0
builder/amazon/common/step_get_password.go
builder/amazon/common/step_get_password.go
+160
-0
builder/amazon/common/step_run_source_instance.go
builder/amazon/common/step_run_source_instance.go
+8
-0
builder/amazon/common/step_security_group.go
builder/amazon/common/step_security_group.go
+10
-6
builder/amazon/ebs/builder.go
builder/amazon/ebs/builder.go
+5
-1
builder/amazon/instance/builder.go
builder/amazon/instance/builder.go
+5
-1
helper/communicator/config.go
helper/communicator/config.go
+12
-0
helper/config/decode.go
helper/config/decode.go
+1
-0
template/parse.go
template/parse.go
+8
-0
template/parse_test.go
template/parse_test.go
+2
-1
website/source/docs/builders/amazon-ebs.html.markdown
website/source/docs/builders/amazon-ebs.html.markdown
+4
-0
website/source/docs/builders/amazon-instance.html.markdown
website/source/docs/builders/amazon-instance.html.markdown
+4
-0
No files found.
builder/amazon/common/run_config.go
View file @
ccf20568
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"os"
"os"
"time"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/helper/communicator"
...
@@ -27,6 +28,7 @@ type RunConfig struct {
...
@@ -27,6 +28,7 @@ type RunConfig struct {
TemporaryKeyPairName
string
`mapstructure:"temporary_key_pair_name"`
TemporaryKeyPairName
string
`mapstructure:"temporary_key_pair_name"`
UserData
string
`mapstructure:"user_data"`
UserData
string
`mapstructure:"user_data"`
UserDataFile
string
`mapstructure:"user_data_file"`
UserDataFile
string
`mapstructure:"user_data_file"`
WindowsPasswordTimeout
time
.
Duration
`mapstructure:"windows_password_timeout"`
VpcId
string
`mapstructure:"vpc_id"`
VpcId
string
`mapstructure:"vpc_id"`
// Communicator settings
// Communicator settings
...
@@ -40,6 +42,10 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
...
@@ -40,6 +42,10 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
"packer %s"
,
uuid
.
TimeOrderedUUID
())
"packer %s"
,
uuid
.
TimeOrderedUUID
())
}
}
if
c
.
WindowsPasswordTimeout
==
0
{
c
.
WindowsPasswordTimeout
=
10
*
time
.
Minute
}
// Validation
// Validation
errs
:=
c
.
Comm
.
Prepare
(
ctx
)
errs
:=
c
.
Comm
.
Prepare
(
ctx
)
if
c
.
SourceAmi
==
""
{
if
c
.
SourceAmi
==
""
{
...
...
builder/amazon/common/step_get_password.go
0 → 100644
View file @
ccf20568
package
common
import
(
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"log"
"time"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/packer"
)
// StepGetPassword reads the password from a Windows server and sets it
// on the WinRM config.
type
StepGetPassword
struct
{
Comm
*
communicator
.
Config
Timeout
time
.
Duration
}
func
(
s
*
StepGetPassword
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
image
:=
state
.
Get
(
"source_image"
)
.
(
*
ec2
.
Image
)
// Skip if we're not Windows...
if
image
.
Platform
==
nil
||
*
image
.
Platform
!=
"windows"
{
log
.
Printf
(
"[INFO] Not Windows, skipping get password..."
)
return
multistep
.
ActionContinue
}
// If we already have a password, skip it
if
s
.
Comm
.
WinRMPassword
!=
""
{
ui
.
Say
(
"Skipping waiting for password since WinRM password set..."
)
return
multistep
.
ActionContinue
}
// Get the password
var
password
string
var
err
error
cancel
:=
make
(
chan
struct
{})
waitDone
:=
make
(
chan
bool
,
1
)
go
func
()
{
ui
.
Say
(
"Waiting for auto-generated password for instance..."
)
ui
.
Message
(
"It is normal for this process to take up to 15 minutes,
\n
"
+
"but it usually takes around 5. Please wait."
)
password
,
err
=
s
.
waitForPassword
(
state
,
cancel
)
waitDone
<-
true
}()
timeout
:=
time
.
After
(
s
.
Timeout
)
WaitLoop
:
for
{
// Wait for either SSH to become available, a timeout to occur,
// or an interrupt to come through.
select
{
case
<-
waitDone
:
if
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error waiting for password: %s"
,
err
))
state
.
Put
(
"error"
,
err
)
return
multistep
.
ActionHalt
}
ui
.
Message
(
fmt
.
Sprintf
(
"
\n
Password retrieved!"
))
s
.
Comm
.
WinRMPassword
=
password
break
WaitLoop
case
<-
timeout
:
err
:=
fmt
.
Errorf
(
"Timeout waiting for password."
)
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
{
// The step sequence was cancelled, so cancel waiting for password
// and just start the halting process.
close
(
cancel
)
log
.
Println
(
"[WARN] Interrupt detected, quitting waiting for password."
)
return
multistep
.
ActionHalt
}
}
}
return
multistep
.
ActionContinue
}
func
(
s
*
StepGetPassword
)
Cleanup
(
multistep
.
StateBag
)
{}
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
(
"[INFO] Retrieve password wait cancelled. Exiting loop."
)
return
""
,
errors
.
New
(
"Retrieve password wait cancelled"
)
case
<-
time
.
After
(
5
*
time
.
Second
)
:
}
resp
,
err
:=
ec2conn
.
GetPasswordData
(
&
ec2
.
GetPasswordDataInput
{
InstanceID
:
instance
.
InstanceID
,
})
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error retrieving auto-generated instance password: %s"
,
err
)
return
""
,
err
}
if
resp
.
PasswordData
!=
nil
&&
*
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
}
log
.
Printf
(
"[DEBUG] Password is blank, will retry..."
)
}
}
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
""
,
errors
.
New
(
"encrypted private key isn't yet supported"
)
/*
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
}
builder/amazon/common/step_run_source_instance.go
View file @
ccf20568
package
common
package
common
import
(
import
(
"encoding/base64"
"fmt"
"fmt"
"io/ioutil"
"io/ioutil"
"log"
"log"
...
@@ -53,7 +54,14 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
...
@@ -53,7 +54,14 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
return
multistep
.
ActionHalt
return
multistep
.
ActionHalt
}
}
// Test if it is encoded already, and if not, encode it
if
_
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
string
(
contents
));
err
!=
nil
{
log
.
Printf
(
"[DEBUG] base64 encoding user data..."
)
contents
=
[]
byte
(
base64
.
StdEncoding
.
EncodeToString
(
contents
))
}
userData
=
string
(
contents
)
userData
=
string
(
contents
)
}
}
ui
.
Say
(
"Launching a source AWS instance..."
)
ui
.
Say
(
"Launching a source AWS instance..."
)
...
...
builder/amazon/common/step_security_group.go
View file @
ccf20568
...
@@ -9,12 +9,13 @@ import (
...
@@ -9,12 +9,13 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/packer"
)
)
type
StepSecurityGroup
struct
{
type
StepSecurityGroup
struct
{
CommConfig
*
communicator
.
Config
SecurityGroupIds
[]
string
SecurityGroupIds
[]
string
SSHPort
int
VpcId
string
VpcId
string
createdGroupId
string
createdGroupId
string
...
@@ -30,8 +31,9 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
...
@@ -30,8 +31,9 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
return
multistep
.
ActionContinue
return
multistep
.
ActionContinue
}
}
if
s
.
SSHPort
==
0
{
port
:=
s
.
CommConfig
.
Port
()
panic
(
"SSHPort must be set to a non-zero value."
)
if
port
==
0
{
panic
(
"port must be set to a non-zero value."
)
}
}
// Create the group
// Create the group
...
@@ -57,15 +59,17 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
...
@@ -57,15 +59,17 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
req
:=
&
ec2
.
AuthorizeSecurityGroupIngressInput
{
req
:=
&
ec2
.
AuthorizeSecurityGroupIngressInput
{
GroupID
:
groupResp
.
GroupID
,
GroupID
:
groupResp
.
GroupID
,
IPProtocol
:
aws
.
String
(
"tcp"
),
IPProtocol
:
aws
.
String
(
"tcp"
),
FromPort
:
aws
.
Long
(
int64
(
s
.
SSHP
ort
)),
FromPort
:
aws
.
Long
(
int64
(
p
ort
)),
ToPort
:
aws
.
Long
(
int64
(
s
.
SSHP
ort
)),
ToPort
:
aws
.
Long
(
int64
(
p
ort
)),
CIDRIP
:
aws
.
String
(
"0.0.0.0/0"
),
CIDRIP
:
aws
.
String
(
"0.0.0.0/0"
),
}
}
// We loop and retry this a few times because sometimes the security
// We loop and retry this a few times because sometimes the security
// group isn't available immediately because AWS resources are eventaully
// group isn't available immediately because AWS resources are eventaully
// consistent.
// consistent.
ui
.
Say
(
"Authorizing SSH access on the temporary security group..."
)
ui
.
Say
(
fmt
.
Sprintf
(
"Authorizing access to port %d the temporary security group..."
,
port
))
for
i
:=
0
;
i
<
5
;
i
++
{
for
i
:=
0
;
i
<
5
;
i
++
{
_
,
err
=
ec2conn
.
AuthorizeSecurityGroupIngress
(
req
)
_
,
err
=
ec2conn
.
AuthorizeSecurityGroupIngress
(
req
)
if
err
==
nil
{
if
err
==
nil
{
...
...
builder/amazon/ebs/builder.go
View file @
ccf20568
...
@@ -94,7 +94,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
...
@@ -94,7 +94,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
},
},
&
awscommon
.
StepSecurityGroup
{
&
awscommon
.
StepSecurityGroup
{
SecurityGroupIds
:
b
.
config
.
SecurityGroupIds
,
SecurityGroupIds
:
b
.
config
.
SecurityGroupIds
,
SSHPort
:
b
.
config
.
RunConfig
.
Comm
.
SSHPort
,
CommConfig
:
&
b
.
config
.
RunConfig
.
Comm
,
VpcId
:
b
.
config
.
VpcId
,
VpcId
:
b
.
config
.
VpcId
,
},
},
&
awscommon
.
StepRunSourceInstance
{
&
awscommon
.
StepRunSourceInstance
{
...
@@ -113,6 +113,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
...
@@ -113,6 +113,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
BlockDevices
:
b
.
config
.
BlockDevices
,
BlockDevices
:
b
.
config
.
BlockDevices
,
Tags
:
b
.
config
.
RunTags
,
Tags
:
b
.
config
.
RunTags
,
},
},
&
awscommon
.
StepGetPassword
{
Comm
:
&
b
.
config
.
RunConfig
.
Comm
,
Timeout
:
b
.
config
.
WindowsPasswordTimeout
,
},
&
communicator
.
StepConnect
{
&
communicator
.
StepConnect
{
Config
:
&
b
.
config
.
RunConfig
.
Comm
,
Config
:
&
b
.
config
.
RunConfig
.
Comm
,
Host
:
awscommon
.
SSHHost
(
Host
:
awscommon
.
SSHHost
(
...
...
builder/amazon/instance/builder.go
View file @
ccf20568
...
@@ -179,8 +179,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
...
@@ -179,8 +179,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
PrivateKeyFile
:
b
.
config
.
RunConfig
.
Comm
.
SSHPrivateKey
,
PrivateKeyFile
:
b
.
config
.
RunConfig
.
Comm
.
SSHPrivateKey
,
},
},
&
awscommon
.
StepSecurityGroup
{
&
awscommon
.
StepSecurityGroup
{
CommConfig
:
&
b
.
config
.
RunConfig
.
Comm
,
SecurityGroupIds
:
b
.
config
.
SecurityGroupIds
,
SecurityGroupIds
:
b
.
config
.
SecurityGroupIds
,
SSHPort
:
b
.
config
.
RunConfig
.
Comm
.
SSHPort
,
VpcId
:
b
.
config
.
VpcId
,
VpcId
:
b
.
config
.
VpcId
,
},
},
&
awscommon
.
StepRunSourceInstance
{
&
awscommon
.
StepRunSourceInstance
{
...
@@ -198,6 +198,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
...
@@ -198,6 +198,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
BlockDevices
:
b
.
config
.
BlockDevices
,
BlockDevices
:
b
.
config
.
BlockDevices
,
Tags
:
b
.
config
.
RunTags
,
Tags
:
b
.
config
.
RunTags
,
},
},
&
awscommon
.
StepGetPassword
{
Comm
:
&
b
.
config
.
RunConfig
.
Comm
,
Timeout
:
b
.
config
.
WindowsPasswordTimeout
,
},
&
communicator
.
StepConnect
{
&
communicator
.
StepConnect
{
Config
:
&
b
.
config
.
RunConfig
.
Comm
,
Config
:
&
b
.
config
.
RunConfig
.
Comm
,
Host
:
awscommon
.
SSHHost
(
Host
:
awscommon
.
SSHHost
(
...
...
helper/communicator/config.go
View file @
ccf20568
...
@@ -31,6 +31,18 @@ type Config struct {
...
@@ -31,6 +31,18 @@ type Config struct {
WinRMTimeout
time
.
Duration
`mapstructure:"winrm_timeout"`
WinRMTimeout
time
.
Duration
`mapstructure:"winrm_timeout"`
}
}
// Port returns the port that will be used for access based on config.
func
(
c
*
Config
)
Port
()
int
{
switch
c
.
Type
{
case
"ssh"
:
return
c
.
SSHPort
case
"winrm"
:
return
c
.
WinRMPort
default
:
return
0
}
}
func
(
c
*
Config
)
Prepare
(
ctx
*
interpolate
.
Context
)
[]
error
{
func
(
c
*
Config
)
Prepare
(
ctx
*
interpolate
.
Context
)
[]
error
{
if
c
.
Type
==
""
{
if
c
.
Type
==
""
{
c
.
Type
=
"ssh"
c
.
Type
=
"ssh"
...
...
helper/config/decode.go
View file @
ccf20568
...
@@ -42,6 +42,7 @@ func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error {
...
@@ -42,6 +42,7 @@ func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error {
if
config
.
InterpolateContext
==
nil
{
if
config
.
InterpolateContext
==
nil
{
config
.
InterpolateContext
=
ctx
config
.
InterpolateContext
=
ctx
}
else
{
}
else
{
config
.
InterpolateContext
.
TemplatePath
=
ctx
.
TemplatePath
config
.
InterpolateContext
.
UserVariables
=
ctx
.
UserVariables
config
.
InterpolateContext
.
UserVariables
=
ctx
.
UserVariables
}
}
ctx
=
config
.
InterpolateContext
ctx
=
config
.
InterpolateContext
...
...
template/parse.go
View file @
ccf20568
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"fmt"
"fmt"
"io"
"io"
"os"
"os"
"path/filepath"
"sort"
"sort"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-multierror"
...
@@ -317,6 +318,13 @@ func ParseFile(path string) (*Template, error) {
...
@@ -317,6 +318,13 @@ func ParseFile(path string) (*Template, error) {
return
nil
,
err
return
nil
,
err
}
}
if
!
filepath
.
IsAbs
(
path
)
{
path
,
err
=
filepath
.
Abs
(
path
)
if
err
!=
nil
{
return
nil
,
err
}
}
tpl
.
Path
=
path
tpl
.
Path
=
path
return
tpl
,
nil
return
tpl
,
nil
}
}
template/parse_test.go
View file @
ccf20568
package
template
package
template
import
(
import
(
"path/filepath"
"reflect"
"reflect"
"strings"
"strings"
"testing"
"testing"
...
@@ -306,7 +307,7 @@ func TestParse(t *testing.T) {
...
@@ -306,7 +307,7 @@ func TestParse(t *testing.T) {
}
}
for
_
,
tc
:=
range
cases
{
for
_
,
tc
:=
range
cases
{
path
:=
fixtureDir
(
tc
.
File
)
path
,
_
:=
filepath
.
Abs
(
fixtureDir
(
tc
.
File
)
)
tpl
,
err
:=
ParseFile
(
fixtureDir
(
tc
.
File
))
tpl
,
err
:=
ParseFile
(
fixtureDir
(
tc
.
File
))
if
(
err
!=
nil
)
!=
tc
.
Err
{
if
(
err
!=
nil
)
!=
tc
.
Err
{
t
.
Fatalf
(
"err: %s"
,
err
)
t
.
Fatalf
(
"err: %s"
,
err
)
...
...
website/source/docs/builders/amazon-ebs.html.markdown
View file @
ccf20568
...
@@ -168,6 +168,10 @@ each category, the available configuration keys are alphabetized.
...
@@ -168,6 +168,10 @@ each category, the available configuration keys are alphabetized.
*
`vpc_id`
(string) - If launching into a VPC subnet, Packer needs the
*
`vpc_id`
(string) - If launching into a VPC subnet, Packer needs the
VPC ID in order to create a temporary security group within the VPC.
VPC ID in order to create a temporary security group within the VPC.
*
`windows_password_timeout`
(string) - The timeout for waiting for
a Windows password for Windows instances. Defaults to 20 minutes.
Example value: "10m"
## Basic Example
## Basic Example
Here is a basic example. It is completely valid except for the access keys:
Here is a basic example. It is completely valid except for the access keys:
...
...
website/source/docs/builders/amazon-instance.html.markdown
View file @
ccf20568
...
@@ -209,6 +209,10 @@ each category, the available configuration keys are alphabetized.
...
@@ -209,6 +209,10 @@ each category, the available configuration keys are alphabetized.
it is perfectly okay to create this directory as part of the provisioning
it is perfectly okay to create this directory as part of the provisioning
process.
process.
*
`windows_password_timeout`
(string) - The timeout for waiting for
a Windows password for Windows instances. Defaults to 20 minutes.
Example value: "10m"
## Basic Example
## Basic Example
Here is a basic example. It is completely valid except for the access keys:
Here is a basic example. It is completely valid except for the access keys:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment