In this episode, we will be using Azure PowerShell, Chocolatey, the Sitecore Installation Framework to set up a virtual machine. This post is not an end-to-end tutorial but points out some of the main concepts needed to get there. A full implementation can be found in my Azure Virtual Machine for Sitecore Development repository on GitHub.
Getting Started
Make sure you have the following software installed on your local development environment:
If you are unaware of why you need Visual Studio Code instead of just using the PowerShell ISE, read this article about PowerShell Core 6.0.
Make sure you have installed everything correctly by connecting to azure in the Visual Studio Code’s built-in terminal using Connect-AzAccount.
Connect-AzAccount
Create the Virtual Machine
You can manually create this virtual machine in the Azure portal, or you can read my previous post on how to Create an Azure VM with ports actually working via Azure PowerShell. Once created, you should see these resources in your resource group:
Installing Software
Now that we have the VM setup, we will use Chocolatey to install the software. A full list of packages can be found on the Chocolatey Packages page.
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
$ChocoPackages = "googlechrome", "visualstudio2017community", "git", "notepadplusplus.install" , "scala"
ForEach ($PackageName in $ChocoPackages) {
choco install $PackageName -y
}
Now you might be wondering, “Won’t executing this install the software on my local machine?” The answer to that is YES. To get the choco install to run on the Azure VM, we will leverage Set-AzVMCustomScriptExtension.
#Make this object as you will use it A LOT
$Vm = Get-AzVM -Name $VmName -ResourceGroupName $ResourceGroup
Set-AzVMCustomScriptExtension `
-ResourceGroupName $Vm.ResourceGroupName `
-VmName $Vm.Name `
-Location $Vm.Location `
-FileUri "$BaseFileUri/InstallSoftware.ps1" `
-Run "InstallSoftware.ps1" `
-Name "VmInstallSoftware" `
-Argument "-VmDownloadFolder $VmDownloadFolder"
The Set-AzVMCustomScriptExtension will download the PowerShell scripts from an accessible URL, such as a public blob storage. You will likely want to pass any secure variables via the Argument parameter.
It is important to note that AzVMCustomScriptExtension creates a string of the PowerShell script it will execute remotely. You may want to encode/decode the variables you are passing.
Enable PowerShell Remoting and Using the Custom Script Extension
Before we can install Sitecore, we will need to get PowerShell remoting enabled so that we can copy the license.xml file to the server. To do this, you can call Set-AzCustomScriptExtension again. Note that you need to pass the same script extension Name parameter as you can only have one.
Set-AzVMCustomScriptExtension `
-ResourceGroupName $Vm.ResourceGroupName `
-VmName $Vm.Name `
-Location $Vm.Location `
-FileUri "$BaseFileUri/InstallPsRemoting.ps1" `
-Run "InstallPsRemoting.ps1" `
-Name "VmInstallSoftware"
The contents of InstallPsRemoting.ps1 would Enable-PSRemoting and open the Windows firewall. Again, this file would be stored in an accessible location such as a public Azure container. I am also taking this execution opportunity to install IIS.
Enable-PSRemoting -Force
netsh advfirewall firewall add rule name="WinRM-HTTP" dir=in localport=5985 protocol=TCP action=allow
if ((Get-WindowsFeature Web-Server).InstallState -ne "Installed") {
Install-WindowsFeature -Name "Web-Server" -IncludeAllSubFeature -IncludeManagementTools
}
While this opens up connectivity on the virtual machine, you’ll also need to set this up on your local instance with the public IP of the virtual machine.
#Get the public IP of your VM
$VmPublicIpAddress = Get-AzPublicIpAddress -Name $Vm.Name
#Update your trustedhosts
Set-Item WSMan:localhost\client\trustedhosts -value $VmPublicIpAddress.IpAddress -Force
#Update your local firewall for WinRM"
netsh advfirewall firewall show rule name="WinRM-HTTP" | netsh advfirewall firewall add rule name="WinRM-HTTP" dir=in localport=5985 protocol=TCP action=allow
#Enable-PSRemoting on your local
Enable-PSRemoting -SkipNetworkProfileCheck -Force
Once this is configured, you can simply use PSSession to copy the file from your local to the VM.
$Cred = New-Object System.Management.Automation.PSCredential ($VmUsername, $VmUserPassword)
$Session = New-PSSession -ComputerName $VmPublicIpAddress.IpAddress -Port 5985 -Credential $Cred
Copy-Item -Path "license.xml" -Destination $SCInstallRoot -ToSession $Session
While you can use Invoke-Command with the $Session to run local scripts remotely, consider the reasons to use AzVMCustomScriptExtension instead.
For my purposes, this script potentially could be run by a number of developers globally. It made more sense for my implementation that these remotely executed PowerShell scripts remained consistent. Using AzVMCustomScriptExtension, most of the execution functions will live in a managed public storage account reducing the risk of them being changed as well as providing less complexity upfront to the users executing the scripts.
Installing Prerequisites and Sitecore
For this specific implementation, I am installing Sitecore 9.1 Update-1, which requires SQL 2016 SP2/2017, and Solr 7.2.1.
This means we will also need to install the Sitecore Install Framework and install the prerequisites on the virtual machine:
$SitecorePSRepository = "https://sitecore.myget.org/F/sc-powershell/api/v2"
$SifModule = Get-InstalledModule SitecoreInstallFramework -AllVersions -ErrorAction SilentlyContinue
if([string]::IsNullOrEmpty($SifModule)) {
Register-PSRepository –Name SitecoreRepo –SourceLocation $SitecorePSRepository -InstallationPolicy Trusted
Install-Module SitecoreInstallFramework -Force
}
Import-Module SitecoreInstallFramework
Install-SitecoreConfiguration 'C:\VmSoftware\Sitecore\prerequisites.json' -Verbose
For Solr, let’s make life easy and use Jeremy Davis‘ Low Effort Solr Install.
For SQL, we will use Brad Christie’s SQL SIF Installer.
Installing SQL may take a while this way as it downloads it from Microsoft. I think it might be worthwhile to create a private repository in Azure, place the download there, and provide access to the VM to increase the speed. However, I haven’t had the opportunity to validate the improvement.
$SqlInstallParams = @{
SqlExpressDownload = "https://download.microsoft.com/download/4/1/A/41AD6EDE-9794-44E3-B3D5-A1AF62CD7A6F/sql16_sp2_dlc/en-us/SQLEXPR_x64_ENU.exe"
SqlAdminPassword = $SqlAdminPassword
TempLocation = $VmDownloadPath
Path = "sqlexpress.json"
}
try {
Install-SitecoreConfiguration @SqlInstallParams
} catch [System.Data.SqlClient.SqlException] {
#The SQL installer produces a "needs restart" error which stops the script. We want to continue
if( $_.Exception.Number -eq 3021) {
continue
}
}
Sitecore can be installed as usual with the XP0-SingleDeveloper.ps1. I have modified mine to decode all the variables. As a reminder, AzVMCustomScriptExtension creates a string of the PowerShell script it will execute remotely. I found it easiest to go ahead and encode all my variables passed to this specific file and decode them before execution.
function Get-Decoded {
[CmdletBinding()]
param(
[String]$Str
)
return [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Str))
}
$DeSCInstallRoot = Get-Decoded -Str $SCInstallRoot
# Install XP0 via combined partials file.
$singleDeveloperParams = @{
Path = "$DeSCInstallRoot\XP0-SingleDeveloper.json"
SqlServer = Get-Decoded -Str $SqlServer
SqlAdminUser = Get-Decoded -Str $SqlAdminUser
SqlAdminPassword = Get-Decoded -Str $SqlAdminPassword
SitecoreAdminPassword = Get-Decoded -Str $SitecoreAdminPassword
SolrUrl = Get-Decoded -Str $SolrUrl
SolrRoot = Get-Decoded -Str $SolrRoot
SolrService = Get-Decoded -Str $SolrService
Prefix = Get-Decoded -Str $Prefix
XConnectCertificateName = Get-Decoded -Str $XConnectSiteName
IdentityServerCertificateName = Get-Decoded -Str $IdentityServerSiteName
IdentityServerSiteName = Get-Decoded -Str $IdentityServerSiteName
LicenseFile = Get-Decoded -Str $LicenseFile
XConnectPackage = Get-Decoded -Str $XConnectPackage
SitecorePackage = Get-Decoded -Str $SitecorePackage
IdentityServerPackage = Get-Decoded -Str $IdentityServerPackage
XConnectSiteName = Get-Decoded -Str $XConnectSiteName
SitecoreSitename = Get-Decoded -Str $SitecoreSiteName
PasswordRecoveryUrl = Get-Decoded -Str $PasswordRecoveryUrl
SitecoreIdentityAuthority = Get-Decoded -Str $SitecoreIdentityAuthority
XConnectCollectionService = Get-Decoded -Str $XConnectCollectionService
ClientSecret = Get-Decoded -Str $ClientSecret
AllowedCorsOrigins = Get-Decoded -Str $AllowedCorsOrigins
}
Push-Location $DeSCInstallRoot
Install-SitecoreConfiguration @singleDeveloperParams *>&1 | Tee-Object XP0-SingleDeveloper.log
Pop-Location
Restarting the Virtual Machine
There are at least two times I needed to restart the machine during the install. One is after installing the Software via Chocolatey because of the .NET installs required for Visual Studio. The other is after installing Solr and MSSQL as the Solr script updates the Windows Environment Variables and MSSQL’s dependencies for a restart. To do this in your local script, you can simply execute the following:
Restart-AzVM -Name $Vm.Name -ResourceGroupName $Vm.ResourceGroupName
Final Thoughts
Again, this has not shown a full implementation, but a sampling of things you might need to do. A full implementation can be found at Azure Virtual Machine for Sitecore Development on GitHub.
You can absolutely do most, if not all of this, with an ARM template and Azure Automation. In fact, those might be a better option for you and your organization.
If you decide to put things on a public container, please do not put your license file or any other sensitive information on the public cloud.
Leave a Reply