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 😎

About CVE-2021-40444

Microsoft has recently published a security bulletin about the Microsoft MSHTML Remote Code Execution Vulnerability https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-40444
Others urge anybody to apply the only workaround against this 0day because it has been publicly disclosed.

How would you do that by Group Policy on a domain joined device.

#Requires -RunasAdministrator
#Requires -Modules ActiveDirectory,GroupPolicy
[CmdletBinding()]
Param()
Begin{}
Process {
# Make sure we can reach the PDC
$PDC = (Get-ADDomainController Service 1 Discover ErrorAction SilentlyContinue).Hostname
if ($PDC) {
# Get the domain name
$DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name
# Create the GPO
try {
$GPO = New-GPO Name 'Workaround for CVE-2021-40444' Domain "$($DomainName)" ErrorAction Stop
} catch {
Write-Warning Message "Failed to create GPO because $($_.Exception.Message)"
}
if ($GPO) {
# Don't need user settings
$GPO.GpoStatus = [Microsoft.GroupPolicy.GpoStatus]::UserSettingsDisabled
$HT = @{ GUID = ($GPO).Id ; ErrorAction = 'Stop' }
# Zones
0..3 |
ForEach-Object {
$ZoneId = $_
1, # URLACTION_DOWNLOAD_SIGNED_ACTIVEX (0x1001)
4 | # URLACTION_DOWNLOAD_UNSIGNED_ACTIVEX (0x1004)
ForEach-Object {
$Value = $_
$reg = @{
Key = 'HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\{0}' -f $ZoneId
ValueName = '100{0}' -f $Value
Type = 'DWORD'
Value = 3
}
try {
Set-GPRegistryValue @HT @reg
} catch {
Write-Warning Message "Faile to set GPO setting because $($_.Exception.Message)"
}
}
}
}
}
}
End{}

If you look in the GPMC snap-in it looks like this:

Next steps are:
– you may need to add a filter if you want
– you may want to change the permissions, delegation…if required in your environement
– you need to link it on a OU or at another level so that the GPO actually applies to vulnerable computers
– wait for Microsoft. They will release a patch as soon as it’s ready

Direct Access tunnel stuck

When you’ve Direct Access configured on Windows 10 using IPHTTPS, your Tunnel adapter Microsoft IP-HTTPS Platform Interface starts to get an IPv6 address when the network stack connection state changes and all pre-requisites are met.

I had actually a very strange issue where my tunnel still had an Ipv6 no matter what the connection state changes. I mean that I could still see its Ipv6 set even after a reboot 😦

This was totally unexpected and I didn’t know what to do to force the computer to release this Ipv6, start over and take into account normal connection state changes.

Gpupdate.exe isn’t available because the tunnel was down, of course.

I used the following process to recover the normal functioning state of the tunnel.

I found the inspiration on this blog post. It’s 10 years old and applied to Windows 7. In my case, I have a Windows 10 and I nonetheless decided to give it a try. Thanks to Ronny and anyone else involved in providing support and help to fix this issue.

I added HKLM\Software\Policies\Microsoft\Windows NT\DNSClient\EnableDAForAllNetworks set to 0x2 and did a restart.
After the restart, the tunnel was recovered. It started to act normally and was mounted. Corporate resources were unavailable but that was expected.

I set it back to 0 (didn’t delete it) and did a restart. The tunnel mounted normally and corporate resources were available.

Happy Days 😎

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'
}