Category: Powershell


Will be presenting a talk this year at the Silicon Valley Code Camp 2010.  The session will be on Powershell Version 2 Modules and how to build and work with them.  Come and learn the new way to automate and manage just about anything using Microsofts new shell.
 
 

CodeCamp at FootHill College.  Click Here for Details and Registration

 
qawarrior, a co-worker of mine, is also presenting a session on Advanced Scripting for Powershell.  See it here.  http://nullreferenceproject.blogspot.com/2010/05/5th-silicon-valley-code-camp-oct-9th.html
 
 
-Shane
Just played with this a bit.  Works like the normal functions in Powershell V1 but thought I would give an example in V2 advanced functions.
 

function Test {

 [CmdletBinding()]
 param(
 [Parameter(Mandatory=$true)]
 [ref]$var
 )
 #Note the $var.value.  $var = 5 will not work.
 $var.value = 5

}

PS C:\Users\spowser> $var = 1
PS C:\Users\spowser> Test -var ([ref]$var)
PS C:\Users\spowser> $var
5

-Shane

 

Ran into a case where I need to know if –Verbose had been passed in as a parameter to an advanced function today.  I initially thought I could simply do the following:

function Test
{

    [CmdletBinding()]
    param()
    if($Verbose)
    {
        $True
    }
    else
    {
        $False
    }
}

PS C:\Users\spowser> Test -Verbose
False

Hmmmm.  Ok.  Powershell doesn’t think –Verbose was used.  Has to be a way to get to the bound parameters.

Found this as the solution.

function Test
{

    [CmdletBinding()]
    param()
    if($PSBoundparameters["Verbose"].IsPresent)
    {
        $True
    }
    else
    {
        $False
    }
}

PS C:\Users\spowser> Test -Verbose
True

Much better 🙂

Hope this helps.

-Shane

WSUS Client Issues

 
Ran into the following error today on a Windows XP SP3 machine when trying to get Windows updates working.
 
"The Background Intelligent Transfer Service service terminated with service-specific error 2147500053 (0x80004015). "
 
Created the following script to fix it.
 
 
-Shane

Powershell REST Client

The past couple of days I have been working on a Powershell based client for our REST web services.  While the client is specific to our business the core logic behind the client should apply to just about all REST based web services.  I have tried to generalize(read: remove company stuff) the code so hopefully everything still makes sense.

In our implementation of REST, we allow a person to either submit a GET request with query string parameters or attach some XML to the request and submit as a POST.  Our response will always be some xml.  We handle it with 3 xml root types.  Success, Error, and Data.  Success just lets the user know a particular POST was successful.  Error will give the user back an error code with a description.  Data will return an xml document with whatever data the user requested.  Eventually a Success I would think will also return the changes made but hey, this is version 1.

The core of each GET request is just making the request with the query strings defined.  The declaration of the core function with the parameters section is:

function Invoke-APICall {
    param(
        [string]$Username,
        [string]$Password,
        [string]$UserAgent = "Powershell API Client",
        [string]$URL,
        [xml]$XMLObject
        )

 

   1: #Create a URI instance since the HttpWebRequest.Create Method will escape the URL by default.
   2: $URI = New-Object System.Uri($URL,$true)
   3:  
   4: #Create a request object using the URI
   5: $request = [System.Net.HttpWebRequest]::Create($URI)
   6:  
   7: #Build up a nice User Agent
   8: $request.UserAgent = $(
   9: "{0} (PowerShell {1}; .NET CLR {2}; {3})" -f $UserAgent, 
  10: $(if($Host.Version){$Host.Version}else{"1.0"}),
  11: [Environment]::Version,
  12: [Environment]::OSVersion.ToString().Replace("Microsoft Windows ", "Win")
  13: )
  14:  
  15: #Establish the credentials for the request
  16: $creds = New-Object System.Net.NetworkCredential($Username,$Password)
  17: $request.Credentials = $creds
  18:  
  19: $response = $request.GetResponse()
  20:  
  21: $reader = [IO.StreamReader] $response.GetResponseStream()
  22:  
  23: #Our response will always be xml except in 404/401 case so cast as such        
  24: [xml]$responseXML = $reader.ReadToEnd()
  25:         
  26: $reader.Close()
  27:         
  28: #Let others down the pipeline have fun with our xml object
  29: Write-Output $responseXML
  30:  
  31: $response.Close()

 

To decide if this is a POST I use the following: 

if($XMLObject){ Do the POST stuff}else{Do the GET stuff}

And now the POST specific code.

   1: $creds = New-Object System.Net.NetworkCredential($Username,$Password)
   2: $request.Credentials = $creds
   3:  
   4: #Since this is a POST we need to set the method type
   5: $request.Method = "POST"
   6:  
   7: #Set the Content Type as text/xml since the content will be a block of xml.
   8: $request.ContentType = "text/xml"
   9:  
  10: #Create a new stream writer to write the xml to the request stream.
  11: $stream = New-Object IO.StreamWriter $request.GetRequestStream()
  12: $stream.AutoFlush = $True
  13: $stream.Write($($XMLObject.psbase.OuterXML),0,$($XMLObject.psbase.OuterXml.Length))
  14: $stream.Close()
  15:  
  16: #Make the request and get the response
  17: $response = $request.GetResponse()
  18:  
  19: #Create a stream reader to read the response stream.
  20: $reader = New-Object IO.StreamReader $response.GetResponseStream()
  21:  
  22: #Read the response and cast the response to XML
  23: [xml]$responseXML = $reader.ReadToEnd()
  24:  
  25: #Dump the XML out to the pipeline for others to consume
  26: Write-Output $responseXML
  27:  
  28: $response.Close()

 

There was one thing in the POST that I had trouble with at first.  When trying to execute the $stream.Write method, if I had set the $request.ContentLength property with $request.ContentLength = $$XMLObject.psbase.OuterXML.Length, it would fail stating the bytes being written were longer than the bytes specified.  So I removed the code to set the $request.ContentLength and everything seemed to work fine.  Not sure how Joel Bennett got that to work but it drove me nuts for a while.

So that just leaves the calling functions.  Here is a GET

   1: function Invoke-APIGetStuffInGroup {
   2:     param( 
   3:       [string]$Group,
   4:       [string]$Username,
   5:       [string]$Password,
   6:       [string]$UserAgent = "Powershell API Client"      
   7:    )
   8:    
   9:    
  10:         $Group = [System.Web.HttpUtility]::UrlEncode($Group)
  11:         $URL = "https://api.website.com/api/1.0/Groups/GetStuff?group=$Group"
  12:         Invoke-APICall -URL $URL -Username $Username -Password $Password -UserAgent $UserAgent
  13:         
  14: }

 

And a POST

   1: function Invoke-APIAddStuffToGroups {
   2:     param( 
   3:       [string]$Username,
   4:       [string]$Password,
   5:       [string]$UserAgent = "Powershell API Client"      
   6:    )
   7:         $xml = #Create your XML Document here
   8:         
   9:         $URL = "https://api.website.com/api/1.0/Groups/AddStuff"
  10:             
  11:         Invoke-APICall -URL $URL -XMLObject $xml -Username $Username -Password $Password -UserAgent $UserAgent
  12: }
  13:         

 

One change I might make at some point is to require a SecureString for the password since clear text can be bad.

Hope this helps someone!

-Shane

Powershell is showing up in recent news articles surrounding Windows 7.
 
 
-Shane

It’s been 9 months since I put in a bug on Microsoft Connect for the IE visible property not working when running in a remote runspace.  While I completely understand the security reason to not allow a person to monkey with a desktop that may not be their own, I want to plead my case to allow it via some/any method.

I am currently trying to automate a whole series of test cases that require remote systems to act as launch points for browsing to websites.  These websites may or may not have Popup windows that appear during surfing.  Not a problem.  Couple of ways to launch IE and browse to a website on a remote system.  InternetExplorer.Application COM object or just creating and starting an IE process are the first two that come to mind.  The problem you will run into is everything that is invoked through a runspace seems to suppress any interaction with the desktop.  You set the visible flag on the COM object and nothing happens.  Launch an IE process on the remote machine and IExplore.exe starts running but is not visible.

“Why do you want to see these windows so bad?” you might ask?  Well, the short answer is, I want to see the popup windows as they would normally appear.  It is critical to my testing that this happens and I grab the html content of each popup.  I could not find a way to get a handle on a hidden (because all desktop interaction is being suppressed) popup window that is created from a JavaScript Window.Open() function.  Kinda frustrating.

If there was a way to get a handle on these Ninja (read: hidden) popup windows then I wouldn’t have much of an issue.  Please Microsoft, give me a way to either grab these hidden popups or allow me to set IE to visible. 🙂

-Shane

The Power of ParameterSets

This is one of the many features that sold me on Powershell when I started looking into the language.  This enables admins or devs to produce some really polished stuff and give the end user a nice experience.
 
 
-Shane
 
Powershell CTP3 is now available.  Get it here and submit feedback here 
 
-Shane