Well, I gotta say, the way that script is presented in the PDF is awful. Who publishes scripts using screenshots? And who butchers the code by slicing the script into pieces (each a portion of the whole script). Once you get over that and actually assemble the pieces, you have to fix mistakes in the code (not many, but they're there). And the code itself is in a style that looks to be an adaptation from Visual Basic. And there's way too much code and intermediate variable use, and the indentation is inconsistent. It also uses a PSSession (unnecessarily) after making sure that WinRM is running on the remote machine! Oh, and did I mention that there are Wa-a-a-y to many comments? It's a very cluttered bit of work.
I straightened out most of it, removed a number of unnecessary variables, and used pipelining in the main body of the code.
I only have one machine so I can't verify that it works, but it's easier to read and has no syntax errors, missing parentheses, or missing braces. See if this version at least runs without errors. Just change the first two lines to agree with the location of the two files:
$SystemNames = 'c:\junk\SystemNames.txt'
$OutputCSV = 'c:\Junk\GSECGOLDResults.csv'
# Declares values and defines the strings of program names that are being searched for
$chrome = "Google Chrome"
$firefox = "Mozilla Firefox"
$java = "Java"
$adobereader = "Adobe Acrobat Reader"
$adobeflash = "Adobe Flash"
# create a SCRIPT-BLOCK to be run on the remote systems
# Check for windows updates on the remote PSSession - This is a remote session so value $Searchresult is transferred back to the main script with the write-output command
$windowsupdate =
{
# below does a search for Critical and Important updates that have not been installed
$UpdateSession = New-Object -ComObject Microsoft.Update.Session
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
$SearchResult = $UpdateSearcher.Search("IsAssigned=1 and Hidden=0 and IsInstalled=0")
# Below writes the amount of Important and Critical patches missing on the remote system to the screen
Write-Host "total=$(SearchResult.updates.count)"
# Below exports the results (total number of patches missing) to the $windows update variable in the main script
Write-Output $SearchResult.updates.count
}
########### End of windows update on remote session
###########
Function queryComputer($SYstemName)
{
# creation of the HASH $returnformfunction
$ReturnfromFunction = [ordered]@{
'Chrome' = "Not Installed"
'Firefox' = "Not Installed"
'Java' = "Not Installed"
'Reader' = "Not Installed"
'Flash' = "Not Installed"
}
$Branch = "LocalMachine" # Branch of the registry HKEY for local machine
# Main sub branch of the registry you need to open
$SubBranch = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" # 32-bit version
#$SubBranch = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" # 64-bit version
$restartreg = $false # sets value of remote registry status to $false - when set to $true it means that this WMI call found remote registry on remote system was off
# check if the remote registry service is stopped. if so, start it
$regserv = Get-WmiObject -ComputerName $SystemName -Class Win32_Service -Filter "Name='RemoteRegistry"
if ($regserv.State -eq 'Stopped'){
$regserv.StartService() | Out-Null
$restartreg = $true
}
# Attempt to open the HKLM branch. Return from the current query if it fails
Try{
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Branch,$SystemName)
}
Catch{
return
}
# Get the names of each installed program
$registrykey = $registry.OpenSubKey($SubBranch)
$SubKeys = $registrykey.GetSubKeyNames
# get the display name for each installed program
ForEach ($key in $SubKeys)
{
# the below lines will drill down to the uninstall directory in the registry to grab all the program display names that are installed
$exactkey = $key
$NewSubKey = $SubBranch+"\\"+$exactkey
$ReadUninstall = $registry.OpenSubKey($NewSubKey)
$value = $ReadUninstall.GetValue("DisplayName")
# for each value which is not blank
if ($null -ne $value -and $value -ne ""){
# The below if statement looks through the values discovered above in the uninstall Registry location and only for the programs defined in the variables above
$DisplayVersion = $ReadUninstall.GetValue("DisplayVersion") # Declares the value that is holding the display version of each application
if ($value.startswith($chrome)){
$ReturnfromFunction.Chrome = $DisplayVersion
}
elseif ($value.startswith($firefox)){
$ReturnfromFunction.Firefox = $DisplayVersion
}
elseif ($value.startswith($java)){
$ReturnfromFunction.java = $DisplayVersion
}
elseif ($value.startswith($adobereader)){
$ReturnfromFunction.Reader = $DisplayVersion
}
elseif ($value.startswith($adobeflash)){
$ReturnfromFunction.Flash = $DisplayVersion
}
}
}
if ($restartReg -eq $true) {
$regserv.StopService() | out-null
}
return $ReturnFromFunction
}
# Begin main program code
Get-Content $SystemNames |
ForEach-Object {
# This line prints to the command screen below to show what system the script is running against as the script proceeds
Write-Host $_
ping -n 1 $_ | Out-Null # Sends ping echo reauest one time to see if the system is alive
if ($? -eq $false){
Write-Host "$_ : System not reachable" # Prints to screen "System not reachable" if no ping results are returned
}
else {
# Registry check for Specific Applications Versions installed (Firefox, Adobe Reader, Adobe Flash, Java)
###################################
###################################
$ReturnFunction = queryComputer $_
###################################
###################################
# Declaring the value for the WMI call that will pull the system OS and last boot time
$obj = Get-WmiObject Win32_OperatingSystem -ComputerName $_
# Below statement makes a WMI call to remote eystem to see if the WinRM service is running and if it isn't running it starts the service so the PSSession can be run
$restartPSSession = $false
$regservPSSession = Get-WmiObject -ComputerName $_ -Class Win32_Service -Filter "Name='WinRM'"
if ($regservPSSession.State -eq "Stopped"){
$regservPSSession.StartService() | Out-Null
$restartPSSession = $true
}
# WHY is a PSSession being used at all? Invoke-Command works without it!
# If there are additional data needed to connect to the remote machine (different protocol, specific credentials, etc.) Then create a CIMSession and use Get-CIMInstance
# instead of Invoke-Command!
# Declaring value for the PSSession that will be called to pull total number of missing patches
$Remotesession = New-PSSession -ComputerName $_
# This is the calling of the PSSession windowsUpdate on lines 13-21 in this script. Then the results from the PSSession and WMICall (to gather OS and LastBootTime) and (->Next 1
# the RemoteRegistry call are all stored in the variabe $RemoteReturnedResult in a printable format, for each system scanned, to be later exported to csv
$RemoteReturnResult= Invoke-Command -Session $Remotesession -ScriptBlock $windowsupdate |
Select-Object @{n="System Name";v={$_}},
@{n="Last Boot Time";v={$obj.ConverTiDateTime($obj.LastBootUpTime)}},
@{n="Operating System";v={$obj.Caption}},
@{n="Missing Patches";v={$_}},
@{n="Chrome Version";v={$ReturnFunction.Chrome}},
@{n="Firefox Version";v={$ReturnFunction.Firefox}},
@{n="Java Version";v={$ReturnFunction.Java}},
@{n="Adobe Flash Version";v={$ReturnFunction.Flash}},
@{n="Adobe Reader Version";v={$ReturnFunction.Reader}}
# this closes the open PSSession for each system in the loop
Remove-PSSession $Remotesession
# the below command checks to see if the WinRM service was started on the remote system and turns it off if and only if it was turned on earlier in the script
if ($restartPSSession -eq $true){
$regservPSSession.StopService() | Out-Null
}
$RemoteReturnResult
}
} | Export-Csv $OutputCSV -NoTypeInformation