Wednesday, December 22, 2010

A diversion

Gonna take a break from the System Admin stuff for a second to share the best darn game deal out there right now. The Humble Indie Bundle. Pay what you want for 6 fun games, or pay more than the average ($7.44 when I wrote this) and get last years bundle as well! World of Goo is easily worth the price of admission. And you can activate all the games on Steam, or just download them and keep the DRM free installers. The Humble Indie Bundle, it's awesome.

Thursday, December 16, 2010

Reset Live@Edu passwords with a simple Windows Form

Once again, I’ve gone what seems like ages without taking the time to share.  We’ve been so busy with implementing Microsoft’s Live@Edu service as well as piloting VMWare’s View I just haven’t taken the time.  In any case, here’s a little form I put together in Primalforms that connects to Live@Edu, and allows you to search for your students and change their passwords easily.  You can embed credentials, but by default the script will prompt for credentials each time the script is run.  The code is below, I think I've decided to go with poshcode.org for pasting code and what not.

Friday, October 1, 2010

When is an empty string not an empty string?

Man, it’s been a long time since I took the time to post something for which I apologize.  It’s been a busy few months.  But today we’ll get back into the swing of things with a mystery to solve.  We start with a distilled version of an advanced function a friend of mine was working with:
function Test-Mystery
{
    [CmdletBinding()]
    param
    (
        [ValidateNotNullOrEmpty()]
        $paramOne
    )
    
    $paramOne
    $paramOne.length
}
Pretty straightforward advanced function stuff, we have a parameter that we need to validate. It can be anything it wants as long as it’s not null, and is not an empty string. If we test this, we’ll find that it works. Mostly.

PS > $x = "HELLO WORLD"
PS > Test-Mystery -paramOne $x
HELLO WORLD
11


PS > $x = ""
PS > Test-Mystery -paramOne $x
Test-Mystery : Cannot validate argument on parameter 'paramOne'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.
At line:2 char:23
+ Test-Mystery -paramOne <<<<  $x
    + CategoryInfo          : InvalidData: (:) [Test-Mystery], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Test-Mystery


Okay, that works as expected.  But let’s try that with input from the Read-Host cmdlet.  First we’ll enter in a string, then we’ll just hit Enter to return nothing (in theory).

PS > $x = Read-Host "Test "
PS > Test-Mystery -paramOne $x
Test: HELLO WORLD
HELLO WORLD
11


PS > $x = Read-Host "Test "
PS > Test-Mystery -paramOne $x
Test:

0

Uhh….what?  That totally should have exploded, and yet it doesn’t.  Adding to the mystery is that the variable being assigned the return value from Read-Host is indeed a [System.String] variable and it is assigned the value of ‘’ (or an empty string).  At least that’s what PSDebug tells us:

PS > $x = read-host 'test '
DEBUG:    1+ $x = <<<<  read-host 'test '
test :
DEBUG:     ! SET $x = ''.


PS > $x.gettype() 
IsPublic IsSerial Name    BaseType
-------- -------- ------  --------
True     True     String  System.Object


PS > $x –eq “”
True


And so thus is given to us the mystery:  when is an empty string not an empty string?  Evidently, when it’s created by Read-Host.  But, thankfully there is a fairly straight forward solution.  We strongly type the variable being assigned the output from Read-Host.

PS > [string]$x = Read-Host 'Test '
Test :

PS > Test-Mystery $x
Test-Mystery : Cannot validate argument on parameter 'paramOne'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.
At line:2 char:13
+ Test-Mystery <<<<  $x
    + CategoryInfo          : InvalidData: (:) [Test-Mystery], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Test-Mystery


I’ll pose this question on the PowerShell Technet Forums, and see if it this is ‘as designed’ or a genuine bug that we need to file on Connect.  Until then, case (almost) closed.

Friday, August 6, 2010

Hey! A send email cmdlet!

So from the ‘oh my god I can’t believe I hadn’t found this earlier’ corner comes this:  PowerShell 2.0 ships with a new cmdlet called Send-MailMessage.  Why is this kinda sorta a big deal (at least to me, haha)?  PSCX will be deprecating the Send-SmtpMail cmdlet (that you may or may not have been using profusely, I know I have) in the next major revision.  So it’s time to start going through scripts and updating as needed.  Thankfully, the syntax is very similar.  The only changes that I found I needed to make were:

  • Change –AttachmentLiteralPath/-AttachmentPath to –Attachments
  • Change –SmtpHost to –SmtpServer

Friday, July 9, 2010

Track Mass Exchange Mailbox Moves

Moving a mailbox or two between Exchange databases and/or servers isn’t too hard to keep track of, but what if you’re moving 200?  Or 2000?  I came up with a little “one-liner” (can we really call a do loop a one-liner?) while migrating the bulk of our mailboxes from Exchange 2003 to 2010.  Hopefully you can use it to help you keep an eye on things too.  Here’s the script, first in a more traditional form, then in one-liner form for easy copy and paste into a shell session.  It’s a do {} until ($false) loop so you have to Ctrl-C to break out of it.

Traditional Script Form

do
{
  Clear-Host
  $mr = Get-MoveRequest

  $mr | Group –Property Status | Select Name,Count | Format-Table –auto
  $mr | Where { $_.status –eq “InProgress” } | Select DisplayName,TotalMailboxSize,PercentComplete |
    Format-Table –auto
  Start-Sleep –seconds 60
} until ($false)

One-Liner Form

do { cls ; $mr = Get-MoveRequest ; $mr | group status | select Name,Count | ft –auto ; $mr | ? { $_.status –eq “InProgress” } | select DisplayName,TotalMailboxSize,PercentComplete | ft –auto ; sleep –seconds 60 } until ($false)

Hope you find that helpful!

Monday, June 14, 2010

Updated PDFCreator post

I've updated my post on running PDFCreator as a service to the latest version, 1.0.1.  It's also now a static page with a link at the top.  Enjoy!

Wednesday, June 9, 2010

Things I learned from the Scripting Games 2010 #5

In this final entry in the ‘Things I learned from the Scripting Games’ series, we’ll go over two different yet related bits of information.  The first is displaying balloon tips in the notification area (System Tray).  The second is displaying log-like feedback in a form generated in PowerShell.  Because they both relate to presenting information or feedback to the user, and because it’s been awhile since I posted anything…and because I promised someone I would try real hard to update my PDFCreator post this week, both subjects fit in one post today.

Balloon Tips

Often times when we need to present the user with information in a graphical way, the messagebox is sufficient.  However, because the messagebox is synchronous by default (that is, the script pauses execution until the messagebox is closed) and there isn’t an easy way to create an asynchronous messagebox, they don’t fit every scenario.  Sometimes we just want to display some information to the user and move on.  Enter balloon tips.

For me, the most challenging part of making a balloon tip function wherever the script is running is the fact that it requires an icon.  How, exactly, is one to deliver an icon when (especially during the scripting games) all we have at our disposal is our PowerShell script file?  Let’s get our object setup and then we’ll answer that question.  First things first, load the forms assemblies:

[void][reflection.assembly]::Load("System.Windows.Forms, `
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")

Now we can create a new NotifyIcon object, and setup the text for the balloon tip:

$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
$notifyIcon.BalloonTipTitle = "Balloon Title"
$notifyIcon.BalloonTipText =  "Balloon Text"

So, about that icon…turns out we can extract an icon from a file to use for the NotifyIcon object.  What file with icons should every relevant Windows system have?  Explorer.exe!

$notifyIcon.Icon = [System.drawing.icon]::ExtractAssociatedIcon(`
"$env:windir\explorer.exe")

OK, so now we have everything setup, let’s display that balloon tip.  First we have to make the NotifyIcon visible, then we can display the balloon tip for the number of milliseconds we specify:

$notifyIcon.Visible = $true
$notifyIcon.ShowBalloonTip(3000)

There we go.  Presenting information to the user in a way that doesn’t pause script execution, done.

Display Scrolling Text in a Form Window

Coming up with a “how many times would I actually use this” example for this particular technique has proven difficult.  Maybe it’s because I don’t have much experience creating GUIs designed for end users.  But that said, a wall (or box) of scrolling text seems so deceptively simple that it took me a while to figure out how to accomplish the task.

In advanced event 10 of the scripting games, one element was to present the user with progress of files being moved.  Instantly the picture of the nullsoft installer with it’s wall of text during the install sprung into my mind.  Achieving a similar effect isn’t too involved.  Here’s how we do it.

First we’re going to setup 3 objects: a form, a textbox, and a button.

$form = new-object windows.forms.form
$form.Size = New-Object System.Drawing.Size(270,125)

$txtBox = new-object windows.forms.textbox
$txtbox.Multiline = $true
$txtBox.WordWrap = $false
$txtBox.size = New-Object System.Drawing.Size(250,60)
$txtBox.ScrollBars = [System.Windows.Forms.ScrollBars]::Vertical

$btn = new-object windows.forms.button
$btn.Location = new-object system.drawing.point(0,60)
$btn.Text = "Go!"

$form.Controls.Add($txtBox)
$form.Controls.Add($btn)

So we’ve assigned some size and location information to the controls and added the controls to the form, which is standard business so far.  Two properties of the textbox control are important for this particualr task: Multiline & ScrollBars.  We make sure to set the multiline property to true, and assign vertical scrollbars to the textbox.  I also like to disable wordwrap, but that’s just me.

Next let’s setup the scriptblock that will run when we click the button:

$button_OnClick = {
    1..10 | % {
        $txtBox.Lines += $_
        $txtBox.Select($txtBox.Text.Length, 0)
        $txtBox.ScrollToCaret()
        $form.Update()
        start-sleep 1
    }
}
$btn.add_Click($button_OnClick)

So this is pretty simple.  We create a variable ($button_OnClick) and assign it a scriptblock in which we have a small loop.  We take the array created by the 1..10 range, and for each number we:

  1. Add the number to the lines array property of the textbox
  2. Move the caret (cursor) to the last character in the textbox
  3. Scroll the textbox view to the caret
  4. Update the form
  5. Pause for a second

We add that scriptblock to the click event of the button by using the hidden method add_Click($button_onClick).  As a side note in case you’re wondering, you use the –Force parameter of Get-Method to display hidden members.  Lastly let’s show the form:

$form.showdialog()

Give that button a push!  Sure it’s a simplistic example, but it can be used to pretty good effect.  The $form.update() method caught me by surprise, probably because I’m pretty green with this GUI business.  To see what happens without calling the update method, comment out or delete the $form.Update() line and run $form.showdialog() again.  It’s like the whole form freezes while the loop runs!  Bad jujus.

To see a more complete example you can check out my submission for Event 10 of the Scripting Games here.

Wednesday, June 2, 2010

Sorry for the reposts and instability

I've been trying to troubleshoot an issue with the feed posting, and ended up deleting and reposting all the entries from the last month or so.  Sorry about that, hopefully I can get the feed & keyword business straightened out soon.

Happy Wednesday!

Things I learned from the Scripting Games 2010 #4: Background Jobs

Okay, so background jobs themselves aren’t really the subject of this post.  But a handy function to make efficient use of them, and a quick gotcha when using background jobs via Start-Job is.  Often times we need to execute the same code over and over, with one or two variations in each iteration.  That's what the function we’ll look at today is built to do.  The two mandatory parameters for this function, -scriptblock & -collection, really allow for some flexible backgrounding of similar tasks.  Take a moment and think, how many times have I needed to run the same or similar code against 10+ computers, users, files, directories, etc.  Probably doesn’t take too long before you have at least a few examples.

Before we get into the function let’s address the gotcha.  I learned about a new parameter to Start-Job in Event #5 this year, ArgumentList.  If you look at the documentation on this parameter it is a little bit misleading.  I’ve filed a documentation bug on connect should you wish to vote for it.  The short of it is, even though the parameter help doesn’t mention that the ArgumentList parameter works with the ScriptBlock parameter, it does.  Which is pretty important.  To illustrate this, try the following:
  1. Create a variable
    $arr = 1..5
  2. Start a background job that writes that variable to output
    $job = Start-Job –Scriptblock { Write-Output $arr }
  3. Receive the job
    Receive-Job $job
  4. Notice that there is no output, sadface.
So, what happened?  Why don’t we have any output?  The answer lies in two bits of information.  The first we get from Get-Help about_jobs:
Important: Background jobs that are started by using Start-Job or the AsJob parameter of Invoke-Command rely on the Windows PowerShell remoting infrastructure. To use these features, Windows PowerShell must be configured for remoting, even if the background job is run only on the local computer. For more information, see about_Remote_Requirements.
The second from Get-Help about_scopes:
Sessions: A session is an environment in which Windows PowerShell runs. When you create a session on a remote computer, Windows PowerShell establishes a persistent connection to the remote computer. The persistent connection lets you use the session for multiple related commands.
Because a session is a contained environment, it has its own scope, but a session is not a child scope of the session in which is was created. The session starts with its own global scope. This scope is independent of the global scope of the session. You can create child scopes in the session. For example, you can run a script to create a child scope in a session.
Aha!  The variable scope is separate and distinct inside of the scriptblock that we pass to Start-Job.  So we use the ArgumentList parameter to get the information that we need to the scriptblock.  Let’s see that in action:
  1. Create a variable:
    $arr = 1..5
  2. Start a background job, passing the variable as an argument:
    $job = Start-Job –Scriptblock { Write-Output $args } –ArgumentList $arr
  3. Receive the job
    Receive-Job $job
  4. Hey there’s our information!
Sweet.  Hopefully that information will make working with background jobs a little easier for you.  Below is a function I wrote as part of my solution for Event 5.  It takes a scriptblock and a collection of items & iterates through the collection passing each item into the scriptblock.  By default it displays a progress bar to keep you abreast of the progress of the jobs.  For detailed information on how you can use it and examples, import the function into your powershell session and run ‘Get-Help Start-BackgroundJobs’
Get the function here.

Things I learned from the Scripting Games 2010 #3: Test-Connection

Who knew there was a cmdlet designed just for testing remote connections?  Not me, at least until the scripting games this year.  For the past....forever, I've been doing something like the following to test remote connections

$pingResults = Ping-Host -HostName $ComputerName -Count 2 -Quiet -ErrorAction SilentlyContinue
if ($pingResults.received -gt 0)
{
    # Do something
}

And then I discovered test-connection! Now for simple quick tests we can just do:

if (Test-Connection $ComputerName -count 2 -quiet)
{
    #Do something
}

For larger groups of computers I'll still use PSCX's Ping-Host cmdlet due it's ability to ping all specified hosts asynchronously and it's speed.  You've updated to PSCX 2.0, right?  But Test-Connection is the right tool at the right price for quick connectivity checks.  Learn more about Test-Connection with Get-Help Test-Connection or on Technet.

Things I learned from the Scripting Games 2010 #2: #Requires

So this post comes from the "how embarrassing" folder.  I've been fiddling with and using PowerShell since it was the Monad beta, and until the scripting games I didn't know about #Requires.  '#Requires' is awesome.  You just stick it at or around the start of your script, and the magic happens.  There's a pretty good summary available in PowerShell if you type:

get-help about_Requires

But this all comes with a bit of a caveat, there isn't an option for #Requires for those nifty new things called modules!  So we can '#Requires -version 2' to require PowerShell version 2, we can '#Requires -PSSnapIn Quest.ActiveRoles.ADManagement' to require the Quest AD snap-in....but we can't '#Requires -Module grouppolicy' to require a module.  Bummer.  So how can we require a module the "hard" way?  Here's one way:

function Test-ModuleAvailability([string]$modulename,[switch]$Import)            
{            
    # Check to see if our module is available            
    [System.Management.Automation.PSModuleInfo]$results = `
          Get-Module -ListAvailable | ? { $_.name -ieq $modulename }            
    if ($results)            
    {            
        if ($Import -and $results.sessionState -eq $null)            
        {            
            #Import the module            
            Import-Module $results            
        }            
        #Return true, the module is available            
        $true            
    }            
    else            
    {            
        #Return false, the module is not available            
        $false            
    }                
}

With this function we can easily test for a module's availability, load it if it we need to, and throw an error if $false is returned:

if (-not $(Test-ModuleAvailability FileSystem -Import))            
{            
    Throw "Could not find or load required Module FileSystem"            
}

Not too hard, but maybe those PowerShell guys will hook us up with a beefed up #Requires statement later.

Until tomorrow!

Scripting Games Results

Scripting Games Results

Wow, what a couple of weeks.  Haven't posted much between the scripting games & Exchange 2010 training this week I've been really busy.  Awaiting the official final results from the scripting games, but it looks like I've managed to snag 3rd in the 'Advanced Division'.  Well...at least according to "Clint's Rules" which are different from the official rules - you can download the spreadsheet I created here.  What can I say...I'm a little competitive by nature.  But more importantly, man did I learn a lot from doing the exercises and from reading what other people submitted.  This week I'll try to post one thing I learned each day (maybe even starting today if I can resist the play SC2 beta urge) this week since class is over at 4 and I'm all by myself.  In fact....here we go, the thing I remember first is:

A way better method of looping/waiting for an event and then acting on it:

Look at lines 34-44 in Robert Robelo's Event 6 submission.  Compare that to all the nasty global variables and junk I made to work inside the event handler in my submission for Event 6 (had to make a link to download, the link for my Event 6 submission blows up for some reason).

Which kinda ties into thing #1.5 that I learned - the scriptblocks you supply to the -Action parameter in Register-*Event create a separate variable scope that does not inherit the variables from the script scope etc.  At least that's what it seems like....I could be wrong on that, I haven't looked for any documentation tbh.

So, in my opinion, the way that Robert does it is WAY better than the way I did it, which basically boils down to:
  1. Create ONE global variable that will hold the event object when it is raised
  2. In the -action parameter of Register-*Event just assign the $Event automatic variable (or a property of it if that's all you're interested in) to the global variable that you created in your script
  3. Sleep loop until that global variable isn't $null anymore.
In fact, I totally stole this method from him and used it in a later event that involved eventing.  So....thanks Robert :)

Speaking of Robert, why aren't you reading his blog instead of mine?  He rolled #1 in the Advanced division according to Clint's Rules....better go check out what he has to say.

Until tomorrow,  bubye!!  v(^_^)v

Friday, April 16, 2010

Display a PowerShell session’s RAM and CPU utilization in the Title Bar

So, I had a few other things I’ve cooked up this week that I was thinking about posting, but this was too much fun this morning.  Here’s a little bit of code you can stick in your powershell profile that will update the title bar with the current PowerShell session’s memory usage (working set, technically) and CPU utilization.

# Setup the objects for titlebar manipulation            
# Append a #1,#2,#(n-1) to the process name if there are n pre-existing processes with the same            
# name, as this is how the Performance Counter system references the instances.            
$psProcess = gps -id $PID            
$psInstances = (gps -Name $psProcess.name).count            
if ($psInstances -gt 1)            
{            
    $psName = "{0}#{1}" -f $psProcess.name,$($psInstances - 1)            
}            
else            
{            
    $psName = $psProcess.name            
}            
# Create the Performance Counter Object to track our sessions CPU usage            
$Global:psPerfCPU = new-object System.Diagnostics.PerformanceCounter( "Process","% Processor Time", $psName )            
# Get the first 'NextValue', which will be zero            
$psPerfCPU.NextValue() | Out-Null            
# Create a timer object and set the interval to 1 second            
$Global:psTimer   = New-Object System.Timers.Timer            
$psTimer.Interval = 1000            
# Update the window's title bar every time the timer object raises the            
# elapsed event            
Register-ObjectEvent -InputObject $psTimer -EventName Elapsed -Action {            
    $psInfo = Get-Process -id $pid            
    [int]$ws = $psInfo.workingset/1MB            
    [int]$cpu = $psPerfCPU.NextValue() / $env:NUMBER_OF_PROCESSORS            
    $Host.ui.rawui.WindowTitle = "$($CurrentUser.Name) $($Host.Name) $($Host.Version) | $((get-location).ProviderPath) | RAM: $ws MB CPU: $cpu%"            
} | Out-Null            
$psTimer.start()

The hardest part was getting the CPU utilization right, but these two posts spell out the details pretty well:
Happy Friday!

Friday, April 9, 2010

PowerShell’s Try/Catch/Finally and –erroraction Stop

Update: From an anonymous commenter, evidently error handing when using "-erroraction Stop" now works as one would expect in PowerShell version 3!  However, if you're still using version 2 the information below should help you out.

When planning for exception handling, I’ve recently learned that forcing execution to stop by setting $erroractionpreference to ‘Stop’ or by using the –erroraction common parameter requires some special handling.

First let’s see what doesn’t work even though it seems like it should.  Handling a normal terminating error is fairly straightforward:

image

Here we encountered an exception, got the exception’s type by inspecting the first element in the typenames member of the first error record’s exception’s base object, and then used that type name to catch the exception.  This worked whether or not we specified the –ea or –erroraction parameter.  This works because a ParameterBindingException is a terminating error.  However, let’s see what happens when we try the same thing with an error that isn’t of terminating error family:

image

Hmm, bugger.  We tried the same pattern, but this time it just doesn’t work.  But…why?  Without getting too long winded about it, I tried looking back through the automatic $error variable but couldn’t find an answer.  So I tried a different tactic:

image

Aha!  When we use –erroraction stop, the terminating exception isn’t the error that was encountered but an exception of type System.Management.Automation.ActionPreferencesStopException!  But…wait, if that’s true, how do we know what type of error was encountered?  Almost inexplicably the automatic variable $_ is set to the error record of the error that was encountered.  If you know why, please share in the comments or send me an e-mail.  Anyway, we can use that to re-throw the error as a terminating exception that can be caught and handled:

image

Basically we just wrap our command that makes use of the –erroraction stop inside two try/catch blocks.  The inner block is used to trap the ActionPreferenceStopException generated by the erroraction parameter.  In the inner catch block we throw the exception for the error that was caught which is then caught by the outer catch block statements and can the be handled.  So, let’s finish with a generic pattern that one could turn into a code snippet or something:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
try
{
  try
  {
    # Command that uses -erroraction stop or $erroractionpreference = 'Stop'
  }
  # Here we catch the Exception generated by the ErrorAction "Stop"
  # Only necessary if there is any processing we want to do if the
  # exception is of type ActionPreferenceStopExecution,
  # otherwise this block can be deleted
  catch [System.Management.Automation.ActionPreferenceStopException]
  {
    # Error Handling specific to ActionPreferenceStopException goes here
    # Rethrow the "real" exception as a terminating error
    Throw $_.exception
  }
  # All errors are caught and rethrown to the outer try/catch block
  # as terminating errors to be handled.
  catch
  {
    Throw $_.exception
  }
} 
# Here we can resume exception type handling as usual
catch #[System.Management.Automation.ItemNotFoundException]
{
  "Got it!"
}

And with that, Happy Friday!

Friday, April 2, 2010

Early preview of Group Policy Backup & Search Module

Alright, it’s taken WAY longer than I thought it would, but I finally have something I think is worth sharing in its very unfinished state.  First, what still sucks: the help is only slightly better than useless, the code itself is a bit jumbled and not well commented, not everything is formatted nicely when written to output, only 3 extensions (Software Installation, Scripts, and Policy/Registry – the big 3 for me) are implemented, and while the backup function works, you’re pretty much on your own for configuration at the moment.  That said, what does work is pretty cool (in my opinion):  You can search inside of your live group policy objects, as well as any archived XML Reports from Get-GPOReport, and get meaningful output!

Friday, March 12, 2010

PowerShell Resources – Free and Otherwise

Haven’t had much time to work on our group policy module lately, I’ve been pretty busy getting my co-worker’s collective feet wet in PowerShell.  In the process I’ve been trying to find as many quality resources as I can for them to help them help themselves.  Since I don’t have much to share on the csb.grouppolicy module front, here are the resources that I can remember (haha) and use all the time:

Wednesday, February 24, 2010

Snippets for Comment Based Help and Advanced Function Parameters in Powershell v2

Been working quite a bit on the module for group policy, and almost have something I feel is worth sharing.  In the meantime, if you have a script editor that supports the concept of “snippets”, or saved re-usable bits of script code, you might find this little bit useful.  I’ve had a hard time remembering the syntax and all the options for some of the new cool stuff we can use in scripts and functions in Powershell v2, so I saved these little bits after scouring the output of Get-Help for comment based help and advanced functions for (hopefully) the last time, haha.

Thursday, February 11, 2010

Using Powershell to search XML GPO Reports – Part II – Software Installation

In Part I of our series in querying group policy reports, we learned how to:
  • Turn the XML output from Get-GPOReport into a System.XML.XMLDocument object
  • Build a System.XML.XMLNamespaceManager from the information in the XMLDocument object
  • Use XPath with the namespace manager and document to extract information from the report, including the Extension elements.
We left off with the Get-CGPOReportExtensionData filter, and some questions about it’s output.  This week we’ll look at the output from the filter, and then use that output in another filter to transform the XML report information into an object with the details of a Software Installation extension that can easily be worked with using Powershell’s standard comparison operators and formatting cmdlets.

Monday, February 1, 2010

Get the users that have logged on to a computer

Here’s a multi-line adaptation of a quick one-liner I threw together the other day.  I might have to turn this into a function and add it to my profile if I get asked this question too many more times.  To answer “Who used computer x between these dates?” we can use:

001
002
003
004
005
006
007
Get-EventLog -Before '01/26/2010' -after '01/25/2010' -ComputerName computername -LogName Security | `
    where-object `
    {
        ($_.username -notmatch '^NT AUTHORITY\\(SYSTEM|NETWORK SERVICE|LOCAL SERVICE|ANONYMOUS LOGON)$') `
        -and ($_.category -eq "Logon/Logoff")
    } | `
    select-object timegenerated,username,category,message | sort timegenerated | format-table -auto

Hope that can save some of you a little time :)

BackTrack 4 Final Now Available

I’m a little behind the curve here, as the final build was posted on the 11th of January.  Oh well.  For the security conscious administrator, the backtrack suite makes a great tool.  If you don’t know what it is, or taken the time to at least check it out, cut some time out to do so soon.  It will open your eyes a bit, I promise :)

The final build, available in either .ISO or VM-ware forms, can be downloaded from http://www.backtrack-linux.org/downloads/

If you can, use the torrent links.  Your download will most likely be much faster.

Friday, January 29, 2010

Using PowerShell to search Group Policy XML Reports

I was going to write about creating, editing, and saving XML files using the System.XML objects today but discovered it had been covered quite well already after a quick Google search.  So instead we’ll jump ahead a little bit and use PowerShell & .NET’s XML capabilities to dive into the GPO Reports that can be generated using Get-GPOReport from the grouppolicy module.

This will be the first in a small series of posts that should end up with us having a module capable of searching for any group policy setting that is recorded in the XML reports.  That’s the goal, but I’m posting as I get things 90% complete so hopefully all this proves useful and you’ll be willing to bear with me.

Friday, January 8, 2010

Quickly Discover Domain Joined Computers Available via Ping

Occasionally, well…often, I need to run a series of commands or queries against a set of computers on our network.  Sometimes it doesn’t really matter to me to hit 100% of that set.  For instance, the other day I wanted to know roughly how many computer had login script timeout warnings in the event log for the past week.
In the past I had done something like:
  1. Get the list of computers I need to run against from Active Directory or a csv file.
  2. For each computer, attempt to ping it and if it responds run ye’ commands.
That works quickly enough when there are only 10-20 computers, but when we’re talking a scale of 500-1000+ computers it doesn’t scale so well.  Using Powershell, PSCX, Quest’s AD cmdlets, and the following code I can get the list of all domain joined computers currently available on our network in roughly 30 seconds:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
$adComputers = Get-QADComputer -SearchRoot 'domain.com/OU/OU' `
   -SizeLimit 0 `
   -IncludedProperties lastlogontimestamp | Where-Object `
   {
      (($_.useraccountcontrol -band 0x2) -eq 0) `
      -and ($_.lastlogontimestamp -ne $null) `
      -and (($_.lastlogontimestamp).adddays(30) -gt (Get-Date))
   }
$compNames = $adComputers | % { $_.name }
$pingResults = ping $compNames -Count 2 -Quiet `
               -AllAddresses -Asynchronous -ErrorAction silentlycontinue
$compDict = @{}
foreach ($comp in $adComputers)
{
   $compDict.add($comp.name,$comp)
}
$upComps = @()
foreach ($result in $pingResults)
{
   if ($result.received -gt 1)
   {
      $cName = $result.hostname -replace "\.domain\.com"
      $upComps += $compDict[$cName]
   }
}
Replace the ‘domain.com/OU/OU’ with the searchroot for the workstation groups you’re after, as well as the “\.domain\.com” with the correct information for your domain and $upComps will contain a collection of QADComputer objects that responded to pings.
From there we can use PSRemoting or tools like psexec.exe with the –d switch to (relatively) quickly run a set of commands against our computers that are up and running!
Many thanks to Lee Holmes for the script that creates the above code block on the clipboard for pasting from any script, truly awesome.