Public/Install-PSAzureVMModule.ps1
<#
.SYNOPSIS A function to install PowerShell Modules from a custom PS Repository on an Azure ARM Virtual Machine. .DESCRIPTION A function to isntall PowerShell Modules from a custom PS Repository on an Azure ARM Virtual Machine. Assuming that some form of PowerShell Repository is available and has modules. See: https://urltoinstructions. .PARAMETER Name <String> The name of the module. To be used in Script generation and to keep track of what names to be used when deploying. .PARAMETER SubscriptionId <String> The GUID of an Azure Subscription. .PARAMETER ResourceGroupName <String> The name of the Resource Group that the Virtual Machine is part of. .PARAMETER VMName <String> The name of the Virtual Machine. .PARAMETER FileName <String> The name of the installation script file. Used to keep track when generating install script, or what the existing script is named on the Storage Account. .PARAMETER Argument <String> Additional arguments. If an installl script takes arguments, specify them as a string. $arguments = "-User username -Group group" If it's an array $arguments = "-User user1,user2 -Group group" .PARAMETER StorageAccountName <String> The name of the Storage Account that will contain the script. .PARAMETER Container <String> The container where the script will be located. Used together with 'StorageAccountName'. .PARAMETER RepositoryName <String> The name of the Repository. Used in combination with 'UploadScript' so it can determine on what PSRepository to use. .PARAMETER RepositoryPath <String> The path to the Repository. Used in combination with 'UploadScript' so it can determine on what PSRepository path to use. .PARAMETER UploadScript <Switch> This parameter is used so an install script can be generated during runtime. The script will be generated and uploaded. It will need 'StorageAccountName', 'Container', 'RepositoryName' and 'RepositoryPath' to be correct. .PARAMETER FileUrl <String> A URI to a script file in raw format. It is stand alone and should not be used with the parameters that are associated with StorageAccount and Repository. .NOTES Written by Karl Wallenius, Redeploy AB. #> function Install-PSAzureVMModule { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0)] [String] $Name, [Parameter(Mandatory = $true, Position = 1)] [String] $SubscriptionId, [Parameter(Mandatory = $true, Position = 2)] [String] $ResourceGroupName, [Parameter(Mandatory = $true, Position = 3)] [String] $VMName, [Parameter(Mandatory = $true, Position = 4)] [String] $FileName, [Parameter(Mandatory = $false)] [String] $Argument, [Parameter(Mandatory = $false, ParameterSetName = "StorageAccount")] [String] $StorageAccountName, [Parameter(Mandatory = $false, ParameterSetName = "StorageAccount")] [String] $Container, [Parameter(Mandatory = $false, ParameterSetName = "StorageAccount")] [String] $RepositoryName, [Parameter(Mandatory = $false, ParameterSetName = "StorageAccount")] [String] $RepositoryPath, [Parameter(Mandatory = $false, ParameterSetName = "StorageAccount")] [switch] $UploadScript = $false, [Parameter(Mandatory = $false, ParameterSetName = "FileUri")] [String] $FileUri ) process { # Check if user is logged on. $loggedOn = $false $storageAccount = $false $uri = $false $extensionName = 'InstallModulesFromBuild' if ([string]::IsNullOrEmpty($StorageAccountName) -and [string]::IsNullOrEmpty($FileUri)) { throw 'You must specify either specify StorageAccount/Container or a FileUri' } elseif (-not [String]::IsNullOrEmpty($StorageAccountName)) { if ([String]::IsNullOrEmpty($Container)) { throw 'Storage Account selected, but no Container specified.' } else { $storageAccount = $true } } elseif (-not [string]::IsNullOrEmpty($FileUri)) { $uri = $true } Write-Verbose("Checking that a session to Azure exists.") try { [void](Get-AzureRmContext) $loggedOn = $true } catch [System.Exception] { $loggedOn = $false } if (!($loggedOn)) { Write-Verbose("No current session to Azure. Prompting for login.") $login = Login-AzureRmAccount if ($login -eq $null) { throw 'User authentication failed.' } } # Try to select the Azure Subscription try { [void](Select-AzureRmSubscription -SubscriptionId $SubscriptionId) } catch [System.Exception] { throw($_.Exception) } # Get Azure VM. $vm = $null try { $vm = Get-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $VMName -InformationAction Ignore } catch [System.Exception] { throw($_.Exception) } # Common parameters. $runParams = @{ Name = $extensionName ResourceGroupName = $vm.ResourceGroupName VMName = $vm.Name Location = $vm.Location Run = $FileName } if ($storageAccount) { Write-Verbose("Storage Account selected as medium for script file.") # If Storage Account, get the Key. try { $key = (Get-AzureRmStorageAccountKey -ResourceGroupName $ResourceGroupName -Name $StorageAccountName).Value[0] } catch [System.Exception] { throw($_.Exception) } $runParams.StorageAccountName = $StorageAccountName $runParams.StorageAccountKey = $key $runParams.Container = $Container $runParams.FileName = $FileName if ($UploadScript) { Write-Verbose("Generating install script and uploading to Storage Account: {0}" -f $StorageAccountName) # Code to generate and upload script. $blobName = $FileName $scriptPath = Join-Path $env:TEMP $blobName New-PSModuleInstallScript ` -RepositoryName $RepositoryName ` -RepositoryPath $RepositoryPath ` -Module $Name ` -OutputPath $scriptPath ` -StorageAccountName $StorageAccountName ` -StorageAccountKey $key $stgContext = New-AzureStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $key $upload = Set-AzureStorageBlobContent -File $scriptPath -Container $Container -Blob $blobName -Context $stgContext -Force # Remove files from temp and blob storage. [void](Remove-Item -Path $scriptPath -Force) } } if ($uri) { Write-Verbose("File Uri selected as medium for script file.") $runParams.FileUri = $fileUri } # Check arguments to install script. if (-not [String]::IsNullOrEmpty($Argument)) { # Argument validation. Write-Verbose("Arguments specified: {0}" -f $Argument) $runParams.Argument = $Argument } # Predefine result object, this so it can be asserted by build steps. $result = [PSCustomObject]@{ 'Status' = $null 'Message' = $null } Write-Verbose("Setting CustomScriptExtension: {0}" -f $extensionName) try { $install = Set-AzureRmVMCustomScriptExtension @runParams -ErrorAction Stop $result.Status = $install.StatusCode $result.Message = 'Modules where installed without errors.' } catch { $result.Status = 'Error' $result.Message = 'At least one Module failed installation. Please check logs on target server for more information.' } Write-Verbose("Removing CustomScriptExtension: {0}" -f $extensionName) try { $remove = Remove-AzureRmVMCustomScriptExtension -ResourceGroupName $vm.ResourceGroupName -VMName $vm.Name -Name $extensionName -Force -ErrorAction Stop } catch [System.Exception] { $result.Message = 'Modules where installed, but CustomScriptExtension could not be removed.' } if ($UploadScript) { Write-Verbose("Removing install script from Storage Account: {0}" -f $StorageAccountName) $removeBlob = Remove-AzureStorageBlob -Blob $blobName -Container $Container -Context $stgContext -Force } $result } } |