Category Archives: Powershell

Use PowerShell Instead of cURL

I recently had to come up with a way to schedule regular downloads of a file from a website that uses cURL. cURL is a command line tool for transferring data with URL syntax.  Getting cURL approved for use in my company is a bit difficult, so I set out to find a PowerShell solution. What I found was surprisingly easy to write and implement. You may want to take a look at the cURL Sourceforge page to get more information. Keep in mind that cURL parameters are specific to the website you are downloading from so every website will be different. You may need to contact that site’s tech support to find out what you need to pass if you can’t figure it out from the URL.

As always, if you use this script, please give credit.

$path     = "c:\PoShOut\"
$username = "username"
$password = "password"
$filename = "somefile.zip"
$somefile = "downloadedfile.zip"
 
# Create COM object, open a connection to www.somewhere.com,
# and set the header type
$document = New-Object -ComObject msxml2.ServerXMLHTTP
$document.open("POST", "https://www.somewhere.com", $false)
$document.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
 
# Create the necessary login and report information from the parms we passed
$postline = "user=" + $username + "&password=" + $password + "&filename=" + $filename
 
# Login to Markit with the information we passed above
$document.send($postline)
 
# Check to see if we have a document matching the parms we passed
$response = $document.getResponseHeader("Content-Type")
 
# If we have a document, make sure it's a Zip file, get the filename,
# and put the Zip into a Byte array. Once the file is downloaded, write
# the Byte array to a file.
If($response.Substring(0,17).ToLower() -eq "application/x-zip")
{
     $filename = $response.Substring(24)
     [Byte[]]$file = $document.responseBody
     [System.IO.File]::WriteAllBytes($path + $filename,$somefile)
}

Really Kill that Excel Object

If you’ve tried to use Excel 2007 or above with any of your Powershell scripts, you know that more often than not, when you quit Excel, a process stays running.  Not a huge deal until you’ve got a script scheduled to run several times a week and you end up silly with memory-eating Excel processes. You could kill all EXCEL.EXE processes, but what if you’ve got a scheduler that may be running other Excel processes as well?  If you kill all  running EXCEL.EXE processes, you’ll also be killing other Excel processes that should be running.

I’ve managed to put together some code that will determine what Excel processes are running before your create your Excel object and kill only the process spawned by your script.

## Grab all EXCEL.EXE processes before we create our Excel object.  This will find
## all Excel processes that we don't want to kill.  After we have those, we create
## our Excel object and immediately look at the Excel processes afterward.  There
## obviously a slight chance that you'll grab a process that spawned between our
## $startprocs and $endprocs scans, but it's a pretty small chance.
$startprocs = Get-Process | Where {($_.Name.ToLower() -eq "excel")}
$excel      = New-Object -ComObject Excel.Application
$endprocs   = Get-Process | Where {($_.Name.ToLower() -eq "excel")}
 
//             Your Code Goes Here                                        //
 
## Try to quit Excel (after you've saved your doc).  It's probably not going to work,
## but we're gonna try anyway.
$excel.Quit()
 
## Check to make sure $startprocs isn't null.  If it is null and we don't check, the
## script will blow.  If $startprocs is null, we're going to kill the only running
## Excel process...the one we created.
If($startprocs -gt $null)
{
     $compare = Compare-Object $startprocs $endprocs
     $process  = $compare | % {If($_.SideIndicator -match "=>") {$_.InputObject}}
}
else
{
     $process = $endprocs
}
 
Kill -Id $process.Id

Use Powershell to Pass Embedded Credentials

I needed to pass the ID/password of a service account to a Microsoft HPC grid for some jobs we were submitting automatically.  Powershell was able to do that pretty easily.

***WARNING***  This script reads a domain ID and password from an unencrypted file.  Our implementation of this script used a protected file.  Yours should too!  You’ve been warned.  I’m not responsible if your account password is compromised.

$domain      = "[Your domain name]"
$filename    = "[Path to filename]\filename"
$username    = $domain + "\" + ($username.substring(($username.Length - 5),5))
$password    = Get-Content $filename | ConvertTo-SecureString -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential($username,$password)

A couple notes here.  The $filename variable is used to get the entire path of the password file.  For ease of scripting, I’ve created a file named the same as the user ID I’m using with no file extension.  In the $username line, I’ve concatenated the $domain variable, a backslash, and the user ID that I’ve derived by taking the substring of the length of the $filename variable, minus the length of the ID, plus the length.  There is a cleaner way to do this, but the ID I’m using will never change (yes, I said never), so I chose this method.

$username    = $domain + "\" + ($username.substring(($username.Length - 5),5))

From that point, we read the password from the file and pipe it into the ConvertTo-SecureString commandlet.  Since we are using a plaintext password here (see the warning above), we have to use the -AsPlainText and  -Force parameters.  If you don’t use these, PoSh will complain, you’ll be frustrated.

$password    = Get-Content $filename | ConvertTo-SecureString -AsPlainText -Force

The last line is used to actually pass the credentials to a variable that can be used in a script that requires authentication.

$credentials = New-Object System.Management.Automation.PSCredential($username,$password)

Turn Off the UAC with Powershell

I wrote this script to turn off the UAC on our HPC R2 grid with 64 servers.  It works very well, but there is not any real error handling in there to account for offline computers.  If you are doing this for Windows 7 computers that may or may not be online, you may want to add some custom ping checks to keep the script from blowing chunks.

As always, uses the script in whole or in part, but I’d appreciate some props if you do.

########################################################################
#
#	Disable User Account Control
#
#	Author:  Jim Melton
#
#	This script checks the registry for all computer in a flat file
#		and checks to see if the UAC is turned on.  If it is, it will
#		set the EnableLUA value to 0.  The script can be modified to
#		enable the UAC if need be.  A machine reboot is required to
#		complete the change.
#
########################################################################
 
########################################################################
#
#	Initialize variables and set paths
#
########################################################################
$computers = Get-Content ""
$WriteUAC = $true
$UACvalue = "EnableLUA"
$UACoff   = 0
$UACpath  = "Software\Microsoft\Windows\CurrentVersion\policies\system"
 
########################################################################
#
#	Loop through the input file for all computers, open the remote
#		HKLM hive using .NET, open the subkey to the UAC setting, and
#		get the Enable UAC value.
#
########################################################################
ForEach($computer in $computers){
 
  $OpenRegistry = [Microsoft.Win32.RegistryKey]::`
	OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$computer)
 
  $UACsubkey = $OpenRegistry.OpenSubKey($UACpath,$WriteUAC)
  $UACsubkey.ToString()
  $UACstate  = $UACsubkey.GetValue($UACvalue)
  Write-Host "The UAC on $computer is currently set to $UACstate."
 
########################################################################
#
#	If the EnableLUA value is set to 1, set it to 0.  Grab the EnableLUA
#		value after the change to make sure it was successful.
#
########################################################################
 
  If($UACstate -eq 1)
    {
	Write-Host "Turning the UAC off..."
	$UACsubkey.SetValue($UACvalue, $UACoff)
	$UACstate = $UACsubkey.GetValue($UACvalue)
	Write-Host "The UAC on $computer is now set to $UACstate.`
             The computer needs to be rebooted."
    }
}

Determine PC SAS Bundles

Here is a PoSh script that I wrote to determine what bundle of PC SAS is installed.  If you aren’t familiar, there are a lot of components for PC SAS and it’s expensive.  They are installed in “Bundles” and you are billed based on what bundle is installed.  The more components in the bundle, the higher the license cost. 

Unfortunately, when SAS is installed, there is no method for determining which bundle is installed because the SAS installer makes no note of what bundle or what components are installed.  I had to come up with a method for determining what bundle each user had on his/her computer.  The script below is a result of installing every bundle we had and analyzing what components where in each bundle…and quite a bit of swearing.

As always, feel free to use this code, but please give credit.

#############################################################################
##																			#
##	PC SAS Bundle Finder													#
##																			#
##		Written by Jim Melton												#
##
##																			#
##	Reads computer names with PC SAS installed from a spreadsheet created	
##	by SMS or some other means and determines what version of PS SAS is		#
##  installed.  Also determines what bundle is installed, as well as 		
##	whether or not the Graph and Enterprise Guide components are installed	
##																			#
##	Known Issues:															#
##		-If PC SAS installed incorrectly, the bundle cannot be determined.	#
##		-If WMI is broken on the computer, the logged on user will not be	#
##			determined.														#
##		-When Excel quits at the end, the Excel process has a tendency		#
##			to remain running.  You may need to kill these at some point.	#
##																			#
#############################################################################
 
$ErrorActionPreference = "Continue"
 
##
##  Put the path to your text,csv, or xls here
##
$computers = Get-Content "<path to csv>\pcsasinstalled.csv"
 
##
##	Create the objects to ping machines and open Excel
##
$ping = New-Object System.Net.NetworkInformation.Ping
$excel = New-Object -ComObject excel.application
 
##
## You can change this to $false if you don't want to see Excel
##
$excel.visible = $true
$worksheets = $excel.workbooks.add()
##
##	Create the spreadsheet headers
##
$sheet = $excel.worksheets.item(1)
$sheet.cells.item(1,1) = "Computer Name"
$sheet.cells.item(1,2) = "Username"
$sheet.cells.item(1,3) = "SAS Version"
$sheet.cells.item(1,4) = "Bundle"
$sheet.cells.item(1,5) = "Graph Installed?"
$sheet.cells.item(1,6) = "Enterprise Guide Installed?"
$introw = 2
$workbook = $sheet.usedrange
$workbook.font.bold = $true
##
##	Bundle finder function
##
Function Bundles {
  ForEach ($computer in $computers){
  ##
  ##	Zero variables
  ##
  $eguide = $false
  [int]$count = 0
  ##
  ##	Set variable for pinging computers
  ##
  $reply = $ping.send($computer)
  ##
  ##	Ping computer and see if it is on the network.  If it is, open the registry in $pathtest
  ##	to see if SAS is installed and if so, what version.  Also open the registry in $basepathtest
  ##	to see if the Enterprise Guide is installed.
  ##
	If ($reply.status -eq "success"){
	$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$computer)
	$basepathtest = $registry.OpenSubKey("SOFTWARE\SAS Institute Inc.")
	  $pathtest = $registry.OpenSubKey("SOFTWARE\SAS Institute Inc.\The SAS System")
	If ($pathtest.GetSubKeyNames() -contains "9.1"){
	  $sasver = "9"
	  $components = $registry.OpenSubKey("SOFTWARE\SAS Institute Inc.\The SAS System\9.1\Setup\Components")
	}
	ElseIf
	  ($pathtest.GetSubKeyNames() -contains "8"){
	  $sasver = "8"
	  $components = $registry.OpenSubKey("SOFTWARE\SAS Institute Inc.\The SAS System\8\Setup\Components")
	}
	  ##
	  ##	Check to see if the Enterprise Guide is installed and if so, set $eguide to $true
	  ##
	  If ($basepathtest.GetSubKeyNames() -contains "Enterprise Guide"){
		$eguide = $true
		}
	  ##
	  ##	Get SAS component names and assign to $bundle.  Also, do WMI query on $computer
	  ##
	  $bundle = $components.GetValueNames()
	  $user = gwmi Win32_computersystem -ComputerName "$computer"
	##
	##	Start the analysis of each bundle installed.  Count the number of components installed by
	##	incrementing [int]$count.
	ForEach ($component in $bundle){
	  [int]$count = [int]$count + 1
	}
	  ##
	  ##	Do this if SAS 8 is installed.  When the number of components installed is determined,
	  ##	write the computer name, logged on user (from WMI query above), SAS version, whether Graph is
	  ##	installed, and whether the Enterprise Guide is installed.
	  ##
	  If ($sasver -eq "8"){
	 	Switch ($count){
	  	  8 {If ($bundle -contains "fsp"){
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "Bundle 9"
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
			      $sheet.cells.item($introw,6) = "Yes"
				}
			  }
			}
		  7 {If ($bundle -contains "connect"){
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "Bundle 8"
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
			}
			  ElseIf ($bundle -contains "eis"){
				$sheet.cells.item($introw,1) = $computer
				$sheet.cells.item($introw,2) = $user.username
				$sheet.cells.item($introw,3) = $sasver
				$sheet.cells.item($introw,4) = "Bundle 7"
				  If ($bundle -contains "graph"){
					$sheet.cells.item($introw,5) = "Yes"
					}
					If ($eguide -eq $true){
					  $sheet.cells.item($introw,6) = "Yes"
					}
				}
			}
		  6 {If ($bundle -contains "assist"){
			  $sheet.cells.item($introw,1) = $computer
			  $sheet.cells.item($introw,2) = $user.username
			  $sheet.cells.item($introw,3) = $sasver
			  $sheet.cells.item($introw,4) = "Bundle 6"
				If ($bundle -contains "graph"){
				  $sheet.cells.item($introw,5) = "Yes"
				  }
				  If ($eguide -eq $true){
					$sheet.cells.item($introw,6) = "Yes"
					}
				}
			}
		  5 {If ($bundle -contains "stat"){
			  $sheet.cells.item($introw,1) = $computer
			  $sheet.cells.item($introw,2) = $user.username
			  $sheet.cells.item($introw,3) = $sasver
			  $sheet.cells.item($introw,4) = "Bundle 5"
				If ($bundle -contains "graph"){
				  $sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
			}
			Else {
			  $sheet.cells.item($introw,1) = $computer
			  $sheet.cells.item($introw,2) = $user.username
			  $sheet.cells.item($introw,3) = $sasver
			  $sheet.cells.item($introw,4) = "Bundle 4"
				If ($bundle -contains "graph"){
				  $sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				 }
				}
			}
			4 {If ($bundle -contains "pcfile"){
				$sheet.cells.item($introw,1) = $computer
				$sheet.cells.item($introw,2) = $user.username
				$sheet.cells.item($introw,3) = $sasver
				$sheet.cells.item($introw,4) = "Bundle 3"
				  If ($bundle -contains "graph"){
					$sheet.cells.item($introw,5) = "Yes"
					}
				  If ($eguide -eq $true){
					$sheet.cells.item($introw,6) = "Yes"
					}
				}
				Else{
				  $sheet.cells.item($introw,1) = $computer
				  $sheet.cells.item($introw,2) = $user.username
				  $sheet.cells.item($introw,3) = $sasver
				  $sheet.cells.item($introw,4) = "Bundle 2"
					If ($bundle -contains "graph"){
					  $sheet.cells.item($introw,5) = "Yes"
					}
					  If ($eguide -eq $true){
						$sheet.cells.item($introw,6) = "Yes"
						}
					}
				}
			3 {
			  $sheet.cells.item($introw,1) = $computer
			  $sheet.cells.item($introw,2) = $user.username
			  $sheet.cells.item($introw,3) = $sasver
			  $sheet.cells.item($introw,4) = "Bundle 1"
			    If ($bundle -contains "graph"){
			  	  $sheet.cells.item($introw,5) = "Yes"
				}
				  If ($eguide -eq $true){
				    $sheet.cells.item($introw,6) = "Yes"
				  }
				}
			##
			##	If the SAS 8 bundle can't be determined, SAS is probably installed incorrectly.
			##	Display the number of components installed and then list them.
			##
			Default {
			  $sheet.cells.item($introw,1) = $computer
			  $sheet.cells.item($introw,2) = $user.username
			  $sheet.cells.item($introw,3) = $sasver
			  $sheet.cells.item($introw,4) = "There are $count components installed and they are $bundle ."
				If ($bundle -contains "graph"){
				  $sheet.cells.item($introw,5) = "Yes"
				}
				  If ($eguide -eq $true){
					$sheet.cells.item($introw,6) = "Yes"
					}
				}
		}
	}
	##
	##	If SAS version is not 8, it must be 9 as of 2009.  Analysis is the same as for version 8
	##
	Else {
	##
	##	If Graph is installed, it adds three components to the total.  Subtract
	##	3 components from $count to make the total components correct.
	  If ($bundle -contains "graph"){
		$count = $count - 3
		}
	    Switch ($count){
		  16 {If ($bundle -contains "fsp"){
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "Bundle 9"
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
			  }
			}
		  14 {If ($bundle -contains "connect"){
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "Bundle 8"
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
			  }
			}
		  13 {If ($bundle -contains "eis"){
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "Bundle 7"
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
			  }
			}
		  11 {If ($bundle -contains "assist"){
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "Bundle 6"
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
			  }
			}
		  10 {If ($bundle -contains "stat"){
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "Bundle 5"
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
			  }
			Else {
			  $sheet.cells.item($introw,1) = $computer
			  $sheet.cells.item($introw,2) = $user.username
			  $sheet.cells.item($introw,3) = $sasver
			  $sheet.cells.item($introw,4) = "Bundle 4"
				If ($bundle -contains "graph"){
				  $sheet.cells.item($introw,5) = "Yes"
				}
				  If ($eguide -eq $true){
					$sheet.cells.item($introw,6) = "Yes"
	 			  }
			}
		  }
		  8 {If ($bundle -contains "pcfile"){
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "Bundle 3"
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
			}
			Else{
			  $sheet.cells.item($introw,1) = $computer
			  $sheet.cells.item($introw,2) = $user.username
			  $sheet.cells.item($introw,3) = $sasver
			  $sheet.cells.item($introw,4) = "Bundle 2"
				If ($bundle -contains "graph"){
				  $sheet.cells.item($introw,5) = "Yes"
				}
				  If ($eguide -eq $true){
					$sheet.cells.item($introw,6) = "Yes"
					}
			}
		  }
		  6 {
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "Bundle 1"
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
		  }
		  Default {
			$sheet.cells.item($introw,1) = $computer
			$sheet.cells.item($introw,2) = $user.username
			$sheet.cells.item($introw,3) = $sasver
			$sheet.cells.item($introw,4) = "There are $count components installed and they are $bundle ."
			  If ($bundle -contains "graph"){
				$sheet.cells.item($introw,5) = "Yes"
				}
				If ($eguide -eq $true){
				  $sheet.cells.item($introw,6) = "Yes"
				}
			}
		  }
	  }
	}
  ##
  ##	If machine doesn't respond to a ping, write the computer name and the no
  ##	response message.
  ##
  Else{
    $sheet.cells.item($introw,1) = $computer
    $sheet.cells.item($introw,4) = "Didn't respond to a ping."
  }
    ##
    ##	Increment the row counter for Excel
    ##
    $introw = $introw + 1
  }
  ##
  ##	Make the Excel columns autofit the data so nothing is cut off, save the
  ##	spreadsheet, and quit Excel.
  ##
  $workbook.entirecolumn.autofit()
  $worksheets.saveas("C:\bundlelist.xls")
  $excel.quit()
}
##
##	Execute the function
##
Bundles

Repair a Broken Symantec Management Client Service

As mentioned in a previous post, a SEP client can have a broken SMC service and you may very well never know that there is anything wrong (unless you use the script found in that post :-) ).  This is the Powershell script that will allow you to remotely repair most of those broken clients.  Again, as with the audit script, you must have admin rights to the computer that you are running this script on.  Since it uses WMI to run a remote process, WMI must also be working properly on the remote computer.

The script will repair one client at a time using the “-compname” parameter.  However, you can modify the script to read a text file by adding another “ForEach” and a “Get-Content” to the script.

#################################################################
#							        #
#- Antivirus SMC service repair for clients with corrupt        #
#     security policies.                                        #
#- Requires a commandline parm of -compname          		#
# - Parms can be changed to accept a list for multiple PCs	#
#								#
#- Written by Jim Melton					#
#								#
#################################################################
 
## Accept computer name from the commandline with -compname
param ([string]$CompName = $(throw "Text Input is Required"))
Write-Host $CompName
 
## Load the System.ServiceProcess .net class, set the Error Action to
## Sliently Continue, and initialize some variables.
[System.Reflection.Assembly]::LoadWithPartialName('system.serviceprocess')
$ErrorActionPreference = "Continue"
$SEPreg = "SOFTWARE\Symantec\Symantec Endpoint Protection\AV"
$SEPhome = "Home Directory"
$SEPinstall = ""
$DefPaths = "SOFTWARE\Symantec\SharedDefs"
$SMCkey = "SOFTWARE\Symantec\Symantec Endpoint Protection\SMC"
$SMCsvc = "Symantec Management Client"
$SEPnetwork = ""
 
## Create a new Pint object to test to see if the remote computer
## is online so the script doesn't bomb, assign -compname to a
## variable and ping the computer.
$ping = New-Object System.Net.NetworkInformation.Ping
$computer = $CompName
$reply = $ping.send($computer)
 
## If the computer replies, open a remote process on the broken computer,
## open the registry (HKLM in this case), and count the SEP registry entries
## to make sure SEP is actually installed.
If ($reply.status -eq 'Success'){
	$remoteProcess = [WmiClass]"\\$computer\ROOT\CIMV2:Win32_Process"
	$OpenRegistry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$computer)
	$DefsKey = $OpenRegistry.OpenSubKey($DefPaths,$true)
	[int]$ValueCount = $DefsKey.ValueCount
 
	## If SEP is installed and has valid registry entries to get paths from,
	## find the SEP install path, write it to the screen, rename the existing
	## serdef.dat, copy a new serdef.dat, and restart the SMC service using
	## our remote WMI process.
	If ([int]$ValueCount -gt 0){
 
		If ($DefsKey.GetValueNames() -ne $null){
			$SEPpath = $OpenRegistry.OpenSubKey($SEPreg,$true)
			$SEPinstall = $SEPpath.GetValue("$SEPhome")
			$SEPtemp = $SEPinstall.length
			[int]$SEPtemp = [int]$SEPtemp - 3
			$SEPinstall = $SEPinstall.substring($SEPinstall.length - [int]$SEPtemp)
			Write-Host "Symantec Endpoint Protection is installed at $SEPinstall."
			Copy-Item "\\$computer\c$\$SEPinstall\serdef.dat" "\\$computer\c$\$SEPinstall\serdef.old" -Recurse
			$remoteProcess.Create("c:\$SEPinstall\smc.exe -start")
			Write-Host "Starting SMC service."
 
			Write-Host "Finished"
			}
		Else{
			Write-Host "SEP Install Not Found"
			}
	}
	Else{
		Write-Host "Not installed"
	}
}
 
Else{
	Write-Host "Computer is not responding to a ping"
#	}
}

Audit Symantec Endpoint Protection Clients

If you’ve ever administered clients running Symantec Endpoint Protection 11 (SEP), you know how woefully inadequate the SEP management interface is for determining the health of your clients.  If a computer get’s a corrupt policy, chances are the Symantec Management Client (SMC) service will stop and refuse to restart.  When this happens, the component of SEP responsible for updating definitions and reporting out of date definitions stops working.  The real-time scanning component – the Symantec Endpoint Protection service – will continue to run, but it will never update.  Since the service that tells the SEP Manager that a client is out of date doesn’t work, chances are you will never know about it unless one (or more) of your end users happens to report it.

So, in order to find the broken SEP clients in my company – about 14,000 computers in various locations around the country – I turned to Powershell and the .NET Framework to do the heavy lifting.  The results were both very surprising and disconcerting.  Hundreds of our computers were out of date and not reporting this to the SEP Manager.  This information allowed us to update our computers using a combination of some automation and the good ol’ fashioned Sneaker Net.

This script assumes a few things:

  1. You must have admin privileges on the computers you are auditing.
  2. File and Print Sharing is enabled on the computers.
  3. You have a pretty solid list of computer names to run the Powershell script against (I used an SCCM list of all discovered Active Directory computer objects).
#################################################################
#
#
#	- Antivirus Health Check Script
#	- Audit computer objects from a text file specified at
#		runtime.  Checks for AV version, stopped/running
#		services, and definition dates.
#
#	- Written by Jim Melton
#        ** Feel free to use and modify this script as needed.
#            Just please give credit if you do.
#
#################################################################
 
## Setup command line parameter to allow user to specify content
## file at runtime.  Parameter is -FileName
param ([string]$FileName = $(throw "Text Input is Required"))
Write-Host $FileName
 
## Load assembly used to interrogate services on the remote machine.
[System.Reflection.Assembly]::LoadWithPartialName('system.serviceprocess')
$erroractionpreference = "Continue"
 
## Set variables
$SMCsvc = "Symantec Management Client"
$SMCstatus = ""
$SEPsvc = "Symantec Endpoint Protection"
$SAVsvc = "Symantec Antivirus"
$SAV8svc = "Symantec Antivirus Client"
$Path = "c$\Program Files\Common Files\Symantec Shared\VirusDefs"
$SavePath = "\\SERVER\SHARE\"
 
## Instantiate Excel ComObject and write headings on spreadsheet.
$excel = New-Object -comobject Excel.Application
$excel.visible = $true
 
$workbook = $excel.Workbooks.Add()
$worksheet = $workbook.Worksheets.Item(1)
 
$worksheet.Cells.Item(1,1) = "Machine Name"
$worksheet.Cells.Item(1,2) = "IP Address"
$worksheet.Cells.Item(1,3) = "AV Version"
$worksheet.Cells.Item(1,4) = "Virus Definition"
$worksheet.Cells.Item(1,5) = "Status"
 
$range = $worksheet.UsedRange
$range.Interior.ColorIndex = 19
$range.Font.ColorIndex = 11
$range.Font.Bold = $True
 
$intRow = 2
 
## Create Ping object
$ping = New-Object System.Net.NetworkInformation.Ping
 
## Get data from user specified file
$colComputers = get-content $FileName 
 
foreach ($computer in $colComputers){
 
	## Zero variables at the top of the loop and ping the next machin
	## in the list.
	$IPaddress = ""
	$AVservice = ""
	[string]$service = ""
	$VirDate = ""
	$reply = $ping.send($computer)
	$IPaddress = [string]$reply.Address
 
	## Check ping reply.  If it's a success, test the Symantec Shared files path to see if
	## it exists.  If the path is valid, get the content of definfo.dat and parse the
	## definition date and rev.
	If ($reply.status -eq 'Success'){
		$ValidPath = Test-Path "\\$computer\$path"
		If ($ValidPath -eq "True"){
		     $DefInfo = Get-Content "\\$computer\$Path\definfo.dat"
		     $DefLine = $DefInfo[1]
		     $DefYear = $DefLine.substring(8,4)
		     $DefMonth = $DefLine.substring(12,2)
		     $DefDay = $DefLine.substring(14,2)
		     $Revision = $DefLine.substring(17,3)
		     [int]$DefDate = $DefLine.Substring(8,8)
		     [string]$DefDate = "$DefMonth" + "-" + "$DefDay" + "-" + "$DefYear"
		     $VirDate = [datetime]$DefDate
		}
 
		## If the path or definfo.dat doesn't exist, bail out of the if statement.
		Else{
		     $VirDate = $null
		     Continue
		}
	## Query the remote machine for the SEP, SAV, or SAV 8x RTVScan services.  Also,
	## query the SMC service for SEP.  These are separate in order to make sure
	## that both the SEP and SMC services are running on a machine.
	$services = [System.ServiceProcess.ServiceController]::GetServices($computer) | `
			where { (($_.displayname -eq $SEPsvc) -or ($_.displayname -eq $SAVsvc) `
			                                      -or ($_.displayname -eq $SAV8svc))}
	$SMCservice = [System.ServiceProcess.ServiceController]::GetServices($computer) | `
			where { ($_.displayname -eq $SMCsvc)}
 
	## Check to see what services are present.  If there are no services on the machine, write that
	## into the spreadsheet and continue on to the next computer.
	foreach ($service in $services){
		If ($service.displayname -eq $null){
			$worksheet.Cells.Item($intRow,1) = $computer
			$worksheet.Cells.Item($intRow,2) = $IPaddress
			$worksheet.Cells.Item($intRow,5) = "Services are not present on $computer."
			$intRow = $intRow + 1
		}
 
		## Otherwise, if it's SAV or SAV 8, insert SAV into $AVService.
		Else{
			If (($service.DisplayName -eq $SAVsvc) -or ($service.DisplayName -eq $SAV8svc)){
				$AVservice = "Symantec Antivirus"
			}
 
			## If not Null or SAV, then it must be SEP.  Insert SEP into $AVService.  Also check to see if
			## the SMC service is stopped.  If so, set $SMCstatus to Stopped.
			Else {
				If ($service.DisplayName -eq $SEPsvc){
				     $AVservice = "Symantec Endpoint Protection"
				     If ($SMCservice.DisplayName -eq $SMCsvc){
		        	          If ([string]$SMCservice.status -eq 'Stopped'){
			        		$SMCstatus = "Stopped"
					  }
				     }
				}
			}
 
		## If computer is online and has AV installed, insert its information into the spreadsheet.
		## If the SMC service is stopped, append that info to the Status block showing the SEP
		## RTVScan status.  Otherwise,just insert the status of RTVScan.
		$worksheet.Cells.Item($intRow,1) = $computer
		$worksheet.Cells.Item($intRow,2) = $IPaddress
		$worksheet.Cells.Item($intRow,3) = $AVservice
		$worksheet.Cells.Item($intRow,4) = "$DefDate" + " rev " + "$Revision"
		If ($SMCstatus -eq "Stopped"){
			$worksheet.Cells.Item($intRow,5) = [string]$service.Status + ", `
				however, the SMC Service is stopped."
		}
		Else{
			$worksheet.Cells.Item($intRow,5) = [string]$service.Status
		}
	}
 
	## Increment the spreadsheet row and reset $SMCstatus to blank.
	$intRow = $intRow + 1
	$SMCstatus = ""
		}
	}
 
	## If the machine doesn't respond to a ping, insert that in the spreadsheet.
	Else{
		If ($reply.status -eq "TimedOut"){
			$worksheet.Cells.Item($intRow,1) = $computer
			$worksheet.Cells.Item($intRow,3) = $IPaddress
			$worksheet.Cells.Item($intRow,5) = "Did not respond to a ping."
			$intRow = $intRow + 1
		}
		## If the machine doesn't respond to a ping and the IP is not valid, the computer name
		## must not bevalid any longer.
		Else{
			$IPaddress = "Computer name is not valid."
			$worksheet.Cells.Item($intRow,1) = $computer
			$worksheet.Cells.Item($intRow,3) = $IPaddress
			$worksheet.Cells.Item($intRow,5) = "Did not respond to a ping."
			$intRow = $intRow + 1
		}
	}
 
## Once the data is completely written to the spreadsheet, auto-fit all columns to the data width.
$range.EntireColumn.AutoFit() | Out-Null
}
 
## Switch statement using a regular expression switch to determine location being audited based on the
## input file filename.  If you need an Adhoc report, use computers.txt as the input file.
Switch -regex ($FileName){
 
	"loc01comps.txt" {$Location = "Location1"}
	"loc02comps.txt" {$Location = "Location2"}
	"loc03comps.txt" {$Location = "Location3"}
	"loc04comps.txt" {$Location = "Location4"}
	"loc05comps.txt" {$Location = "Location5"}
	"loc06comps.txt" {$Location = "Location6"}
	"loc07comps.txt" {$Location = "Location7"}
	"loc08comps.txt" {$Location = "Location8"}
	"loc09comps.txt" {$Location = "Location9"}
	"loc10comps.txt" {$Location = "Location10"}
	"loc11comps.txt" {$Location = "Location11"}
	"computers.txt"    {$Location = "Adhoc Report"}
	default {$Location = "Other"}
}
 
## Save the spreadsheet to the path in the variable using the name determined from the above Switch.
$SpreadSheetName = $SavePath+$Location+".xls"
$workbook.Worksheets.item(1).Name = $Location
 
## Check to see if the spreadsheet exists.  If it does, delete it and recreate it, and save it.
## Attempt to close Excel and end the ComObject.  This doesn't seem to work well with Excel 2K7,
## so we are killing processes called Excel.  This will close all Excel processes.
If(Test-Path $SpreadSheetName){
	Remove-Item $SpreadSheetName
	Write-host $SpreadSheetName
	$excel.ActiveWorkbook.SaveAs($SpreadSheetName)
	$excel.Quit()
	[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
	Remove-Variable excel
	Get-Process -Name Excel | Kill
}
Else{
	$excel.ActiveWorkbook.SaveAs($SpreadSheetName)
	$excel.Quit()
	[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
	Remove-Variable excel
	Get-Process -Name Excel | Kill
}