Friday, May 17, 2024
HomePowershellPowerShell Refresh • The Lonely Administrator

PowerShell Refresh • The Lonely Administrator


The opposite day on X, I used to be requested about what issues I’d setup or configure on a brand new PowerShell set up. That is one thing I even have considered and face on a regular basis after I setup a brand new demo digital machine. I had been that means to construct new tooling to satisfy this problem, and the query supplied the spark I wanted to get off my butt and get it accomplished.

Home windows PowerShell Necessities

If you’re working PowerShell 7, lots of the important objects on my record are already addressed, so my code samples at present will deal with Home windows PowerShell 5.1 working on Home windows 10 or Home windows 11. The purpose is to configure the Home windows PowerShell surroundings for interactive use and code improvement.

Out of the field, Home windows PowerShell ships with a number of options which have undergone important adjustments. I need to replace these options. The difficult factor is that as a result of they’re included with the working system, you possibly can’t carry out a easy replace. For me, I need to ensure I’ve the most recent model of the PSReadLine and Pester modules. I additionally need to use the newer Microsoft.PowerShell.PSResourceGet module which replaces the older PowerShellGet module. Nevertheless, earlier than I can set up and use PSResourceGet, I have to replace PowerShellGet.

Test to see what model you’re utilizing.

PS C:> Get-Module PowerShellGet -ListAvailable


    Listing: C:Program FilesWindowsPowerShellModules


ModuleType Model    Identify                       ExportedCommands
---------- -------    ----                       ----------------
Script     1.0.0.1    PowerShellGet              {Set up-Module, Discover-Modul...}

In case you see this, that you must replace the module. Nevertheless, you possibly can’t replace it as a result of it wasn’t put in utilizing Set up-Module. This implies you must set up as a brand new module.

Set up-Module -Identify PowerShellGet -Pressure -AllowClobber -repository PSGallery

Reply sure if prompted about updating the NuGet supplier.

Relying in your system, you would possibly want to regulate your TLS settings to speak with the PowerShell Gallery.

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

As soon as the brand new model is put in, you both have to restart PowerShell to make use of the brand new model or manually take away and re-import it.

Take away-Module PowerShellGet
Import-Module PowerShellGet
``

Subsequent, I can set up PSResourceGet module utilizing the older `Set up-Module` command.

```powershell
Set up-Module -Identify Microsoft.PowerShell.PSResourceGet -Pressure -repository PSGallery

With the brand new module, I can refresh the Pester and PSReadLine modules. These too ship with Home windows PowerShell on Home windows 10 and 11 so I would like tdo set up them as new modules.

Set up-Module -Identify Pester -TrustRepository -repository PSGallery
Set up-Module -Identify PSReadLine -TrustRepository -repository PSGallery

The final minimal PowerShell refresh step is to replace the assistance recordsdata.

Replace-Assist -Pressure

WinGet

The opposite setup job I’d need to take is so as to add the mandatory instruments to my Home windows 10 or Home windows 11 surroundings associated to PowerShell scripting. At a minimal this implies putting in:

  • git
  • VSCode
  • The GitHub CLI

The perfect answer can be to make use of a package deal supervisor to put in these instruments. You need to use no matter package deal supervisor you favor, however I’m going to make use of the Home windows Package deal Supervisor, WinGet.

I may take the simple route and set up the instruments manually, however I need to automate the method, together with putting in Winget. This will get barely sophisticated. Winget has a number of dependencies I’ve to account for. I can get them from Nuget.org however I’ll want so as to add a brand new package deal supply.

Register-PackageSource -Identify Nuget.org -ProviderName NuGet -Pressure -ForceBootstrap -Location 'https://nuget.org/api/v2'

I can use this supply to put in the primary dependency.

Set up-Package deal -Identify microsoft.ui.xaml -Supply nuget.org -Pressure

This course of doesn’t set up it like a chunk of software program. However I can get what I would like from the package deal contents. The put in file has a .nuget extension, however the file is a zipper file. I can extract the contents to a folder get the file I would like, which I can then set up utilizing Add-AppxPackage.

Copy-Merchandise -Path (Get-Package deal microsoft.ui.xaml).supply -Vacation spot $env:TEMPmicrosoft.ui.xaml.zip
Increase-Archive $env:tempmicrosoft.ui.xaml.zip -DestinationPath "$env:tempui" -Pressure
Add-AppxPackage $env:tempuitoolsappxx64releaseMicrosoft.UI.Xaml.2.8.appx

The opposite dependency I can obtain instantly and add with Add-AppxPackage.

Invoke-WebRequest -Uri'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx' -OutFile $env:tempVCLibs.appx
Add-AppxPackage $env:tempVCLibs.appx

As soon as these dependencies are put in, I can set up Winget by downloading it from Github.

#get the present launch
$uri = 'https://api.github.com/repos/microsoft/winget-cli/releases'
$get = Invoke-RestMethod -Uri $uri -Technique Get -ErrorAction cease
#get the URL to the present launch asset
$present = $get[0].property | The place-Object title -Match 'msixbundle'
#outline a path to save lots of the file
$out = Be a part of-Path -Path $env:temp -child $present.title
#obtain the file
Invoke-WebRequest -Uri $present.browser_download_url -OutFile $out
#set up the file
Add-AppxPackage -Path $out

That is a few 250MB file so in case you suppose you may be putting in it typically, you’ll want to save the file.

Packages

Now, I can set up the Winget packages. I take advantage of the package deal ID. I need the set up to be silent and utterly hands-free.

winget set up --id git.git --silent --accept-package-agreements --accept-source-agreements --source winget
winget set up --id github.cli --silent --accept-package-agreements --accept-source-agreements --source winget
winget set up --id Microsoft.VisualStudioCode --silent --accept-package-agreements --accept-source-agreements --source winget

An Automated Answer

Naturally, I need to automate this complete course of. So I wrote a PowerShell script file.

#requires -version 5.1
#requires -RunAsAdministrator

#PSRefresh.ps1

<#
Replace key PowerShell elements on a brand new Home windows 10/11 set up.

This script will not be meant for server working methods. The script
ought to be run in an interactive console session and never in a remoting session.

You'll be able to modify this script to make use of a unique package deal supervisor like Chocolatey.

In case you use the Offline possibility, ensure your file names match the script.

This script is obtainable AS-IS and with out guarantee. Use at your individual danger.
#>

#TODO: Add SupportsShouldProcess code
#TODO: Add correct error dealing with

[CmdletBinding()]
Param(
    [Parameter(Position = 0,Mandatory,HelpMessage="The path to a configuration data file")]
    [ValidateScript({ Test-Path -Path $_})]
    [ValidatePattern('.psd1$')]
    [string]$ConfigurationData,
    [Parameter(HelpMessage="Specify a location with previously installed Appx packages")]
    [ValidateScript({ Test-Path -Path $_ })]
    [string]$Offline
)

#this script ought to be run within the console, not the ISE or VSCode
if ($Host.title -ne 'ConsoleHost') {
    Write-Warning 'This script ought to be run within the PowerShell console, not the ISE or VSCode'
    return
}

#area Setup
Strive {
    $information = Import-PowerShellDataFile -Path $ConfigurationData -ErrorAction Cease
}
Catch {
    Write-Warning "Did not import $ConfigurationData"
    Return
}

#outline an inventory of winget package deal IDs
#$wingetPackages = @('Microsoft.VisualStudioCode', 'Git.Git', 'GitHub.cli', 'Microsoft.WindowsTerminal')
$wingetPackages = $information.wingetPackages
$PSModules = $information.PSModules
$Scope = $information.Scope
$vscExtensions = $information.vscExtensions

#set up winget apps and extra PowerShell modules by way of background jobs
$jobs = @()

$installParams = @{
    Scope        = $Scope
    Repository   = 'PSGallery'
    Pressure        = $true
    AllowClobber = $true
    Identify         = $null
}

$progParams = @{
    Exercise = $MyInvocation.MyCommand
    Standing="Initializing"
    CurrentOperation = 'Bootstrapping a NuGet supplier replace'
    PercentComplete = 1
}

#set TLS simply in case
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

Write-Progress @progParams

#Bootstrap Nuget supplier replace  to keep away from interactive prompts
[void](Set up-PackageProvider -Identify Nuget -ForceBootstrap -Pressure)

#endregion

#area Replace PowerShellGet
$progParams.Standing = "Module updates"
$progParams.CurrentOperation = "Updating PowerShellGet"
$progParams.PercentComplete = 10
Write-Progress @progParams

$get = Get-Module PowerShellGet -ListAvailable | Choose-Object -First 1
if ($get.Model.main -eq 1) {
    #Write-Host 'Putting in the most recent model of PowerShellGet' -ForegroundColor Yellow
    $installParams.Identify="PowerShellGet"
    Set up-Module @installParams
}
else {
    #Write-Host 'Updating PowerShellGet' -ForegroundColor Yellow
    Replace-Module -Identify PowerShellGet -Pressure
}

#reload PowerShellGet
Take away-Module PowerShellGet
Import-Module PowerShellGet

#endregion

#area Set up Microsoft.PowerShell.PSResourceGet
$progParams.Standing = "Module updates"
$progParams.CurrentOperation = "Microsoft.PowerShell.PSResourceGet"
$progParams.PercentComplete = 20
Write-Progress @progParams
#Write-Host 'Putting in Microsoft.PowerShell.PSResourceGet' -ForegroundColor Yellow
$installParams.Identify="Microsoft.PowerShell.PSResourceGet"
Set up-Module @installParams

Import-Module Microsoft.PowerShell.PSResourceGet

#endregion

#area Set up up to date Modules

$progParams.Standing = "Module updates"
$progParams.CurrentOperation = "PSReadLine"
$progParams.PercentComplete = 25
Write-Progress @progParams

#Write-Host 'Putting in PSReadLine' -ForegroundColor Yellow
Set up-PSResource -Identify PSReadLine -Scope $Scope -Repository PSGallery -TrustRepository

$progParams.Standing = "Module updates"
$progParams.CurrentOperation = "Pester - You may even see a warning."
$progParams.PercentComplete = 30
Write-Progress @progParams

#Write-Host 'Putting in Pester. You would possibly see a warning.' -ForegroundColor Yellow
Set up-PSResource -Identify Pester -Scope $Scope -Repository PSGallery -TrustRepository

#endregion

#area set up winget dependencies

$progParams.Standing = "Putting in Winget"
$progParams.CurrentOperation = "Processing dependencies"
$progParams.PercentComplete = 40
Write-Progress @progParams
#Write-Host 'Including Nuget.org as a package deal supply' -ForegroundColor Yellow
[void](Register-PackageSource -Identify Nuget.org -ProviderName NuGet -Pressure -ForceBootstrap -Location 'https://nuget.org/api/v2')

#Write-Host 'Putting in winget dependencies' -ForegroundColor Yellow

if ($Offline) {
    Add-AppxPackage "$Offlinemicrosoft.ui.xaml.2.8.appx"
    Add-AppxPackage "$OfflineVCLibs.appx"
}
else {
    [void](Set up-Package deal -Identify microsoft.ui.xaml -Supply nuget.org -Pressure)
    Copy-Merchandise -Path (Get-Package deal microsoft.ui.xaml).supply -Vacation spot $env:TEMPmicrosoft.ui.xaml.zip
    Increase-Archive $env:tempmicrosoft.ui.xaml.zip -DestinationPath "$env:tempui" -Pressure
    Add-AppxPackage $env:tempuitoolsappxx64releaseMicrosoft.UI.Xaml.2.8.appx
    $uri = 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx'
    Invoke-WebRequest -Uri $uri -OutFile $env:tempVCLibs.appx
    Add-AppxPackage $env:tempVCLibs.appx
}

#endregion

#area Set up winget

#Write-Host 'Putting in winget' -ForegroundColor Yellow
$progParams.Standing = "Putting in Winget"
$progParams.CurrentOperation = "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
$progParams.PercentComplete = 50
Write-Progress @progParams
if ($Offline) {
    Add-AppxPackage "$OfflineMicrosoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
}
else {
    #Winget is a 246MB obtain
    $uri = 'https://api.github.com/repos/microsoft/winget-cli/releases'
    $get = Invoke-RestMethod -Uri $uri -Technique Get -ErrorAction cease
    $present = $get[0].property | The place-Object title -Match 'msixbundle'

    #Write-Host "Downloading $($present.title)" -ForegroundColor Yellow
    $out = Be a part of-Path -Path $env:temp -child $present.title
    Strive {
        Invoke-WebRequest -Uri $present.browser_download_url -OutFile $out -ErrorAction Cease
        Add-AppxPackage -Path $out
    }
    Catch {
        $_
    }
}

#endregion

#area Set up winget packages
$progParams.Standing = "Putting in packages by way of winget"
$pct = 50

foreach ($package deal in $wingetPackages) {
    $progParams.CurrentOperation = $package deal
    $progParams.PercentComplete = $pct+=2
    Write-Progress @progParams
    $jobs+= Begin-Job -Identify $package deal -ScriptBlock {
        #This script doesn't take scope under consideration for Winget installations.
        #You would possibly need to change that.
        Param($package deal)
        winget set up --id $package deal --silent --accept-package-agreements --accept-source-agreements --source winget
    } -ArgumentList $package deal

    #Write-Host "Putting in $package deal" -ForegroundColor Yellow
    #winget set up --id $package deal --silent --accept-package-agreements --accept-source-agreements --source winget
} #foreach package deal

#endregion

#area set up extra PowerShell modules

if ($PSModules) {
    $progParams.Standing = "Putting in extra PowerShell modules"
    foreach ($Mod in $PSModules) {
        $progParams.CurrentOperation = $Mod
        $progParams.PercentComplete = $pct+=2
        Write-Progress @progParams
        $jobs+= Begin-Job -Identify $Mod -ScriptBlock {
            #This script doesn't take scope under consideration for Winget installations.
            #You would possibly need to change that.
            Param($ModuleName,$scope)
            Import-Module Microsoft.PowerShell.PSResourceGet
            Set up-PSResource -Identify $ModuleName -Scope $Scope -Repository PSGallery -AcceptLicense -TrustRepository -Quiet
        } -ArgumentList $Mod,$Scope
    } #foreach module
}

#endregion

#area set up VSCode extensions

if ($vscExtensions) {

    $progParams.Standing = "Putting in VSCode extensions"
    $progParams.PercentComplete = $pct+=2
    foreach ($Extension in $vscExtensions) {
        $progParams.CurrentOperation = $Extension
        Write-Progress @progParams
        $jobs+= Begin-Job -Identify $Extension -ScriptBlock {
        Param($Identify)
        &"$HOMEAppDataLocalProgramsMicrosoft VS Codebincode.cmd" --install-extension $Identify --force
        } -ArgumentList $Extension
    }
}

#endregion

#area Replace assist

$progParams.Standing = "Updating Assist. Some errors are to be anticipated."
$progParams.CurrentOperation = "Replace-Assist -Pressure"
$progParams.PercentComplete = $pct+=5
Write-Progress @progParams
#Write-Host 'Updating PowerShell assist. Some errors are to be anticipated.' -ForegroundColor Yellow
Replace-Assist -Pressure

#endregion

#area Watch for finish
$progParams.Standing = "Ready for $($jobs.depend) background jobs to finish"
$progParams.CurrentOperation = "Wait-Job"
$progParams.PercentComplete = $pct+=2
Write-Progress @progParams
$jobs | Wait-Job | Choose-Object Identify,State

Write-Progress -Exercise $progParams.Exercise -Accomplished -Standing "All duties accomplished." -PercentComplete 100

$msg = @'

Refresh is full. You may also need to set up the next modules utilizing Set up-PSResource:

    Microsoft.Winget.Shopper
    Microsoft.PowerShell.SecretStore
    Microsoft.PowerShell.SecretManagement
    Platyps

And the next packages by way of Winget:

    Microsoft.PowerToys
    Microsoft.PowerShell

You will want to configure VSCode along with your most popular extensions and settings or configure
it to synch your saved settings.

Please restart your PowerShell session.

'@

Write-Host $msg -ForegroundColor Inexperienced

#endregion

#EOF

The script makes use of Write-Progress, however I’ve left the unique Write-Host instructions within the script commented out.

Within the preliminary variations of the script, I included extra PowerShell modules and packages instantly within the code. Some objects, like putting in Pester and PSReadLine are particular instances so I don’t have drawback with that code. However all the pieces else may be moved to a configuration information file. It is a good instance of the idea of separating the info that you must use from the code itself.

#PSRefresh.psd1
#PSModules ought to be new modules to put in
#Scope may be 'CurrentUser' or 'AllUsers'
#vscExtensions are Visible Studio Code extensions to put in
@{
    wingetPackages = @(
        'Microsoft.VisualStudioCode',
        'Git.Git',
        'GitHub.cli',
        'Microsoft.WindowsTerminal',
        'github.githubdesktop',
        'Microsoft.PowerShell'
        )
    PSModules = @("PSScriptTools","PSProjectStatus","PSStyle","Platyps")
    vscExtensions = @(
        'github.copilot',
        'github.copilot-chat',
        'github.remotehub',
        'ms-vscode.powershell',
        'davidanson.vscode-markdownlint',
        'inu1255.easy-snippet',
        'gruntfuggly.todo-tree'
    )
    Scope="AllUsers"
    Model = '1.2.0'
}

My script may even set up VSCode extensions. I included this extra as an indication than something. Usually, I synchronize my VSCode settings throughout installations, however it’s potential I’m establishing a brand new demo surroundings the place I’m not going to login so this may very well be helpful.

The configuration file is obligatory.

c:scriptspsrefresh.ps1 -configurationdata c:scriptspsrefresh.psd1

If I determine to alter modules or packages, all I’ve to do is edit the configuration file. I don’t have to fret about messing up my code with revisions.

The opposite function in my script is the power to put in the Appx packages offline. The Winget dependencies don’t change typically so I can save the recordsdata that I’ve downloaded. Likewise, I can periodically obtain the most recent Winget launch for future installations. The packages ought to go in the identical folder. Be sure you use the identical file names as within the script.

To make use of, all I have to do is specify the trail to the saved recordsdata.

c:scriptspsrefresh.ps1 -configurationdata c:scriptspsrefresh.psd1 -offline d:saved

Background Jobs

The opposite function of the script to notice is the usage of background jobs. As soon as the core steps are out of the way in which, putting in extra modules and packages can occur in parallel. The best means in Home windows PowerShell is to run every exercise in a background job.

foreach ($Mod in $PSModules) {
    $progParams.CurrentOperation = $Mod
    $progParams.PercentComplete = $pct+=2
    Write-Progress @progParams
    $jobs+= Begin-Job -Identify $Mod -ScriptBlock {
        Param($ModuleName,$scope)
        Import-Module Microsoft.PowerShell.PSResourceGet
        Set up-PSResource -Identify $ModuleName -Scope $Scope -Repository PSGallery -AcceptLicense -TrustRepository -Quiet
    } -ArgumentList $Mod,$Scope
} #foreach module

The roles run whereas PowerShell is up to date. The script waits for all jobs to finish earlier than displaying a completion message.

```powershell
$jobs | Wait-Job | Choose-Object Identify,State
$msg = @'

Refresh is full. You may also need to set up the next modules utilizing Set up-PSResource:

    Microsoft.Winget.Shopper
    Microsoft.PowerShell.SecretStore
    Microsoft.PowerShell.SecretManagement
    Platyps

And the next packages by way of Winget:

    Microsoft.PowerToys
    Microsoft.PowerShell

You will want to configure VSCode along with your most popular extensions and settings or configure
it to synch your saved settings.

Please restart your PowerShell session.

'@

Write-Host $msg -ForegroundColor Inexperienced

Your Flip

You might be welcome to provide my code a attempt. I’ve posted the recordsdata as a GitHub gist, which I’ll attempt to hold up to date. The recordsdata are supplied AS-IS for academic functions as a lot as something. The script will not be 100% production-ready and lacks error dealing with. And naturally, the additional modules and packages are issues that I’d need to set up it. I’d love to listen to what you suppose.



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments