Click an Ad

If you find this blog helpful, please support me by clicking an ad!

Monday, July 7, 2014

Using Powershell to find out which Windows Updates were Installed since $Date?

I wrote this script some time ago, and now I am going back through my old scripts and making functions out of them.

The purpose of this script was to enable me to find out which Windows Update patches were installed on a server in the past X days. I wanted to be able to fully answer the "what changed" question when it came up to troubleshoot some server issue.

Here is the script as it was originally:

#------------- BEGIN SCRIPT ----------------------

#Prompt for the computer name and how far to go back
$HostChoice = (Read-Host -Prompt "Please Enter the computer name you'd like to query")
$DaysBackChoice = (Read-Host -Prompt "How many days would you like to go back?")

#Get the date from X Days ago
$DateXDaysAgo = ((get-date).adddays(-$DaysBackChoice).toshortdatestring())
$DateXDaysAgo = ($DateXDaysAgo + " 12:00:00 AM")

#Get the info from the remote computer and pass it to GridView
Get-WMIObject -ComputerName $HostChoice -Class Win32_QuickFixEngineering | 
where {$_.InstalledOn -ge $DateXDaysAgo} | sort InstalledOn | 
out-gridview

#------------- END SCRIPT ----------------------

Since writing this, I've learned that "Read-Host" kills puppies every time you use it. Or something. I've therefore decided to turn this script into a full-blown function using this as a template:

#------------- BEGIN SCRIPT ----------------------

function Do-Something {
  <#
  .SYNOPSIS
  What does this script do?
  .DESCRIPTION
  A better list of what the script does
  .EXAMPLE
  Command example
  .PARAMETER <ParameterName>
  What is the purpose of this parameter?
  #>
  [CmdletBinding()]
  param($ParameterName)
  (
    [Parameter(Mandatory=$True,
    ValueFromPipeline=$True,
    ValueFromPipelineByPropertyName=$True,
      HelpMessage='What does this parameter do?')]
    [Alias('AliasHere')]
    [ValidateLength(3,30)]
    [string]$ParameterVariableName
  )

#Put the meat of your function inside the Process block
  process {
    write-verbose "Beginning process loop"
  } #End Process
} #End Function

#------------- END SCRIPT ----------------------

After much trial and error - I had to reformat the dates for some reason, here is the final product:

#------------- BEGIN SCRIPT ----------------------


function Get-UpdatesInstalled{
  <#
  .SYNOPSIS
  Find out which updates have been installed on a computer in the past X days
  .DESCRIPTION
  This script is passed two arguments, Computername and DaysBack. You should be a member of the local administrators group on the target machine. The script then returns, in GridView, which updates have been installed within that timeframe.
  .EXAMPLE
  Get-UpdatesInstalled -ComputerName <NameString> -DaysBack <PositiveInteger>
  .PARAMETER <ComputerName>
  This sets the target computer to query.
  .PARAMETER <DaysBack>
  This sets how far back to query for updates.
  #>
  [CmdletBinding()]
  param(
    [Parameter(Mandatory=$True,
    Position=1,
    ValueFromPipeline=$True,
    ValueFromPipelineByPropertyName=$True,
      HelpMessage='Name of computer to query')]
    [ValidateLength(3,30)]
    [string]$ComputerName, 

    [Parameter(Mandatory=$True,
    Position=2,
    ValueFromPipeline=$True,
    ValueFromPipelineByPropertyName=$True,
      HelpMessage='Updates installed within how many days?')]
    [string]$DaysBack
  ) #End Param


#Put the meat of your function inside the Process block
  process {
    write-verbose "Beginning process loop"
      #Ping Test
      $PingTest = test-connection $ComputerName -quiet
      
      If ($PingTest -eq $true){
        Write-Verbose "Ping Test Successful"
#Get the date from X Days ago and reformat for use in this context
$DaysBack = "-" + $DaysBack
        $DaysBackDouble = $DaysBack -as [double]
        $DateXDaysAgo = ((get-date).adddays($DaysBackDouble).toshortdatestring())
$DateXDaysAgo = ($DateXDaysAgo + " 12:00:00 AM")

#Get the info from the remote computer and pass it to GridView
$Updates = Get-WMIObject -ComputerName $ComputerName -Class Win32_QuickFixEngineering | 
        where {$_.InstalledOn -ge $DateXDaysAgo} | sort InstalledOn | 
        out-gridview -Title "Updates installed on $ComputerName since $DateXDaysAgo"
      } #End If Pingtest
      Else {
      Write-Host "Unable to Ping Host"
      } #End Else
  } #End Process
} #End Function

#------------- END SCRIPT ----------------------



Thursday, July 3, 2014

Powershell Shenanigans - Kill Remote RANDOM Processes

It's Friday for me, so here's something a little off-beat!

A few months ago my Padowan and I were sitting around and I was showing him some intro to Powershell type things. It occurred to me that it might be funny to write a script that would randomly kill processes on a user's machine.

DISCLAIMER: Using this tool could result in the loss or corruption of data since you aren't closing files properly. I haven't ever used this on an end-user, and you shouldn't either. It's mean. Also, this isn't the cleanest code possible. It was for fun!

#--------------------- BEGIN SCRIPT -------------------------

#Get the target computer name
$cpname = read-host "Enter the computer name"

#Get a list of running processes from the target system.
#This was refined so that I wouldn't kill system processes
do {
$Processes = get-process -computername $cpname | where {$_.ProcessName -ne "svchost" -and $_.ProcessName -ne "winlogon" -and $_.ProcessName -ne "wininit" -and $_.ProcessName -ne "wmiprvse" -and $_.ProcessName -ne "system" -and `
$_.ProcessName -ne "spoolsv" -and $_.ProcessName -ne "lsass" -and $_.ProcessName -ne "csrss" -and $_.ProcessName -ne "conhost" -and $_.ProcessName -ne "smss" -and $_.ProcessName -ne "services" -and $_.ProcessName -ne "idle"}

#Spit out a list of processes
$Processes | select id, processname | ft -autosize

#Prompt for a course of action. At this point the script isn't entirely without merit. I could use it to kill a stuck process on a user's system.
$Choice = Read-Host "Enter Process Number to kill, R for Random, or Q to quit"

#Kill a random process
If ($Choice -like "R"){
$RandProc = $Processes | get-random
get-wmiobject -computername $cpname win32_process | where-object {$_.handle -eq $RandProc.ID} | foreach-object {$_.terminate()}
$ProcessKilled = $RandProc.Processname + " has been killed"
Write-Host $ProcessKilled
} #End If

#Quit Choice
If ($Choice -like "Q"){
exit
} #End If

#If you chose a specific process number to kill
If ($Choice -gt 0){
$Result = get-wmiobject -computername $cpname win32_process | where-object {$_.handle -eq $Choice}
$Result | foreach-object {$_.terminate()}
$ProcessKilled = $Result.Processname + " has been killed"
Write-Host $ProcessKilled
} #End If
$answer = read-host "Press 1 to kill another process, or q to quit"
}
while ($answer -eq 1)

Wednesday, July 2, 2014

Are you Monitoring PasteBin for Your Employer? You should be!

Last year I went to a great Hacker Security convention called GrrCon here in Grand Rapids, Michigan. I'll be going this year too, so give me a shout if you want to meet up. It's not an expensive ticket, and the content was SO amazing last year.

One of the things I took away was that I needed to be monitoring things like PasteBin. If you monitor security websites, you probably recognize Pastebin as a popular place for hackers to post pilfered data, though the site has many more worthwhile uses. I was surprised to find computer system data for an old employer of mine (including usernames and passwords!).

Getting it taken down was easy enough, but I then went out and found PasteLert, which gives me alerting on any search terms that I plug into it. I can even pipe them into an RSS feed so I can read them along with my daily news in Feedly, which is pretty great.

I highly recommend that everyone looks into ways to find leaked data about your organization!

Tuesday, July 1, 2014

Stepping up my Powershell Game

I watched a fascinating video about Powershell in the enterprise from a talk at TechEd given by Don Jones. It made me realize that it was time to step up my game a bit when it came to Powershell.

Two things I've decided to get more serious about are functions and version control. Version control doesn't look like it will be too hard to implement. We don't have any developers in-house so I don't have anything at-hand to use, but Sapien makes a version control system that I'm going to take a look at.

Most of the scripts I write automate certain things. What I've found over time is that I'm constantly stealing code from one script to use in another. Most of the time the syntax is even identical. I really should automate these snippets into full blown functions. I also think that it's time for me to make some tools for use by our helpdesk. We have a little command line batch file that predates me and is pretty popular within the department. So, for my first function, I'm going to create an uptime function, but I'm going to tie in a bunch of other handy information, too.

From the video, I gathered that [CmdletBinding()] is the cat's meow, and I can now confirm this. Using the Cmdletbinding tag allows me to use Write-Debug and Write-Verbose within my scripts, and then use -verbose and -debug options while calling the function to get that info (among a lot of other things).

Here's what I've come up with so far:

function Get-PCInfo {
  <#
  .SYNOPSIS
  Lists a number of useful pieces of information from a computer
  .DESCRIPTION
  Gets the following information:
  Logged on User
  Uptime
  CPU Usage
  Memory Usage
  C Drive Free Space
  .EXAMPLE
  Get-ComputerProperties <ComputerName>
  .PARAMETER computername
  The computer name to query. Just one.
  #>
  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory=$True,
    ValueFromPipeline=$True,
    ValueFromPipelineByPropertyName=$True,
      HelpMessage='What computer name would you like to target?')]
    [Alias('host')]
    [ValidateLength(3,30)]
    [string[]]$computername

  )

  process {

    write-verbose "Beginning process loop"

    foreach ($computer in $computername) {
      Write-Verbose "Processing $computer"
      # use $computer to target a single computer

      #Ping Test
      $PingTest = test-connection $computer -quiet
      Write-Verbose "Ping Test Successful"

      If ($PingTest -eq $true){
          #Get Logged On User
          $LoggedOnUser = (Get-WMIObject -ComputerName $Computer -class Win32_ComputerSystem | select username).username

          #Get Uptime
          $lastboottime = (Get-WmiObject -Class Win32_OperatingSystem -computername $computer).LastBootUpTime
          $sysuptime = (Get-Date) – [System.Management.ManagementDateTimeconverter]::ToDateTime($lastboottime) 
          $Uptime = ($sysuptime.days).ToString() + " days, " + ($sysuptime.hours).ToString() + " hours, " + 
            ($sysuptime.minutes).ToString() + " minutes, " + ($sysuptime.seconds).ToString() + " seconds"
      
          #Get CPU Usage
          $AVGProc = (Get-WmiObject -computername $computer win32_processor | Measure-Object -property LoadPercentage -Average | Select Average).Average

          #Get Memory Usage
          $MemoryUsage = (Get-WMIObject -Class win32_operatingsystem -computername $computer | 
            Select-Object @{Name = "MemoryUsage"; Expression = {“{0:N2}” -f ((($_.TotalVisibleMemorySize - $_.FreePhysicalMemory)*100)/ $_.TotalVisibleMemorySize) }}).MemoryUsage

          #C Drive Free Space
          $CDriveFree = (Get-WMIObject -Class Win32_LogicalDisk -ComputerName $computer | where {$_.DeviceID -Like "C:"} | ForEach-Object {[math]::truncate($_.freespace / 1GB)}).ToString()

          # create an object with your output info
          $InfoItem = New-Object System.Object
          $InfoItem | Add-member -type NoteProperty -Name 'Logged On User' -value $LoggedOnUser
          $InfoItem | Add-member -type NoteProperty -Name 'Uptime' -value $Uptime
          $InfoItem | Add-member -type NoteProperty -Name 'CPU Usage (Load Percentage)' -value $AVGProc
          $InfoItem | Add-member -type NoteProperty -Name 'MemoryUsage (Percent)' -value $MemoryUsage
          $InfoItem | Add-member -type NoteProperty -Name 'C Drive Free (GB)' -value $CDriveFree
      
          #Display the Info
          $InfoItem
      } #End If
      Else {
        Write-Host "Unable to Ping Host"
      } #End Else
    } #End Foreach
  } #End Process
} #End Function