Using Remote PowerShell on Azure
When we have hundreds of Virtual Machines to manage, manual tasks just don’t cut it. they’re error prone and can be quite boring to carry out.
On Azure, there are two approaches to automation for Windows Virtual Machines. The first is PowerShell Desired State Configuration (DSC). This approach is well suited for the initial configuration of complex environments. The second is Remote PowerShell, and this blog post will focus on this approach.
Remote PowerShell is great for executing commands and performing maintenance on existing Virtual Machines. It uses the WinRM protocol over HTTPS to secure the communication transport layer. By default, if we don’t specify a certificate, the provisioning agent will automatically generate a self-signed certificate and configure the SSL listener.
In order to connect to a Virtual Machine that uses a self-signed certificate, we need to download and install it to the Azure Automation Instance that executes our runbook. Failure to do so, forces us to use the -SkipCACheck PowerShell session option that leaves us vulnerable to a man in the middle attack.
The Challenge
The goal of this post is to walk through what is required to execute commands through Remote PowerShell from Azure Automation.
The exit criteria for this example, is to be able to create a text file on an existing Azure Virtual Machine.
The Solution
We need to start by Configuring Azure Automation. This requires us to create a new Azure Active Directory user to manage our Azure subscription.
The credentials for this new user, are securely stored in Azure Automation Credential Assets. In order to authorize Azure Automation to interact with our Azure Subscription, we use the Get-AutomationPSCredential CmdLet to retrieve user credentials and the Add-AzureAccount Azure CmdLet to Authenticate against the Azure Management APIs.
To secure the Remote PowerShell Session we use an InlineScript block to fetch the default WinRM Certificate from the Classic Azure Virtual Machine. Then we install it to the Local Machine Certificate Store.
The URI used by the Invoke-Command CmdLet is secured via HTTPS and contains the public WinRM port configured on the Azure Virtual Machine.
Note: On Azure Classic Compute, the port information is configured through Endpoints. On Azure Resource Manager driven Compute, it is defined in the Network Security Groups (NSG).
Ok, now we’re almost ready. We need to store the Virtual Machine user credentials in an Azure Automation Credential Asset.
Using the Virtual Machine user credentials and the WinRM URI, we can now use the Invoke-Command CmdLet to open a Remote PowerShell Session and execute commands on out Virtual Machine.
The Runbook
workflow Invoke-AzureVMCommand { # Get the Azure Management Credentials $creds = Get-AutomationPSCredential -Name 'runbook' #Connect to your Azure Account $Account = Add-AzureAccount -Credential $creds if(!$Account) { Throw "Could not authenticate to Azure using the credential asset '${CredentialAssetName}'. Make sure the user name and password are correct." } InlineScript { # Get the Azure certificate for remoting into this VM $winRMCert = (Get-AzureVM -ServiceName 'trial-brisebois' ` -Name 'trial-brisebois' ` | select -ExpandProperty vm) .DefaultWinRMCertificateThumbprint $AzureX509cert = Get-AzureCertificate -ServiceName 'trial-brisebois' ` -Thumbprint $winRMCert ` -ThumbprintAlgorithm sha1 # Add the VM certificate into the LocalMachine if ((Test-Path Cert:\LocalMachine\Root\$winRMCert) -eq $false) { Write-Progress "VM certificate is not in local machine certificate store - adding it" $certByteArray = [System.Convert]::fromBase64String($AzureX509cert.Data) $CertToImport = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList (,$certByteArray) $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "Root", "LocalMachine" $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) $store.Add($CertToImport) $store.Close() } } # Build the connection URI with the WinRM port configured on the VM $uri = 'https://trial-brisebois.cloudapp.net:5986/' # Get the VM Admin Credentials $admin = Get-AutomationPSCredential -Name 'Brisebois' # Run a command on the Azure VM InlineScript { Invoke-command -ConnectionUri $Using:uri -credential $Using:admin -ScriptBlock { # This script executes on the remote VM New-Item c:\trial-brisebois.txt -type file } } }
Resources
- Automation documentation
- Configuring Azure Automation
- Credential assets in Azure Automation
- Getting Started with Azure Automation: Automation Assets
- My first textual runbook
- Runbook Gallery
- Authoring Integration Modules for SMA
- Managing Azure with SMA
- Azure Automation DSC Overview
- Azure Automation administration
- Azure Automation Release Notes
- Invoke-Command Runs commands on local and remote computers.
- Connect to an Azure Virtual Machine
- Introduction to Remote PowerShell with Windows Azure
- Configures Secure Remote PowerShell Access to Windows Azure Virtual Machines
Very nice and useful it helped me alot
LikeLike
Very nice and useful, it helped me a lot. Please add a ` at the end of line 17 and it run fine!
Thanks!
LikeLike
Hi guys… How can we implement this on AzureRM vms?
LikeLike