EvergreenModules.ps1

<#PSScriptInfo
 
.VERSION 2101.4
 
.GUID 0f309416-1337-43d0-93dd-f44988136fe8
 
.AUTHOR Arjan Mensch
 
.COMPANYNAME IT-WorXX
 
.TAGS Modules Evergreen Automation
 
.LICENSEURI https://github.com/msfreaks/EvergreenModules/blob/main/LICENSE
 
.PROJECTURI https://github.com/msfreaks/EvergreenModules
 
#>
 

<#
.SYNOPSIS
 Script to automatically update your installed PowerShell modules.
 
.DESCRIPTION
 Script to automatically update your installed PowerShell modules.
 Optionally keep old versions or process Preview versions.
 
.PARAMETER Include
 Optionally scopes the update process to one or more module names.
 If more than one module name need to be included, pass in an array of strings.
 Supports '*' wildcard.
  
.PARAMETER Exclude
 Optionally provide one or more module names to exclude from the update process.
 If more than one module name need to be excluded, pass in an array of strings.
 Supports '*' wildcard.
 
.PARAMETER IncludePreview
 Preview versions for modules are not processed by default.
 Use this switch parameter to include preview versions in the update process.
 This checks for modules that have '-preview' in their Version attributes.
 
.PARAMETER KeepVersions
 If used this switch ensures the script will not uninstall older versions if a new version is found and installed.
 
.PARAMETER ReportOnly
 If used this switch ensures no modules are updated but available updates are reported.
 
.EXAMPLE
 .\EvergreenModules.ps1
 Updates all installed PowerShell modules (excluding those that have '-preview' in their Version attribute).
 
.EXAMPLE
 .\EvergreenModules.ps1 -IncludePreview
 Updates all installed PowerShell modules, including those that have '-preview' in their Version attribute.
 
.EXAMPLE
 .\EvergreenModules.ps1 -ReportOnly
 Reports all available updates for all installed PowerShell modules, excluding modules that have '-preview' in their Version attribute.
 
.EXAMPLE
 .\EvergreenModules.ps1 -Include @('Az', 'Microsoft*') -Exclude 'Microsoft.Graph.Intune'
 Only updates the 'Az' module and modules starting with 'Microsoft', except the module 'Microsoft.Graph.Intune'
 
.LINK
 https://github.com/msfreaks/EvergreenModules
 https://msfreaks.wordpress.com
 
#>


#Requires -Modules @{ ModuleName="PowerShellGet"; ModuleVersion="2.2.4.1" }
#Requires -Version 5.1
#Requires -RunAsAdministrator

[CmdletBinding(SupportsShouldProcess)]
param(
    [Parameter(Mandatory = $false, Position = 0)]
    [String[]] $Include = @(),
    [Parameter(Mandatory = $false, Position = 1)]
    [String[]] $Exclude = @(),
    [Parameter(Mandatory = $false, Position = 2)]
    [switch] $IncludePreview,
    [Parameter(Mandatory = $false, Position = 3)]
    [switch] $KeepVersions,
    [Parameter(Mandatory = $false, Position = 4)]
    [switch] $ReportOnly
)

Write-Verbose -Message ('Include: {0}' -f ($Include -join ','))
Write-Verbose -Message ('Exclude: {0}' -f ($Exclude -join ','))
Write-Verbose -Message ('IncludePreview: {0}' -f $IncludePreview)
Write-Verbose -Message ('KeepVersions: {0}' -f $KeepVersions)
Write-Verbose -Message ('ReportOnly: {0}' -f $ReportOnly)

#region Functions

function Update-PowerShellModule {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory = $true)]
        [PSCustomObject[]] $Module,
        [Parameter(Mandatory = $true)]
        [PSCustomObject] $ModuleOnline,
        [switch] $KeepVersions,
        [switch] $ReportOnly
    )

    Write-Verbose -Message ('Module version: {0}' -f $Module[0].Version)
    Write-Verbose -Message ('Online version: {0}' -f $ModuleOnline.Version)
    Write-Verbose -Message ('KeepVersions: {0}' -f $KeepVersions)
    Write-Verbose -Message ('ReportOnly: {0}' -f $ReportOnly)

    $isPreview = $null
    if ($Module[0].Version -like '*-preview') { $isPreview = ' (preview)' }

    if ($Module[0].Version -eq $ModuleOnline.Version) {

        Write-Output -InputObject ('{0}{1}: Newest version already installed ({2}).' -f $Module[0].Name, $isPreview, $Module[0].Version)
    } else {
        Write-Output -InputObject ('{0}{1}: Newer version available: {2} (Current version: {3}).' -f $Module[0].Name, $isPreview, $ModuleOnline.Version, $Module[0].Version)
        if (-not $ReportOnly) {
            Write-Output -InputObject ('{0}{1}: Updating to version {2}' -f $Module[0].Name, $isPreview, $ModuleOnline.Version)
            try {
                if ($isPreview) {
                    if ($PSCmdlet.ShouldProcess(('{0}{1}' -f $Module[0].Name, $isPreview), ('Updating to {0}' -f $ModuleOnline.Version))) {
                        Update-Module -Name $Module[0].Name -AllowPrerelease -WhatIf:$WhatIfPreference
                    }
                } else {
                    if ($PSCmdlet.ShouldProcess($Module[0].Name, ('Updating to {0}' -f $ModuleOnline.Version))) {
                        Update-Module -Name $Module[0].Name -WhatIf:$WhatIfPreference
                    }
                }
            }
            catch {
                Write-Warning -Message ('{0}: Failed to update.' -f $Module[0].Name)
                throw $_
                break
            }
        }
    }
    if (-not $ReportOnly -and -not $KeepVersions) {
        $Module | Where-Object { $_.Version -ne $ModuleOnline.Version } | ForEach-Object {
            $current = $_
            Write-Output -InputObject ('{0}: Uninstalling version {1}' -f $current.Name, $current.Version)
            try {
                if ($PSCmdlet.ShouldProcess($Module[0].Name, ('Uninstalling {0}' -f $current.Version))) {
                    $current | Uninstall-Module -WhatIf:$WhatIfPreference
                }
            }
            catch {
                Write-Warning -Message ('{0}: Failed to uninstall {1}.' -f $current.Name, $current.Version)
                throw $_
            }
        }
    }
}
#endregion

# build module array
$modules = @()

# process includes
$Include | ForEach-Object { 
    $current = $_
    Write-Verbose -Message ('Including "{0}"' -f $current)
    if ($current -match '\*') {
        Get-InstalledModule | Where-Object { $_.Name -like $current } | ForEach-Object { $modules += $_ }
    } else {
        $modules += Get-InstalledModule -Name $current
    }
}
if (-not $modules) { $modules = Get-InstalledModule }

# process excludes
$Exclude | ForEach-Object {
    $current = $_
    Write-Verbose -Message ('Excluding "{0}"' -f $current)
    $modules = $modules | Where-Object { $_.Name -notlike $current }
}

# process module array
($modules | Sort-Object Name) | ForEach-Object {
    $current = $_
    $module  = Get-InstalledModule -Name $current.Name -AllVersions | Where-Object { $_.Version -notlike '*-preview' } | Sort-Object -Property @{ Expression = { [System.Version]$_.Version }; Descending = $true }
    if ($module) {
        $moduleOnline  = Find-Module -Name $current.Name

        Update-PowerShellModule -Module $module -ModuleOnline $moduleOnline -KeepVersions:$KeepVersions -ReportOnly:$ReportOnly
    }

    if ($IncludePreview) {
        $preview = Get-InstalledModule -Name $current.Name -AllVersions -AllowPrerelease | Where-Object { $_.Version -like '*-preview' } | Sort-Object -Property @{ Expression = { [System.Version]($_.Version.Replace('-preview', '')) }; Descending = $true }
        if ($preview) { 
            $previewOnline = Find-Module -Name $current.Name -AllowPrerelease

            Update-PowerShellModule -Module $preview -ModuleOnline $previewOnline -KeepVersions:$KeepVersions -ReportOnly:$ReportOnly
        }
    }
}