PowerShell your DevOps - part 3

A picture

Datum

25-10-2019

Leestijd

7 minuten

Expertise

Deploy ARM templates to Azure and install with Azure Custom Script Extension a in-house created PowerShell script to deploy roles, features and 3rd party applications to the Azure VM.

In my other blogposts I wrote about using scripts to install Microsoft roles, features and 3rd party software during deployment of a Windows Server VM. In this blog, I’ll explain how to integrate the PowerShell commands with ARM templates deployments.

What you need is:

  • Visual Studio Code
  • PowerShell AZ CLI module
  • ARM template, I used the 201 Custom Script VM template on GitHub in this case
  • ARM Custom Script Extension For Windows

In order to connect to the Azure Command Line module, you need to install the new Microsoft Azure CLI module. Follow the instructions in the link if you haven’t done this yet: https://docs.microsoft.com/en-us/powershell/azure/new-azureps-module-az?view=azps-2.7.0

I’m creating PowerShell scripts in Microsoft Visual Studio Code, because it is light and fast for creating scripts. There are lot of free extensions available to use with Microsoft Visual Studio Code. For example, you can install the ARM template viewer for visualisation of the ARM template.

So now we can start coding. We are going to use a predefined ARM template with Microsoft Azure Custom Script Extension feature. There are many more Azure quick-start templates available for other purposes. They can help you and also speed up the coding process. In this case, I used the ARM template that originally came from “Azure-quickstart-templates / 201-vm-custom-script-windows”: https://github.com/Azure/azure-quickstart-templates/tree/master/201-vm-custom-script-windows

To use a PowerShell script that you have created in-house with an ARM template, you need to use the Azure Custom Script Extension. The Azure Custom Script Extension will copy the script from a repository like GitHub or Azure Storage and execute it locally during the deployment of the ARM template. For more details, please go to: https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/custom-script-windows

This ARM template copies a Copy-FileFromAzure.ps1 script and executes it locally on the deployed ARM template. But what if you want to install Microsoft server roles, features or third-party software on the template? We can add some code to the Copy-FileFromAzure.ps1 and use the same methods I described in the first part of my PowerShell your DevOps series: https://www.centric.eu/NL/Default/Craft/Blogs/2018/10/08/Powershell-your-DevOps-environment---Part-1

We’re going to automate the installation for Microsoft serverfeature DotNet framework and install a 3rd party application Notepad++. The first part is copying files like the Copy-FileFromAzure.ps1 and software installation resources to the VM for execution on the VM with the Invoke-WebRequest command.

Also, we are going to create a task in the task scheduler to restart the Copy-FileFromAzure.ps1 and add the -SkiptoPhaseTwo parameter. Don’t forget to delete the task after the reboot, otherwise the script will rerun after the next reboot. But first you need to know where the Azure Custom Script Extension is copying the Copy-FileFromAzure.ps1. In this case, it is copying to "C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.9.5\Downloads\0\”. You can use this location in the TaskScheduler task.

Now the script is finished, we need to deploy the ARM template and place the files in the cloud. The scripts and ARM template will be placed on GitHub, but the installation files are placed on Azure storage.

How do we connect and start the deployment? First we connect to the Azure CLI with Connect-AzAccount.



  # PowerShell Azure 

# Connect to Microsoft Azure
#vervangen door  Connect-AzAccount 
Connect-AzAccount

Secondly, we create a separate Azure ResourceGroup for the deployment of the ARM template.



  # Variables
$ResourceGroup              = "***RG01"
$Location                   = "westeurope"

# Azure ResourceGroup
New-AzResourceGroup -Name $ResourceGroup -Location $Location

Finally, we can start our deployment using the following PowerShell command:



  #Deployment ARM Template to Azure
New-AzResourceGroupDeployment -ResourceGroupName  $ResourceGroup -TemplateUri https://raw.githubusercontent.com/***/master/azuredeploy.json

If your Azure VM already exists, all you need to do is execute a PowerShell script on the Azure VM using the Set-AzVMCustomScriptExtension command. The only requirement is that the script must be located on an Azure Storage Account instead of Github.



  #Execute CustomScriptExtension 
Set-AzVMCustomScriptExtension -ResourceGroupName $ResourceGroup  -Location $Location -VMName "***" -ContainerName  "***" -StorageAccountName "***" -FileName " Copy-FileFromAzure.ps1" -Name "CustomScriptExtension"

For this blog, I automated the installation and download of the DotNet 3.5 and Notepad++ resources using a task that executes the same Copy-FileFromAzure.ps1 PowerShell script after the reboot, with logging enabled.

A picture

If you're interested in the coding, I have added the coding of the Copy-FileFromAzure.ps1 PowerShell script to this blogpost:



  $PostInstallationScriptLocation = "C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.9.5\Downloads\0\"

# Output Installation Status
$OutputResult = "C:\CentricInstallation.txt"

# Timestamp Function
filter Timestamp {"$(Get-Date -Format G): $_"}

# Part One Script run
If (!$SkipToPhaseTwo)
{ 
  
 "Script started successfully" | Timestamp |  Out-File -FilePath $OutputResult -Append
   
Invoke-WebRequest "https://***.blob.core.windows.net/***/microsoft-windows-netfx3-ondemand-package.cab" -OutFile "C:\WindowsAzure\Applications\microsoft-windows-netfx3-ondemand-package.cab"
Invoke-WebRequest "https://***.blob.core.windows.net/***/npp.7.7.1.Installer.exe" -OutFile "C:\WindowsAzure\Applications\npp.7.7.1.Installer.exe"

# This code schedules the currently running script to restart and forward to a given checkpoint:
schtasks.exe /create /tn "HeadlessRestartTask" /ru *** /sc ONSTART /tr "powershell.exe Start-Process -filepath $PostInstallationScriptLocation\Copy-FileFromAzure.ps1 -SkipToPhaseTwo"

"HeadlessRestartTask scheduler succesfully created" | Timestamp |  Out-File -FilePath $OutputResult -Append

# Installation Prerequirements
Set-ExecutionPolicy UnRestricted -Force

If ($PSVersionTable.PSVersion | Where {$_.Major -ne "5"})
  {
    "PowerShellVersion:" | Out-File -FilePath $OutputResult -Append
    $PSVersionTable.PSVersion |  Out-File -FilePath $OutputResult -Append
    "Wrong Powershell version, please upgrade Powershell to V5.0" | Timestamp| Out-File -FilePath $OutputResult -Append
    "Script failed" | Timestamp | Out-File -FilePath $OutputResult -Append
    Break
  }

$CheckFramework = Get-WindowsFeature | Where {$_.DisplayName -eq ".NET Framework 3.5 (includes .NET 2.0 and 3.0)"}
  If ($CheckFramework | Where {$_.InstallState -eq "Removed" -or "Available"})
  {

    Install-WindowsFeature Net-Framework-Core -source "C:\WindowsAzure\Applications\"
    Get-WindowsFeature | Where {$_.DisplayName -eq ".NET Framework 3.5 (includes .NET 2.0 and 3.0)"} | Out-File -FilePath $OutputResult -Append
  } 
  Else
  {
    Get-WindowsFeature | Where {$_.DisplayName -eq ".NET Framework 3.5 (includes .NET 2.0 and 3.0)"} | Out-File -FilePath $OutputResult -Append
    ".NET Framework 3.5 (includes .NET 2.0 and 3.0) Installed" | timestamp |  Out-File -FilePath $OutputResult -Append
  }
    
    
  Shutdown /r /t 100
} 

# Part Two Script run
"The system has been restarted, script continuing..." | Timestamp | Out-File -FilePath $OutputResult -Append

# self-delete the scheduled task
Start-Process schtasks.exe -ArgumentList "/delete /f /tn HeadlessRestartTask" -Wait -PassThru

# Install Notepad++ part Two
If ((gp HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | ? { $_.DisplayName -eq "Notepad++ (32-bit x86)"}) -eq $null)
{
  Start-Process -FilePath "C:\WindowsAzure\Applications\npp.7.7.1.Installer.exe" -ArgumentList "/S" -PassThru -Wait
    Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |  Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | Format-Table -AutoSize | Out-File -FilePath $OutputResult -Append
  "Notepad++ installed" | Timestamp | Out-File -FilePath $OutputResult -Append
}

# Finish
"Script Finished" | Timestamp| Out-File -FilePath $OutputResult -Append
 Shutdown /r /t 100