Extending Microsoft.Web.Administration through PowerShell (Part II)

Posted: Dec 01, 2006  5 comments  

Average Rating

Tags
IIS 7
PowerShell

Share this Post

In my previous post, I showed you how easy it was to leverage your knowledge of the IIS 7 managed SDK in Windows PowerShell.  We loaded the IIS 7 managed assemblies and then traversed the object model to display site information and stop application pools.  While this in itself was pretty cool, I don't think I quite got my point across about how powerful IIS 7 and PowerShell are together. As such, I wanted to show you some more fun things to do with PowerShell in the name of easy IIS 7 administration.

First, our examples still required a great deal of typing and piping and filtering.  Let's modify our profile script from my previous post by adding at least one new global variable that will give us access to the ServerManager without much typing.  Add the following line to your profile script from my previous post.

new-variable iismgr -value (New-Object Microsoft.Web.Administration.ServerManager) -scope "global"

(if you don't have a profile script yet, go back to my previous post to learn how to create one). 

Note: If you signed your script before, you'll have to do it again after modifying the script

Open a new instance of PowerShell and now you can access the site collection just by typing:

PS C:\> $iismgr.Sites

 That's considerably smaller than our previous examples.  But let's not stop there.  What happens if I want to search the site collection? PowerShell has some fun syntax for this as well. I simply pipe the output of my SiteCollection to a "Where-Object" cmdlet and then specify what site I'm looking for:

$iismgr.Sites | Where-Object {$_.Name -match "^Default*"}

This is still quite a bit of typing when all I really want to do is find the default website. You may ask "Wouldn't it be easier if we could just add a "Find" method to the SiteCollection object?" Well I'm glad you asked *cough*!  Next, we are going to do just that! Open up another instance of notepad and add the following XML to it:

<?xml version="1.0" encoding="utf-8" ?>
<!-- *******************************************************************
Copyright (c) Microsoft Corporation.  All rights reserved.
 
THIS SAMPLE CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
OF ANY KIND,WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE. IF THIS CODE AND INFORMATION IS MODIFIED, THE ENTIRE RISK OF USE
OR RESULTS IN CONNECTION WITH THE USE OF THIS CODE AND INFORMATION
REMAINS WITH THE USER.
******************************************************************** -->

<Types>
  <Type>
    <Name>Microsoft.Web.Administration.SiteCollection</Name>
      <Members>
        <ScriptMethod>
          <Name>Find</Name>
          <Script>
          $rtr = "";
          if ( $args[0] )
          { $name = $args[0];
            $rtr = ((New-Object Microsoft.Web.Administration.ServerManager).Sites | Where-Object {$_.Name -match $name})
          } 
          else
          {
            $rtr = "No sites found."
          };
          $rtr
          </Script>
        </ScriptMethod>
      </Members>
  </Type>
</Types>

What we've done is identified that we want to add a scripted method to the Microsoft.Web.Administration.SiteCollection object. In our case, I've added a "Find" method by using the same cmdlet that we typed before to search the site collection. The difference is, this type I use the $args array variable to check for a parameter and use it if one is available.  Now, save this file into your %windir%\system32\WindowsPowerShell\v1.0\ directory as "iis.types.ps1xml".  Once you've saved the file, sign it the same way you signed your profile script.  Keep in mind that these xml files contain code, so signing your xml is required to keep your PowerShell experience a secure one.  Now, open your profile script (again) and add the following lines to the end:

new-variable iissites -value (New-Object Microsoft.Web.Administration.ServerManager).Sites -scope "global"
new-variable iisapppools -value (New-Object Microsoft.Web.Administration.ServerManager).ApplicationPools -scope "global"
update-typedata -append (join-path -path $PSHome -childPath "iis.types.ps1xml")

Note: Once again, you'll have to re-sign this profile if your execution policy requires signed scripts.

Notice that I added two more variables: $iissites and $iisapppools. These variables allow me to access the site collection and application pool collection with a simple keyword.  Lets try them out in PowerShell. Make sure you open a new instance of PowerShell so your profile script and xml type data are updated properly. Once your new instance of PowerShell is open, type the following:

PS C:\> $iissites.Find("^Default*")

PowerShell will do all the work for you and you have MUCH less typing to do.

Another alternative to using xml files is to simply create a function and add it to your profile. For instance, we can create a function called "findsite" that provides the same functionality as our previous example. Either type the following command into PowerShell or add it to your profile script:

PS C:\> function findsite { $name=$args[0]; ((New-Object Microsoft.Web.Administration.ServerManager).Sites | Where-Object {$_.Name -match $name}); } }

Now you can search for a site using the following syntax:

PS C:\> findsite default*

Whatever way we choose to extend Microsoft.Web.Administration and/or PowerShell, we can use our output as we did before:

PS C:\> (findsite default*).Id

The previous line should display the Id of the default web site.  We can also stop the website:

PS C:\> (findsite default*).Stop()

We can keep taking this to extremes and truncate every operation that we perform on a semi-regular basis.  These scripts are not one-offs. Each script function we create or append to an existing object model can be reused and piped as input to another function.  The possibilities are endless and completely customizable to your needs.

Comments

In my haste to get this post out before a meeting, I neglected to make some disclaimers.  While the code above is meant as an example of a few things you can do with IIS, this is by no means "production ready". In fact, I ran into a few problems last night with regards to the global variables.  For instance, since we are assigning those variables when PowerShell starts, any changes we make to the objects may not be reflected in our output.  The $iissites is a great addition if all we ever do in powershell is view site state.  However, consider what would happen if you did the following:

(findsite default*).Name = "Test";$iismgr.CommitChanges();

Doing a search for the "Default Web Site" again would return the same website even though we've renamed it. That's because it still has the old cache.  We might be better off defining findsite as:

function findsite { $iismgr.Sites | Where-Object {$_.Name -match $args[0]} }

Once again, this is conjecture and I haven't tested the code, so please do not consider this production-ready. Test your scripts thoroughly to validate that they are updating your data properly before you put your reputation behind them!

Dec 01 2006 by TobinTitus

I've been playing around with the bugs in my PowerShell code and have made some updates to the article. If you are interested, aside from the problems I listed above, I was not getting good matches because the $args[0] inside the Where-Object statement are not honored. I changed function definition to:

function findsite { $name=$args[0]; (New-Object Microsoft.Web.Administration.ServerManager).Sites | Where-Object {$_.Name -match $name}; }

This appears to have fixed the problem.

Dec 03 2006 by TobinTitus

Submit a Comment

  • Plain text is accepted.
  • URLs starting with http:// are converted to links.