From 846f94c96487cf2a3fac245b4a4f5bda7c35e331 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 8 May 2018 10:35:09 -0700 Subject: [PATCH 1/2] implement template variable for accessing WinRM password in either environemnt variables or directly in execute_command or inline commands. --- common/shell-local/config.go | 5 ++ common/shell-local/config_test.go | 16 ++++++ common/shell-local/run.go | 56 ++++++++++++++++--- .../docs/provisioners/shell-local.html.md | 25 ++++++--- 4 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 common/shell-local/config_test.go diff --git a/common/shell-local/config.go b/common/shell-local/config.go index 9eb657ff9..82a41a192 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -56,6 +56,11 @@ type Config struct { } func Decode(config *Config, raws ...interface{}) error { + //Create passthrough for winrm password so we can fill it in once we know it + config.Ctx.Data = &EnvVarsTemplate{ + WinRMPassword: `{{.WinRMPassword}}`, + } + err := configHelper.Decode(&config, &configHelper.DecodeOpts{ Interpolate: true, InterpolateContext: &config.Ctx, diff --git a/common/shell-local/config_test.go b/common/shell-local/config_test.go new file mode 100644 index 000000000..7c74581ae --- /dev/null +++ b/common/shell-local/config_test.go @@ -0,0 +1,16 @@ +package shell_local + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConvertToLinuxPath(t *testing.T) { + winPath := "C:/path/to/your/file" + winBashPath := "/mnt/c/path/to/your/file" + converted, _ := ConvertToLinuxPath(winPath) + assert.Equal(t, winBashPath, converted, + "Should have converted %s to %s -- not %s", winPath, winBashPath, converted) + +} diff --git a/common/shell-local/run.go b/common/shell-local/run.go index b65196ea9..705a5ff90 100644 --- a/common/shell-local/run.go +++ b/common/shell-local/run.go @@ -9,14 +9,20 @@ import ( "sort" "strings" + commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" ) type ExecuteCommandTemplate struct { - Vars string - Script string - Command string + Vars string + Script string + Command string + WinRMPassword string +} + +type EnvVarsTemplate struct { + WinRMPassword string } func Run(ui packer.Ui, config *Config) (bool, error) { @@ -63,8 +69,12 @@ func Run(ui packer.Ui, config *Config) (bool, error) { // buffers and for reading the final exit status. flattenedCmd := strings.Join(interpolatedCmds, " ") cmd := &packer.RemoteCmd{Command: flattenedCmd} - log.Printf("[INFO] (shell-local): starting local command: %s", flattenedCmd) - + sanitized := flattenedCmd + if len(getWinRMPassword(config.PackerBuildName)) > 0 { + sanitized = strings.Replace(flattenedCmd, + getWinRMPassword(config.PackerBuildName), "*****", -1) + } + log.Printf("[INFO] (shell-local): starting local command: %s", sanitized) if err := cmd.StartWithUi(comm, ui); err != nil { return false, fmt.Errorf( "Error executing script: %s\n\n"+ @@ -96,7 +106,22 @@ func createInlineScriptFile(config *Config) (string, error) { log.Printf("[INFO] (shell-local): Prepending inline script with %s", shebang) writer.WriteString(shebang) } + config.Ctx.Data = &EnvVarsTemplate{ + WinRMPassword: getWinRMPassword(config.PackerBuildName), + } + + // generate context so you can interpolate the command + config.Ctx.Data = &EnvVarsTemplate{ + WinRMPassword: getWinRMPassword(config.PackerBuildName), + } + for _, command := range config.Inline { + // interpolate command to check for template variables. + command, err := interpolate.Render(command, &config.Ctx) + if err != nil { + return "", err + } + if _, err := writer.WriteString(command + "\n"); err != nil { return "", fmt.Errorf("Error preparing shell script: %s", err) } @@ -118,9 +143,10 @@ func createInlineScriptFile(config *Config) (string, error) { // the host OS func createInterpolatedCommands(config *Config, script string, flattenedEnvVars string) ([]string, error) { config.Ctx.Data = &ExecuteCommandTemplate{ - Vars: flattenedEnvVars, - Script: script, - Command: script, + Vars: flattenedEnvVars, + Script: script, + Command: script, + WinRMPassword: getWinRMPassword(config.PackerBuildName), } interpolatedCmds := make([]string, len(config.ExecuteCommand)) @@ -142,8 +168,17 @@ func createFlattenedEnvVars(config *Config) (string, error) { envVars["PACKER_BUILD_NAME"] = fmt.Sprintf("%s", config.PackerBuildName) envVars["PACKER_BUILDER_TYPE"] = fmt.Sprintf("%s", config.PackerBuilderType) + // interpolate environment variables + config.Ctx.Data = &EnvVarsTemplate{ + WinRMPassword: getWinRMPassword(config.PackerBuildName), + } // Split vars into key/value components for _, envVar := range config.Vars { + envVar, err := interpolate.Render(envVar, &config.Ctx) + if err != nil { + return "", err + } + // Split vars into key/value components keyValue := strings.SplitN(envVar, "=", 2) // Store pair, replacing any single quotes in value so they parse // correctly with required environment variable format @@ -162,3 +197,8 @@ func createFlattenedEnvVars(config *Config) (string, error) { } return flattened, nil } + +func getWinRMPassword(buildName string) string { + winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName) + return winRMPass +} diff --git a/website/source/docs/provisioners/shell-local.html.md b/website/source/docs/provisioners/shell-local.html.md index a7400c589..6a04272ad 100644 --- a/website/source/docs/provisioners/shell-local.html.md +++ b/website/source/docs/provisioners/shell-local.html.md @@ -41,6 +41,10 @@ Exactly *one* of the following is required: - `command` (string) - This is a single command to execute. It will be written to a temporary file and run using the `execute_command` call below. + If you are building a windows vm on AWS, Azure or Google Compute and would + like to access the generated 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. - `inline` (array of strings) - This is an array of commands to execute. The commands are concatenated by newlines and turned into a single file, so they @@ -60,24 +64,22 @@ Exactly *one* of the following is required: Optional parameters: -- `execute_command` (array of strings) - The command to use to execute - the script. By default this is `["/bin/sh", "-c", "{{.Command}}"]`. The value - is an array of arguments executed directly by the OS. The value of this is - treated as [configuration - template](/docs/templates/engine.html). The only available - variable is `Command` which is the command to execute. - - `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. + as well, which are covered in the section below. If you are building a + windows vm on AWS, Azure or Google Compute and would like to access the + generated 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: + `"environment_vars": "WINRMPASS={{.WinRMPassword}}"` - `execute_command` (array of strings) - The command used to execute the script. By default this is `["/bin/sh", "-c", "{{.Vars}}, "{{.Script}}"]` on unix and `["cmd", "/c", "{{.Vars}}", "{{.Script}}"]` on windows. This is treated as a [template engine](/docs/templates/engine.html). There are two available variables: `Script`, which is the path to the script - to run, and `Vars`, which is the list of `environment_vars`, if configured + to run, and `Vars`, which is the list of `environment_vars`, if configured. If you choose to set this option, make sure that the first element in the array is the shell program you want to use (for example, "sh"), and a later @@ -94,6 +96,11 @@ Optional parameters: sake of clarity, as even when you set only a single `command` to run, Packer writes it to a temporary file and then runs it as a script. + If you are building a windows vm on AWS, Azure or Google Compute and would + like to access the generated 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. + - `inline_shebang` (string) - The [shebang](http://en.wikipedia.org/wiki/Shebang_%28Unix%29) value to use when running commands specified by `inline`. By default, this is `/bin/sh -e`. If From 3afca6905b7a8631f4049c33bfb8a3e742b101d2 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 29 May 2018 11:47:27 -0700 Subject: [PATCH 2/2] remove duplicate assignmnet --- common/shell-local/run.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/common/shell-local/run.go b/common/shell-local/run.go index 705a5ff90..51bb047b0 100644 --- a/common/shell-local/run.go +++ b/common/shell-local/run.go @@ -106,9 +106,6 @@ func createInlineScriptFile(config *Config) (string, error) { log.Printf("[INFO] (shell-local): Prepending inline script with %s", shebang) writer.WriteString(shebang) } - config.Ctx.Data = &EnvVarsTemplate{ - WinRMPassword: getWinRMPassword(config.PackerBuildName), - } // generate context so you can interpolate the command config.Ctx.Data = &EnvVarsTemplate{