blob: 5e5f8f9c0e3ae538bd8026454845791d3eddaf0e [file] [log] [blame] [view]
---
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.