Installing Azure PowerShell on Server 2012 R2 using PowerShell

Currently I am working on a project to create a CICD pipeline in Azure Stack.  I am planning to use Team Foundation Server 2015, running on a virtual machine on Azure Stack.  I want the installation of TFS to be automated using ARM templates and a CustomScript extension.  (I would use DSC, but I feel I’ve shaved the yak enough already).  The installer and configuration files are sitting in Azure Blob storage, and I want to be able to pull them from the Server 2012 R2 instance that will be running TFS.  I can do it using Invoke-WebRequest or Start-BitsTransfer, but I wanted to try using the Azure PowerShell storage cmdlets instead.  Of course the vanilla install of Server 2012 R2 does not have the Azure modules or PowerShellGet module to access the PowerShell Gallery and install them.  So instead I am going to use Invoke-WebRequest and Github to grab it instead.

First of all the most recent release of the Azure PowerShell module is on Github under: https://github.com/Azure/azure-powershell/releases/latest.  So I grab the whole page and store it in a variable:

$resp = Invoke-WebRequest -Uri "https://github.com/Azure/azure-powershell/releases/latest"

Now if you look at the response members, there is a Links property:

PS C:\Windows\system32> $resp | gm
TypeName: Microsoft.PowerShell.Commands.HtmlWebResponseObject

Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
AllElements Property Microsoft.PowerShell.Commands.WebCmdletElementCollection AllElements {get;}
BaseResponse Property System.Net.WebResponse BaseResponse {get;set;}
Content Property string Content {get;}
Forms Property Microsoft.PowerShell.Commands.FormObjectCollection Forms {get;}
Headers Property System.Collections.Generic.Dictionary[string,string] Headers {get;}
Images Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Images {get;}
InputFields Property Microsoft.PowerShell.Commands.WebCmdletElementCollection InputFields {get;}
Links Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Links {get;}
ParsedHtml Property mshtml.IHTMLDocument2 ParsedHtml {get;}
RawContent Property string RawContent {get;}
RawContentLength Property long RawContentLength {get;}
RawContentStream Property System.IO.MemoryStream RawContentStream {get;}
Scripts Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Scripts {get;}
StatusCode Property int StatusCode {get;}
StatusDescription Property string StatusDescription {get;}

At the bottom of the page is a link to an msi file, which is the latest Azure PowerShell installer.  I find that link and store it as an object.

$link = ($resp.Links | ?{$_.href -like "https*.msi"}).href

There are actually two links that end in msi, one is a relative path and the other an absolute path.  By prefixing https to the search string, I get the absolute path href.  The $link variable has the string “https://github.com/Azure/azure-powershell/releases/download/v3.3.0-December2016/azure-powershell.3.3.0.msi” stored in it.  Obviously your string will change depending on the latest release.

I could do a one-liner to get the file and save it, then execute it, but I like to keep things readable.  Here I am grabbing the filename and then downloading it to my selected location.

$installer = $link.Split("/") | select -Last 1
Invoke-WebRequest -Uri $link -OutFile "F:\$installer"

Now that I have the msi file, I can use msiexec to install it.  Only one problem, installing Azure PowerShell automatically closes all instances of PowerShell.  Not a big deal if you are doing this interactively, or you are done your work in PowerShell.  But I am assuming you are running a script next that needs Azure PowerShell, or at least that is what I am doing.  What I want to do is set a scheduled task for 5 minutes in the future to run the next piece of the script.

$A = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "F:\MyScript.ps1"
$T = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(5)
$S = New-ScheduledTaskSettingsSet
$D = New-ScheduledTask -Action $A -Settings $S -Trigger $T
Register-ScheduledTask PS1 -InputObject $D

In five minutes time, the next script will run and have the Azure PowerShell cmdlets available.  Now we install the msi, and since I have the filename in a variable it is easy to reference.

& msiexec /i F:\$installer /qn

The installer should only take a minute or so to complete.  Hope this helps someone besides me!  Now onto installing TFS 2015.

Leave a Reply

Your email address will not be published. Required fields are marked *