Reinstall preinstalled (appx) Apps

  • Context

I’ve discovered recently this link to reinstall-preinstalledApps.zip that was provided on this page

  • Solution

The code is straightforward and probably does the job in most situations.
But, there are some assumptions about the version, admin rights,.. and the code has no error handling. You can see the file here.
I’ve decided to improve it by adding a help, a #require statement, error handling and the ability to use the -WhatIf parameter.

If you use my “slightly improved” version, you can do:

# Show what would be done using the -WhatIf parameter
Start-ReInstallAppx -Filter "*Microsoft.WindowsStore*" -WhatIf

# Do it:
Start-ReInstallAppx -Filter "*Microsoft.WindowsStore*" -Verbose

#Requires -RunAsAdministrator
Function Start-ReInstallAppx {
<#
.SYNOPSIS
Get back all the apps that come default with Windows 10
.DESCRIPTION
Get back all the apps that come default with Windows 10
.EXAMPLE
Start-ReInstallAppx -Filter "*Microsoft.WindowsStore*"
.NOTES
http://go.microsoft.com/fwlink/?LinkId=619547
Adapted from
http://download.microsoft.com/download/5/F/0/5F04003A-035E-4A0F-9662-43E32C546F6C/reinstall-preinstalledApps.zip
#>
[CmdletBinding(SupportsShouldProcess)]
Param(
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$Filter
)
Begin {
$HT = @{ ErrorAction = 'Stop'}
}
Process {
try {
# Get all the provisioned packages
$Packages = Get-Item -Path 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\Applications' @HT |
Get-ChildItem @HT
} catch {
Write-Warning -Message "Failed to get packages list from the registry or filesystem because $($_.Exception.Message)"
}
if ($Packages) {
if ($Filter) {
$Packages = $Packages | Where-Object {$_.Name -like "$($Filter)" }
}
$Packages |
ForEach-Object -Process {
$p = $_
$PackageName = $PackagePath = $null
# get package name & path
try {
$d = $p | Get-ItemProperty @HT
if ($d) {
$PackageName = $d | Select-Object -ExpandProperty PSChildName
$PackagePath = [System.Environment]::ExpandEnvironmentVariables(($d| Select-Object -ExpandProperty Path))
}
} catch {
Write-Warning -Message "Failed to get package name and path for package $($p) because $($_.Exception.Message)"
}
# register the package
if ($PackageName -and $PackagePath) {
Write-Verbose -Message "Attempting to register package: $($PackageName)"
if ($PSCmdlet.ShouldProcess("$($PackageName))",'Register Appx package')) {
try {
Add-AppxPackage -Register $PackagePath -DisableDevelopmentMode @HT
Write-Verbose -Message "Successfully registered package from path $($PackagePath)"
} catch {
Write-Warning -Message "Failed to regiter from $($PackagePath) because $($_.Exception.Message)"
}
}
}
}
}
}
End {}
}

#PowerShell at #Microsoft #MSIgnite

The Microsoft Ignite conference took place this year on https://myignite.microsoft.com/home

Here’s some content related to #PowerShell, what else ! (NB: it’s not the only content in this year’s event):

  • Taking your automation to the next level with #PowerShell 7

Please allow me to add a quick note about the presentation and this slide:

WinRM and Remoting have been released in PowerShell 2.0 with the RTM release of Windows 7 and 2008 R2.
It means Remoting is available since August 2009.
PowerShell 3.0 was released in 2012 and shipped in Windows 8 and Server 2012.

What Joey Aiello meant on the slide is that Remoting is enabled by default since Windows Server 2012, released in September 2012.

Source: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_requirements

Windows Server 2012 and newer releases of Windows Server are enabled for PowerShell remoting by default.

Remoting is the killer feature that made me adopt PowerShell in 2009, it’s so cool because it enables remote management of anything in a secure manner 😎
Remoting is on and rocks since 11 years.

Please let me also add another thought about this presentation.
Windows PowerShell is part of the WMF, a.k.a. the Windows Management Framework that adds these components:

So, my question is: what are the investments made into the above components of the WMF since Microsoft open-sourced PowerShell?

  • PowerShell Unplugged – Challenge Edition

What a mind-blowing kickboxing match of new features 😉 !

History of previous feature updates

  • Context

I’ve discovered that the history of previous versions you had before running a feature update is stored in the registry under HKLM\System\Setup

  • Solution

I wrote a function to read the information under this key:

Function Get-PreviousUpgrade {
<#
.SYNOPSIS
Get info about previous feature updates
.DESCRIPTION
Get info about previous feature updates being used to upgrade Windows 10 to a new branch.
.EXAMPLE
Get-PreviousUpgrade
#>
[CmdletBinding()]
Param()
Begin {}
Process {}
End {
try {
$names = (Get-Item -Path 'HKLM:\SYSTEM\Setup' -ErrorAction Stop).GetSubKeyNames() |
Where-Object { $_ -match '^Source\sOS\s\(Updated\son\s' }
} catch {
Write-Warning -Message "Failed to read the registry key because $($_.Exception.Message)"
}
if ($names) {
$names |
ForEach-Object {
$date = ([regex]'^Source\sOS\s\(Updated\son\s(?<Date>.+)\)').Matches($_) |
Select-Object -ExpandProperty Groups |
Select-Object -Last 1 -ExpandProperty Value
$FromBranch = (Get-ItemProperty -Path (Join-Path -Path 'HKLM:\SYSTEM\Setup' -ChildPath $_) -Name 'ReleaseId').'ReleaseId'
[PSCustomObject]@{
Date = [datetime]$date
Branch = $FromBranch
}
}
} else {
Write-Warning -Message 'No previous upgrades found'
}
}
}

Here’s how to use it 🙂

Get-PreviousUpgrade | 
Where  { $_.Branch -eq '1809' } | 
Select -Property Date

Friday fun: Windows Feature Update progress

  • Context

I started recently to launch some feature updates of Windows 10 in a scheduled task running as ‘NT AUTHORIY\SYSTEM’ account. It also means that the UI is hidden for whoever is logged on the computer.

I was also looking for some additional info about:
– how to detect that a feature is running
– how to detect that a feature has just finished running
– how to detect that a reboot is pending because of the feature update that just ran

  • Solution

I started investigating using procmon.exe and tracked what’s written under HKLM\System\setup.

I noticed that I can track the progress of a feature update by using a one-liner:

While (ps setup* ) { Write-Progress -Activity Upgrade -PercentComplete "$((gp HKLM:\SYSTEM\Setup\MoSetup\Volatile -Name SetupProgress).SetupProgress)" -Status "at $((gp HKLM:\SYSTEM\Setup\MoSetup\Volatile -Name SetupProgress).SetupProgress)%" }

It appears that a SetupProgress value is written under the registry key HKLM:\SYSTEM\Setup\MoSetup\Volatile
Here’s a more readable splatted version of the one-liner:

While (Get-Process -Name setup*) {
 $k = 'HKLM:\SYSTEM\Setup\MoSetup\Volatile'
 $n = 'SetupProgress'
 $p = Get-ItemProperty -Path $k -Name $n
 $HT = @{
  Activity =  'Upgrade'
  PercentComplete = "$($($p).$n)"
  Status = "at $($($p).$n)%"
 }
 Write-Progress @HT
}