[Powershell] Download Certificate Chain from HTTPS site

Had to help a consultant who was trying to access a client’s VDI.  There was some issue with their certs.  In any case, I just needed to get their trusted root cert to get the consultant back up and running.  Client didn’t know how to get their cert, so I thought I’d speed things along.

$webRequest = [Net.WebRequest]::Create("https://www.google.com")
$cert = $webRequest.ServicePoint.Certificate
$chain = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Chain
$chain.ChainElements.Certificate | % {set-content -value $($_.Export([Security.Cryptography.X509Certificates.X509ContentType]::Cert)) -encoding byte -path "$pwd\$($_.Thumbprint).cer"}



[Powershell] Populate AD Group Based on a CSV File

Here, we assume that we are working with computer accounts and the CSV file and AD description field both contain the serial number.  Of course, with a few tweaks here and there, this could easily be applied to hostname, SID, or whatever.  I was working in an environment where installing the RSAT tools were not an option, hence the adsi objects.  Even so, I still needed to copy over dsmod and dsget.

[adsi]$group = "LDAP://CN=GroupName,OU=Example,OU=Groups,DC=contoso,DC=com"
$snTable = Import-Csv "\\fileshare\serials.csv" | select serial_number

$adsi = [adsisearcher]"(&(objectcategory=computer)(description=*))"
$adsi.SizeLimit = 100000
$adsi.PageSize = 1000
$adsi.SearchRoot = "LDAP://ou=Workstations,dc=contoso,dc=com"
$adsiComps = $adsi.FindAll()

$arrCompsPrimary = New-Object System.Collections.ArrayList
foreach($serial in $snTable) {
    $compsToAdd = New-Object System.Collections.ArrayList #in case multiple comps are found
    $serial = $serial.serial_number
    #if($serial -like "*:*"){continue}
    $adsiComps | ? {$_.Properties.description -like "*$serial"} | % {$compsToAdd.Add($_) | out-null}
    if($compsToAdd){$compsToAdd | % {$arrCompsPrimary.Add($_) | out-null}}

#clear out the group
[adsi]$group = $group.path #refresh the group in case members were added whilst the above code ran
#$group.putex(1,"member",$null) # https://blogs.technet.microsoft.com/heyscriptingguy/2009/07/28/hey-scripting-guy-how-do-i-remove-all-group-members-in-active-directory/
#$group.SetInfo() # doesn't work, not sure why
#$group.member | % {$group.remove("LDAP://$_")} # doesn't work either -- freezes
dsget group "$($group.path -replace('LDAP://',''))" -members | dsmod group "$($group.path -replace('LDAP://',''))" -rmmbr
do{[adsi]$group = $group.path; sleep -s 10}while($group.member) #wait until we know the group is empty

#add comps
$arrCompsPrimary | % {$group.add($_.Path)}

Bite Size: Get Windows Update Event Log via PowerShell

I was having the darndest time trying to get the Windows Update Event Log in PowerShell.  Turns out, Get-Event and Get-EventLog apparently only apply to the “old” event logs (Application, Security, and System, plus a handful of others like IE and PowerShell).  Get-WinEvent is what you want for the newer event logs, introduced with Vista IIRC.  Thus for example to get Windows Updates log:

Get-WinEvent -LogName "Microsoft-Windows-WindowsUpdateClient/Operational"


[Powershell] Get Windows Update History via Command Line

This is simply a modified version of TravisEz13’s script from Stack Overflow.  I corrected a bug that didn’t account for a ResultCode of 1 (and threw in 0 and 5, even though they’re not needed, since why not?), changed the number of records pulled to 50, and converted the results to an ArrayList containing PSCustomObjects, instead of modifying the COM object itself.  I also reformatted the spacing to my liking.

function Convert-WuaResultCodeToName
param( [Parameter(Mandatory=$true)]
[int] $ResultCode
    $Result = $ResultCode
    switch($ResultCode) {
        0 {$Result = "Not Started"}
        1 {$Result = "In Progress"}
        2 {$Result = "Succeeded"}
        3 {$Result = "Succeeded With Errors"}
        4 {$Result = "Failed"}
        5 {$Result = "Aborted"}
    return $Result

$session = New-Object -ComObject 'Microsoft.Update.Session'
$history = New-Object System.Collections.ArrayList
$session.QueryHistory("",0,50) | % {
        $Result = Convert-WuaResultCodeToName -ResultCode $_.ResultCode
        $Product = $_.Categories | Where-Object {$_.Type -eq 'Product'} | Select-Object -First 1 -ExpandProperty Name
        $history.Add([pscustomobject]@{"Result"=$Result;"Date"=$_.Date;"Title"=$_.Title;"Product"=$Product;"UpdateId"=$_.UpdateIdentity.UpdateId;"RevisionNumber"=$_.UpdateIdentity.RevisionNumber}) | Out-Null

Group Policy Preference to Allow Flash on a per-site Basis for Edge

I couldn’t find this one documented anywhere.  Relevant regkey is HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage\microsoft.microsoftedge_8wekyb3d8bbwe\MicrosoftEdge\FlashClickToRunListIgnoreScheme.  Create a DWORD containing the (sub)domain, and set the value to 1.

From the Archives: Just for Fun: Brutal Doom Multiplayer Batch

Digging through my old files, I stumbled upon a pair of batch files I wrote for my less tech-savvy friends when we were on a Brutal Doom kick.  We were using Hamachi to play on a virtual LAN, and I whipped up two quick scripts to let the host know what their IP is, so they could relay it to the others, and all they would have to do is type it in.  It even lets you know if Hamachi is not turned on!  Enjoy.


@echo off
cd %~dp0
set ip=
set break=
set wmicmd=wmic NICCONFIG WHERE "IPEnabled=true and Description LIKE '%%Hamachi%%'" GET IPAddress
(for /f "tokens=1 delims={, skip=1" %%a in ('%wmicmd%') do if not defined break ((set ip=%%a) & (set break=yes))) > nul 2>&1
(for /f "delims=" %%i in (%ip%) do echo %%i | findstr /c:"." & if errorlevel 0 (set ip=%%i)) > nul 2>&1
if "%ip%"=="" ((set ip=[not found]) & color C & (echo No Hamachi Adapter found.  Are you connected?) & pause & color 7 & cls) else ((echo Hosting multiplayer session.  Your Hamachi IP is %ip%.) & pause)
start "" "zandronum.exe" -iwad doom.wad -file .\brutal\brutalv20b.pk3 -file .\brutal\mapsofchaos.wad -host 16 -private +dmflags 16384


@echo off
cd %~dp0
set ip=
set break=
set wmicmd=wmic NICCONFIG WHERE "IPEnabled=true and Description LIKE '%%Hamachi%%'" GET IPAddress
(for /f "tokens=1 delims={, skip=1" %%a in ('%wmicmd%') do if not defined break ((set ip=%%a) & (set break=yes))) > nul 2>&1
if "%ip%"=="" (color C & (echo No Hamachi Adapter found.  Are you connected?) & pause & color 7 & cls)
set /p ip="Enter IP: "
start "" "zandronum.exe" -iwad doom.wad -file .\brutal\brutalv20b.pk3 -file .\brutal\mapsofchaos.wad -connect %ip%

Salesforce Slow in Internet Explorer

Short but sweet.  I am not involved directly with my org’s administration of Salesforce, but I was asked to investigate why certain actions, usually pertaining to lists, would cause IE to freeze and give the “force.com is not responding” warning.  Fiddler traces for IE, Chrome, Firefox, and Edge looked near identical.  I then realized it was rendering that was the issue, which brought my attention to the UI Responsiveness tool included with the F12 tools.


Upon showing to the internal SFDC team, who then relayed the information to SFDC themselves, it was found that there was an AJAX bug in their Winter 19 release.  Upon reversion to Summer 18, it was fixed!