PowerShell for Windows Admins


May 7, 2012  10:31 AM

Scripting Games 2012 comments: #14 local computer name

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

There are a number of ways to pass the names of the local machine into a script or function:

  • use the actual name
  • use the IP address (if the processing in the script can work with IP addresses)
  • use 127.0.0.1 – the loop back address (if the processing in the script can work with IP addresses)
  • use a dot  “.”  to signify the local machine
  • use “localhost”
  • use the environmental variable holding the machine name

Normally we don’t use any of the first three because you either have to type out the name – chance of error or the functionality within the script won’t accept IP addresses. Also the PSsession cmdlets only use computer names by default

Traditionally we have used . or localhost especially when working with WMI

Get-WmiObject -Class Win32_OperatingSystem -ComputerName .
Get-WmiObject -Class Win32_OperatingSystem -ComputerName localhost

There are a number of cmdlets that will accept a computer name as a parameter

PS> Get-Help * -Parameter ComputerName | select name

Name
—-
Get-WinEvent
Get-Counter
Test-WSMan
Invoke-WSManAction
Connect-WSMan
Disconnect-WSMan
Get-WSManInstance
Set-WSManInstance
Remove-WSManInstance
New-WSManInstance
Invoke-Command
New-PSSession
Get-PSSession
Remove-PSSession
Receive-Job
Enter-PSSession
Get-EventLog
Clear-EventLog
Write-EventLog
Limit-EventLog
Show-EventLog
New-EventLog
Remove-EventLog
Get-WmiObject
Invoke-WmiMethod
Get-Process
Remove-WmiObject
Register-WmiEvent
Get-Service
Set-Service
Set-WmiInstance
Get-HotFix
Test-Connection
Restart-Computer
Stop-Computer

 

This works:

Get-Process -ComputerName .

but this throws an error

PS> Get-Process -ComputerName localhost
Get-Process : Couldn’t connect to remote machine.
At line:1 char:12
+ Get-Process <<<<  -ComputerName localhost
    + CategoryInfo          : NotSpecified: (:) [Get-Process], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException,Microsoft.PowerShell.Commands.GetProcessCommand

I’ve seen similar errors with Get-Eventlog

PS> Get-EventLog -List -ComputerName .

  Max(K) Retain OverflowAction        Entries Log
  —— —— ————–        ——- —
  20,480      0 OverwriteAsNeeded      21,814 Application
     512      7 OverwriteOlder              0 DemoMate
  20,480      0 OverwriteAsNeeded           0 HardwareEvents
     512      7 OverwriteOlder              0 Internet Explorer
  20,480      0 OverwriteAsNeeded           0 Key Management Service
   8,192      0 OverwriteAsNeeded           3 Media Center
     512      7 OverwriteOlder              0 MyNewLog
     128      0 OverwriteAsNeeded         357 OAlerts
  20,480      0 OverwriteAsNeeded           1 Scripts
                                              Security
  20,480      0 OverwriteAsNeeded      56,225 System
  15,360      0 OverwriteAsNeeded      10,646 Windows PowerShell

PS> Get-EventLog -List -ComputerName localhost
Get-EventLog : The network path was not found.
At line:1 char:13
+ Get-EventLog <<<<  -List -ComputerName localhost
    + CategoryInfo          : NotSpecified: (:) [Get-EventLog], IOException
    + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.GetEventLogCommand

My solution and recommendation is to use the environmental variable for the computer name.

PS> Get-ChildItem -Path env: | where {$_.name -like "*computer*"}

Name                           Value
—-                           —–
COMPUTERNAME                   RSLAPTOP01

The environment provider doesn’t allow the use of filters so we have to use where

How do we use this:

Get-WmiObject -Class Win32_OperatingSystem -ComputerName $env:COMPUTERNAME
Get-Process -ComputerName $env:COMPUTERNAME
Get-EventLog -List -ComputerName $env:COMPUTERNAME

It can also be used as the default value on a function parameter that asks for a computername – that way you get the local machine if you don’t specify a value to the parameter.

I have never seen this fail – doesn’t mean it can’t just that I’ve never seen it – and it has the advantage of being easier to read than using a dot

May 6, 2012  8:00 AM

Scripting Games 2012 comments: #13 Default Printer

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

In one of the events you had to find the default printer. This can be done using WMI.

The full list of printers can seen using:

Get-WmiObject -Class Win32_Printer

 

If you want to examine the printer objecy – to determine what information is available – use get-member or select the first printer in the list

Get-WmiObject -Class Win32_Printer | select -f 1 | fl *

 

you will see that there is a property Default

PS> Get-WmiObject -Class Win32_Printer | Get-Member Default

   TypeName: System.Management.ManagementObject#root\cimv2\Win32_Printer

Name    MemberType Definition
—-    ———- ———-
Default Property   System.Boolean Default {get;set;}

 

which is Boolean i.e. it has to return true or false

 

Your first thought might ne to do this:

Get-WmiObject -Class Win32_Printer | where {$_.Default -eq $true}

 

but it would be better coding practice to do this:

Get-WmiObject -Class Win32_Printer | where {$_.Default}

 

On the local machine this is OK but if you are working remotely than all of the Win32_Printer objects would be returned and the filtering performed locally. Could be an expensive operation.

The better option is to use the –Filter parameter on Get-WmiObject

Get-WmiObject -Class Win32_Printer -Filter "Default = $true"

 

this only returns a single  object

If you want to use WQL then it becomes

Get-WmiObject -Query "SELECT * FROM Win32_Printer WHERE Default = $true"

 

In either case the filtering is done early to reduce the amount of data you are dealing with.

Remember – Filter early & format late

 

For more information on working with printers see chapter 10 of PowerShell and WMIhttp://www.manning.com/powershellandwmi


May 5, 2012  12:50 PM

Scripting Games 2012 comments: #12 date conversions

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

 

A few times in the games you were asked for date time based information. Now WMI has a lot of classes that return WMI information. It is in an awkward format though

PS> Get-WmiObject -Class Win32_OperatingSystem | fl *time*

CurrentTimeZone : 60
LastBootUpTime  : 20120505101515.296000+060
LocalDateTime   : 20120505192615.170000+060

This translates as:

Year – four digits
Month – two digits
Day – two digits
Hour – two digits
Minute – two digits
Second – two digits
.
millionth of second but is usually constrained to milliseconds
+
minutes difference from GMT (UTC)

Its readable but only if you are used to it.

Ideally we want to convert this to a more reasonable looking date such as

05 May 2012 19:35:03

It is possible to take the WMI information, break it up into its components – its is returned as a string – and create a datetime object. Alternatively you can hunt through the .NET classes and find some date conversion methods.

That’s the hard way.

The easy way is to use the built in functionality on the WMI class!

If you put a WMI class through get-member you will discover a couple of Scriptmethods at the end of the data

PS> Get-WmiObject -Class Win32_OperatingSystem | Get-Member -MemberType ScriptMethod

   TypeName: System.Management.ManagementObject#root\cimv2\Win32_OperatingSystem

Name                MemberType   Definition
—-                ———-   ———-
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime   ScriptMethod System.Object ConvertToDateTime();

They are simple to use

If you need the rest of the data on the object

PS> $os = Get-WmiObject -Class Win32_OperatingSystem
PS> $os.ConvertToDateTime($os.LastBootUpTime)

05 May 2012 10:15:15

or if only want the last bootup time

PS> $os = [wmiclass]"\\.\root\cimv2:Win32_OperatingSystem"
PS> $os.ConvertToDateTime($(Get-WmiObject Win32_OperatingSystem | select -ExpandProperty LastBootUpTime))

05 May 2012 10:15:15

There are variations on these themes.

The conversion routines can be used in calculated fields in select and format cmdlets

See PowerShell and WMI chapters 3 and 4 for more details – http://www.manning.com/powershellandwmi


May 5, 2012  7:55 AM

Scripting Games 2012 comments: #11–its true

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

The games are over for another year. The number of entries was huge – 150% increase over last year. Congratulations to the winners and to everyone who took part.

One thing I noticed was the number of scripts that made testing booleans harder than it needed to be.

A boolean can take one of two values – True or False. These are represented in PowerShell by $true and $false respectively

Lets create a couple of variables

PS> $x = $true
PS> $x
True
PS> $y = $false
PS> $y
False

A common test was:

PS> $a = $true
PS> $b = $false

PS> if ($a -eq $true){"Do something"}
Do something

or you might see

PS> if ($b -ne $true){"Do something else"}
Do something else

These work and are perfectly understandable.

They can be made simpler

PS> if ($a){"Do something"}
Do something

we get two ways of testing false using the –not operator and its alias of !

PS> if (-not $b){"Do something else"}
Do something else
PS> if (! $b){"Do something else"}
Do something else

 

Just as a final test to show this really works

PS> if ($b){"Do something"}else{"Do something else"}
Do something else

 

The thing to remember is that on an if statement or anywhere else where a condition is being tested it has to resolve to true or false. In this case the variable (or object property) already carries a boolean value so we can use it directly.

Its not a big saving but will mount up over time – keeps the scripts simpler and therefore keeps the errors down


April 29, 2012  6:01 AM

PowerShell news April 2012

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

As April comes to close so does the extension for grading scripts in the recent games. I’ve been so busy grading haven’t had time to blog about the games. I’ll start to catch up on the backlog over the next few weeks.  This years games have been huge with more than 150% growth in the number of scripts submitted. That is a lot of PowerShell.

 

The Deep Dive starts tonight – with keynotes tomorrow. This is shaping up to be the PowerShell event of the year.

 

Powershell and WMI will be available very soon.

 

Advanced Powershell – http://www.manning.com/jones2/ is well on the way and still on target for a release soon after Windows 8 and Powershell v3

 

Windows 8/windows 2012 release candidate will be with us in early June. Already had one user group session on the topic and there will be another one in May


April 24, 2012  2:01 PM

UK PowerShell Group–April 2012 slides and recording

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

The slides, demo script and recording are available from

https://skydrive.live.com/?cid=43cfa46a74cf3e96#cid=43CFA46A74CF3E96&id=43CFA46A74CF3E96%212957

Thank you again to everyone who attended the Live Meeting this evening


April 23, 2012  2:56 PM

Reminder–UK User group 24 April

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Quick reminder that 24 April sees the first of 2 sessions on new PowerShell functionality in Windows 2012 – Windows Server 8

Details from

http://msmvps.com/blogs/richardsiddaway/archive/2012/04/13/uk-powershell-group-24-april-2012.aspx


April 21, 2012  4:56 AM

Scripting Games 2012 comments: #10 Advanced event 5

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

http://blogs.technet.com/b/heyscriptingguy/archive/2012/04/06/2012-scripting-games-advanced-event-5-list-errors.aspx

This is the one I was asked to supply a commentary for

http://blogs.technet.com/b/heyscriptingguy/archive/2012/04/20/expert-commentary-2012-scripting-games-advanced-event-5.aspx

 

This one has raised an interesting interpretation issues.  The last design point asks:

  • Your output should be organized such that the largest source of errors appears at the top of the output.

The figure shows the data sorted by Event source  

 

I read the design point as log with the largest number of errors comes first.  The script I produced for the commentary doesn’t meet that last design point so I’ve revised it

#Requires -Version 2            
function Get-EventEntryCount{             
[CmdletBinding()]             
param (             
 [parameter(Position=0,            
   ValueFromPipeline=$true,             
   ValueFromPipelineByPropertyName=$true)]            
 [Alias("CN", "Computer")]              
 [string[]]$computername="$env:COMPUTERNAME",            
            
 [parameter(Position=1)]            
 [ValidateSet("Error", "Information", "FailureAudit", "SuccessAudit", "Warning", "All", "*")]            
 [string]$eventtype="Error"             
)             
BEGIN{            
  $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()            
  $testadmin = `
  (New-Object Security.Principal.WindowsPrincipal $currentUser).IsInRole(`
  [Security.Principal.WindowsBuiltinRole]::Administrator)            
              
  if (!$testadmin){            
   Throw "Must be run with elevated privileges"            
  }            
}#begin             
PROCESS{            
  foreach ($computer in $computername ){            
    switch ($computer) {            
     "."         {$computer="$env:COMPUTERNAME"}            
     "localhost" {$computer="$env:COMPUTERNAME"}            
    }            
            
    Write-Verbose "Processing computer: $computer"            
                
    $data = @()            
    if (Test-Connection -ComputerName $computer -Count 1 -Quiet){            
            
    Write-Verbose "Starting Remote Registry service on $computer"            
    $origrrsrv = Get-WmiObject -Class Win32_Service -Filter "Name='RemoteRegistry'" `
    -ComputerName $computer            
            
    if ($origrrsrv.StartMode -eq "Disabled") {            
      Set-Service -Name RemoteRegistry -ComputerName $computer -StartupType "Manual"            
    }            
            
    if ($origrrsrv.State -ne "Running") {            
      $origrrsrv.StartService() | Out-Null            
    }            
            
    Write-Verbose "Retrieving logs for $computer"            
                  
    Get-EventLog -List -ComputerName $computer |            
    foreach {            
      $log = $_.Log            
      Write-Verbose "Processing log: $log"            
                    
      if ($_.Entries.Count -gt 0) {            
         Write-Debug "Processing event type $eventtype"            
                      
         $n = Get-EventLog -LogName $($_.Log) -EntryType $eventtype -ComputerName $computer `
         -ErrorAction SilentlyContinue            
                      
         if ($n -ne $null){            
           Write-Debug "Entries found"            
           $n | group Source -NoElement |             
           foreach{            
                       
              $data += New-Object -TypeName PSObject -Property @{            
                 ComputerName = $computer            
                 LogName = $log            
                 EntryType = $eventtype            
                 EntrySource = $($_.Name)            
                 EntryCount = $($_.Count)            
             }            
           }            
         }            
       }  # end if entries            
       else {            
         Write-Verbose "$($computer): $log is empty"            
       }            
                    
            
        if ($origrrsrv.State -eq "Stopped") {            
          $origrrsrv.StopService() | Out-Null            
        }            
            
        if ($origrrsrv.StartMode -eq "Disabled") {            
          Set-Service -Name RemoteRegistry -ComputerName $computer -StartupType "Disabled"            
        }            
            
     } # end of log processing foreach            
   }            
   else {            
     Write-Warning "Cannot contact $computer"            
   } # end if ping            
   Write-Output $data             
 } ## end computer foreach            
}#process             
END{}#end            
            
<# 
.SYNOPSIS
Counts the number of entries of a given type
in the event logs of a system

.DESCRIPTION
One or more computers - from pipeline or parameter -  are
accessed to read the envent logs and count the entries of a 
given type. Empty logs are tested and the count is set to zero

.PARAMETER  computername
Name of computer for which log information
is to be retrieved

.PARAMETER  eventtype
Log entry type to count.
Accepted values are - 
"Error", "Information", "FailureAudit", 
"SuccessAudit", "Warning", "All", "*"

.EXAMPLE
Get-EventEntryCount 

Accesses logs on local machine. Peforms default display

.EXAMPLE
Get-EventEntryCount -computername "." | 
sort LogName, EntryCount -Descending | 
Format-Table EntrySource, EntryCount -GroupBy LogName

Accesses logs on local machine. Format display and group by logname

.EXAMPLE
"dc02", "webr201", "server02" | 
Get-EventEntryCount | 
sort Computer, LogName, EntryCount -Descending | 
Format-Table Logname, EntrySource, EntryCount -GroupBy Computer

Accesses logs on remote machines. Computer names accepted from pipeline.
Format display and group by computer

.EXAMPLE
Get-EventEntryCount -computername "dc02", "webr201", "server02" |
sort Computer, LogName, EntryCount -Descending | 
Format-Table Logname, EntrySource, EntryCount -GroupBy Computer

Accesses logs on remote machines. Computer names accepted as array.
Format display and group by computer

.INPUTS
Computer name - string or string array
Envent type - string. Must be member of set

.OUTPUTS
Returns a custom object with
properties:
ComputerName - name of computer
LogName - name of log
EntryType - Type of log entry
EntryCount - count of entries
EntrySource - event source

.NOTES


.LINK

#>            
            
}


April 17, 2012  12:45 PM

Scripting Games 2012 comments: #9 Beginners event 5

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Beginners event 5

http://blogs.technet.com/b/heyscriptingguy/archive/2012/04/06/2012-scripting-games-beginner-event-5-provide-a-source-and-errors.aspx

Looking for problem applications. Need report from each server form application log listing source and number of errors.

Requirements:

  • should be capable of running against remote machine
  • doesn’t need to run remotely for this scenario
  • don’t need to check for admin rights
  • assume have permissions to query application log
  • use standard PowerShell cmdlets
  • solution should be simple and straightforward
  • don’t need to write to text file BUT should should be easily redirectable when needed
  • don’t need comment based help
  • extra points for sorting so source with greatest number of errors at top of list

Reading the event log using standard PowerShell cmdlets -  what do we have available?

Get-Command *event* -CommandType cmdlet

shows a bunch of cmdlets. Lets narrow it down

Get-Command *eventlog* -CommandType cmdlet

shows that Get-EventLog is a good choice.

NOTE: Get-WinEvent is also available but the syntax can be more convoluted. As we are reading a classic event log Get-Eventlog is the simplest solution.

What parameters do we have available?

PS> Get-Help Get-EventLog

NAME
    Get-EventLog

SYNOPSIS
    Gets the events in an event log, or a list of the event logs, on the local or remote computers.

SYNTAX 
 

Get-EventLog [-AsString] [-ComputerName <string[]>] [-List] [<CommonParameters>]

Get-EventLog [-LogName] <string> [[-InstanceId] <Int64[]>] [-After <DateTime>] [-AsBaseObject]
[-Before <DateTime>] [-ComputerName <string[]>] [-EntryType <string[]>] [-Index <Int32[]>]
[-Message <string>] [-Newest <int>] [-Source<string[]>] [-UserName <string[]>] [<CommonParameters>]

Look at the synopsis – local or remote computers. Computername for local/remote machines. EntryType gives the type of entry. Thats about it.

PS> Get-Help Get-EventLog -Parameter EntryType

-EntryType <string[]>
    Gets only events with the specified entry type. Valid values are Error, Information, FailureAudit, SuccessAudit, and Warning. The default is all events.

    Required?                    false
    Position?                    named
    Default value                All events
    Accept pipeline input?       false
    Accept wildcard characters?  false

What does an entry look like?

PS> Get-EventLog -LogName Application | select -f 1 | Format-List

Index              : 83084
EntryType          : Information
InstanceId         : 1073872902
Message            : BBSvc has stopped.
Category           : (0)
CategoryNumber     : 0
ReplacementStrings : {BBSvc}
Source             : BBSvc
TimeGenerated      : 17/04/2012 18:00:17
TimeWritten        : 17/04/2012 18:00:17
UserName           :

 

We have an entry type so we can select on that and we have a source.  How can we can the total number of entries per source?

 

PS> Get-EventLog -LogName Application -EntryType Error | group Source

Count Name                      Group
—– —-                      —–
  166 SideBySide                {System.Diagnostics.EventLogEntry, System.Di
   27 Application Error         {System.Diagnostics.EventLogEntry, System.Di
    1 System Restore            {System.Diagnostics.EventLogEntry}
    2 Microsoft-Windows-Rest… {System.Diagnostics.EventLogEntry, System.Di
    3 VSS                       {System.Diagnostics.EventLogEntry, System.Di
   20 Application Hang          {System.Diagnostics.EventLogEntry, System.Di
    1 MsiInstaller              {System.Diagnostics.EventLogEntry}

 

All we need is the Count and the name so we can use the –NoElement parameter

PS> Get-EventLog -LogName Application -EntryType Error | group Source -NoElement

Count Name
—– —-
  166 SideBySide
   27 Application Error
    1 System Restore
    2 Microsoft-Windows-Rest…
    3 VSS
   20 Application Hang
    1 MsiInstaller

 

But  we want them in descending order – actually requirements state largest number of errors at the top of the list which we already have but we will show willing and actually sort the list Smile

PS> Get-EventLog -LogName Application -EntryType Error |
group Source -NoElement | sort Count -Descending

Count Name
—– —-
  166 SideBySide
   27 Application Error
   20 Application Hang
    3 VSS
    2 Microsoft-Windows-Rest…
    1 MsiInstaller
    1 System Restore

 

If you want to see the whole name

PS> Get-EventLog -LogName Application -EntryType Error | group Source -NoElement |
sort Count -Descending | Format-table Count, Name -AutoSize

Count Name
—– —-
  166 SideBySide
   27 Application Error
   20 Application Hang
    3 VSS
    2 Microsoft-Windows-RestartManager
    1 MsiInstaller
    1 System Restore

 

We haven’t shown how to access a remote machine

PS> Get-EventLog -LogName Application -EntryType Error -Computername $env:COMPUTERNAME |
group Source -NoElement | sort Count -Descending | Format-table Count, Name –AutoSize

saving to a file:

PS> Get-EventLog -LogName Application -EntryType Error -Computername $env:COMPUTERNAME |
group Source -NoElement | sort Count -Descending | Format-table Count, Name -AutoSize |
Out-File beg5.txt

is legitimate and gives you exactly what you see on screen – though object police will scream at you for using format-table so if you want to conform try

PS> Get-EventLog -LogName Application -EntryType Error -Computername $env:COMPUTERNAME |
group Source -NoElement | sort Count -Descending | Out-File beg5.txt

but long source names may be truncated

if you really want to demonstrate you know what you are doing

PS> Get-EventLog -LogName Application -EntryType Error -Computername $env:COMPUTERNAME |
group Source -NoElement | sort Count -Descending | Tee-Object -FilePath beg5.txt

tee-object writes to a file and passes along pipeline to finally display

don’t use Add-Content or Set-Content as as all you will get is a set of lines like this

Microsoft.PowerShell.Commands.GroupInfoNoElement

These cmdlets don’t resolve the objects to the data

The Get-WinEvent syntax would be

Get-WinEvent -ComputerName $env:COMPUTERNAME -FilterHashtable @{‘LogName’=’Application’; ‘Level’=2}

which to my mind isn’t as simple as Get-EventLog.

And finally if you just can’t live without aliases – get-eventlog doesn’t have one.


April 15, 2012  10:17 AM

PowerShell and WMI

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

My latest book – PowerShell and WMI – has gone to the printers. Expect it soon at a book shop near you.

www.manning.com/powershellandwmi

for more details


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: