Wednesday, December 22, 2010
A diversion
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?
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
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:
- Add the number to the lines array property of the textbox
- Move the caret (cursor) to the last character in the textbox
- Scroll the textbox view to the caret
- Update the form
- 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
Happy Wednesday!
Things I learned from the Scripting Games 2010 #4: Background Jobs
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:
- Create a variable
$arr = 1..5 - Start a background job that writes that variable to output
$job = Start-Job –Scriptblock { Write-Output $arr } - Receive the job
Receive-Job $job - Notice that there is no output, sadface.
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.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:
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.
- Create a variable:
$arr = 1..5 - Start a background job, passing the variable as an argument:
$job = Start-Job –Scriptblock { Write-Output $args } –ArgumentList $arr - Receive the job
Receive-Job $job - Hey there’s our information!
Get the function here.
Things I learned from the Scripting Games 2010 #3: Test-Connection
if ($pingResults.received -gt 0)
{
# Do something
}
And then I discovered test-connection! Now for simple quick tests we can just do:
{
#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
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
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:
- Create ONE global variable that will hold the event object when it is raised
- 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
- Sleep loop until that global variable isn't $null anymore.
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
# 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
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:
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:
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:
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:
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
Friday, March 12, 2010
PowerShell Resources – Free and Otherwise
Wednesday, February 24, 2010
Snippets for Comment Based Help and Advanced Function Parameters in Powershell v2
Thursday, February 11, 2010
Using Powershell to search XML GPO Reports – Part II – Software Installation
- 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.
Monday, February 1, 2010
Get the users that have logged on to a computer
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
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
In the past I had done something like:
- Get the list of computers I need to run against from Active Directory or a csv file.
- For each computer, attempt to ping it and if it responds run ye’ commands.
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] } } |
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.