Following on from my previous two posts regarding WinRM over HTTPS the next stage was to automate the steps that needed to be carried out inside the Azure VM. In my original post I had a mix of PowerShell, command prompt and copy and paste! Andy Slowey provided me with the following PowerShell to optimize the WinRM over HTTPS server side configuration:
<br /> # Ensure PS remoting is enabled, although this is enabled by default for Azure VMs<br /> Enable-PSRemoting -Force </p> <p> # Create rule in Windows Firewall<br /> New-NetFirewallRule -Name "WinRM HTTPS" -DisplayName "WinRM HTTPS" -Enabled True -Profile Any -Action Allow -Direction Inbound -LocalPort 5986 -Protocol TCP </p> <p> # Create Self Signed certificate and store thumbprint<br /> $thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My).Thumbprint </p> <p> # Run WinRM configuration on command line. DNS name set to computer hostname.<br /> $cmd = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname=""$env:computername""; CertificateThumbprint=""$thumbprint""}" </p> <p> cmd.exe /C $cmd<br />
In search of going one better, I decided to find a way to avoid the need to use RDP so that the whole process could be automated. Scripts can be executed within an Azure VM without logging into the server using Custom Script extensions. Initially the script needs to be created locally. I have used the PowerShell above, but if you wanted to do something different within the VM, just replace the PowerShell between the curly brackets, { }.
<br /> # define a temporary file in the users TEMP directory<br /> $file = $env:TEMP + "\ConfigureWinRM_HTTPS.ps1" </p> <p> #Create the file containing the PowerShell </p> <p> { </p> <p> # POWERSHELL TO EXECUTE ON REMOTE SERVER BEGINS HERE </p> <p> # Ensure PS remoting is enabled, although this is enabled by default for Azure VMs<br /> Enable-PSRemoting -Force </p> <p> # Create rule in Windows Firewall<br /> New-NetFirewallRule -Name "WinRM HTTPS" -DisplayName "WinRM HTTPS" -Enabled True -Profile Any -Action Allow -Direction Inbound -LocalPort 5986 -Protocol TCP </p> <p> # Create Self Signed certificate and store thumbprint<br /> $thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My).Thumbprint </p> <p> # Run WinRM configuration on command line. DNS name set to computer hostname, you may wish to use a FQDN<br /> $cmd = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname=""$env:computername""; CertificateThumbprint=""$thumbprint""}"<br /> cmd.exe /C $cmd </p> <p> # POWERSHELL TO EXECUTE ON REMOTE SERVER ENDS HERE </p> <p> } | out-file $file<br />
Once the script is created it needs to be uploaded to Azure Blob storage. To do this I found the storage account that the VM OS disk was created in, created a container within that account and uploaded the script:
<br /> # Get the VM we need to configure<br /> $vm = Get-AzureRmVM -ResourceGroupName $rgname -Name $VMName </p> <p> # Get storage account name<br /> $storageaccountname = $vm.StorageProfile.OsDisk.Vhd.Uri.Split('.')[0].Replace('https://','') </p> <p> # get storage account key<br /> $key = (Get-AzureRmStorageAccountKey -Name $storageaccountname -ResourceGroupName $rgname).Key1 </p> <p> # create storage context<br /> $storagecontext = New-AzureStorageContext -StorageAccountName $storageaccountname -StorageAccountKey $key </p> <p> # create a container called scripts<br /> New-AzureStorageContainer -Name "scripts" -Context $storagecontext </p> <p> #upload the file<br /> Set-AzureStorageBlobContent -Container "scripts" -File $file -Blob "ConfigureWinRM_HTTPS.ps1" -Context $storagecontext<br />
Finally, I create the custom script extension:
<br /> # Create custom script extension from uploaded file<br /> Set-AzureRmVMCustomScriptExtension -ResourceGroupName $rgname -VMName $vmname -Name "EnableWinRM_HTTPS" -Location $vm.Location -StorageAccountName $storageaccountname -StorageAccountKey $key -FileName "ConfigureWinRM_HTTPS.ps1" -ContainerName "scripts"<br />
This, along with the previous post regarding creating the network security group rule successfully configures WinRM over HTTPS within the VM without the need to do anything outside of a local PowerShell prompt. I understand this needs to be easier to use so my next post I will bring it all together into a function that can easily be reused.
For reference the PowerShell to connect to the remote server is as follows:
<br /> # Disable, CA and CN check. If use correct FQDN and install certificate locally as is a best practice this is not required<br /> $so = New-PsSessionOption -SkipCACheck -SkipCNCheck </p> <p> # Disable, CA and CN check. If use correct FQDN and install certificate locally as is a best practice this is not required<br /> Enter-PSSession -ComputerName <server_ip_or_fqdn> -Credential <admin_username> -UseSSL -SessionOption $so<br />
Note: We have noticed some differences between different versions of the Azure RM PowerShell modules. For reference, here is the module versions that I tested the scripts on:
Name Version
Azure 1.0.4
Azure.Storage 1.0.4
AzureRM.Compute 1.2.2
AzureRM.Network 1.0.4
AzureRM.Profile 1.0.4
AzureRM.Resources 1.0.4
AzureRM.Storage 1.0.4
As always, your posts come first whenever am searching for any techie stuff regarding Azure.
I was thinking of having a look at your blog, until I realized that am actually in your blog! 🙂
Keep going Marcus!
Cheers
Thanks! Do let me know what you would like to read about!
Hi Marcus, Can you please guide me to use the custom extension script via ARM template? I have stored my script in Azure blob storage and i have added my customscript extension in ARM template. I am trying to deploy this on existing load balanced VMs via Powershell. However I am getting this error: New-AzureRmResourceGroupDeployment : 4:27:43 PM – Resource Microsoft.Compute/virtualMachines 'myVM1' failed with message 'The resource operation completed with terminal provisioning state 'Failed'.' At line:1 char:1 + New-AzureRmResourceGroupDeployment -ResourceGroupName MyResUS -TemplateFile "C:\ … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [New-AzureRmResourceGroupDeployment], Exception + FullyQualifiedErrorId : Microsoft.Azure.Commands.Resources.NewAzureResourceGroupDeploymentCommand New-AzureRmResourceGroupDeployment : 4:27:43 PM – VM has reported a failure when processing extension ‘keysExtension’. Error message: “Failed to download all specified files. Exiting. Error Message: The remote server returned an error: (400) Bad Request.”. At line:1 char:1 + New-AzureRmResourceGroupDeployment -ResourceGroupName MyResUS -TemplateFile “C:\ … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [New-AzureRmResourceGroupDeployment], Exception + FullyQualifiedErrorId : Microsoft.Azure.Commands.Resources.NewAzureResourceGroupDeploymentCommand New-AzureRmResourceGroupDeployment : 4:27:58 PM – Resource Microsoft.Compute/virtualMachines ‘myVM0’ failed with message ‘The resource operation completed with terminal provisioning state ‘Failed’.’ At line:1 char:1 + New-AzureRmResourceGroupDeployment -ResourceGroupName MyResUS -TemplateFile “C:\ … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [New-AzureRmResourceGroupDeployment], Exception + FullyQualifiedErrorId : Microsoft.Azure.Commands.Resources.NewAzureResourceGroupDeploymentCommand
Hello,
You will need to paste the template for me to review. It is likely the URL to the script isn’t valid.
Marcus
Â
Set-AzureRmVMCustomScriptExtension -ResourceGroupName $ResourceGroupName -VMName $VMname -Name "RunMyTest" -Location $VM.location -StorageAccountName $storageAccountName -StorageAccountKey $storagekey -FileName "killallrdp00.ps1" -Run "killallrdp00.ps1" -ContainerName $StorageContainer
Run this in the: Azure / Automation/ RunBooks.
In the Target server TS2 i see – Script Run. This is OK.
But, in the RunBook i see : Script work, work,work……….. Command Set-AzureRmVMCustomScriptExtension not stop ;((
Please, Help me.
Target server TS2 is: Win2008R2
Hi, thanks for writing this, I have been using it to try and create my own custom script execution process.
Unfortunately, the line:
Doesn't seem to work for me, PowerShell / Azure complains about this:
Basically it thinks the -StorageAccountName parameter is invalid. Now I can get the extension to install without specifying this, but then it has a failed status because no script file has been specified. Do you have any idea what I might be doing wrong?
thanks!
The storage account name must be unique and.. "The name of the storage account within the specified resource group. Storage account names must be between 3 and 24 characters in length and use numbers and lower-case letters only."
Is there any way how to use custom script while provisioning machines? This is showing how to use custom script on already provisioned VM. It was possible in old Azure, but seems like its not possible in new one.
Yes, I use ARM templates and the custom script extension: https://azure.microsoft.com/en-gb/documentation/articles/virtual-machines-windows-extensions-customscript/
some of the commands may not work due to Powershell compatability.
For example, I use this line instead to grab the key1 value:
(Get-AzureRmStorageAccountKey -ResourceGroupName "RG01" -AccountName "MyStorageAccount").Value[0]
Hi Marcus, Is there a way I can get the output of my script? For example if I run a script with just "whoami", where can I see the output of the script?
Regards
You can use Get-AzureRMVMDiagnosticsExtension as per https://msdn.microsoft.com/en-us/library/mt603678.aspx . there is a good example here: http://stackoverflow.com/questions/38152873/how-can-i-get-the-output-of-a-customscriptextenstion-when-using-azure-resource-m . Or alternatively browse to the extension in the portal.
i get error running the $cmd = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname=""$env:computername""; CertificateThumbprint=""$thumbprint""}" cmd.exe /C $cmd
Error:
At line:1 char:144
+ … thumbprint""}" cmd.exe /C $cmd
+ ~~~~~~~
Unexpected token 'cmd.exe' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
Looks like a line break was missing, before cmd.exe, have updated the post. Let me know if that helps.
Hi Marcus,
I have followed your steps its working good up to Set-AzureRmVMCustomScriptExtension, I got the file into my new VM. But when I tried to run Enter-PSSession its showing below error.
Enter-PSSession : Connecting to remote server xxxxx.cloudapp.net failed with the following error message : The WinRM client
cannot process the request because the server name cannot be resolved. For more information, see the about_Remote_Troubleshooting Help topic.
At line:1 char:1
+ Enter-PSSession -ComputerName "xxxxxxx …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (xxxxxxx:String) [Enter-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : CreateRemoteRunspaceFailed
I have enabled WinRM local machine also.
Another doubt is currently am using Azure Free Trial subscribtion. I have used nslookup <VM name> to see the VM full name to use Enter-PSSession.
Regards,
Ramesh
As you suggest looks like a DNS issue. Does the DNS name resolve to an IP address?
Hi Marcus, this page seems like same i am working on i am getting error while executing customcreatevmextension
Set-AzureRmVMCustomScriptExtension : Long running operation failed with status 'Failed'. Additional Info:'VM has reported a failure when processing extension 'DisableFirewall'. Error message: "Failed to
download all specified files. Exiting. Error Message: The remote server returned an error: (404) Not Found.".'
ErrorCode: VMExtensionProvisioningError
ErrorMessage: VM has reported a failure when processing extension 'DisableFirewall'. Error message: "Failed to download all specified files. Exiting. Error Message: The remote server returned an error:
(404) Not Found.".
Hey,
I want to pass Credentials to the Custom Script that is used to securely connect to SMTP Server.
I tried passing the plain password via -Argument parameter. But is this secure?
Is it secure with -SecureExecution?
In the link "https://docs.microsoft.com/en-us/powershell/module/azurerm.compute/set-azurermvmcustomscriptextension?view=azurermps-5.5.0", the document says -SecureExecution makes it safe to pass Passwords/Creds to the -Run parameter but says nothing about the -Argument parameter.
How can I pass Creds/Passwords to -Run parameter?
Or is there another way to pass creds securely in Set-AzureRmVMCustomScriptExtension?