From a42f8fac4db5ca9a34297a71ac25c6fc8e32896a Mon Sep 17 00:00:00 2001 From: Matt Dainty Date: Fri, 7 Dec 2018 11:08:11 +0000 Subject: [PATCH] Elevated support for puppet-masterless provisioner This should fix #5478. --- provisioner/puppet-masterless/provisioner.go | 56 ++++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index 3254a6134..8368e37d3 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/hashicorp/packer/common" + commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/provisioner" @@ -65,6 +66,12 @@ type Config struct { // The directory from which the command will be executed. // Packer requires the directory to exist when running puppet. WorkingDir string `mapstructure:"working_directory"` + + // Instructs the communicator to run the remote script as a Windows + // scheduled task, effectively elevating the remote user by impersonating + // a logged-in user + ElevatedUser string `mapstructure:"elevated_user"` + ElevatedPassword string `mapstructure:"elevated_password"` } type guestOSTypeConfig struct { @@ -117,6 +124,7 @@ var guestOSTypeConfigs = map[string]guestOSTypeConfig{ type Provisioner struct { config Config + communicator packer.Communicator guestOSTypeConfig guestOSTypeConfig guestCommands *provisioner.GuestCommands } @@ -135,7 +143,17 @@ type ExecuteTemplate struct { WorkingDir 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 + p.config.ctx.Data = &EnvVarsTemplate{ + WinRMPassword: `{{.WinRMPassword}}`, + } + err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &p.config.ctx, @@ -240,6 +258,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { ui.Say("Provisioning with Puppet...") + p.communicator = comm ui.Message("Creating Puppet staging directory...") if err := p.createDir(ui, comm, p.config.StagingDir); err != nil { return fmt.Errorf("Error creating staging directory: %s", err) @@ -316,6 +335,13 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { return err } + if p.config.ElevatedUser != "" { + command, err = provisioner.GenerateElevatedRunner(command, p) + if err != nil { + return err + } + } + cmd := &packer.RemoteCmd{ Command: command, } @@ -432,10 +458,7 @@ func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir stri } func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error { - cmd := &packer.RemoteCmd{ - Command: fmt.Sprintf("rm -fr '%s'", dir), - } - + cmd := &packer.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)} if err := cmd.StartWithUi(comm, ui); err != nil { return err } @@ -460,3 +483,28 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds return comm.UploadDir(dst, src, nil) } + +func getWinRMPassword(buildName string) string { + winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName) + packer.LogSecretFilter.Set(winRMPass) + return winRMPass +} + +func (p *Provisioner) Communicator() packer.Communicator { + return p.communicator +} + +func (p *Provisioner) ElevatedUser() string { + return p.config.ElevatedUser +} + +func (p *Provisioner) ElevatedPassword() string { + // Replace ElevatedPassword for winrm users who used this feature + p.config.ctx.Data = &EnvVarsTemplate{ + WinRMPassword: getWinRMPassword(p.config.PackerBuildName), + } + + elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx) + + return elevatedPassword +}