linux/powershell/setupPowerShell.ps1 (149 lines of code) (raw):
# This script is run at image build time to install and configure the PowerShell modules that are preinstalled with Cloud Shell
param(
[Parameter(Mandatory = $True, Position = 0)]
[ValidateSet("Base", "Top")]
[System.String]
$Image
)
$ProgressPreference = 'SilentlyContinue' # Suppresses progress, which doesn't render correctly in docker
# PowerShellGallery PROD site
$prodGallery = 'https://www.powershellgallery.com/api/v2'
$script:pscloudshellBlob = $null # Version folder for the pscloudshell blob storage
$shareModulePath = ([System.Management.Automation.Platform]::SelectProductNameForDirectory('SHARED_MODULES'))
$modulePath = if ($shareModulePath) {$shareModulePath}else {Microsoft.PowerShell.Management\Join-Path $PSHOME 'Modules'}
$script:dockerfileDataObject = $null # json object holding data from dockerfile.data.json file
# In almost all cases, Cloud Shell pulls modules from the regular PowerShell Gallery or preview gallery at build time.
# In a few legacy cases, we include modules which are not intended for broader use. These are pulled from an Azure storage
# account using the code below.
# PSCloudShell depends on files under Azure blob storage. The name of 'folder' (Azure container) is the version number.
# Read the version info from the Dockerfile.Data.json. In such way, only the Dockerfile.Data.json to be updated if there is
# any version changes.
function Get-DockerfileData {
$dockerFileData = Microsoft.PowerShell.Management\Join-Path $PSScriptRoot -ChildPath 'Dockerfile.Data.json'
Write-Output "Calling Get-Content from $dockerFileData"
$script:dockerfileDataObject = Microsoft.PowerShell.Management\Get-Content $dockerFileData | Microsoft.PowerShell.Utility\ConvertFrom-Json
if (-not $script:dockerfileDataObject) {
throw "Error while reading $dockerFileData file."
}
$pscloudshellVer = $script:dockerfileDataObject.PSCloudShellVersion
Write-Output "pscloudshellVersion= $pscloudshellVer;"
}
# Install Azure and AzureAD (Active Directory) modules
# This function replaces the old poshtestgallery issue
function Install-AzAndAzAdModules {
Write-Output "Install-AzAndAdModules.."
mkdir temp
curl -o az-cmdlets.tar.gz -sSL "https://azpspackage.blob.core.windows.net/release/Az-Cmdlets-latest.tar.gz"
tar -xf az-cmdlets.tar.gz -C temp
rm az-cmdlets.tar.gz
cd temp
cp /usr/cloudshell/powershell/pkgs/azuread.standard.preview.0.0.0.10.nupkg ./AzureAD.Standard.Preview.nupkg
$SourceLocation = $PSScriptRoot
Write-Output "Source Location: $SourceLocation"
$gallery = [guid]::NewGuid().ToString()
Write-Output "Registering temporary repository $gallery with InstallationPolicy Trusted..."
Register-PSRepository -Name $gallery -SourceLocation $($pwd.providerPath) -PackageManagementProvider NuGet -InstallationPolicy Trusted
try {
Write-Output "Installing Az..."
Install-Module -Name Az -Repository $gallery -Scope AllUsers -AllowClobber -Force
Write-Output "Installing AzureAD.Standard.Preview..."
Install-Module -Name "AzureAD.Standard.Preview" -Repository $gallery -Scope AllUsers -AllowClobber -Force
}
finally {
Write-Output "Unregistering gallery $gallery..."
Unregister-PSRepository -Name $gallery
}
cd ..
rm -rf temp
}
# Download files from the PSCloudShell Azure storage blob
function Install-PSCloudShellFile {
param(
[string]$Source,
[string]$FileName,
[string]$Destination,
[string]$FileHash
)
$FullPath = Microsoft.PowerShell.Management\Join-Path -Path $Source -ChildPath $FileName
Write-Output "URL= $script:pscloudshellBlob/$FileName; FullPath= $FullPath; Destination=$Destination"
Microsoft.PowerShell.Utility\Invoke-WebRequest -Uri "$script:pscloudshellBlob/$FileName" -UseBasicParsing -OutFile $FullPath
$hash = (Microsoft.PowerShell.Utility\Get-FileHash $FullPath).Hash
if ($hash -eq $FileHash) {
Microsoft.PowerShell.Archive\Expand-Archive -Path $FullPath -DestinationPath $Destination -Verbose -Force
}
else {
throw "Hash mismatch for $FullPath. Expected: $FileHash Actual:$hash."
}
}
try {
# Get the pscloudshell version info and Az version info from from the ..\..\Windows\Dockerfile.Data.json
Get-DockerfileData
# Set up repo as trusted to avoid prompts
PowerShellGet\Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
$prodAllUsers = @{Repository = "PSGallery"; Scope = "AllUsers"}
if ($image -eq "Base") {
Write-Output "Installing modules from production gallery"
PowerShellGet\Install-Module -Name SHiPS @prodAllUsers
PowerShellGet\Install-Module -Name SQLServer -MaximumVersion $script:dockerfileDataObject.SQLServerModuleMaxVersion @prodAllUsers
PowerShellGet\Install-Module -Name MicrosoftPowerBIMgmt -MaximumVersion $script:dockerfileDataObject.PowerBIMaxVersion @prodAllUsers
PowerShellGet\Install-Module -Name MicrosoftTeams @prodAllUsers
# MS Graph packages
PowerShellGet\Install-Module -Name Microsoft.Graph.Authentication @prodAllUsers
PowerShellGet\Install-Module -Name Microsoft.Graph.Users.Actions @prodAllUsers
PowerShellGet\Install-Module -Name Microsoft.Graph.Users.Functions @prodAllUsers
PowerShellGet\Install-Module -Name Microsoft.Graph.Groups @prodAllUsers
PowerShellGet\Install-Module -Name Microsoft.Graph.Identity.DirectoryManagement @prodAllUsers
PowerShellGet\Install-Module -Name Microsoft.Graph.Identity.Governance @prodAllUsers
PowerShellGet\Install-Module -Name Microsoft.Graph.Identity.SignIns @prodAllUsers
PowerShellGet\Install-Module -Name Microsoft.Graph.Applications @prodAllUsers
}
else {
# Installing modules from Azure Powershell and AzureAD
Write-Output "Installing modules from Azure Powershell and AzureAD"
Install-AzAndAzAdModules
# Install modules from PSGallery
Write-Output "Installing modules from production gallery"
PowerShellGet\Install-Module -Name AzurePSDrive @prodAllUsers
PowerShellGet\Install-Module -Name GuestConfiguration -MaximumVersion $script:dockerfileDataObject.GuestConfigurationMaxVersion -ErrorAction SilentlyContinue @prodAllUsers
PowerShellGet\Install-Module -Force PSReadLine @prodAllUsers
PowerShellGet\Install-Module -Name Az.Tools.Predictor @prodAllUsers
PowerShellGet\Install-Module -Name ExchangeOnlineManagement @prodAllUsers
PowerShellGet\Install-Module -Name Microsoft.PowerShell.SecretManagement @prodAllUsers
PowerShellGet\Install-Module -Name Microsoft.PowerShell.SecretStore @prodAllUsers
# With older base image builds, teams 1.1.6 is already installed
if (Get-Module MicrosoftTeams -ListAvailable) {
# For some odd reason, Update-Module was creating the MicrosoftTeams module twice with different version numbers.
# Uninstalling and then installing it again was the only way to keep it as one module.
Uninstall-Module MicrosoftTeams -Force
PowerShellGet\Install-Module -Name MicrosoftTeams @prodAllUsers
} else {
PowerShellGet\Install-Module -Name MicrosoftTeams @prodAllUsers
}
# Install PSCloudShell modules
$tempDirectory = Microsoft.PowerShell.Management\Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName())
$null = Microsoft.PowerShell.Management\New-Item -ItemType Directory $tempDirectory -ErrorAction SilentlyContinue
if (Microsoft.PowerShell.Management\Test-Path $tempDirectory) {
Write-Output ('Temp Directory: {0}' -f $tempDirectory)
}
# Copy the startup script to the all-users profile
$psStartupScript = Microsoft.PowerShell.Management\Join-Path $PSHOME 'PSCloudShellStartup.ps1'
Microsoft.PowerShell.Management\Copy-Item -Path $PSScriptRoot\PSCloudShellStartup.ps1 -Destination $psStartupScript
Write-Output "Installing powershell profile to $($PROFILE.AllUsersAllHosts)"
Microsoft.PowerShell.Management\Copy-Item -Path $psStartupScript -Destination $PROFILE.AllUsersAllHosts -Verbose
Write-Output "Installed powershell profile."
# Update PowerShell Core help files in the image, ensure any errors that result in help not being updated does not interfere with the build process
# We want the image to have latest help files when shipped.
Write-Output "Updating help files."
$null = Microsoft.PowerShell.Core\Update-Help -Scope AllUsers -Force -ErrorAction Ignore
Write-Output "Updated."
}
Write-Output "All modules installed:"
Write-Output (Get-InstalledModule | Sort-Object Name | Select-Object Name, Version, Repository)
}
finally {
# Clean-up the PowerShell Gallery registration settings
PowerShellGet\Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted -ErrorAction Ignore
if ($tempDirectory -and (Microsoft.PowerShell.Management\Test-Path $tempDirectory)) {
Microsoft.PowerShell.Management\Remove-Item $tempDirectory -Force -Recurse -ErrorAction Ignore
}
}