PowerShell for Windows Admins


January 17, 2013  1:49 PM

Starting virtual machines for WSUS

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

My test environment usually has a dozen or so machines at any one time. Some of these are short lived and used for a particular piece of testing – others are kept for  years. I decided that I wanted to keep up to date on the patching of these virtual machines so installed WSUS on a Windows 2012 box.

One issue is that if a VM isn’t started for 10 days WSUS starts complaining that it hasn’t been contacted and if you run the WSUS clean up wizard the non-reporting servers may be removed. Checking the WSUS console for which machines haven’t sync’d recently is a chore.

In Windows 2012 both WSUS and Hyper-V come with a PowerShell module. This means I can do this:

$date = (Get-Date).AddDays(-10)            
Get-WsusComputer -ToLastSyncTime $date |            
sort  LastSyncTime |            
select -First 4 |            
foreach {             
 $computer = ($_.FullDomainName -split "\.")[0]            
 Start-VM -Name $computer -ComputerName Server02 -Passthru            
}

I’m using the WSUS server as my admin box but if you were accessing a remote WSUS machine change the code to

Get-WsusServer -Name w12sus -PortNumber 8530 | Get-WsusComputer –ToLastSyncTime $date |

I sorted the computers WSUS knows about by date – picked the last 4 to sync so I didn’t overwhelm the Hyper-V host and started them up. Only trick is to get the computer name out of the FullDomainName property.

January 17, 2013  2:26 AM

Account SIDs–hopefully my last word

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Ok the embarrassing moral of this story is that you shouldn’t answer questions in a hurry at the end of the evening. 5 minutes after shutting down I realised that there is a far, far simpler way to get the info. Win32_AccountSID is a WMI linking class. It links Win32_SystemAccount and Win32_SID classes.

Get-WmiObject -Class Win32_SystemAccount | select Caption, Domain, Name, SID, LocalAccount

gets you all you need


January 16, 2013  4:47 PM

Account SIDs revisited

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I realised there is an easier way to get the data

function get-SID {            
param (            
 [string]$computername = $env:COMPUTERNAME            
)            
            
Get-WmiObject -Class Win32_AccountSID -ComputerName $computername |            
foreach {            
             
 $exp = "[wmi]'" + $($_.Element) + "'"            
 Invoke-Expression -Command $exp |            
 select Domain, Name, SID, LocalAccount            
}            
}

Use the wmi type accelerator with the path from the Element and you can just select the data you want.  As a bonus you can discover if the account is local or not


January 16, 2013  4:33 PM

Passing function names

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A question asked about passing a function name into another function which then called the function. It sounds worse than it is. if you need to pass the name of a command and then call it try using invoke-expression

function ffour {            
Get-Random            
}            
            
            
function fthree {            
Get-Date            
}            
            
            
function ftwo {            
param(            
 [string]$fname            
)            
            
Invoke-Expression $fname            
}            
            
            
"date"            
ftwo fthree            
            
"random"            
ftwo ffour


January 16, 2013  4:21 PM

Account SIDs

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A question on the forum asked about finding the accounts and SIDs on the local machine.

function get-SID {            
param (            
 [string]$computername = $env:COMPUTERNAME            
)            
            
Get-WmiObject -Class Win32_AccountSID -ComputerName $computername |            
foreach {            
 $da =  (($_.Element).Split(".")[1]).Split(",")            
 $sid = ($_.Setting -split "=")[1] -replace '"',''            
            
 $props = [ordered]@{            
 Domain = ($da[0] -split "=")[1] -replace '"',''            
 Account = ($da[1] -split "=")[1] -replace '"',''            
 SID = $sid            
 }            
             
 New-Object -TypeName PSObject -Property $props            
}            
            
}

Pass a computer name into the function – default is local machine.

Use the AccountSID class which links Win32_SystemAccount and Win32_SID.  For each returned instance clean up the data and create an object with three properties – domain, account and SID.

You will see more than you thought – some very useful information buried in there


January 16, 2013  2:29 PM

UK PowerShell group – 29 January 2013

Richard Siddaway Richard Siddaway Profile: Richard Siddaway


When: Tuesday, Jan 29, 2013 7:30 PM (GMT)


Where: virtual

*~*~*~*~*~*~*~*~*~*

Active Directory is one of the commonest automation targets for administrators. This session will covert the basics of automating your AD admin – scripts and the Microsoft cmdlets. The new features in PowerShell for Windows 2012 AD will also be covered

Notes


Richard Siddaway has invited you to attend an online meeting using Live Meeting.
Join the meeting.
Audio Information
Computer Audio
To use computer audio, you need speakers and microphone, or a headset.
First Time Users:
To save time before the meeting, check your system to make sure it is ready to use Microsoft Office Live Meeting.
Troubleshooting
Unable to join the meeting? Follow these steps:

  1. Copy this address and paste it into your web browser:
    https://www.livemeeting.com/cc/usergroups/join
  2. Copy and paste the required information:
    Meeting ID: RCRWH3
    Entry Code: 5p7$}S_!h
    Location: https://www.livemeeting.com/cc/usergroups

If you still cannot enter the meeting, contact support

Notice
Microsoft Office Live Meeting can be used to record meetings. By participating in this meeting, you agree that your communications may be monitored or recorded at any time during the meeting.


January 16, 2013  12:18 PM

PowerShell wins award

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

PowerShell has won one on InfoWorld’s Technology of the Year awards for 2013

See http://www.infoworld.com/slideshow/80986/infoworlds-2013-technology-of-the-year-award-winners-210419#slide24

for details


January 16, 2013  11:02 AM

Workflow article 4

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

The next in the series of articles on PowerShell workflows that are appearing on the Scripting Guy blog has been published.

The articles in the series that have been published are:

http://blogs.technet.com/b/heyscriptingguy/archive/2012/12/26/powershell-workflows-the-basics.aspx
http://blogs.technet.com/b/heyscriptingguy/archive/2013/01/02/powershell-workflows-restrictions.aspx
http://blogs.technet.com/b/heyscriptingguy/archive/2013/01/09/powershell-workflows-nesting.aspx
http://blogs.technet.com/b/heyscriptingguy/archive/2013/01/16/powershell-workflows-job-engine.aspx

Look for the next article in one weeks time.

Until then Enjoy!


January 15, 2013  3:38 PM

Updating Help on PowerShell v3

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

One of the new features in PowerShell v3 is the capability to update the help files. In fact you have to do this because PowerShell v3 doesn’t ship with any help files. Since Windows 8 RTM’d there have been a succession of new help files released.

I discovered one of my netbooks didn’t have the latest version of the help files installed. So I needed to update them. This got me thinking that it would be better if the machine did this for me.

I could think of two easy ways to do this – a scheduled job or a scheduled task. I chose the scheduled task because the ScheduledTasks module is available on the version of PowerShell v3 for Windows 7 and other legacy versions of Windows. The PSScheduledJob module is only available on Windows 8/2012 as it’s based on WMI classes not present on older versions of Windows.

$actionscript = '-NonInteractive -WindowStyle Normal -NoLogo -NoProfile -NoExit -Command "& {Update-Help -UICulture en-US -Force}"'            
$pstart =  "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"            
#$days = "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"             
$days = 'Wednesday'            
            
Get-ScheduledTask -TaskName UpdatePSHelp | Unregister-ScheduledTask -Confirm:$false            
            
$act = New-ScheduledTaskAction -Execute $pstart -Argument $actionscript            
$trig = New-ScheduledTaskTrigger -Weekly -WeeksInterval 4 -At 19:00 -DaysOfWeek $days            
Register-ScheduledTask -TaskName UpdatePSHelp -Action $act -Trigger $trig -RunLevel Highest

Start by creating the command strings to start PowerShell and the arguments you pass to it.  I left it as a visible PowerShell window that stays opn so I can see the results. The PowerShell command

Update-Help -UICulture en-US –Force

performs the actual update.  You will need to change the culture to match yours if you aren’t using English.  You can find it by using

Get-UICulture

I’m only going to run this on Wednesdays .

Any old copies of the task are cleaned out and new task actions (to execute PowerShell) and trigger to define when it runs are created. The last line registers the task.

You can view the task

Get-ScheduledTask -TaskName UpdatePSHelp

or start the task manually

Start-ScheduledTask -TaskName UpdatePSHelp


January 11, 2013  1:40 PM

Select-string – keeping in context

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

 

Today’s question involves using the Context parameter:

It’s probably just me, but I’ve never gotten the switch ‘-context 5
or -context 2, 7′ to work predictably – where 5 lines before and after or 2
before and 7 after will come out – have you?

Let’s start by looking at the default behaviour of select-string using the search pattern you’ve seen previously:

PS> Select-String -Path c:\test\*.txt -Pattern "\A\w{5}ABCD"

C:\test\fixedcol.txt:1:12345ABCD123451234512345

C:\test\fixedcol.txt:3:12345ABCD123451234512345

C:\test\fixedcol.txt:4:12345abcd123451234512345

C:\test\fixedcol.txt:6:12345ABCD123451234512345

C:\test\fixedcol2.txt:1:12345ABCD123451234512345

As you can see the line which matches your pattern is returned.  Often this is all that is required but there are occasions when you need to be able to put the line into context that is you need to understand how the line containing you pattern relates to the data around it.  The is what the context parameter can provide. 

If you look at the Select-String help file you will find this information on context.

-Context <Int32[]>       

Captures the specified number of lines before and after the line with the match. This allows you to view the match in context.               

Required?                    false       

Position?                    named       

Default value                       

Accept pipeline input?       false       

Accept wildcard characters?  false

The first thing to note is that the parameter takes an array of integers. The first (or only member of the array) tells PowerShell how many lines to show from before and after the matching line while the second member of the array controls the number of lines that are displayed after the matching line. Put simply if you supply one values it controls the number of lines from before and after you match that are displayed but if you specify two values then you explicitly control the lines from before the match with the first value and the lines from after the match with the second. Some examples should make this clear.

I’m going to use a file where we know the contents – it makes the explanations easier. If you run this:

Get-Process | sort CPU -Descending | Out-File -FilePath c:\test\proc.txt –Force

You get a text file with the processes listed by CPU usage. You can examine the file for a particular process – this case let’s look at Word:

PS> Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch

 

C:\test\proc.txt:8:    360      25    20368      61728   331    41.89   4976 WINWORD

You know that the file is ordered by CPU usage so what are the processes using similar amounts of CPU to Word?

PS> Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context 2

 

  C:\test\proc.txt:6:    653      34    66640      94416   286    48.77   3724 powershell

  C:\test\proc.txt:7:   1124      29    13912      19336   210    47.71   5868 LiveComm

> C:\test\proc.txt:8:    360      25    20368      61728   331    41.89   4976 WINWORD

  C:\test\proc.txt:9:    212       9     2788      10956    78    28.67   4112 SynTPEnh

  C:\test\proc.txt:10:   565      35    49172      82572   347    12.50   5660 WWAHost

 

The matching line is marked with a > symbol. I’ve made it bold in the above listing for emphasis.

If you specify a number such that the file doesn’t have enough lines to display then only those lines that are available will be displayed for instance

Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context 12

This can only display the seven lines prior to the match so that’s all it does.

What about the situation where you only want the three processes that are using more CPU than Word?

PS> Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context 3,0

 

  C:\test\proc.txt:5:   1555      74    30660      87176   465    80.70   5308 explorer

  C:\test\proc.txt:6:    653      34    66640      94416   286    48.77   3724 powershell

  C:\test\proc.txt:7:   1124      29    13912      19336   210    47.71   5868 LiveComm

> C:\test\proc.txt:8:    360      25    20368      61728   331    41.89   4976 WINWORD

This works as well

PS> Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context 3,$null

 

  C:\test\proc.txt:5:   1555      74    30660      87176   465    80.70   5308 explorer

  C:\test\proc.txt:6:    653      34    66640      94416   286    48.77   3724 powershell

  C:\test\proc.txt:7:   1124      29    13912      19336   210    47.71   5868 LiveComm

> C:\test\proc.txt:8:    360      25    20368      61728   331    41.89   4976 WINWORD

The one thing you can’t do is this:

PS> Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context 3,

>> 

PowerShell expects something after the comma and will prompt you to supply it.

The converse holds true if you want the lines that occur after the match. You can use a 0 as the first element:

PS> Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context 0,3

 

> C:\test\proc.txt:8:    360      25    20368      61728   331    41.89   4976 WINWORD

  C:\test\proc.txt:9:    212       9     2788      10956    78    28.67   4112 SynTPEnh

  C:\test\proc.txt:10:    565      35    49172      82572   347    12.50   5660 WWAHost

  C:\test\proc.txt:11:    276      19     6608      12628    88     9.33   5252 taskhostex

Or $null

PS> Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context $null,3

 

> C:\test\proc.txt:8:    360      25    20368      61728   331    41.89   4976 WINWORD

  C:\test\proc.txt:9:    212       9     2788      10956    78    28.67   4112 SynTPEnh

  C:\test\proc.txt:10:    565      35    49172      82572   347    12.50   5660 WWAHost

  C:\test\proc.txt:11:    276      19     6608      12628    88     9.33   5252 taskhostex

 

You can’t leave the first element blank

PS> Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context ,3

At line:1 char:76

+ Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context ,3

+                                                                            ~

Missing argument in parameter list.

    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException

    + FullyQualifiedErrorId : MissingArgument

This leads to the situation where you need to display a different number of lines before and after the match:

PS> Select-String -Path c:\test\*.txt -Pattern "Winword" -SimpleMatch -Context 3,2

 

  C:\test\proc.txt:5:   1555      74    30660      87176   465    80.70   5308 explorer

  C:\test\proc.txt:6:    653      34    66640      94416   286    48.77   3724 powershell

  C:\test\proc.txt:7:   1124      29    13912      19336   210    47.71   5868 LiveComm

> C:\test\proc.txt:8:    360      25    20368      61728   331    41.89   4976 WINWORD

  C:\test\proc.txt:9:    212       9     2788      10956    78    28.67   4112 SynTPEnh

  C:\test\proc.txt:10:    565      35    49172      82572   347    12.50   5660 WWAHost

What happens if you have multiple matches and their contexts overlap?

PS> Select-String -Path c:\test\*.txt -Pattern "PowerShell" -SimpleMatch

 

C:\test\proc.txt:9:    669      34    67544      62228   286    54.99   3724 powershell

C:\test\proc.txt:12:    473      23    69112      86616   366    11.73   1688 powershell_ise

C:\test\proc.txt:19:    346      13    39576      44540   207     3.15    280 PowerShell

This file shows matches on lines 9, 12 and 19 so let’s try this

PS> Select-String -Path c:\test\*.txt -Pattern "PowerShell" -SimpleMatch -Context 4,5

 

  C:\test\proc.txt:5:   1840      91    35836      75652   545   142.49   5308 explorer

  C:\test\proc.txt:6:    618      23    68524      28788   194   108.84   5072 SkyDrive

  C:\test\proc.txt:7:   1377      29    15668      23440   210    95.07   5868 LiveComm

  C:\test\proc.txt:8:    211       9     2792       4516    78    59.98   4112 SynTPEnh

> C:\test\proc.txt:9:    669      34    67544      62228   286    54.99   3724 powershell

  C:\test\proc.txt:10:    560      35    53480      88760   370    22.95   2656 WWAHost

  C:\test\proc.txt:11:    240       8     1952       2340    71    12.32   5580 TabTip

> C:\test\proc.txt:12:    473      23    69112      86616   366    11.73   1688 powershell_ise

  C:\test\proc.txt:13:    254       9     4684       9940    86    11.31   6136 RuntimeBroker

  C:\test\proc.txt:14:    285      14     4116       4724    84    10.19   5252 taskhostex

  C:\test\proc.txt:15:     82       5     2008       6148    55     7.52   2416 conhost

  C:\test\proc.txt:16:    305      14    17608       5088   184     5.19    404 IAStorIcon

  C:\test\proc.txt:17:    337       8     2180        536    76     4.79    376 InputPersonalization

  C:\test\proc.txt:18:    409      12     4416       5464    79     4.26   5276 taskhost

> C:\test\proc.txt:19:    346      13    39576      44540   207     3.15    280 powershell

  C:\test\proc.txt:20:    125       5     2820        744    70     2.61    908 splwow64

  C:\test\proc.txt:21:    347      13    12180        804   183     2.40   4788 PopUp_DM

  C:\test\proc.txt:22:    335      10     2576       1756    83     2.20   4636 AdobeARM

  C:\test\proc.txt:23:    249      21     6628        540   129     1.44   5220 SRSPremiumPanel

  C:\test\proc.txt:24:    387      11     3436      12280    83     0.94   3828 WSHost

I’ve highlighted the lines that actually match.

Starting with the first match you get 4 lines before it as requested. There should be 5 lines after the match BUT the next match is only 3 lines on and you asked for 5 lines after that. The lines before the last match overlap the lines after the second match. The lines after the last match are shown as requested.

At first glance it looks like the command hasn’t worked but what seems to be happening is that only unique lines are displayed.

I looked at the individual matches

$finds = Select-String -Path c:\test\*.txt -Pattern "PowerShell" -SimpleMatch -Context 4,5

for ($i=0; $i -le $finds.count; $i++){$finds[$i]; "###"*8}

and received this output (I’ve split the display so you can see what is produced.

First match:

  C:\test\proc.txt:5:   1840      91    35836      75652   545   142.49   5308 explorer

  C:\test\proc.txt:6:    618      23    68524      28788   194   108.84   5072 SkyDrive

  C:\test\proc.txt:7:   1377      29    15668      23440   210    95.07   5868 LiveComm

  C:\test\proc.txt:8:    211       9     2792       4516    78    59.98   4112 SynTPEnh

> C:\test\proc.txt:9:    669      34    67544      62228   286    54.99   3724 powershell

  C:\test\proc.txt:10:    560      35    53480      88760   370    22.95   2656 WWAHost

  C:\test\proc.txt:11:    240       8     1952       2340    71    12.32   5580 TabTip

########################

Correct number before but restricted output after

Second match:

> C:\test\proc.txt:12:    473      23    69112      86616   366    11.73   1688 powershell_ise

  C:\test\proc.txt:13:    254       9     4684       9940    86    11.31   6136 RuntimeBroker

  C:\test\proc.txt:14:    285      14     4116       4724    84    10.19   5252 taskhostex

  C:\test\proc.txt:15:     82       5     2008       6148    55     7.52   2416 conhost

  C:\test\proc.txt:16:    305      14    17608       5088   184     5.19    404 IAStorIcon

  C:\test\proc.txt:17:    337       8     2180        536    76     4.79    376 InputPersonalization

########################

Nothing before and correct output after the match

Last match:

 C:\test\proc.txt:18:    409      12     4416       5464    79     4.26   5276 taskhost

> C:\test\proc.txt:19:    346      13    39576      44540   207     3.15    280 powershell

  C:\test\proc.txt:20:    125       5     2820        744    70     2.61    908 splwow64

  C:\test\proc.txt:21:    347      13    12180        804   183     2.40   4788 PopUp_DM

  C:\test\proc.txt:22:    335      10     2576       1756    83     2.20   4636 AdobeARM

  C:\test\proc.txt:23:    249      21     6628        540   129     1.44   5220 SRSPremiumPanel

  C:\test\proc.txt:24:    387      11     3436      12280    83     0.94   3828 WSHost

########################

One line before the match and correct number after the match.

This confirms that if a line has appeared in a previous match you won’t see it again. Is there a way to see the full context for each match? Unfortunately, Select-String doesn’t appear to provide that capability directly. A little bit of working with the output should enable this.

Select-String -Path c:\test\*.txt -Pattern "PowerShell" -SimpleMatch -Context 4,5 |

foreach {

#matching line

$padlength = (" {0}:{1:00}: " -f $_.Path, $_.LineNumber).Length

$pad = " "*$padlength

 

$_.Context.PreContext | foreach {$_.Trim().Insert(0,$pad)}

""

" {0}:{1:00}: {2}" -f $_.Path, $_.LineNumber, ($_.Line).Trim()

""

$_.Context.PostContext | foreach {$_.Trim().Insert(0,$pad)}

""

""

}

 

Run the select-string as before. For each of the matches find the length of the formatted path and line number and create a blank string of that length.

If you examine the MatchInfo type that Select-String produces you will see a Property called Context. If you examine that you will see it contains the collection of data for the pre and post context. (There are also display versions of the context). 

For each line in the pre-context insert the pad characters at the beginning. Display the formatted match line and then display the post-context data.  I’ve inserted some blank lines to help format the display

You will get output like this:

                       1840      91    35836      75652   545   142.49   5308 explorer

                      618      23    68524      28788   194   108.84   5072 SkyDrive

                      1377      29    15668      23440   210    95.07   5868 LiveComm

                      211       9     2792       4516    78    59.98   4112 SynTPEnh

 

 C:\test\proc.txt:09: 669      34    67544      62228   286    54.99   3724 powershell

 

                      560      35    53480      88760   370    22.95   2656 WWAHost

                      240       8     1952       2340    71    12.32   5580 TabTip

                      473      23    69112      86616   366    11.73   1688 powershell_ise

                      254       9     4684       9940    86    11.31   6136 RuntimeBroker

                      285      14     4116       4724    84    10.19   5252 taskhostex

 

 

                      211       9     2792       4516    78    59.98   4112 SynTPEnh

                      669      34    67544      62228   286    54.99   3724 powershell

                      560      35    53480      88760   370    22.95   2656 WWAHost

                      240       8     1952       2340    71    12.32   5580 TabTip

 

 C:\test\proc.txt:12: 473      23    69112      86616   366    11.73   1688 powershell_ise

 

                      254       9     4684       9940    86    11.31   6136 RuntimeBroker

                      285      14     4116       4724    84    10.19   5252 taskhostex

                      82       5     2008       6148    55     7.52   2416 conhost

                      305      14    17608       5088   184     5.19    404 IAStorIcon

                      337       8     2180        536    76     4.79    376 InputPersonalization

 

 

                      82       5     2008       6148    55     7.52   2416 conhost

                      305      14    17608       5088   184     5.19    404 IAStorIcon

                      337       8     2180        536    76     4.79    376 InputPersonalization

                      409      12     4416       5464    79     4.26   5276 taskhost

 

 C:\test\proc.txt:19: 346      13    39576      44540   207     3.15    280 powershell

 

                      125       5     2820        744    70     2.61    908 splwow64

                      347      13    12180        804   183     2.40   4788 PopUp_DM

                      335      10     2576       1756    83     2.20   4636 AdobeARM

                      249      21     6628        540   129     1.44   5220 SRSPremiumPanel

                      387      11     3436      12280    83     0.94   3828 WSHost

Not the greatest of displays but you do get to see the data. It should be possible to do this through PowerShell’s formatting system but that’s a post for another day.

Bottom line – the context parameter only displays unique lines so you won’t necessarily get what you expect if there are multiple matches in a file.


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: