Thats My Shell

Pester 5.0 Released

Some sysadmins tinker with PowerShell scripts and write code that gets a task done. Other sysadmins write highly involved and complex scripts that are full of conditional testing. However, there is not really a lot of administrators that write their code with testing in mind. That is where Pester comes in.

I admit, I am not in the category of people that use Pester. But I should be. I have written very large scripts comprising of multiple module files. Pester will allow a sysadmin or syseng to make changes to the underlying code and if the pester file successfully processes then we know that any code changes did not break any functionality of the overall script.

With that said, check out the new release of Pester 5.0. https://github.com/pester/Pester/releases/tag/5.0.0

Properly Sealing a VDI Image

I have been doing VMware Horizon View for a long time.  I am going to share the skeleton of a script that I have been using during that time to seal an image.  Some VDI admins will call the process ‘sealing an image’ while other’s will call it ‘cleaning up’.  Regardless of what an administrator will call the process, it needs to be a required process to have in place to keep the environment running as smoothly as possible.

My script began its life as a simple batch file to release the images network information. It grew over time to a robust and convoluted PowerShell script to account for every possibility. Recently I stripped the script down to a pure sealing task. My advice to most VDI administrators is to let the image be pure windows with the required optimizations and let the published desktop use GPO or DEM (Dynamic Environment Manager) enforce lock down policies.

Even if you do not use the script that I am providing.  Here are some things that you might want to consider having in  yours.

  • Network Clean Up
  • Disk usage and cleanup.
  • Windows Event Cleanup

Let’s examine the various tasks in detail.   The network cleanup removes any cached ARP tables or DNS information that might cause problems during the provisioning process of a desktop pool.  I would also recommend using these as part of the a shutdown script to help DHCP reclaim the IP Address assignment lease. Network cleanup should happen at the end of the script.

try {
    Start-Process -FilePath "$env:SystemRoot\System32\ipconfig.exe" -ArgumentList "/flushdns" -Verb "RunAs" -WindowStyle Hidden -Wait
    Start-Process -FilePath "$env:SystemRoot\System32\ipconfig.exe" -ArgumentList "/releaseall" -Verb "RunAs" -WindowStyle Hidden -Wait
    Start-Process -FilePath "$env:SystemRoot\System32\netsh.exe" -ArgumentList "interface ip delete arpcache" -Verb "RunAs" -WindowStyle Hidden -Wait
}
catch {
    # log the error
}

Files and disk utilization.  This is tacked on several fronts.  Manually deleting files from common locations.  Using the Disk Cleanup utility to cleanup updates and other tasks.  Using defrag and a SysInternals Tool SDelete

Manually deleting files from common directories is easy in PowerShell and self-explanatory.  Common target folders are the Windows Temp directory, User directories, Windows Prefetch, and Windows Update. 

try {

    $tempfolders = @("C:\Windows\Temp\*", "C:\Windows\Prefetch\*", "C:\Documents and Settings\*\Local Settings\temp\*", "C:\Users\*\Appdata\Local\Temp\*", "C:\Windows\SoftwareDistribution\*")

    Remove-Item $tempfolders -Force -Recurse -ErrorAction "SilentlyContinue"  
}
catch {
    # log the error
}

The Disk Cleanup wizard can be configured to run a predefined task.  It is configured easily by setting a few registry keys.  I suggest the Internet Cache, Update Cleanup, Temporary Files, and the Windows Error files. 

try {
	Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\*' -Name StateFlags0001 -ErrorAction SilentlyContinue | Remove-ItemProperty -Name StateFlags0001 -ErrorAction SilentlyContinue

	# create the necessary registry entries for the disk cleanup
	New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Internet Cache Files' -Name StateFlags0001 -Value 2 -PropertyType DWord
	New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Update Cleanup' -Name StateFlags0001 -Value 2 -PropertyType DWord
	New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Temporary Files' -Name StateFlags0001 -Value 2 -PropertyType DWord
	New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Error Files' -Name StateFlags0001 -Value 2 -PropertyType DWord
	
	# start the automated process, and wait for it to finish
	Start-Process -FilePath CleanMgr.exe -ArgumentList "/sagerun:1" -WindowStyle Hidden -Wait 
}
catch {
    # log the error
}

Lastly, using the defrag goes without saying.  This moves all data near each other.  SDelete however is necessary because it changes sectors that might have data to a zero.  VDI Desktops are naturally thin provisioned and therefore having a large zero out space will help with datastore storage efficiency.  Daily this might only account for a few megabytes of space or during a large patch cycle it could amount to a few gigs.  In a large VDI deployment that could amount to a large amount of space consumed on the SAN. 

# Starting the defrag process
try {
    Start-Process -FilePath "Defrag" -ArgumentList "c: -f" -Wait
}
catch {
    # log the error
}

# Starting the zero out process
try {
    Start-Process -FilePath "sdelete64.exe" -WorkingDirectory "C:\Windows\System32\" -ArgumentList "-z c:" -Verb RunAs -Wait
}
catch {
    # log the error
}

Lastly, the events that have been recorded in Windows needs to be cleared.  This will help going forward with troubleshooting issues during provisioning or daily use.

$EventLogs = Get-EventLog -List | Where-Object { $_.Log -match "Application|Security|System"} | ForEach-Object {$_.Log}

$EventLogs | ForEach-Object -Process {
    try{
        Clear-EventLog -Log $_ 
    }
    catch{
        # log the error
    }
}

The script that I use is written in PowerShell.  I would recommend putting it in a a hidden folder on the drive that regular users are unable to see normally and remove all permissions but administrators from the folder. 

For the full script, please visit the project page on Github page

PowerShell 7 Release

With each new release of PowerShell, we get closer and closer to full compatibility with the legacy PowerShell. This is a very exciting release. The link below will take you to the Microsoft announcement release.

https://devblogs.microsoft.com/powershell/announcing-powershell-7-0/

A couple of the features that I think that have been long overdue are the foreach threading and the ternary operators. As someone who has been coding in C# for a long while, these have been fundamental language deficiencies.

For a full list of cmdlet implementation and compatibility, visit the following site: https://docs.microsoft.com/en-us/powershell/scripting/whats-new/cmdlet-versions?view=powershell-7

Going forward, PowerShell 7 will be the “one, true PowerShell” as well, supplanting the previous forked versions between Windows PowerShell and PowerShell Core.

PowerShell TLS 1.2 Only

Ever try to communicate with a remote API or URL only to receive an error message?  Reviewing the code is usually the first step in troubleshooting. 

tls_error
PowerShell Error

However, as administrators in a large enterprise we have to understand the ever-growing changes in security framework requirements.  SSL 3.0 and TLS 1.0 have already all become deprecated. TLS 1.1 will become end of life on March 31, 2020. This has triggered most enterprises to start disabling this functionality in their server infrastructure.

As an administrator, you can validate the security ciphers that PowerShell is currently utilizing with the following command

[Net.ServicePointManager]::SecurityProtocol

This will generate the following output.

PowerShell validation

To update the default communication cipher that PowerShell will utilize to communicate with a remote server, execute the following command:

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