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.