Install Windows 11 in a Hyper-V VM

Context

I wanted to test the new Windows 11 released on Hyper-V

The readiness script reported that both storage and TPM are missing 😥

Here’s what I did to get a PASS on all the hardware prerequisites on the VM:

~\Downloads\HardwareReadiness.ps1|Out-String | ConvertFrom-Json

Solution

$VMName = 'MyVMName'

# 1. Add Secure boot
Get-VM -Name $VMName | 
Set-VMFirmware -EnableSecureBoot:On -SecureBootTemplate 'MicrosoftWindows'

# 2. Add a TPM
Set-VMKeyProtector -VMName $VMName -NewLocalKeyProtector
Enable-VMTPM -VMName $VMName

# 3. Resize VHDX
Get-VMHardDiskDrive -VMName $VMName |
Select -First 1 -ExpandProperty Path | Get-VHD | 
Resize-VHD -SizeBytes 65GB

Inside the VM, I had to extend the C: drive

# Remove the offending 5th partition
$null = Remove-Partition -DiskNumber 0 -PartitionNumber 5 -Confirm:$false

# Resize C: to the maximum
Resize-Partition -DiskNumber 0 -PartitionNumber 4 -Size (Get-PartitionSupportedSize -DiskNumber 0 -PartitionNumber 4).SizeMax

A return code of 0 means the VM is Windows 11 capable 😎

How to create a VSS snapshot

I recently needed to quickly create a volume shadow copy (VSS) snapshot on Windows 10.
Using Get-CimInstance -ClassName Win32_ShadowCopy returned nothing.
Let’s inspect what methods are available:

Get-CimClass -ClassName Win32_ShadowCopy|
Select -ExpandProperty CimClassMethods

Can we get more info about the parameters of the Create method?

Ok, it requires an Id as input and will return a ShadowID.

The Create method is documented here. In my case, I didn’t use a Context parameter on Windows 10 and it’s not complaining when it’s missing.

Invoke-CimMethod -ClassName Win32_ShadowCopy `
-MethodName Create -Arguments @{Volume='C:\' }

The return value equals 0 means it’s a success 🙂

Erratum on news and interests on the Windows taskbar

I’ve published a wrong information about the registry key in the following blog post. The path is incorrect and missing a ‘Windows’. Sorry, my bad 😦

Here’s how to remove the wrong path

# pick the appropriate GPO
$gpo = Get-GPO -All | Out-GridView -OutputMode Single

if ($gpo) { 
Remove-GPRegistryValue -Guid $gpo.Id -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Feeds' -ValueName 'EnableFeeds' 
}

Here’s how to add the correct path 🙂

# pick the appropriate GPO
$gpo = Get-GPO -All | Out-GridView -OutputMode Single

if ($gpo) { 
Set-GPRegistryValue -Guid $gpo.Id -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Windows Feeds' -ValueName 'EnableFeeds' -Type DWord -Value 0
}

Find the Parent path of an AD object

I recently needed to find the parent location of a computer, it’s parent distinguishedName. I wanted a function that would:
– work with a broader scope of AD objects, like users, OUs,…
– support piping objects from regular ActiveDirectory module or from itself
Here’s what I wanted to achieve visually

# Pipe an ADUser object
Get-ADUser 'Skywalker' | Get-ADObjectParentPath

# Pipe a DN string (you'd get the 'Users' OU)
'CN=Luc Skywalker,OU=Users,OU=Prod,DC=Star,DC=Wars,DC=com' | Get-ADObjectParentPath

# Do it twice, (you'd get the 'Prod' OU)
'CN=Luc Skywalker,OU=Users,OU=Prod,DC=Star,DC=Wars,DC=com' |
Get-ADObjectParentPath | Get-ADObjectParentPath


Here’s what I quickly wrote to meet my needs.

Function Get-ADObjectParentPath {
<#
.SYNOPSIS
Get the parent DN location of an AD object.
.DESCRIPTION
Get the parent DN location of an AD object.
.PARAMETER DistinguishedName
DistinghedNames objects passed as input (from AD cmdlets).
.PARAMETER DN
DistinghedNames strings passed as input
.EXAMPLE
Get-ADUser Skywalker | Get-ADObjectParentPath
.EXAMPLE
'CN=Luc Skywalker,OU=Users,OU=Prod,DC=Star,DC=Wars,DC=com' | Get-ADObjectParentPath
#>
[CmdletBinding(DefaultParameterSetName = 'String')]
Param(
[Parameter(ParameterSetName = 'Obj', Mandatory,ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
$DistinguishedName,
[Parameter(ParameterSetName = 'String', Mandatory,ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string[]]$DN
)
Begin {
if (-not(Test-Path Path 'AD:\' PathType Container)) {
Write-Warning Message 'Failed to find the AD drive, aborting'
break
}
}
Process {
if ($DistinguishedName) {
$DistinguishedName |
ForEach-Object Process {
$parent = ((Get-Item "AD:\$($_)" ErrorAction SilentlyContinue).PSParentPath | Split-Path NoQualifier ) -replace '//RootDSE/',''
if ($parent) {
$parent
}
}
}
if ($DN ) {
$DN |
ForEach-Object Process {
if (Test-Path Path "AD:\$($_)" ErrorAction SilentlyContinue) {
$parent = ((Get-Item "AD:\$($_)" ErrorAction SilentlyContinue).PSParentPath | Split-Path NoQualifier ) -replace '//RootDSE/',''
if ($parent) {
$parent
}
} else {
Write-Warning Message "Path AD:\$($_) not found"
}
}
}
}
End {}
}

Quick post: Remove a permission on a GPO

There’s no cmdlet named Remove-GPPermission. There’s only Get- GPPermission and Set- GPPermission. How do I remove a GPO permission?

# Choose a single GPO
$gpo = Get-GPO -All | Out-GridView -OutputMode Single
# Choose a single target
$target = Get-GPPermission -Guid $gpo.Id -All | Out-GridView -OutputMode Single
# Remove target from that GPO
Set-GPPermission -Guid $gpo.Id -PermissionLevel None -Replace -TargetName "$($target.Trustee.Name)" -TargetType "$($target.Trustee.SidType)"

The answer is to replace the existing permission with a level of “None”. This is equivalent of a removal.

About news and interests on the Windows taskbar

Context

Microsoft is about to add a new feature “News and interests on the taskbar” to Windows 10.

“Devices running Windows 10, version 1909 (and later) who have installed the May 2021 Windows monthly update (or later) will be included in this phased rollout.”

While it may be welcome in the consumer space, it could be the opposite in a corporate environment.

Problem

They did not publish up-to-date ADMX templates and don’t tell you in their blog post what’s required in the registry to achieve the same as in the Endpoint Manager agent (a.k.a Intune), especially if you just rely on good old robust Group Policies for your fully managed devices.

Solution

Fortunately Michael Niehaus published the following two posts on this subject and addresses the above shortcoming.

  1. https://oofhours.com/2021/02/07/turn-off-the-insider-news-interests-page/
  2. https://oofhours.com/2021/04/22/turn-off-news-interests-page-via-policy/

That said, you don’t really need to grab the ADMX files from an Insider build or wait for Microsoft to publish the required ADMX templates in any relevant Windows build.

Knowing the registry key and values is what matters.

If you’ve the GroupPolicy module and an existing GPO, you can already populate the registry with these 2 lines of code


# pick the appropriate GPO
$gpo = Get-GPO -All | Out-GridView -OutputMode Single

if ($gpo) { 

Set-GPRegistryValue -Guid $gpo.Id -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Feeds' -ValueName 'EnableFeeds' -Type DWord -Value 0

}

Ready for the next brilliant move 😎

Last but not least, if you want to grab the policy templates, Glenn Turner indicated that you can get Feeds.admx and Feeds.adml files by installing the LCU preview KB5001396.

Quick post: Remove an alternate data stream (ADS)

  • Context

I’ve been downloading updates (.msu) files from the Windows Update catalog
As an example, for KB5001330 I go to https://www.catalog.update.microsoft.com/Search.aspx?q=5001330 and click Download

dir ~/downloads/other/*.msu |
gi -Stream * |
Select Stream,Length

  • Problem

I can remove the Zone.Identifier alternate data stream (ADS) using the built-in Unblock-File cmdlet.

dir ~/downloads/other/*.msu |
Unblock-File -Verbose

dir ~/downloads/other/*.msu |
gi -Stream *|
Select Stream,Length

But, I doesn’t remove the SmartScreen ADS.

  • Solution

Here’s how to remove the SmartScreen alternate data stream (ADS):

dir ~/downloads/other/*.msu |
gi -Stream 'SmartScreen' -EA 0|
Foreach-Object {
Remove-Item -Path $_.FileName -Stream 'SmartScreen'
}

Windows Defender Attack Surface Reduction (ASR) Rules module

I’m pleased to share with you a new #PowerShell module about Windows Defender Attack Surface Reduction (ASR) Rules 🚀

After seeing what Palantir did about their ASR telemetry and the content of the following repository, I thought that we need a more “PowerShell-friendly” way to view and set Windows Defender Attack Surface Reduction (ASR) rules.

It seems that I’m not the first person to have this idea and you can find an another implementation of this idea on the PowerShell gallery here.

If you don’t know anything about Windows Defender Attack Surface Reduction, I’d recommend that you watch this 6 minutes long video Susan Bradley made

My approach is slightly different than the 2 other PowerShell code implementations/repositories I mentioned above. I don’t provide any graphical interface (GUI), although you can for sure use the built-in Out-GridView cmdlet to send the output and inspect it in a GUI. I propose to have 3 functions that you can use to bind properly using the pipeline and made an effort to have all the parameters data being discoverable using the TAB key.

Let’s see some practical examples in a video:

I’d like to point out another useful resource to test the defense measures and configuration.

What else?
If you encounter an issue with this module, you’re welcome to open an issue in the github repo with this link.

Last but not least. Let’s say you’ve configured some ASR rules using GPO but not all of them. The Get-ASRRuleConfig function is able to display the effective rules that apply (GPO or local and if GPO, GPO wins over local). But the Set-ASRRuleConfig is only able to set the local rules. It cannot touch GPO rules. If you use it to set a rule that is already managed by GPO, it will work on the local value only. If you then use back Get-ASRRuleConfig, you get the results of what’s effective. Remember GPO wins.

How do I get started?

Find-Module -Name ASRRules -Repository PSGallery
Save-Module -Name ASRRules -Repository PSGallery -Path ~/Downloads

Import-Module ~/Downloads/ASRRules/1.0.0/ASRRules.psd1 -Force -Verbose

# if in PS 7.x and there's a complaint about the required module,
Import-Module -Name ConfigDefender -Force
Import-Module ~/Downloads/ASRRules/1.0.0/ASRRules.psd1 -Force -Verbose

Enjoy 😎

Quick post: Delete a WSUS update

  • Context

I started working on the KB4577586, the update responsible for the removal of Adobe Flash Player.

I’ve encountered two issues.

  • Issue

First issue, I imported the update for Windows 10 2OH2 x64 using the Id of Server 2019, D’oh!

Second issue, when I first imported my files and their Id, it failed with this message:
Exception calling “ImportUpdateFromCatalogSite” with “2” argument(s): “The underlying connection was closed: An unexpected error occurred on a send.”

Solution

Let’s start with the 2nd issue: the fact that I cannot import an update.
It appears that although I’ve set the correct Protocol to use

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

The ImportUpdateFromCatalogSite method is a .Net method and .NET requires to StrongCrypto value to be set in the registry.

My 2nd issue was solved by

$HT = @{
 Name = 'SchUseStrongCrypto'
 Value = '1'
 Type = 'DWord'
}
$null,'Wow6432Node' | 
Foreach-Object {
 Set-ItemProperty @HT -Path "HKLM:\SOFTWARE$($_)\Microsoft\.NetFramework\v4.0.30319" 
}
Restart-Computer

Let’s get back to my first issue. I got the correct file but not the Id.

# Identify the offending update (no Out-GridView available)
(Get-WsusServer).SearchUpdates('4577586') | Select Title
# Find its Id
(Get-WsusServer).SearchUpdates('4577586') | Select-Object -First 3 | 
Select -Last 1 -ExpandProperty Id
# Detete (no output if ok)
(Get-WsusServer).DeleteUpdate('20bd2d6b-26a9-4ddd-8a3f-04a79b683c1f')

I could reimport the same file using the correct Id, happy days 😎