Tools PowerShell script -WhatIf safe

Install-Prerequisites.ps1

Installs or updates the PowerShell modules required by the scripts in this repository.

Overview#

Checks whether the Microsoft Graph PowerShell SDK authentication module (Microsoft.Graph.Authentication) and the Exchange Online Management module (ExchangeOnlineManagement) are installed, installs anything that is missing from the PowerShell Gallery, and optionally updates existing installs to the latest release. With -IncludeDevTools it also installs PSScriptAnalyzer, which the repository's CI workflow uses.

Nothing is installed without your confirmation unless you pass -Confirm:$false, and -WhatIf shows the plan without changing anything.

Safety: Makes changes only when you pass an explicit switch; supports -WhatIf and -Confirm. Run Get-Help .\Install-Prerequisites.ps1 -Full for the complete help text.

Parameters#

ParameterWhat it does
-ScopeInstallation scope passed to Install-Module. CurrentUser (default) needs no elevation; AllUsers requires an elevated session.
-UpdateUpdate modules that are already installed to the newest version available in the PowerShell Gallery.
-IncludeDevToolsAlso install PSScriptAnalyzer for local linting.

Examples#

PowerShell
PS> .\Install-Prerequisites.ps1

Installs any missing module for the current user.

PowerShell
PS> .\Install-Prerequisites.ps1 -Update -IncludeDevTools -Verbose

Installs or updates every module, including PSScriptAnalyzer, and shows what happens along the way.

PowerShell
PS> .\Install-Prerequisites.ps1 -WhatIf

Shows which modules would be installed or updated without doing it.

Permissions, modules and notes#

Author : Omer Eltayeb Blog : https://www.oeltayeb.com GitHub : https://github.com/omer-eltayeb Version : 1.0.0 Requires : PowerShell 5.1 or 7.x, internet access to https://www.powershellgallery.com Permissions : None in Microsoft 365; local admin only when -Scope AllUsers is used Notes : On Windows PowerShell 5.1 make sure TLS 1.2 is enabled for the Gallery (the script sets it for the current session). If PowerShellGet is very old, run 'Install-Module PowerShellGet -Force' first and restart the console.

Full source#

PowerShell · Install-Prerequisites.ps1
<#
.SYNOPSIS
    Installs or updates the PowerShell modules required by the scripts in this repository.
.DESCRIPTION
    Checks whether the Microsoft Graph PowerShell SDK authentication module (Microsoft.Graph.Authentication)
    and the Exchange Online Management module (ExchangeOnlineManagement) are installed, installs anything
    that is missing from the PowerShell Gallery, and optionally updates existing installs to the latest
    release. With -IncludeDevTools it also installs PSScriptAnalyzer, which the repository's CI workflow uses.

    Nothing is installed without your confirmation unless you pass -Confirm:$false, and -WhatIf shows the
    plan without changing anything.
.PARAMETER Scope
    Installation scope passed to Install-Module. CurrentUser (default) needs no elevation; AllUsers requires
    an elevated session.
.PARAMETER Update
    Update modules that are already installed to the newest version available in the PowerShell Gallery.
.PARAMETER IncludeDevTools
    Also install PSScriptAnalyzer for local linting.
.EXAMPLE
    PS> .\Install-Prerequisites.ps1
    Installs any missing module for the current user.
.EXAMPLE
    PS> .\Install-Prerequisites.ps1 -Update -IncludeDevTools -Verbose
    Installs or updates every module, including PSScriptAnalyzer, and shows what happens along the way.
.EXAMPLE
    PS> .\Install-Prerequisites.ps1 -WhatIf
    Shows which modules would be installed or updated without doing it.
.NOTES
    Author      : Omer Eltayeb
    Blog        : https://www.oeltayeb.com
    GitHub      : https://github.com/omer-eltayeb
    Version     : 1.0.0
    Requires    : PowerShell 5.1 or 7.x, internet access to https://www.powershellgallery.com
    Permissions : None in Microsoft 365; local admin only when -Scope AllUsers is used
    Notes       : On Windows PowerShell 5.1 make sure TLS 1.2 is enabled for the Gallery (the script sets it for
                  the current session). If PowerShellGet is very old, run
                  'Install-Module PowerShellGet -Force' first and restart the console.
.LINK
    https://learn.microsoft.com/powershell/microsoftgraph/installation
.LINK
    https://learn.microsoft.com/powershell/exchange/exchange-online-powershell-v2
#>
#Requires -Version 5.1

[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
param(
    [Parameter()]
    [ValidateSet('CurrentUser', 'AllUsers')]
    [string]$Scope = 'CurrentUser',

    [Parameter()]
    [switch]$Update,

    [Parameter()]
    [switch]$IncludeDevTools
)

$ErrorActionPreference = 'Stop'

#region Main
$requiredModules = @(
    [PSCustomObject]@{ Name = 'Microsoft.Graph.Authentication'; Purpose = 'Microsoft Graph sign-in and Invoke-MgGraphRequest' }
    [PSCustomObject]@{ Name = 'ExchangeOnlineManagement';       Purpose = 'Exchange Online and Security & Compliance PowerShell' }
)
if ($IncludeDevTools) {
    $requiredModules += [PSCustomObject]@{ Name = 'PSScriptAnalyzer'; Purpose = 'Local linting (same rules as the CI workflow)' }
}

# Windows PowerShell 5.1 defaults to older TLS versions that the PowerShell Gallery no longer accepts.
if ($PSVersionTable.PSVersion.Major -lt 6) {
    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12
}

$results = foreach ($module in $requiredModules) {
    $installed = Get-Module -Name $module.Name -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1
    $action = 'None'
    $versionAfter = $null

    try {
        if ($null -eq $installed) {
            if ($PSCmdlet.ShouldProcess($module.Name, "Install module ($Scope)")) {
                Write-Verbose "Installing $($module.Name) ..."
                Install-Module -Name $module.Name -Scope $Scope -Repository PSGallery -Force -AllowClobber
                $action = 'Installed'
            }
        }
        elseif ($Update) {
            $latest = Find-Module -Name $module.Name -Repository PSGallery
            if ($latest.Version -gt $installed.Version) {
                if ($PSCmdlet.ShouldProcess($module.Name, "Update $($installed.Version) -> $($latest.Version)")) {
                    Write-Verbose "Updating $($module.Name) from $($installed.Version) to $($latest.Version) ..."
                    Install-Module -Name $module.Name -Scope $Scope -Repository PSGallery -Force -AllowClobber
                    $action = 'Updated'
                }
            }
            else {
                $action = 'UpToDate'
            }
        }
        else {
            $action = 'AlreadyInstalled'
        }
    }
    catch {
        Write-Warning "Could not process $($module.Name): $($_.Exception.Message)"
        $action = 'Failed'
    }

    $current = Get-Module -Name $module.Name -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1
    if ($null -ne $current) { $versionAfter = $current.Version.ToString() }

    [PSCustomObject]@{
        Module  = $module.Name
        Purpose = $module.Purpose
        Action  = $action
        Version = $versionAfter
    }
}

Write-Host ''
Write-Host 'Prerequisite check' -ForegroundColor Cyan
$results | Format-Table -AutoSize | Out-String | Write-Host
if (@($results | Where-Object { $_.Action -eq 'Failed' -or $null -eq $_.Version }).Count -gt 0) {
    Write-Warning 'At least one module is missing. Re-run the script in an elevated session or check your Gallery connectivity.'
}
#endregion Main

Scripts are provided as-is under the MIT licence. Review the permissions a script requests, test in a non-production tenant, and use -WhatIf before letting any script change anything.

Found a bug or have an improvement? Open an issue on GitHub or email me.