I had a customer ask whether it was possible to perform scripted customizations against a recovery VM after an Azure Site Recovery (ASR) fail-over. The catch was that they didn’t necessarily know whether Remote PowerShell ports (5985/5986) would be open on recovery VM nor could they guarantee they’d have valid credentials for accessing the VM with administrative privileges. In this case, the Azure Custom Script Extension fits the requirements perfectly.
The Custom Script Extension downloads and executes scripts on Azure virtual machines. This extension is useful for post deployment configuration, software installation, or any other configuration / management task. Scripts can be downloaded from Azure storage or GitHub, or provided to the Azure portal at extension run time. This means you can create a script to perform the customizations, store that script in Azure Blob Storage and then use an Azure Automation Runbook as part of the ASR Recovery Plan to install the extension in the VM post-failover.
High-level steps and resources:
- Create the script which will perform the VM changes and upload to Azure Blob Storage.
- Setup an Azure Automation account – https://docs.microsoft.com/en-us/azure/automation/automation-offering-get-started. The Automation account can be in any Azure region. The Automation account must be in the same subscription as the Azure Site Recovery vault.
- Create a runbook in Azure Automation which should do the following:
- Receive the Recovery Plan context from ASR as a parameter.
- Authenticate to the Azure subscription using a RunAsConnection or RunAsCertificate
- Using the VM information supplied by ASR in the Recovery Plan Context, use the Set-AzureRmVMCustomExtension cmdlet to install the Custom Script Extension on the VM using the script from Step #1.
- You can use runbook variables to pass in details about the storage account to use, the name of the script, etc. Here is a sample runbook to use as a starting point: CSEASRRunbook.
- Add the runbook to the ASR Recovery Plan as a post-failover step as documented here: https://docs.microsoft.com/en-us/azure/site-recovery/site-recovery-runbook-automation.
In researching this, I also found a post on the Azure blog which provides a runbook that takes the script as an input and builds the script file, uploads it to Azure and configures the Custom Script Extension. It’s a bit more involved than this simple example as it saves you the effort of building the script and uploading to Azure Blob Storage ahead of time. You can check it out at https://azure.microsoft.com/en-in/blog/azure-automation-run-tasks-on-azure-virtual-machines-without-opening-ports/.
A client recently ran Qualys SSL Server Test against their web applications published through the Azure Application Gateway. The test graded the SSL security on the site as a “B” mainly because the server supported weak Diffie-Hellman (DH) key exchange parameters.
Diffie-Hellman key exchange is a popular cryptographic algorithm that allows Internet protocols to agree on a shared key and negotiate a secure connection. SSL sites that support export cipher suites and don’t use 2048-bit or stronger Diffie-Hellman groups with “safe” primes are susceptible to attacks like LogJam. Luckily, a feature known as SSL Policy in the Azure Application Gateway allows you to reduce the potential for these types of attacks.
The SSL handling in Azure Application Gateway (used for things such as SSL offloading and centralized SSL handling) allows you to specify a central SSL policy that’s suited to your organizational security requirements. The SSL policy includes control of the SSL protocol version as well as the cipher suites and the order in which ciphers are used during an SSL handshake. Application Gateway offers two mechanisms for controlling SSL policy: either a predefined policy or a custom policy. Here’s a link to the documentation for SSL policy with Azure Application Gateway. Changing the SSL policy for a new Application Gateway deployment can be accomplished using PowerShell and changing an existing deployment’s SSL policy is also easily done via the cmdlets. Below is an example of how to do this with a few lines of PowerShell.
One “gotcha” is that the predefined SSL policy which disables the weaker cipher suites also sets a minimum TLS version of v1.2 and breaks most older browsers. If that’s not a concern, use the latest predefined SSL policy – otherwise you’ll have to use a custom policy and specify a lower minimum TLS version to support older IE browsers running on Windows 7, for example.
# Get Configuration of AppGW
$appgw = Get-AzureRmApplicationGateway -Name $GWName -ResourceGroupName $GWResourceGroupName
# Set SSL Policy on AppGW to Custom Policy based on Most Recent Security Policy w/TLSv1.0 Support. FYI: Will work on any version of IE > 8.0 running on Windows 7. No Windows XP support!
Set-AzureRmApplicationGatewaySslPolicy -ApplicationGateway $appgw -PolicyType Custom -MinProtocolVersion TLSv1_0 -CipherSuite “TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256″,”TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384″,”TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA”,”TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA”,”TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256″,”TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384″,”TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384″,”TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256″,”TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA”,”TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA”,”TLS_RSA_WITH_AES_256_GCM_SHA384″,”TLS_RSA_WITH_AES_128_GCM_SHA256″,”TLS_RSA_WITH_AES_256_CBC_SHA256″,”TLS_RSA_WITH_AES_128_CBC_SHA256″,”TLS_RSA_WITH_AES_256_CBC_SHA”,”TLS_RSA_WITH_AES_128_CBC_SHA”
# Set SSL Policy on AppGW to Most Recent Policy w/TLSv1.2 Minimum Support. FYI: Becuase TLS v1.0 is not supported, this will break any browser earlier that IE 11!
Set-AzureRmApplicationGatewaySslPolicy -ApplicationGateway $appgw -PolicyType Predefined -PolicyName “AppGwSslPolicy20170401S”
# Update the gateway with validated SSL policy
Set-AzureRmApplicationGateway -ApplicationGateway $appgw
Language Modes in Windows PowerShell essentially define which elements of the PowerShell language can be used in a PowerShell session. As you’ve already discovered, the different language modes are documented publicly here. Language modes are intended to be specified as part of a session configuration file when a new PowerShell session is launched on a system.
Another way of setting the language mode for a PowerShell session would be to use either DeviceGuard or AppLocker in enforced mode with PowerShell 5+ and WMF 5+ or (more easily) to simply make use of a system environment variable called “__PSLockDownPolicy” to configure it. The system environment variable could be controlled on managed PCs in a variety of ways including via Group Policy.
There are two things to keep in mind regarding the use of language modes and the “__PSLockDownPolicy” system variable.
The first is that both the restricted language mode and constrained language mode could very likely have an impact on any SCCM scripts that use PowerShell or with any remote PowerShell management that you may be performing against systems. If scripts contain any scripting elements or code elements from other languages, they will most likely be prevented from running as expected. At its most restrictive setting (restricted and no language), script blocks aren’t allowed and/or the only thing that can be executed are cmdlets without any language elements at all.
The second thing is that when setting the language mode via the system variable, it will apply to all sessions started on that system and can’t be easily overridden on a user-by-user basis or a session-by-session basis. This means that it will affect built-in security principals as well as local users and would certainly have an effect on not only SCCM scripts and remote PowerShell management but other things like setting task scheduler to run as System and kick off a script at a pre-defined time.
The moral of the story is that while enforcing the PowerShell language mode on systems in an environment can reduce the potential for PowerShell to be used as an attack vector in malware or other exploits, it can have a pretty big impact on managing those systems via PowerShell for the purposes of good, not evil.