Tips: Make Get-WinEvent cmdlet perform quicker

Although September was a busy month, I’d like to share a nice tips that makes Get-WinEvent cmdlet perform quicker.

First, start with the FilterHashTable parameter. It’s easier to write.

$HT = @{
 LogName = 'System';
 ProviderName = 'Service Control Manager' ;
 Id = 7040 ;
 Data = 'Windows Modules Installer' ;
 StartTime = (Get-Date).AddDays(-365) ;
Get-WinEvent -FilterHashtable $HT -MaxEvents 1

Now, capture the XML query from the Verbose stream.
To achieve that, I just add the Verbose switch to the previous command

Get-WinEvent -FilterHashtable $HT -Verbose -MaxEvents 1


I copy/paste the XML query into a here-string and use it as input for the FilterXml parameter like this:

Get-WinEvent -FilterXml @'
 <Query Id="0" Path="system">
  <Select Path="system">*
  [System/Provider[@Name='service control manager'] and
  (System/TimeCreated[@SystemTime&gt;='2014-09-06T10:20:22.000Z']) and 
  (EventData/Data='Windows Modules Installer') and
'@ -Verbose -MaxEvents 1

As you can see, when you use the FilterXml parameter, there isn’t any overhead where the hashtable is first converted to a XML query.

The result is that the FilterXml will perform faster than the FilterHashTable parameter


… and you don’t have to figure out how to write the XML query 😀

Max memory for remote shell

The other day I was investigating a “locked” workflow .

The network tab of procexp.exe revealed what remote computer was locking it.
It was actually a Windows 7 computer that had the following 2 events in its log.



To “unlock” the workflow, I just stopped the WinRM service on the remote computer, waited for the workflow to end correctly and finally restart the WinRM service on that remote machine.

c:\windows\system32\sc.exe \\RemoteComputerName stop WinRM

What happened on this remote computer that caused the workflow to stop and wait for it?

It appeared that the code running on the remote computer was the culprit.
The code was supposed to get the total count of a specific event. What makes this computer so special was that it had 45469 of these events and the remote shell on this Windows 7 computer just hit the default maximum memory assigned.

After fixing winrm.cmd to work in my environment, I was able to do:

C:\winrm.cmd get winrm/config/winrs -machine:RemoteComputerName -format:#text
c:\winrm.cmd set winrm/config/winrs @{MaxMemoryPerShellMB="512"} -machine:RemoteComputerName

I’ve increased the default Maximum Memory for a remote shell from 150MB to 512MB


BPUG / August 28, 2015 / meeting summary and presentation materials

I went to the Basel PowerShell User Group that my fellow PowerShell MVP, Stéphane Van Gulick (@Stephanevg) organised.

I gave a presentation about Nano Server 😀

The presentation was a quick introduction directly inspired by this 20 minutes talk from Jeffrey Snover with Jeremy Chapman at Ignite early May 2015:



I gave also a quick demo about installing a Nano Server into a virtual machine through WDS (Windows Deployment Services). It’s directly inspired from the following article published in PowerShell Magazine, except that the code was “adapted” to work with the recently published Windows Server 2016 TP3.

Amanda Debler (@texmandie) who gave a preview of her presentation about Skype for Business for the 2nd Summit in Europe, took a nice picture during my demo 😀


And when you provide the correct credentials and authenticate on the Nano box, you’ll see

Bonus: here are 4 essentials links about NanoServer you should bookmark:

And… here is the code on github:

if (-not(Get-VMSwitch -SwitchType Internal -ErrorAction SilentlyContinue| Where Name -eq 'Internal-Test' )) {
# Create an internal switch on Hyper-V
($VMswitch = New-VMSwitch -Name "Internal-Test" -SwitchType Internal)
# Set a static IP address on Hyper-V switch
Get-NetAdapter |
Where Name -eq "vEthernet ($($VMswitch.Name))" |
Where InterfaceDescription -match "Hyper-V\sVirtual\sEthernet Adapter" |
New-NetIPAddress -IPAddress -PrefixLength 24
} else {
Write-Information -Msg "Got an internal VM switch named Internal-Test" -InfA 2
Get-NetAdapter |
Where Name -eq "vEthernet (Internal-Test)" |
Where InterfaceDescription -match "Hyper-V\sVirtual\sEthernet Adapter" |
Get-NetIPAddress -AddressFamily IPv4
# Set the path to ISO
$iso = $ExecutionContext.SessionState.Path.
# Integrity check of ISO file
if (
(Get-FileHash -Path $ISO -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash -eq
Write-Information -Msg "Got the correct Technical Preview 3 iso file" -InfA 2
} else {
Write-Warning -Message "Don't have the correct ISO of the Technical Preview 3"
# Mount ISO
Mount-DiskImage -ImagePath $iso -StorageType ISO -Access ReadOnly -PassThru
$dl = (Get-DiskImage -ImagePath $iso | Get-Volume).DriveLetter
# Dot source the 2 scripts in the NanoServer folder
if (Test-Path -Path "$($dl):\NanoServer\*.ps1") {
# Dot-sourcing functions inside a script
. "$($dl):\NanoServer\Convert-WindowsImage.ps1"
# Fix hard-coded path in script
(Get-Content -Path "$($dl):\NanoServer\new-nanoserverimage.ps1" -ReadCount 1 -ErrorAction Stop) -replace
[regex]::Escape('. .\Convert-WindowsImage.ps1'),"" |
Set-Content -Path ~/Downloads/new-nanoserverimage.ps1 -Encoding UTF8 -ErrorAction Stop
# Load the modified version
. ~/Downloads/new-nanoserverimage.ps1
# Define VM Name
$VM = "DC1-test"
# Set parent VHD
$ServerVHD = (Join-Path -Path ((Get-VMHost).VirtualHardDiskPath) -ChildPath "$VM.vhd")
# Create parent VHD
# Convert the WIM file to a VHD using the loaded Convert-WindowsImage function
if (-not(Test-Path -Path $ServerVHD -PathType Leaf)) {
Convert-WindowsImage -Sourcepath "$($dl):\sources\install.wim" `
-VHD $ServerVHD `
-VHDformat VHD -Edition "Windows Server 2012 R2 SERVERSTANDARD" `
-VHDPartitionStyle MBR -Verbose:$true
Write-Information -Msg "Created parent VHD: size = $('{0:N2} GB' -f ((Get-item $ServerVHD).Length/1GB))" -InfA 2
# Create child VHD
$cvp = (Join-Path -Path ((Get-VMHost).VirtualHardDiskPath) -ChildPath "$VM-child.vhd")
$childVHD = New-VHD -Path $cvp -ParentPath $ServerVHD -Differencing
# Create a VM Gen 1
New-VM -Name $VM -MemoryStartupBytes 2048MB -NoVHD -SwitchName Internal-Test -Generation 1
# Attach disk
Get-VM $VM | Add-VMHardDiskDrive -Path $childVHD.Path
# Increase processor count for DC
Get-VM $VM | Set-VMProcessor -Count 2
# Mount the VHD
$cm = Mount-VHD -Path $childVHD.Path -Passthru
$cml = (Get-Disk $cm.DiskNumber | Get-Partition | Where DriveLetter | Select -First 1).DriveLetter
# Prepare a Nano VHD with the new script
$bdir = Join-Path (Split-Path $iso -Parent) -ChildPath "Base"
if (-not(Test-Path -Path $bdir -PathType Container)) {
mkdir $bdir
$admincred = Get-Credential -Message 'Admin password of your Nano image' -UserName 'Administrator'
if (Test-Path -Path "$bdir\Target") {
Get-Item -Path "$bdir\Target" | Remove-Item -Verbose
$nnHT = @{
ComputerName = 'Nano-PXE' ;
MediaPath = "$($dl):\" ;
BasePath = $bdir ; # The location for the copy of the source media
TargetPath = "$bdir\Target" ; # The location of the final, modified image
Language = 'en-US' ; # The language locale of the packages
GuestDrivers = $true ; # Add the Guest Drivers package (enables integration of Nano Server with Hyper-V when running as a guest).
EnableIPDisplayOnBoot = $true ; # Configures the image to show the output of 'ipconfig' on every boot
AdministratorPassword = $admincred.Password ;
New-NanoServerImage @nnHT
gcm New-NanoServerImage -Syntax
# Syntax
New-NanoServerImage [[-MediaPath] <String>] [-BasePath] <String> [-TargetPath] <String> [[-ExistingVHDPath]
<String>] [[-Language] <String>] [-Storage] [-Compute] [-Defender] [-Clustering] [-OEMDrivers] [-GuestDrivers]
[-ReverseForwarders] [[-ExtraPackages] <String[]>] [-ForAzure] [[-ComputerName] <String>] [-AdministratorPassword]
<SecureString> [[-DomainName] <String>] [[-DomainBlobPath] <String>] [-ReuseDomainNode] [[-DriversPath] <String>]
[-EnableIPDisplayOnBoot] [[-DebugMethod] <String>] [-EnableEMS] [[-EMSPort] <Int32>] [[-EMSBaudRate] <Int32>]
[-EnableRemoteManagementPort] [<CommonParameters>]
# New in TP3 version:
# -Defender
# -ForAzure
# Setupcomplete.cmd file
$s = @'
@echo off
:: Define a static IP for the DC
netsh int ip set address name="Ethernet" source=static address= gateway=
:: Configure the DNS client
netsh dns set dnsservers name="Ethernet" source=static address= validate=no
mkdir "$($cml):\Windows\Setup\Scripts"
$s | Out-File -FilePath "$($cml):\Windows\Setup\Scripts\setupcomplete.cmd" -Encoding ASCII
# Unattend.xml
$unattendDC1 = @'
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="; xmlns:xsi=""&gt;
<RegisteredOwner>Tuva user</RegisteredOwner>
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="; xmlns:xsi=""&gt;
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="; xmlns:xsi=""&gt;
<component name="Microsoft-Windows-DNS-Client" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="; xmlns:xsi=""&gt;
<DomainName wcm:action="add" wcm:keyValue="1"></DomainName>
<component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="; xmlns:xsi=""&gt;
<cpi:offlineImage cpi:source="wim:c:/iso/sources/install.wim#Windows Server 2012 R2 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
$unattendDC1 | Out-File -FilePath "$($cml):\Unattend.xml" -Encoding UTF8
# Get required DSC resource
if (-not (Get-Module -ListAvailable -Name xActiveDirectory)) {
Find-Module -Name xActiveDirectory -Repository PSGallery | Install-Module -Verbose
# Define environment
$ConfigData = @{
AllNodes = @(
NodeName = 'localhost';
PSDscAllowPlainTextPassword = $true;
RequiredFeatures = @(
@{ Name = 'DHCP'}
@{ Name = 'DNS'}
@{ Name = 'WDS'}
@{ Name = 'RSAT-DHCP'}
@{ Name = 'RSAT-DNS-Server'}
@{ Name = 'WDS-AdminPack'}
DCAdminPassword = New-Object pscredential -ArgumentList 'nanorocks\administrator',
(ConvertTo-SecureString -String 'P@ssw0rd' -Force -AsPlainText)
SafeAdminPassword = New-Object pscredential -ArgumentList 'Password Only',
(ConvertTo-SecureString -String 'Azerty@123' -Force -AsPlainText)
# DSC config
Configuration DCConfig {
Import-DscResource -ModuleName xActiveDirectory
Node localhost {
LocalConfigurationManager {
RebootNodeIfNeeded = $true;
WindowsFeature ADDS {
Name = 'AD-Domain-Services';
Ensure = 'Present';
foreach ($f in $Node.RequiredFeatures)
WindowsFeature $f.Name
Name = $f.Name ;
Ensure = 'Present';
xADDomain DSDC1 {
DomainName = 'nanorocks.local';
DomainAdministratorCredential = $Node.DCAdminPassword
SafemodeAdministratorPassword = $Node.SafeAdminPassword
DependsOn = '[WindowsFeature]ADDS';
# Compile config into MOF file
if (-not(Test-Path -Path ~/Documents/DSC) ){ mkdir ~/Documents/DSC }
DCConfig -outputPath ~/Documents/DSC -ConfigurationData $ConfigData
# Copy DSC resource
$cHT = @{
Path = 'C:\Program Files\WindowsPowerShell\Modules\xActiveDirectory';
Destination = "$($cml):\Program Files\WindowsPowerShell\Modules\xActiveDirectory"
Copy-Item @cHT -Recurse -Force
# Copy DSC config
Copy-Item -Path ~/documents/DSC/*.mof -Destination "$($cml):\Users\Public\Documents"
# Copy original boot image from ISO
Copy-Item -Path "$($dl):\Sources\boot.wim" -Destination "$($cml):\Users\Public\Documents"
# Copy prepared Nano.vhd
Copy-Item -Path "$bdir\Target\*.VHD" -Destination "$($cml):\Users\Public\Documents"
# Unmount ISO file
Get-DiskImage -ImagePath $iso | Dismount-DiskImage
# Unmount VHD
Dismount-VHD -Path $childVHD.Path
Start-Vm -VMName $vm
# Missing in Unattend.xml > skip Product Key > fixed with by entering a product key ending with 46XCB from Internet :\
# Missing in Unattend.xml > skip EULA > fixed with OOBE/HideEULAPage in OOBE phase
# DCPromo over PowerShell Direct
Invoke-Command -VMName $VM -Credential (Get-Credential 'test.local\administrator') -ScriptBlock {
Set-DscLocalConfigurationManager C:\Users\Public\Documents
Start-DscConfiguration C:\Users\Public\Documents -Verbose -Wait
# Post-install
Invoke-Command -VMName $VM -Credential (Get-Credential 'nanorocks\administrator') -ScriptBlock {
# DHCP configuration
# Authorize
if (-not(Get-DhcpServerInDC | Where DnsName -eq "$($env:computername).$($env:USERDNSDOMAIN)")) {
} else {
# Scope
Add-DhcpServerv4Scope -StartRange -EndRange -Name "Nano scope" -State Active -SubnetMask
# Activate: done with add-dhcpserverv4scope -state param
mkdir C:\RemoteInstall
wdsutil /verbose /progress /initialize-server /RemInst:c:\RemoteInstall # /Authorize
wdsutil /start-server
wdsutil /verbose /progress /set-server /AnswerClients:ALL
Import-WdsBootImage -Path C:\Users\Public\Documents\boot.wim
dir C:\Users\Public\Documents\*.vhd | Import-WdsInstallImage
# Create test VM Generation 1 and add legacy network card for PXE boot
$testVM = 'Nano-test-pxe'
New-VHD -Path (Join-Path -Path ((Get-VMHost).VirtualHardDiskPath) -ChildPath "$($testVM).vhdx") -Dynamic -SizeBytes 127GB
New-VM -VMName $testVM -Generation 1 -MemoryStartupBytes 1024MB -NoVHD -SwitchName Internal-test |
Get-VM -VMName $testVM |
Add-VMNetworkAdapter -IsLegacy:$true -SwitchName 'Internal-test'
Get-VM -VMName $testVM |
Add-VMHardDiskDrive -Path (Join-Path -Path ((Get-VMHost).VirtualHardDiskPath) -ChildPath "$($testVM).vhdx")
Start-VM -VMName $testVM
view raw Nano-PXE-TP3.ps1 hosted with ❤ by GitHub