| --- |
| title: WinRM4j Tips and Tricks |
| layout: website-normal |
| --- |
| |
| ### PowerShell |
| |
| PowerShell commands can be supplied using config options such as `launch.powershell.command`. |
| |
| This is an alternative to supplying a standard batch command using config such as `launch.command`. |
| For a given phase, only one of the commands (PowerShell or Batch) should be supplied. |
| |
| |
| ### Execution Phases |
| |
| Bear in mind that |
| the best practices for other entities (e.g. using [VanillaSoftwareProcess](/guide/blueprints/custom-entities.md#vanilla-software-using-bash)) |
| apply for WinRM as well. |
| |
| Blueprint authors are strongly encouraged to provide an implementation for install, launch, stop |
| and checkRunning. These are vital for the generic effectors such as stopping and restarting the |
| process. |
| |
| |
| ### Getting the Right Exit Codes |
| |
| WinRM (or at least the chosen WinRM client!) can return a zero exit code even on error in certain |
| circumstances. It is therefore advisable to follow the guidelines below. |
| |
| *For a given command, write the PowerShell or Batch script as a separate multi-command file. |
| Upload this (e.g. by including it in the `files.preinstall` configuration). For the configuration |
| of the given command, execute the file.* |
| |
| When you have a command inside the PowerShell script which want to report its non zero exiting, |
| please consider adding a check for its exit code after it. |
| Example: |
| |
| & "C:\install.exe" |
| If ($lastexitcode -ne 0) { |
| exit $lastexitcode |
| } |
| |
| For PowerShell files, consider including |
| |
| $ErrorActionPreference = "Stop" |
| |
| at the start of the file. |
| `$ErrorActionPreference` Determines how Windows PowerShell responds to a non-terminating |
| error (an error that does not stop the cmdlet processing) at the |
| command line or in a script, cmdlet, or provider, such as the |
| errors generated by the Write-Error cmdlet. |
| https://technet.microsoft.com/en-us/library/hh847796.aspx |
| |
| See [Incorrect Exit Codes](limitations.md#incorrect-exit-codes) under Known Limitations. |
| |
| |
| ### Executing Scripts From Batch Commands |
| |
| In a batch command, you can execute a batch file or PowerShell file. For example: |
| |
| install.command: powershell -NonInteractive -NoProfile -Command "C:\\install7zip.ps1" |
| |
| Or alternatively: |
| |
| install.command: C:\\install7zip.bat |
| |
| ### Executing Scripts From PowerShell |
| |
| In a PowerShell command, you can execute a batch file or PowerShell file. There are many ways |
| to do this (see official PowerShell docs). For example: |
| |
| install.powershell.command: "& C:\\install7zip.ps1" |
| |
| Or alternatively: |
| |
| install.powershell.command: "& C:\\install7zip.bat" |
| |
| Note the quotes around the command. This is because the "&" has special meaning in a YAML value. |
| |
| ### Parameterised Scripts |
| |
| Calling parameterised Batch and PowerShell scripts is done in the normal Windows way - see |
| offical Microsoft docs. For example: |
| |
| install.command: "c:\\myscript.bat myarg1 myarg2" |
| |
| Or as a PowerShell example: |
| |
| install.powershell.command: "& c:\\myscript.ps1 -key1 myarg1 -key2 myarg2" |
| |
| It is also possible to construct the script parameters by referencing attributes of this or |
| other entities using the standard `attributeWhenReady` mechanism. For example: |
| |
| install.command: $brooklyn:formatString("c:\\myscript.bat %s", component("db").attributeWhenReady("datastore.url")) |
| |
| ### PowerShell - Using Start-Process |
| |
| When you are invoking a command from a PowerShell script with `Start-Process` cmdlet, |
| please use the `-Wait` and the `-PassThru` arguments. |
| Example `Start-Process C:\mycommand -Wait -PassThru` |
| |
| Using `-Wait` guarantees that the script process and its children and thus the WinRM session won't be terminated until it is finished. |
| `-PassThru` Returns a process object for each process that the cmdlet started. By default, this cmdlet does not generate any output. |
| See https://technet.microsoft.com/en-us/library/hh849848.aspx |
| |
| ### Rebooting |
| |
| Where a reboot is required as part of the entity setup, this can be configured using |
| config like `pre.install.reboot.required` and `install.reboot.required`. If required, the |
| installation commands can be split between the pre-install, install and post-install phases |
| in order to do a reboot at the appropriate point of the VM setup. |
| |
| We strongly recommend to **write blueprints in a way that they do NOT restart automatically Windows** and |
| use one of the `pre.install.reboot.required` or `install.reboot.required` parameters to perform restart, |
| as script and Brooklyn connectivity behaviour is difficult to continue across reboots! |
| |
| ### Install Location |
| |
| Blueprint authors are encouraged to explicitly specify the full path for file uploads, and |
| for paths in their PowerShell scripts (e.g. for installation, configuration files, log files, etc). |
| |
| ### How and Why to re-authenticate within a PowerShell script |
| |
| Some installation scripts require the use of security-related operations. In some environments, |
| these fail by default when executed over WinRM, even though the script may succeed when run locally |
| (e.g. by using RDP to connect to the machine and running the script manually). There may be no |
| clear indication from Windows why it failed (e.g. for MSSQL install, the only clue is a |
| security exception in the installation log). |
| |
| When a script is run over WinRM, the credentials under which the script are run are marked as |
| 'remote' credentials, which are prohibited from running certain security-related operations. The |
| solution is to obtain a new set of credentials within the script and use those credentials to |
| required commands. |
| |
| The WinRM client uses Negotiate+NTLM to authenticate against the machine. |
| This mechanism applies certain restrictions to executing commands on the Windows host. |
| |
| For this reason you should enable CredSSP on the Windows host which grants all privileges available to the user. |
| https://technet.microsoft.com/en-us/library/hh849719.aspx#sectionSection4 |
| |
| To use `Invoke-Command -Authentication CredSSP` the Windows Machine has to have: |
| - Up and running WinRM over HTTP. The custom-enable-credssp.ps1 script enables WinRM over HTTP because `Invoke-Command` use WinRM over HTTP by default. |
| Invoke-Command can be used with -UseSSL option but this will lead to modifying PowerShell scripts. |
| With always enabling WinRM over HTTP on the host, blueprint's PowerShell scripts remain consistent and not depend on the WinRM HTTPS/HTTP environments. |
| We hope future versions of winrm4j will support CredSSP out of the box and wrapping commands in Invoke-Command will not be needed. |
| - Added trusted host entries which will use Invoke-Command |
| - Allowed CredSSP |
| |
| All the above requirements are enabled in Apache Brooklyn through [brooklyn-server/software/base/src/main/resources/org/apache/brooklyn/software/base/custom-enable-credssp.ps1](https://github.com/apache/brooklyn-server/blob/master/software/base/src/main/resources/org/apache/brooklyn/software/base/custom-enable-credssp.ps1) |
| script which enables executing commands with CredSSP in the general case. |
| The script works for most of the Windows images out there version 2008 and later. |
| |
| Check Microsoft Documentation for more information on how to [negotiate authenticate mechanisms](https://msdn.microsoft.com/en-us/library/windows/desktop/aa378748(v=vs.85).aspx) |
| |
| Re-authentication also requires that the password credentials are passed in plain text within the |
| script. Please be aware that it is normal for script files - and therefore the plaintext password - |
| to be saved to the VM's disk. The scripts are also accessible via the Brooklyn web-console's |
| activity view. Access to the latter can be controlled via |
| [Entitlements](/guide/blueprints/java/entitlements.md). |
| |
| As an example (taken from MSSQL install), the command below works when run locally, but fails over |
| WinRM: |
| |
| ( $driveLetter + "setup.exe") /ConfigurationFile=C:\ConfigurationFile.ini |
| |
| The code below can be used instead (note this example uses Freemarker templating): |
| |
| & winrm set winrm/config/service/auth '@{CredSSP="true"}' |
| & winrm set winrm/config/client/auth '@{CredSSP="true"}' |
| # |
| $pass = '${attribute['windows.password']}' |
| $secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force |
| $mycreds = New-Object System.Management.Automation.PSCredential ($($env:COMPUTERNAME + "\${location.user}"), $secpasswd) |
| # |
| $exitCode = Invoke-Command -ComputerName $env:COMPUTERNAME -Credential $mycreds -ScriptBlock { |
| param($driveLetter) |
| $process = Start-Process ( $driveLetter + "setup.exe") -ArgumentList "/ConfigurationFile=C:\ConfigurationFile.ini" -RedirectStandardOutput "C:\sqlout.txt" -RedirectStandardError "C:\sqlerr.txt" -Wait -PassThru -NoNewWindow |
| $process.ExitCode |
| } -Authentication CredSSP -ArgumentList $driveLetter |
| # |
| exit $exitCode |
| |
| In this example, the `${...}` format is FreeMarker templating. Those sections will be substituted |
| before the script is uploaded for execution. To explain this example in more detail: |
| |
| * `${attribute['windows.password']}` is substituted for the entity's attribute "windows.password". |
| This (clear-text) password is sent as part of the script. Assuming that HTTPS and NTLM is used, |
| the script will be encrypted while in-flight. |
| |
| * The `${location.user}` gets (from the entity's machine location) the username, substituting this |
| text for the actual username. In many cases, this will be "Administrator". However, on some |
| clouds a different username (with admin privileges) will be used. |
| |
| * The username and password are used to create a new credential object (having first converted the |
| password to a secure string). |
| |
| * Credential Security Service Provider (CredSSP) is used for authentication, to pass the explicit |
| credentials when using `Invoke-Command`. |
| |
| |
| ### Windows AMIs on AWS |
| |
| Windows AMIs in AWS change regularly (to include the latest Windows updates). If using the community |
| AMI, it is recommended to use an AMI name regex, rather than an image id, so that the latest AMI is |
| always picked up. If an image id is used, it may fail as Amazon will delete their old Windows AMIs. |
| |
| If using an image regex, it is recommended to include the image owner in case someone else uploads |
| a similarly named AMI. For example: |
| |
| location: |
| jclouds:aws-ec2:us-west-2 |
| imageNameRegex = Windows_Server-2012-R2_RTM-English-64Bit-Base-.* |
| imageOwner = 801119661308 |
| ... |
| |
| ## stdout and stderr in a PowerShell script |
| |
| When calling an executable in a PowerShell script, the stdout and stderr will usually be output to the console. |
| This is captured by Brooklyn, and shown in the activities view under the specific tasks. |
| |
| An alternative is to redirect stdout and stderr to a file on the VM, which can be helpful if one expects sys admins |
| to log into the VM. However, be warned that this would hide the stdout/stderr from Brooklyn's activities view. |
| |
| For example, instead of running the following: |
| |
| D:\setup.exe /ConfigurationFile=C:\ConfigurationFile.ini |
| |
| The redirect can be achieved by using the `Start-Process` scriptlet: |
| |
| Start-Process D:\setup.exe -ArgumentList "/ConfigurationFile=C:\ConfigurationFile.ini" -RedirectStandardOutput "C:\sqlout.txt" -RedirectStandardError "C:\sqlerr.txt" -PassThru -Wait |
| |
| The `-ArgumentList` is simply the arguments that are to be passed to the executable, `-RedirectStandardOutput` and |
| `RedirectStandardError` take file locations for the output (if the file already exists, it will be overwritten). The |
| `-PassThru` argument indicates that PowerShell should write to the file *in addition* to the console, rather than |
| *instead* of the console. The `-Wait` argument will cause the scriptlet to block until the process is complete. |
| |
| Further details can be found on the [Start-Process documentation page](https://technet.microsoft.com/en-us/library/hh849848.aspx) |
| on the Microsoft TechNet site. |
| |