Merge pull request #5998 from hashicorp/do_5895

allow users of AWS to use the dynamically-generated admin password wh…
This commit is contained in:
Megan Marsh 2018-03-22 16:01:22 -07:00 committed by GitHub
commit 072cd6b745
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 23 deletions

View File

@ -12,6 +12,7 @@ import (
"time"
"github.com/aws/aws-sdk-go/service/ec2"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
@ -92,11 +93,15 @@ WaitLoop:
ui.Message(fmt.Sprintf(
"Password (since debug is enabled): %s", s.Comm.WinRMPassword))
}
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword)
return multistep.ActionContinue
}
func (s *StepGetPassword) Cleanup(multistep.StateBag) {}
func (s *StepGetPassword) Cleanup(multistep.StateBag) {
commonhelper.RemoveSharedStateFile("winrm_password")
}
func (s *StepGetPassword) waitForPassword(state multistep.StateBag, cancel <-chan struct{}) (string, error) {
ec2conn := state.Get("ec2").(*ec2.EC2)

View File

@ -3,14 +3,12 @@ package common
import (
"context"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net"
"net/http"
"os"
"path/filepath"
"github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -77,25 +75,21 @@ func (s *StepHTTPServer) Run(_ context.Context, state multistep.StateBag) multis
return multistep.ActionContinue
}
func httpAddrFilename(suffix string) string {
uuid := os.Getenv("PACKER_RUN_UUID")
return filepath.Join(os.TempDir(), fmt.Sprintf("packer-%s-%s", uuid, suffix))
}
func SetHTTPPort(port string) error {
return ioutil.WriteFile(httpAddrFilename("port"), []byte(port), 0644)
return common.SetSharedState("port", port)
}
func SetHTTPIP(ip string) error {
return ioutil.WriteFile(httpAddrFilename("ip"), []byte(ip), 0644)
return common.SetSharedState("ip", ip)
}
func GetHTTPAddr() string {
ip, err := ioutil.ReadFile(httpAddrFilename("ip"))
ip, err := common.RetrieveSharedState("ip")
if err != nil {
return ""
}
port, err := ioutil.ReadFile(httpAddrFilename("port"))
port, err := common.RetrieveSharedState("port")
if err != nil {
return ""
}
@ -107,6 +101,6 @@ func (s *StepHTTPServer) Cleanup(multistep.StateBag) {
// Close the listener so that the HTTP server stops
s.l.Close()
}
os.Remove(httpAddrFilename("port"))
os.Remove(httpAddrFilename("ip"))
common.RemoveSharedStateFile("port")
common.RemoveSharedStateFile("ip")
}

View File

@ -0,0 +1,31 @@
package common
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
// Used to set variables which we need to access later in the build, where
// state bag and config information won't work
func sharedStateFilename(suffix string) string {
uuid := os.Getenv("PACKER_RUN_UUID")
return filepath.Join(os.TempDir(), fmt.Sprintf("packer-%s-%s", uuid, suffix))
}
func SetSharedState(key string, value string) error {
return ioutil.WriteFile(sharedStateFilename(key), []byte(value), 0600)
}
func RetrieveSharedState(key string) (string, error) {
value, err := ioutil.ReadFile(sharedStateFilename(key))
if err != nil {
return "", err
}
return string(value), nil
}
func RemoveSharedStateFile(key string) {
os.Remove(sharedStateFilename(key))
}

View File

@ -17,6 +17,7 @@ import (
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/uuid"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -99,11 +100,22 @@ type Provisioner struct {
}
type ExecuteCommandTemplate struct {
Vars string
Path string
Vars string
Path string
WinRMPassword string
}
type EnvVarsTemplate struct {
WinRMPassword string
}
func (p *Provisioner) Prepare(raws ...interface{}) error {
//Create passthrough for winrm password so we can fill it in once we know it
log.Printf("MEGAN context is %#v", p.config.ctx)
p.config.ctx.Data = &EnvVarsTemplate{
WinRMPassword: `{{.WinRMPassword}}`,
}
err := config.Decode(&p.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &p.config.ctx,
@ -236,6 +248,7 @@ func extractScript(p *Provisioner) (string, error) {
}
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
log.Printf("MEGAN context is %#v", p.config.ctx)
ui.Say(fmt.Sprintf("Provisioning with Powershell..."))
p.communicator = comm
@ -358,13 +371,22 @@ func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string) {
// Always available Packer provided env vars
envVars["PACKER_BUILD_NAME"] = p.config.PackerBuildName
envVars["PACKER_BUILDER_TYPE"] = p.config.PackerBuilderType
httpAddr := common.GetHTTPAddr()
if httpAddr != "" {
envVars["PACKER_HTTP_ADDR"] = httpAddr
}
// interpolate environment variables
p.config.ctx.Data = &EnvVarsTemplate{
WinRMPassword: getWinRMPassword(),
}
// Split vars into key/value components
for _, envVar := range p.config.Vars {
envVar, err := interpolate.Render(envVar, &p.config.ctx)
if err != nil {
return
}
keyValue := strings.SplitN(envVar, "=", 2)
// Escape chars special to PS in each env var value
escapedEnvVarValue := psEscape.Replace(keyValue[1])
@ -423,8 +445,9 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro
}
p.config.ctx.Data = &ExecuteCommandTemplate{
Path: p.config.RemotePath,
Vars: envVarPath,
Path: p.config.RemotePath,
Vars: envVarPath,
WinRMPassword: getWinRMPassword(),
}
command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
@ -436,6 +459,11 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro
return command, nil
}
func getWinRMPassword() string {
winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password")
return winRMPass
}
func (p *Provisioner) createCommandTextPrivileged() (command string, err error) {
// Prepare everything needed to enable the required env vars within the remote environment
envVarPath, err := p.prepareEnvVars(true)
@ -444,8 +472,9 @@ func (p *Provisioner) createCommandTextPrivileged() (command string, err error)
}
p.config.ctx.Data = &ExecuteCommandTemplate{
Path: p.config.RemotePath,
Vars: envVarPath,
Path: p.config.RemotePath,
Vars: envVarPath,
WinRMPassword: getWinRMPassword(),
}
command, err = interpolate.Render(p.config.ElevatedExecuteCommand, &p.config.ctx)
if err != nil {
@ -501,6 +530,12 @@ func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath strin
log.Printf("Elevated user %s converted to %s after escaping chars special to PowerShell",
p.config.ElevatedUser, escapedElevatedUser)
}
// Replace ElevatedPassword for winrm users who used this feature
p.config.ctx.Data = &EnvVarsTemplate{
WinRMPassword: getWinRMPassword(),
}
p.config.ElevatedPassword, _ = interpolate.Render(p.config.ElevatedPassword, &p.config.ctx)
// Escape chars special to PowerShell in the ElevatedPassword string
escapedElevatedPassword := psEscape.Replace(p.config.ElevatedPassword)

View File

@ -72,7 +72,20 @@ Optional parameters:
- `environment_vars` (array of strings) - An array of key/value pairs to
inject prior to the execute\_command. The format should be `key=value`.
Packer injects some environmental variables by default into the
environment, as well, which are covered in the section below.
environment, as well, which are covered in the section below. If you are
using AWS and would like to use the randomly-generated unique
If you are running on AWS and would like to access the AWS-generated
Administrator password that Packer uses to connect to the instance via
WinRM, you can use the template variable `{{.WinRMPassword}}` to set this
as an environment variable. For example:
```json
{
"type": "powershell",
"environment_vars": "WINRMPASS={{.WinRMPassword}}",
"inline": ["Write-Host \"Automatically generated aws password is: $Env:WINRMPASS\""]
},
```
- `execute_command` (string) - The command to use to execute the script. By
default this is as follows:
@ -89,7 +102,15 @@ Optional parameters:
- `elevated_user` and `elevated_password` (string) - If specified, the
PowerShell script will be run with elevated privileges using the given
Windows user.
Windows user. If you are running a build on AWS and would like to run using
the AWS-generated password that Packer uses to connect to the instance via,
WinRM, you may do so by using the template variable {{.WinRMPassword}}.
For example:
``` json
"elevated_user": "Administrator",
"elevated_password": "{{.WinRMPassword}}",
```
- `remote_path` (string) - The path where the script will be uploaded to in
the machine. This defaults to "c:/Windows/Temp/script.ps1". This value must