<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" version="2.0">
  <channel>
    <title>beefycode.com</title>
    <description>Serving all of your beefy code needs...</description>
    <link>http://www.beefycode.com/</link>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>BlogEngine.NET 1.4.5.0</generator>
    <language>en-US</language>
    <blogChannel:blogRoll>http://www.beefycode.com/opml.axd</blogChannel:blogRoll>
    <blogChannel:blink>http://www.dotnetblogengine.net/syndication.axd</blogChannel:blink>
    <dc:creator>Jimbo</dc:creator>
    <dc:title>beefycode.com</dc:title>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/beefycode" type="application/rss+xml" /><item>
      <title>ConvertFrom-PDF PowerShell Cmdlet</title>
      <description>&lt;p&gt;
I hate PDFs.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
And now I need to search through several hundred of them, ranging from 30 to 300 pages in length, for cross-references and personnel names which ... um ... well, let&amp;#39;s just say they no longer apply.&amp;nbsp; Sure reader has the search feature built-in, so does explorer, but that&amp;#39;s so 1980&amp;#39;s.&amp;nbsp; And I sure don&amp;#39;t want to do each one manually...
&lt;/p&gt;
&lt;p&gt;
I poked around the &amp;#39;net for a few minutes to find a way to read PDFs in powershell, but no donut.&amp;nbsp; So I rolled my own cmdlet around the &lt;a href="http://itextsharp.sourceforge.net/" target="_blank"&gt;iTextSharp library&lt;/a&gt; and Zollor&amp;#39;s &lt;a href="http://www.codeproject.com/KB/cs/PDFToText.aspx" target="_blank"&gt;PDF to Text converter&lt;/a&gt; project.
&lt;/p&gt;
&lt;p&gt;
There isn&amp;#39;t much to the cmdlet code, given that all of the hard work of extracting the PDF text is done in the PDFParser class of the converter project:
&lt;/p&gt;
&lt;pre class="c#"&gt;
using System;
using System.IO;
using System.Management.Automation;
namespace PowerShell.PDF
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; [Cmdlet( VerbsData.ConvertFrom, &amp;quot;PDF&amp;quot; )]
&amp;nbsp;&amp;nbsp;&amp;nbsp; public class ConvertFromPDF : Cmdlet
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Parameter( ValueFromPipeline = true, Mandatory = true )]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public string PDFFile { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected override void ProcessRecord()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var parser = new PDFParser();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; using( Stream s = new MemoryStream() )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if( ! parser.ExtractText(File.OpenRead(PDFFile), s) )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WriteError( 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new ErrorRecord(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new ApplicationException(),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;failed to extract text from pdf&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ErrorCategory.ReadError,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PDFFile
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; s.Position = 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; using( StreamReader reader = new StreamReader( s ) )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WriteObject( reader.ReadToEnd() );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p&gt;
The code accepts a file path as input; it runs the conversion on the PDF data and writes the text content of the file to the pipeline.&amp;nbsp; Not pretty, but done.
&lt;/p&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;
Here is the simple case of transforming a single file:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; convertfrom-pdf -pdf my.pdf
&lt;/pre&gt;
&lt;p&gt;
or
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; my.pdf | convertfrom-pdf 
&lt;/pre&gt;
&lt;p&gt;
More complex processing can be accomplished using PowerShell&amp;#39;s built-in features; e.g., to convert an entire directory of PDFs to text files:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; dir *.pdf | %{ $_ | convertfrom-pdf | out-file &amp;quot;$_.txt&amp;quot; } 
&lt;/pre&gt;
&lt;p&gt;
More relevant to my current situation would be something along these lines:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; dir *.pdf | ?{ ( $_ | convertfrom-pdf ) -match &amp;quot;ex-employee name&amp;quot; } 
&lt;/pre&gt;
&lt;p&gt;
Download the source: &lt;a href="http://www.beefycode.com/file.axd?file=2009%2f7%2fPowerShell.PDF.zip"&gt;PowerShell.PDF.zip (1.10 mb)&lt;/a&gt;&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
Enjoy! 
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.beefycode.com/file.axd?file=2009%2f7%2fPowerShell.PDF.zip"&gt;&lt;br /&gt;
&lt;/a&gt;
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/ebn6mHntfAg" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/ConvertFrom-PDF-Cmdlet.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/ConvertFrom-PDF-Cmdlet.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=cc9eba2b-e68c-4ce1-b564-c75f41f4bdf4</guid>
      <pubDate>Wed, 08 Jul 2009 06:14:00 -1300</pubDate>
      <category>powershell</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=cc9eba2b-e68c-4ce1-b564-c75f41f4bdf4</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=cc9eba2b-e68c-4ce1-b564-c75f41f4bdf4</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/ConvertFrom-PDF-Cmdlet.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=cc9eba2b-e68c-4ce1-b564-c75f41f4bdf4</wfw:commentRss>
    </item>
    <item>
      <title>Creating a PowerShell Provider pt 3: Getting Items</title>
      <description>&lt;p&gt;
Now that the &lt;a href="http://www.beefycode.com/post.aspx?id=bf3e69aa-64ce-46db-9872-fba6d8b3a056"&gt;drive object of the ASP.NET Membership PowerShell provider&lt;/a&gt; is fully functional, it&amp;#39;s time to extend the PowerShell provider to fetch users from the membership store.
&lt;/p&gt;
&lt;p&gt;
The PowerShell cmdlet used to retrieve one or more items from a provider is get-item, or gi for you alias monkeys out there.&amp;nbsp; get-item works against any provider that support item retrieval; e.g., it works for files and folders:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; get-item c:\temp
&amp;nbsp;&amp;nbsp;&amp;nbsp; Directory: Microsoft.PowerShell.Core\FileSystem::C:\
Mode&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastWriteTime&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Length Name
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -------------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ------ ----
d----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3/12/2009&amp;nbsp; 2:23 PM&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;DIR&amp;gt; temp
&lt;/pre&gt;
&lt;p&gt;
It works for environment variables:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; get-item env:PROGRAMFILES
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Value
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -----
ProgramFiles&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; C:\Program Files
&lt;/pre&gt;
&lt;p&gt;
It works for certificates:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; get-item cert:/CurrentUser/My/E0ADE6F1340FDF59B63452D067B91FFFA09A621F
&amp;nbsp;&amp;nbsp;&amp;nbsp; Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My
Thumbprint&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Subject
----------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -------
E0ADE6F1340FDF59B63452D067B91FFFA09A621F&amp;nbsp; E=jimbo@null.com, CN=127.0.0.1, OU=sec, O=ptek, L=here, S=nc, C=US
&lt;/pre&gt;
&lt;p&gt;
So, how does PowerShell know which provider to invoke when it processes a get-item cmdlet?&amp;nbsp;&amp;nbsp; 
&lt;/p&gt;
&lt;h2&gt;PowerShell Paths&lt;/h2&gt;
&lt;p&gt;
The item&amp;#39;s full
path contains all of information necessary to locate the appropriate
provider.&amp;nbsp; In the examples above, the drive portion of
the path indicates which provider should be used to process
the item request; you can see the mapping of drives to providers using the get-psdrive cmdlet:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; get-psdrive
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Provider&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Root
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
Alias&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Alias
C&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FileSystem&amp;nbsp;&amp;nbsp;&amp;nbsp; C:\&amp;nbsp;&amp;nbsp; 
cert&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Certificate&amp;nbsp;&amp;nbsp; \
Env&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Environment
Function&amp;nbsp;&amp;nbsp; Function
Gac&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AssemblyCache Gac
HKCU&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Registry&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HKEY_CURRENT_USER
HKLM&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Registry&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HKEY_LOCAL_MACHINE
Variable&amp;nbsp;&amp;nbsp; Variable
&lt;/pre&gt;
&lt;p&gt;
Each PowerShell drive is directly associated with a provider type:
the C: drive maps to the FileSystem provider, the cert: drive to the
Certificate provider, and the env: drive to the Environment provider.
&lt;/p&gt;
&lt;p&gt;
PowerShell recognizes several forms of path syntax, all of them designed to allow for provider discovery; providers are expected to support these path formats as appropriate:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Drive-qualified&lt;/strong&gt;: this is equivalent to a fully-qualified file path.&amp;nbsp; The drive is explicitly specified at the start of the path, as in &amp;quot;c:/temp/junk.txt&amp;quot; or &amp;quot;cert:/localmachine&amp;quot;.&amp;nbsp;&amp;nbsp; &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Provider-direct&lt;/strong&gt;: starts with &amp;quot;\\&amp;quot; (or &amp;quot;//&amp;quot;); the provider for the current location  (i.e., the value of $pwd) is assumed to be the provider of the path. This syntax is often used to identify resources on other machines, such as UNC paths or remote registry hives. &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Provider-qualified&lt;/strong&gt;: the provider name is prepended to the
	drive-qualified or provider-direct item path, delimited by &amp;#39;::&amp;#39;.&amp;nbsp; E.g., &amp;quot;FileSystem::c:/temp/junk.txt&amp;quot;, or
	&amp;quot;FileSystem::\\share\temp\junk.txt&amp;quot;.&amp;nbsp; This format is used when
	the appropriate provider must be explicity stated. &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Provider-internal&lt;/strong&gt;: this is the portion of the provider-qualified path following the &amp;#39;::&amp;#39; delimiter.&amp;nbsp; &lt;br /&gt;
	&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Of the four supported path formats, the ASP.NET Membership PowerShell provider will support these three:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Drive-qualified&lt;/strong&gt;: users:/username&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Provider-qualified&lt;/strong&gt;: ASPNETMembership::users:/username &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Provider-internal&lt;/strong&gt;: this is idential to the drive-qualified path syntax 
	&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Provider-direct paths and UNC-style provider-internal paths will not be supported by the ASP.NET Membership PowerShell provider. 
&lt;/p&gt;
&lt;p&gt;
Knowing the path formats to expect, it&amp;#39;s time to implement support for the get-item cmdlet.
&lt;/p&gt;
&lt;h2&gt;Enabling Get-Item
&lt;/h2&gt;
&lt;p&gt;
Enabling item cmdlet support for the ASP.NET Membership PowerShell provider begins with deriving the provider from ContainerCmdletProvider:
&lt;/p&gt;
&lt;pre class="c#"&gt;
using System.Management.Automation;
using System.Management.Automation.Provider;
namespace ASPNETMembership
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; [CmdletProvider( &amp;quot;ASPNETMembership&amp;quot;, ProviderCapabilities.None )]
&amp;nbsp;&amp;nbsp;&amp;nbsp; public class Provider : ContainerCmdletProvider
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // ...
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p&gt;
Deriving from ContainerCmdletProvider adds many item-related methods to the provider.&amp;nbsp; To enabling the get-item cmdlet, at least two of these methods must be overridden.&amp;nbsp; 
&lt;/p&gt;
&lt;h3&gt;GetItem &lt;br /&gt;
&lt;/h3&gt;
&lt;p&gt;
The first required override is the GetItem method, which is called to process a get-item invocation at runtime:
&lt;/p&gt;
&lt;pre class="c#"&gt;
protected override void GetItem( string path )
{
&amp;nbsp;&amp;nbsp; &amp;nbsp;var user = GetUserFromPath(path);
&amp;nbsp;&amp;nbsp; &amp;nbsp;if( null != user )
&amp;nbsp;&amp;nbsp; &amp;nbsp;{
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;WriteItemObject( user, path, false );
&amp;nbsp;&amp;nbsp; &amp;nbsp;}
}
&lt;/pre&gt;
&lt;p&gt;
The GetItem override delegates almost all of the work to the GetUserFromPath utility method; if GetUserFromPath returns a valid object reference, it is written back to the
current pipeline using the WriteItemObject method of the provider
object&amp;#39;s base (line 6).
&lt;/p&gt;
&lt;p&gt;
GetUserFromPath uses the provider&amp;#39;s custom drive object to access the ASP.NET Membership provider.&amp;nbsp; The drive object for the path is available in the PSDriveInfo property; PowerShell conveniently sets this value to the appropriate DriveInfo
object for the item&amp;#39;s path before calling GetItem: 
&lt;/p&gt;
&lt;pre class="c#"&gt;
MembershipUser GetUserFromPath( string path )
{
&amp;nbsp;&amp;nbsp; &amp;nbsp;var drive = this.PSDriveInfo as MembershipDriveInfo;
&amp;nbsp;&amp;nbsp; &amp;nbsp;var username = ExtractUserNameFromPath( path );
&amp;nbsp;&amp;nbsp; &amp;nbsp;return drive.MembershipProvider.GetUser( username, false );
}
static string ExtractUserNameFromPath( string path )
{
&amp;nbsp;&amp;nbsp; &amp;nbsp;if( String.IsNullOrEmpty( path ) )
&amp;nbsp;&amp;nbsp; &amp;nbsp;{
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;return path;
&amp;nbsp;&amp;nbsp; &amp;nbsp;}
&amp;nbsp;&amp;nbsp; &amp;nbsp;// this regex matches all supported powershell path syntaxes:
&amp;nbsp;&amp;nbsp; &amp;nbsp;//&amp;nbsp; drive-qualified - users:/username
&amp;nbsp;&amp;nbsp; &amp;nbsp;//&amp;nbsp; provider-qualified - membership::users:/username
&amp;nbsp;&amp;nbsp; &amp;nbsp;//&amp;nbsp; provider-internal - users:/username
&amp;nbsp;&amp;nbsp; &amp;nbsp;var match = Regex.Match( path, @&amp;quot;(?:membership::)?(?:\w+:[\\/])?(?&amp;lt;username&amp;gt;[-a-z0-9_]*)$&amp;quot;, RegexOptions.IgnoreCase );
&amp;nbsp;&amp;nbsp; &amp;nbsp;if( match.Success )
&amp;nbsp;&amp;nbsp; &amp;nbsp;{
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;return match.Groups[ &amp;quot;username&amp;quot; ].Value;
&amp;nbsp;&amp;nbsp; &amp;nbsp;}
&amp;nbsp;&amp;nbsp; &amp;nbsp;return String.Empty;
}
&lt;/pre&gt;
&lt;p&gt;
The custom drive object exposes the ASP.NET Membership Provider, which offers a GetUser method that returns the MembershipUser object for a valid username (line 5).&amp;nbsp; The username is extracted from the path string using a simple regular expression that matches the three path formats supported by the PowerShell provider (line 17). 
&lt;/p&gt;
&lt;h3&gt;ItemExists &lt;br /&gt;
&lt;/h3&gt;
&lt;p&gt;
The second required override is the ItemExists method, which is called by PowerShell to determine if the provider contains an item at a specified path (e.g., by the test-path cmdlet).
&lt;/p&gt;
&lt;p&gt;
PowerShell calls ItemExists before the GetItem method when processing get-item; if ItemExists returns false, GetItem is not called and a &amp;quot;cannot find path&amp;quot; error is reported on the pipeline.&amp;nbsp; The ASP.NET Membership provider reuses the GetUserFromPath utility method to ascertain whether the path contains a valid username:
&lt;/p&gt;
&lt;pre class="c#"&gt;
protected override bool ItemExists( string path )
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; return null != GetUserFromPath( path );
} 
&lt;/pre&gt;
&lt;p&gt;
With these two overrides and their supporting utility methods, our provider can support the get-item cmdlet.
&lt;/p&gt;
&lt;h2&gt;Testing Get-Item &lt;/h2&gt;
&lt;p&gt;
Build and run; in the PowerShell console, create the users drive as follows:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; new-psdrive -psp aspnetmembership -root &amp;quot;&amp;quot; -name users -server localhost -catalog awesomewebsitedb; 
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Provider&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Root&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CurrentLocation
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ---------------
users&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASPNETMemb... 
&lt;/pre&gt;
&lt;p&gt;
Once the drive is created, you can use get-item to fetch MembershipUser objects from the ASP.NET Membership user store:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; get-item users:/testuser
PSPath&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : ASPNETMembership::testuser
PSDrive&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : users
PSProvider&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : ASPNETMembership
PSIsContainer&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : False
UserName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : testuser
ProviderUserKey&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 09a9c356-a400-4cff-825d-231207946c94
Email&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : user@hotmail.com
PasswordQuestion&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : what is your favorite color?
Comment&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :
IsApproved&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : True
IsLockedOut&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : False
LastLockoutDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 12/31/1753 7:00:00 PM
CreationDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 6/11/2009 12:59:45 PM
LastLoginDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 6/11/2009 12:59:45 PM
LastActivityDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 6/11/2009 12:59:45 PM
LastPasswordChangedDate : 6/11/2009 12:59:45 PM
IsOnline&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : False
ProviderName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : AspNetSqlMembershipProvider
&lt;/pre&gt;
&lt;p&gt;
At this point, a whole new world of complexity is available from our provider:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; ( get-item users:/testuser ).ResetPassword( &amp;#39;asdf1234&amp;#39; )
^PlpmNMON@7A]w
&lt;/pre&gt;
&lt;p&gt;
We can also leverage some of the built-in goodies of PowerShell against our ASP.NET Membership store in a natural way:
&lt;/p&gt;
&lt;pre class="ps"&gt;
$u = get-item users:/testuser;
if( $u.IsLockedOut ) 
{ 
&amp;nbsp;&amp;nbsp;&amp;nbsp; $u.UnlockUser(); 
} &amp;nbsp;
&lt;/pre&gt;
&lt;p&gt;
Pretty cool.
&lt;/p&gt;
&lt;h2&gt;Coming Up&lt;/h2&gt;
&lt;p&gt;
Discovery is a big part of PowerShell, and in the post I&amp;#39;ll extend the ASP.Net Membership PowerShell provider to support the get-childitem (alias dir or ls) cmdlet, to enable listing of all users in the store.&amp;nbsp; I&amp;#39;ll also add support for the set-location (alias cd) cmdlet, which will allow operators to set the shell&amp;#39;s current location to our custom users drive.
&lt;/p&gt;
&lt;p&gt;
The code for this post is available here: 
&lt;a href="http://www.beefycode.com/file.axd?file=2009%2f7%2fASPNETMembership_GetItem.zip"&gt;ASPNETMembership_GetItem.zip (5.55 kb)&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
As always, thanks for reading, and if you liked this post, please Kick
It, Shout It, trackback, tweet it, and comment using the clicky
thingies below! 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/KBg-_m9kL4o" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-3-Getting-Items.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-3-Getting-Items.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=824c418b-4869-447a-af08-40eb5c3748f0</guid>
      <pubDate>Tue, 07 Jul 2009 04:51:00 -1300</pubDate>
      <category>powershell</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=824c418b-4869-447a-af08-40eb5c3748f0</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=824c418b-4869-447a-af08-40eb5c3748f0</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-3-Getting-Items.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=824c418b-4869-447a-af08-40eb5c3748f0</wfw:commentRss>
    </item>
    <item>
      <title>Twitter Appender for Log4Net</title>
      <description>&lt;p&gt;
Casey Watson just published a post describing how to create a Twitter appender for log4net!&amp;nbsp; Not only is the idea creative and fun, but it&amp;#39;s also great sample code if you want to build your own custom appenders. 
&lt;/p&gt;
&lt;p&gt;
Please check it out here: &lt;a href="http://caseywatson.com/2009/07/07/log4net-twitter-awesome/"&gt;http://caseywatson.com/2009/07/07/log4net-twitter-awesome/ &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
NICE WORK CASEY! 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/t6ZkIbaA6SQ" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Twitter-Appender-for-Log4Net.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Twitter-Appender-for-Log4Net.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=231d5c3e-df86-42cc-97ab-ee606387560b</guid>
      <pubDate>Tue, 07 Jul 2009 04:13:00 -1300</pubDate>
      <category>community</category>
      <category>log4net</category>
      <category>software development</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=231d5c3e-df86-42cc-97ab-ee606387560b</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=231d5c3e-df86-42cc-97ab-ee606387560b</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Twitter-Appender-for-Log4Net.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=231d5c3e-df86-42cc-97ab-ee606387560b</wfw:commentRss>
    </item>
    <item>
      <title>Death by Logging Mantra #1 - Logs Consume Space</title>
      <description>&lt;p&gt;
&lt;img src="http://www.beefycode.com/image.axd?picture=2009%2f6%2fcemetery_tree.jpg" alt="" /&gt;As much as I love and lean on logging, it&amp;#39;s not immune from problems.&amp;nbsp; In fact, it can be the source of some serious headaches.&amp;nbsp; A recent snafu at work prompted me to write about some situations where logging can bring your application performance to a screeching halt, or crash your application altogether.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Here&amp;#39;s what happened...
&lt;/p&gt;
&lt;p&gt;
An incredibly complex system of hardware and software had been running smoothly for months; as part of an instrumentation layer we opted to change the rolling log strategy from 50 10MB text files:
&lt;/p&gt;
&lt;pre class="xml"&gt;
...
&amp;lt;appender name=&amp;quot;TextFile&amp;quot; type=&amp;quot;log4net.Appender.RollingFileAppender&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;file value=&amp;quot;logs\log.txt&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;appendToFile value=&amp;quot;true&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;rollingStyle value=&amp;quot;Size&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;maxSizeRollBackups value=&amp;quot;50&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;maximumFileSize value=&amp;quot;10MB&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;layout type=&amp;quot;log4net.Layout.XMLLayout&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;lt;prefix value=&amp;quot;&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/layout&amp;gt;
&amp;lt;/appender&amp;gt;
... 
&lt;/pre&gt;
&lt;p&gt;
to 500 1MB xml files: 
&lt;/p&gt;
&lt;pre class="xml"&gt;
...
&amp;lt;appender name=&amp;quot;XmlFile&amp;quot; type=&amp;quot;log4net.Appender.RollingFileAppender&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;file value=&amp;quot;logs\log.xml&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;appendToFile value=&amp;quot;true&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;rollingStyle value=&amp;quot;Size&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;maxSizeRollBackups value=&amp;quot;500&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;maximumFileSize value=&amp;quot;1MB&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;staticLogFileName value=&amp;quot;false&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;countDirection value=&amp;quot;1&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;layout type=&amp;quot;log4net.Layout.XMLLayout&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;lt;prefix value=&amp;quot;&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/layout&amp;gt;
&amp;lt;/appender&amp;gt;
... 
&lt;/pre&gt;
&lt;p&gt;
As an ardent log4net user, I am aware of the performance impact of rolling a large number of files - if the CountDirection setting of the RollingFileAppender is less than 0 (which it is by default), the system will rename every log file each time the log rolls over.&amp;nbsp; This is costly, and in our product configuration that would mean up to 500 file renames on each roll.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
&amp;quot;Good thing I know what I&amp;#39;m doing....&amp;quot;
&lt;/p&gt;
&lt;p&gt;
Several hours after firing up the new configuration a college asked me to come look at the device.&amp;nbsp; It had slowed to a wounded crawl. &amp;nbsp; I went to dig into the logs - I popped over to the log drive and started to find the most recent one. 
&lt;/p&gt;
&lt;p&gt;
... but there were 2000 log files, not 500.&amp;nbsp; The 2GB drive dedicated to the log was completely full.&amp;nbsp; And the application was still trying to write new log entries, which meant a slew of IO Exceptions were being continuously thrown by multiple threads.
&lt;/p&gt;
&lt;p&gt;
&amp;quot;Um, I think I may have found the problem...&amp;quot;
&lt;/p&gt;
&lt;p&gt;
I removed the oldest 1999 log files and the device immediately recovered. 
&lt;/p&gt;
&lt;p&gt;
So what happened?
&lt;/p&gt;
&lt;p&gt;
The configuration XML is 100% correct.&amp;nbsp; The problem was that I accidentally deployed the software to the device with an old beta version of log4net 2.1.9; that particular version contains a bug in the RollingFileAppender code that prevents the MaxSizeRollBackups from being honored when CountDirection was &amp;gt;= 0.&amp;nbsp; Without the logs being limited in number, the software eventually filled up the hard disk with log entries.
&lt;/p&gt;
&lt;p&gt;
Which bring me to my first death-by-logging mantra...
&lt;/p&gt;
&lt;h3&gt; &lt;strong&gt;Logs Consume Space&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;
It sounds silly I know, but this is the single most prevalent antipattern I see with any logging implementation.&amp;nbsp; There is a finite amount of storage space, and you need to make sure your logging activity doesn&amp;#39;t consume more than its share.
&lt;/p&gt;
&lt;p&gt;
I frequently see this when apps use FileAppender - this beast has no chains and, as I&amp;#39;ve &lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-3-Appenders.aspx" target="_blank"&gt;stated elsewhere&lt;/a&gt;, you should never ever use it.&amp;nbsp; Ever.&amp;nbsp; Even in &amp;quot;little&amp;quot; applications it can cause massive trouble because the log accumulates over process time or application runs with no checks.&amp;nbsp; I&amp;#39;ve seen a 1KB application with 3GB of logs spanning almost a year of activity.
&lt;/p&gt;
&lt;p&gt;
But don&amp;#39;t think the problem is limited to the file-based appenders.&amp;nbsp; Remember....
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;memory is finite...&lt;/li&gt;
	&lt;li&gt;event logs fill up more often than you think...&lt;/li&gt;
	&lt;li&gt;a database can be configured to boundlessly expand as needed...&lt;/li&gt;
	&lt;li&gt;mail servers generally put caps on the size of an inbox...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Whichever appender strategies you choose, you should carefully consider the following: 
&lt;/p&gt;
... How much persistent storage are you allocating to the log?&amp;nbsp; Your answer should be a firm number, like &amp;quot;500MB&amp;quot;, and not &amp;quot;the rest of the disk&amp;quot;.&amp;nbsp; If you can, base this on the amount of information you need to have access to.&amp;nbsp; If a typical run of your application results in 10MB of logs, you can base the allocated size on the number of runs you want to persist.&amp;nbsp; If your application runs continuously - like a web site or windows service - you can plot out the logging activity over time, then gauge the allocation size from the time span of activity you want to persist.&lt;br /&gt;
&lt;br /&gt;
... How will you cope when the allocated storage is used up?&amp;nbsp; Some appenders, like the RollingFileAppender, can handle this for you by freeing up space used by old log entries.&amp;nbsp; Others, like the EventLogAppender or the AdoNetAppender, blithely log without regard to the amount of space being consumed, and it&amp;#39;s up to you to manage the size of the log in other ways.&amp;nbsp; E.g., I&amp;#39;ve seen SQL jobs dedicated to removing log records older than N hours, or truncating the log table to the N newest records. &lt;br /&gt;
&lt;br /&gt;
... What happens when you log to a full repository?&amp;nbsp; Plan for success, but understand the causes of failure.&amp;nbsp; As I recently learned, our application slows down significantly when the log drive is full, so now checking the free space of the log drive is now included in our troubleshooting guide as a checklist item under &amp;quot;Application Performance Issues&amp;quot;.&amp;nbsp; Test your application under limited logging resources to understand how it will behave. &lt;br /&gt;
&lt;p&gt;
The most important thing to remember is that logging, like any other subsystem of your application, needs to be planned, tested, and verified. 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/Obhrx-bW6i4" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Death-By-Logging-1-Logs-Consume-Space.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Death-By-Logging-1-Logs-Consume-Space.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=1731bbe4-7ae3-48ef-aa52-2f811a97ea04</guid>
      <pubDate>Mon, 15 Jun 2009 18:03:00 -1300</pubDate>
      <category>log4net</category>
      <category>software development</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=1731bbe4-7ae3-48ef-aa52-2f811a97ea04</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=1731bbe4-7ae3-48ef-aa52-2f811a97ea04</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Death-By-Logging-1-Logs-Consume-Space.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=1731bbe4-7ae3-48ef-aa52-2f811a97ea04</wfw:commentRss>
    </item>
    <item>
      <title>Creating a PowerShell Provider pt 2: Parameters and Default Drives</title>
      <description>&lt;p&gt;
My previous post in this series discussed the first major step in developing a PowerShell provider: &lt;a href="http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-1-Creating-the-Drive.aspx" target="_blank"&gt;creating a custom drive object&lt;/a&gt;.&amp;nbsp; This post rounds out the discussion by making the drive configurable with custom initialization parameters.&amp;nbsp; In addition, I&amp;#39;ll demonstrate how a provider can create one or more &amp;quot;default&amp;quot; drives without user intervention.
&lt;/p&gt;
&lt;p&gt;
This post builds on code from the previous post, which you can download from &lt;a href="http://www.beefycode.com/file.axd?file=2009%2f6%2fASPNETMembership_pt1.zip"&gt;here&lt;/a&gt;.&amp;nbsp; The &lt;a href="http://www.beefycode.com/file.axd?file=2009%2f6%2fASPNETMembership_pt1_drives.zip"&gt;completed code for this post&lt;/a&gt; is also available.
&lt;/p&gt;
&lt;h2&gt;
Parameterizing the Drive
&lt;/h2&gt;
&lt;p&gt;
At the moment, the ASP.NET Membership provider configuration lives
in code; that should really change so this drive can be used with other
providers or for multiple sites.&amp;nbsp; Such drive configurability is gained by supporting the NewDriveDynamicParameters method of the PowerShell provider.
&lt;/p&gt;
&lt;p&gt;
The NewDriveDynamicParameters method
is called each time the new-psdrive cmdlet is invoked to allow the provider to add drive creation parameters.&amp;nbsp; Although the MSDN documentation of this method isn&amp;#39;t terribly helpful, implementing it is pretty simple.&amp;nbsp; All you need to do is return an object containing properties or fields for each custom parameter.&amp;nbsp;&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
First, we need to know what parameters are needed to create the ASP.NET Membership provider.&amp;nbsp; Take a quick look at the configuration items hard-coded in the drive creation logic:
&lt;/p&gt;
&lt;pre class="c#"&gt;
// DriveInfo.cs
public MembershipDriveInfo( PSDriveInfo driveInfo )
&amp;nbsp;&amp;nbsp;&amp;nbsp; : base( driveInfo )
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; var connectionStrings = ConfigurationManager.ConnectionStrings;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; var fi = typeof( ConfigurationElementCollection )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .GetField( &amp;quot;bReadOnly&amp;quot;, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic );
&amp;nbsp;&amp;nbsp;&amp;nbsp; fi.SetValue( connectionStrings, false );
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; connectionStrings.Add(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new ConnectionStringSettings(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;ProviderConnectionString&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;data source=localhost;Integrated Security=SSPI;Initial Catalog=AwesomeWebsiteDB&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )
&amp;nbsp;&amp;nbsp;&amp;nbsp; );
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; provider = new SqlMembershipProvider();
&amp;nbsp;&amp;nbsp;&amp;nbsp; var nvc = new System.Collections.Specialized.NameValueCollection
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;connectionStringName&amp;quot;, &amp;quot;ProviderConnectionString&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;enablePasswordRetrieval&amp;quot;, &amp;quot;false&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;enablePasswordReset&amp;quot;, &amp;quot;true&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;requiresQuestionAndAnswer&amp;quot;, &amp;quot;false&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;requiresUniqueEmail&amp;quot;, &amp;quot;false&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordFormat&amp;quot;, &amp;quot;Hashed&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;maxInvalidPasswordAttempts&amp;quot;, &amp;quot;5&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;minRequiredPasswordLength&amp;quot;, &amp;quot;6&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;minRequiredNonalphanumericCharacters&amp;quot;, &amp;quot;0&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordAttemptWindow&amp;quot;, &amp;quot;10&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordStrengthRegularExpression&amp;quot;, &amp;quot;&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;applicationName&amp;quot;, &amp;quot;/&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; };
&amp;nbsp;&amp;nbsp;&amp;nbsp; provider.Initialize( &amp;quot;AspNetSqlMembershipProvider&amp;quot;, nvc );
}
&lt;/pre&gt;
&lt;p&gt;
There are a number of parameters used to initialize the ASP.NET membership provider.&amp;nbsp; Based on the code, I would want to configure any of the following facets of the ASP.NET Membership provider: 
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;the Connection String server and databse name;
	&lt;/li&gt;
	&lt;li&gt;EnablePasswordRetrieval;
	&lt;/li&gt;
	&lt;li&gt;EnablePasswordReset; 
	&lt;/li&gt;
	&lt;li&gt;RequiresQuestionAndAnswer;
	&lt;/li&gt;
	&lt;li&gt;RequiresUniqueEmail;&lt;/li&gt;
	&lt;li&gt;PasswordFormat; 
	&lt;/li&gt;
	&lt;li&gt;MaxInvalidPassordAttempts;&lt;/li&gt;
	&lt;li&gt;MinRequiredPasswordLength; 
	&lt;/li&gt;
	&lt;li&gt;MinRequiredNonalphanumericCharacters;&lt;/li&gt;
	&lt;li&gt;PasswordAttemptWindow;&lt;/li&gt;
	&lt;li&gt;PasswordStrengthRegularExpression;&lt;/li&gt;
	&lt;li&gt;ApplicationName. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
While all of these should be configurable, assumptions can be made
for most of them.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
The first step towards implementing the NewDriveDynamicParameters method is to define an object containing read/write properties for each configurable parameter:
&lt;/p&gt;
&lt;pre class="c#"&gt;
// DriveParameters.cs
using System;
using System.Management.Automation;
using System.Web.Security;
namespace ASPNETMembership
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; public class DriveParameters
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public DriveParameters()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; EnablePasswordReset = true;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; EnablePasswordRetrieval = false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RequiresQuestionAndAnswer = false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RequiresUniqueEmail = false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MaxInvalidPasswordAttempts = 5;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MinRequiredNonalphanumericCharacters = 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MinRequiredPasswordLength = 6;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PasswordAttemptWindow = 10;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PasswordStrengthRegularExpression = String.Empty;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ApplicationName = &amp;quot;/&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PasswordFormat = MembershipPasswordFormat.Hashed;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Parameter( Mandatory=true )]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public string Server { get; set; }&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Parameter( Mandatory = true )]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public string Catalog { get; set; }
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public bool EnablePasswordRetrieval { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public bool EnablePasswordReset { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public bool RequiresQuestionAndAnswer { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public bool RequiresUniqueEmail { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MembershipPasswordFormat PasswordFormat { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int MaxInvalidPasswordAttempts { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int MinRequiredPasswordLength { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int MinRequiredNonalphanumericCharacters { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int PasswordAttemptWindow { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public string PasswordStrengthRegularExpression { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public string ApplicationName { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p&gt;
Note the use of the System.Management.Automation.ParameterAttribute on lines 24 and 27 to identify the Server and Catalog parameters as required (Mandatory=true).&amp;nbsp; The remaining optional parameters are initialized to default values in the constructor.
&lt;/p&gt;
&lt;p&gt;
With the DriveParameters type defined, the NewDriveDyanmicParameters method of the ASP.NET Membership provider can now be implemented:
&lt;/p&gt;
&lt;pre class="c#"&gt;
// Provider.cs
protected override object NewDriveDynamicParameters()
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; return new DriveParameters();
}
&lt;/pre&gt;
&lt;p&gt;
PowerShell will call this method each time the new-psdrive cmdlet is invoked for this provider.&amp;nbsp; The object returned to PowerShell is filled with parameter values and passed back to the provider&amp;#39;s NewDrive method via the DynamicParameters property; in the Membership PowerShell provider, NewDrive simply passes the parameters to the MembershipDriveInfo constructor:
&lt;/p&gt;
&lt;pre class="c#"&gt;
// Provider.cs
protected override System.Management.Automation.PSDriveInfo NewDrive( PSDriveInfo drive )
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; var driveParams = this.DynamicParameters as DriveParameters;
&amp;nbsp;&amp;nbsp;&amp;nbsp; return new MembershipDriveInfo(drive, driveParams);
}
&lt;/pre&gt;
&lt;p&gt;
which must then act on the parameters when initializing the ASP.NET Membership provider:
&lt;/p&gt;
&lt;pre class="c#"&gt;
// DriveInfo.cs
using System;
using System.Configuration;
using System.Management.Automation;
using System.Web.Security;
namespace ASPNETMembership
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; public class MembershipDriveInfo : PSDriveInfo
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MembershipProvider provider;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MembershipDriveInfo( PSDriveInfo driveInfo, DriveParameters parameters )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : base( driveInfo )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var connectionStrings = ConfigurationManager.ConnectionStrings;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var fi = typeof( ConfigurationElementCollection )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .GetField( &amp;quot;bReadOnly&amp;quot;, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fi.SetValue( connectionStrings, false );
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var connectionString = String.Format(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;data source={0};Integrated Security=SSPI;Initial Catalog={1}&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; parameters.Server,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; parameters.Catalog
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var moniker = Guid.NewGuid().ToString(&amp;quot;N&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var connectionStringName = &amp;quot;cxn_&amp;quot; + moniker;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var providerName = &amp;quot;pvdr_&amp;quot; + moniker;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connectionStrings.Add(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new ConnectionStringSettings(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connectionStringName,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connectionString
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; provider = new SqlMembershipProvider();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var nvc = new System.Collections.Specialized.NameValueCollection
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;connectionStringName&amp;quot;, connectionStringName },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;enablePasswordRetrieval&amp;quot;, parameters.EnablePasswordRetrieval.ToString() },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;enablePasswordReset&amp;quot;, parameters.EnablePasswordReset.ToString() },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;requiresQuestionAndAnswer&amp;quot;, parameters.RequiresQuestionAndAnswer.ToString() },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;requiresUniqueEmail&amp;quot;, parameters.RequiresUniqueEmail.ToString() },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordFormat&amp;quot;, parameters.PasswordFormat.ToString() },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;maxInvalidPasswordAttempts&amp;quot;, parameters.MaxInvalidPasswordAttempts.ToString() },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;minRequiredPasswordLength&amp;quot;, parameters.MinRequiredPasswordLength.ToString() },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;minRequiredNonalphanumericCharacters&amp;quot;, parameters.MinRequiredNonalphanumericCharacters.ToString() },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordAttemptWindow&amp;quot;, parameters.PasswordAttemptWindow.ToString() },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordStrengthRegularExpression&amp;quot;, parameters.PasswordStrengthRegularExpression },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;applicationName&amp;quot;, parameters.ApplicationName },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; provider.Initialize( providerName, nvc );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MembershipProvider MembershipProvider
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; get
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this.provider;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p&gt;
Not a lot of new code here; the constructor now accepts an additional DriveParameters argument that is used to initialize the connection string and membership provider configuration.&amp;nbsp; Using dynamically-generated connection string and provider names prevents naming collisions if more than one drive is initialized by the provider (lines 26-28).
&lt;/p&gt;
&lt;h3&gt;Using the Drive Parameters&lt;/h3&gt;
&lt;p&gt;
Build and run. The new dynamic drive parameters can be specified directly to the new-psdrive cmdlet just like any other cmdlet parameter:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; new-psdrive -psprovider aspnetmembership -name users -root &amp;#39;&amp;#39; 
&amp;nbsp;&amp;nbsp;&amp;nbsp; -server localhost -catalog AwesomeWebsiteDB 
&amp;nbsp;&amp;nbsp;&amp;nbsp; -MinRequiredPasswordLength 8 -PasswordAttemptWindow 4 
&amp;nbsp;
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Provider&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Root&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CurrentLocation
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ---------------
users&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASPNETMemb...
&lt;/pre&gt;
&lt;p&gt;
Note that PowerShell prompts the user for the any required parameters if they aren&amp;#39;t supplied:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; remove-psdrive users
&amp;gt; new-psdrive -psprovider aspnetmembership -name users -root &amp;#39;&amp;#39;
cmdlet New-PSDrive at command pipeline position 1
Supply values for the following parameters:
Catalog: 
&amp;gt; AwesomeWebsiteDB
Server:
&amp;gt; localhost
&amp;nbsp;
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Provider&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Root&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CurrentLocation
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ---------------
users&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASPNETMemb...
&lt;/pre&gt;
&lt;h2&gt;Creating Default Drives&lt;/h2&gt;
&lt;p&gt;
Another feature available to PowerShell providers is the ability to initialize a set of default drives when the provider assembly is first loaded.&amp;nbsp; This is accomplished by overriding the&amp;nbsp;provider&amp;#39;s InitializeDefaultDrives method to return a list of PSDriveInfo objects describing the drives to create: 
&lt;/p&gt;
&lt;pre class="c#"&gt;
// Provider.cs
&amp;nbsp;
protected override Collection&amp;lt;PSDriveInfo&amp;gt; InitializeDefaultDrives()
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; var driveInfo = new PSDriveInfo(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;users&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.ProviderInfo,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;Default ASP.NET Membership Drive&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; null
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );
&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp; var parameters = new DriveParameters
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Catalog = &amp;quot;AwesomeWebsiteDB&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Server = &amp;quot;localhost&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; return new Collection&amp;lt;PSDriveInfo&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new MembershipDriveInfo(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; driveInfo,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; parameters
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };
}
&lt;/pre&gt;
&lt;p&gt;
Each PSDriveInfo object in the returned collection will then be passed to the NewDrive method of the provider to complete the drive creation (the NewDriveDynamicParameters method is not called in this case).&amp;nbsp; As such, the ASP.NET Membership PowerShell provider&amp;#39;s NewDrive method must be modified to acommodate pre-initialized drives:
&lt;/p&gt;
&lt;pre class="c#"&gt;
protected override PSDriveInfo NewDrive( PSDriveInfo drive )
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; // see if the drive has already been initialized
&amp;nbsp;&amp;nbsp;&amp;nbsp; //    e.g., via InitializeDefaultDrives()
&amp;nbsp;&amp;nbsp;&amp;nbsp; if( drive is MembershipDriveInfo )
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return drive;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; var driveParams = this.DynamicParameters as DriveParameters;
&amp;nbsp;&amp;nbsp;&amp;nbsp; return new MembershipDriveInfo(drive, driveParams);
}
&lt;/pre&gt;
&lt;h3&gt;Testing Default Drive Creation&lt;br /&gt;
&lt;/h3&gt;
&lt;p&gt;
Build and run; you can verify that the default drive was created using the get-psdrive cmdlet:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; get-psdrive
&amp;nbsp;
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Provider&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Root&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CurrentLocation
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ---------------
...
users&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASPNETMemb...
&lt;/pre&gt;
&lt;p&gt;
No need to use the new-psdrive cmdlet - the users drive is created automagically as PowerShell loads the provider assembly. 
&lt;/p&gt;
&lt;h2&gt;
Coming Up
&lt;/h2&gt;
&lt;p&gt;
That&amp;#39;s about it for drives; for here on in, the focus will be on making the provider work with all the features available in PowerShell.&amp;nbsp; The next post will add support for the get-item cmdlet and discuss the various flavors of item paths the provider must support. 
&lt;/p&gt;
&lt;p&gt;
The code for this post is available here:
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.beefycode.com/file.axd?file=2009%2f6%2fASPNETMembership_pt1_drives.zip"&gt;ASPNETMembership_pt1_drives.zip (4.26 kb)&lt;/a&gt;&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
As always, thanks for reading, and if you liked this post, please Kick It, Shout It, trackback, tweet it, and comment using the clicky thingies below! 
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.beefycode.com/file.axd?file=2009%2f6%2fASPNETMembership_pt1_drives.zip"&gt;&lt;br /&gt;
&lt;/a&gt;
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/m2ZKYPzKor0" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-2-Initializing-the-Drive.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-2-Initializing-the-Drive.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=bf3e69aa-64ce-46db-9872-fba6d8b3a056</guid>
      <pubDate>Thu, 11 Jun 2009 16:26:00 -1300</pubDate>
      <category>powershell</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=bf3e69aa-64ce-46db-9872-fba6d8b3a056</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=bf3e69aa-64ce-46db-9872-fba6d8b3a056</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-2-Initializing-the-Drive.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=bf3e69aa-64ce-46db-9872-fba6d8b3a056</wfw:commentRss>
    </item>
    <item>
      <title>Creating a PowerShell Provider pt 1: Creating the Drive</title>
      <description>&lt;p&gt;
&lt;img src="http://www.beefycode.com/image.axd?picture=2009%2f6%2fclimbingwall.jpg" alt="" /&gt;The first step to creating a full-featured PowerShell provider is to be able to create a new drive.&amp;nbsp; The goal of this post is to enable the most basic functional drive that provides access to the ASP.NET Membership user store.&amp;nbsp; Before you begin, make sure you have the project set up properly as I describe &lt;a href="http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-0-Gearing-Up.aspx"&gt;here&lt;/a&gt;, or you can &lt;a href="http://www.beefycode.com/file.axd?file=2009%2f6%2fASPNETMembership_pt1.zip"&gt;download the project pre-coded (3.24 kb)&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
We&amp;#39;ll start by creating a minimal provider implementation.&amp;nbsp; Add a class to the project named Provider and fill it in like so:
&lt;/p&gt;
&lt;pre class="c#"&gt;
// Provider.cs
using System.Management.Automation;
using System.Management.Automation.Provider;
namespace ASPNETMembership
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; [CmdletProvider( &amp;quot;ASPNETMembership&amp;quot;, ProviderCapabilities.None )]
&amp;nbsp;&amp;nbsp;&amp;nbsp; public class Provider : DriveCmdletProvider
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected override PSDriveInfo NewDrive( PSDriveInfo drive )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return base.NewDrive( drive );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected override object NewDriveDynamicParameters()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return base.NewDriveDynamicParameters();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
} &amp;nbsp;
&lt;/pre&gt;
&lt;p&gt;
A few items of note:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;
	the Provider class derives from System.Automation.Provider.DriveCmdletProvider.  All PowerShell providers derive from one of the abstract *CmdletProvider base classes.  I&amp;#39;ll discuss these classes a bit in the next post, but for now know that DriveCmdletProvider is the most basic base class you can use to create a provider.
	&lt;/li&gt;
	&lt;li&gt;
	the class is decorated with a System.Management.Automation.Provider.CmdletProviderAttribute.&amp;nbsp; This attribute is required to get PowerShell to recognize your class as a provider when the assembly is loaded.&amp;nbsp; In addition, the attribute identifies some of the capabilities the provider supports; more on this in a later post.
	&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
The class overrides two methods related to drive creation - at the moment they are simply placeholders and defer to the DriveCmdletProvider implementations.&amp;nbsp; When PowerShell is presented with a new-psdrive cmdlet specifying the ASPNETMembership provider, it will ferret the call to our provider&amp;#39;s NewDrive method to create the drive. 
&lt;/p&gt;
&lt;p&gt;
Compile and run; in the PowerShell console that opens, enter the following command (note that my convention is to mark commands you type in with leading &amp;gt;&amp;#39;s):
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; new-psdrive -PsProvider ASPNETMembership -Name users -Root &amp;quot;&amp;quot;;
&lt;/pre&gt;
&lt;p&gt;
This will invoke the NewDrive method of the ASPNETMembership provider and create a new drive named &amp;quot;users&amp;quot; in the PowerShell session.&amp;nbsp; To verify the drive exists, use the get-psdrive cmdlet.&amp;nbsp; You should see output similar to the following  :
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; get-psdrive
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Provider&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Root&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CurrentLocation
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ---------------
C&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FileSystem&amp;nbsp;&amp;nbsp;&amp;nbsp; C:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...ensions\Profile
cert&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Certificate&amp;nbsp;&amp;nbsp; \
Env&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Environment
Function&amp;nbsp;&amp;nbsp; Function
Gac&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AssemblyCache Gac
HKCU&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Registry&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HKEY_CURRENT_USER
HKLM&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Registry&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HKEY_LOCAL_MACHINE
users&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASPNETMemb...
&lt;/pre&gt;
&lt;h2&gt;
Custom Drive Objects 
&lt;/h2&gt;
&lt;p&gt;
Providers are transient: PowerShell will create many instances of this provider in response to different cmdlets.&amp;nbsp; So it&amp;#39;s not possible to persist any state in a provider object.&amp;nbsp; Instead, state needs to be stored in a custom drive object that is returned from the NewDrive method of the provider.&amp;nbsp; PowerShell caches this instance and makes it available to the provider via the DriveCmdletProvider.PsDriveInfo property.
&lt;/p&gt;
&lt;p&gt;
The only real requirement for a custom drive object is that it derive from System.Automation.PSDriveInfo.&amp;nbsp; This class is the most basic implementation of a custom drive object possible; go ahead and add it to the project:
&lt;/p&gt;
&lt;pre class="c#"&gt;
// DriveInfo.cs
using System.Management.Automation;
namespace ASPNETMembership
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; public class MembershipDriveInfo : PSDriveInfo
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MembershipDriveInfo( PSDriveInfo driveInfo )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : base( driveInfo )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p&gt;
Then modify the Provider class to return a new instance of this drive object in the NewDrive method: 
&lt;/p&gt;
&lt;pre class="c#"&gt;
// Provider.cs
// ... 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected override PSDriveInfo NewDrive( PSDriveInfo drive )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new MembershipDriveInfo( drive );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
// ...
&lt;/pre&gt;
&lt;p&gt;
Build and run; in the console, enter the following commands: 
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; new-psdrive -psp aspnetmembership -name users -root &amp;quot;&amp;quot;
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Provider&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Root&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CurrentLocation
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ---------------
users&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASPNETMemb...
&amp;gt; ( get-psdrive users ).gettype().fullname
ASPNETMembership.MembershipDriveInfo
&lt;/pre&gt;
&lt;p&gt;
Note that the type of the users drive is now our custom drive object type - at this point, we have our first soild hook into the powershell provider system.&amp;nbsp; Now it&amp;#39;s time to fold ASP.NET Membership into the custom drive.... but there&amp;#39;s a small problem to deal with first.
&lt;/p&gt;
&lt;h3&gt;Configuring ASP.NET Membership Programatically&lt;/h3&gt;
&lt;p&gt;
Configuring ASP.NET Membership in a web application is pretty easy.&amp;nbsp; You just pop in a little config section into the web.config and everything just works; e.g., this web.config sets up the SqlMembershipProvider for a website:
&lt;/p&gt;
&lt;pre class="xml"&gt;
&amp;lt;configuration&amp;gt;
... 
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;connectionStrings&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;add name=&amp;quot;MembershipProviderServices&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connectionString=&amp;quot;data source=localhost;Integrated Security=SSPI;Initial Catalog=AwesomeWebsiteDB&amp;quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/connectionStrings&amp;gt;
... 
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;membership&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;providers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;clear/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;add name=&amp;quot;AspNetSqlMembershipProvider&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type=&amp;quot;System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connectionStringName=&amp;quot;MembershipProviderServices&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; enablePasswordRetrieval=&amp;quot;false&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; enablePasswordReset=&amp;quot;true&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; requiresQuestionAndAnswer=&amp;quot;false&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; requiresUniqueEmail=&amp;quot;false&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; passwordFormat=&amp;quot;Hashed&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; maxInvalidPasswordAttempts=&amp;quot;5&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; minRequiredPasswordLength=&amp;quot;6&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; minRequiredNonalphanumericCharacters=&amp;quot;0&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; passwordAttemptWindow=&amp;quot;10&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; passwordStrengthRegularExpression=&amp;quot;&amp;quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; applicationName=&amp;quot;/&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/providers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/membership&amp;gt;
...
&amp;lt;/configuration&amp;gt; 
&lt;/pre&gt;
&lt;p&gt;
In PowerShell, there is no equivalent to a web.config or app.config we can use to configure the membership provider.&amp;nbsp; Instead, we have to configure the memberhip provider via code.&amp;nbsp; Not a big deal; this short snippit of code results in the same membership provider configuration defined by the web.config example: 
&lt;/p&gt;
&lt;pre class="c#"&gt;
var provider = new SqlMembershipProvider();
var nvc = new System.Collections.Specialized.NameValueCollection
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;connectionStringName&amp;quot;, &amp;quot;MembershipProviderServices&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;enablePasswordRetrieval&amp;quot;, &amp;quot;false&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;enablePasswordReset&amp;quot;, &amp;quot;true&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;requiresQuestionAndAnswer&amp;quot;, &amp;quot;false&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;requiresUniqueEmail&amp;quot;, &amp;quot;false&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordFormat&amp;quot;, &amp;quot;Hashed&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;maxInvalidPasswordAttempts&amp;quot;, &amp;quot;5&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;minRequiredPasswordLength&amp;quot;, &amp;quot;6&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;minRequiredNonalphanumericCharacters&amp;quot;, &amp;quot;0&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordAttemptWindow&amp;quot;, &amp;quot;10&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordStrengthRegularExpression&amp;quot;, &amp;quot;&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;applicationName&amp;quot;, &amp;quot;/&amp;quot; },
};
provider.Initialize( &amp;quot;AspNetSqlMembershipProvider&amp;quot;, nvc );
&lt;/pre&gt;
&lt;p&gt;
The only wrinkle is on line 4, where we reference a connection string by its name.&amp;nbsp; The ConfigurationManager.ConnectionStrings collection is read-only, so how can we add a new connection string to it?&amp;nbsp; Simple, we cheat and make the collection writeable (many thanks to David Gardiner for &lt;a href="http://david.gardiner.net.au/2008/09/programmatically-setting.html" target="_blank"&gt;posting a similar solution on his blog&lt;/a&gt;!): 
&lt;/p&gt;
&lt;pre class="c#"&gt;
using System.Configuration;
var connectionStrings = ConfigurationManager.ConnectionStrings;
// find the private bReadOnly field in the connection strings base type
var fi = typeof( ConfigurationElementCollection );
&amp;nbsp;&amp;nbsp;&amp;nbsp; .GetField( &amp;quot;bReadOnly&amp;quot;, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic );
// change the field value to false, making the object writeable
fi.SetValue( connectionStrings, false );
// add a new connection string
connectionStrings.Add(
&amp;nbsp;&amp;nbsp;&amp;nbsp; new ConnectionStringSettings(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;MembershipProviderServices&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;data source=localhost;Integrated Security=SSPI;Initial Catalog=AwesomeWebsiteDB&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )
&amp;nbsp;&amp;nbsp;&amp;nbsp; );
&lt;/pre&gt;
&lt;p&gt;
Now with the ability to add connection strings to the configuration manager programmatically, we can add ASP.NET Membership services to our custom drive object.
&lt;/p&gt;
&lt;h3&gt;Adding ASP.NET Membership Features to the Drive&lt;br /&gt;
&lt;/h3&gt;
&lt;p&gt;
Modify the custom drive class with our ASP.NET Membership configuration code as follows:
&lt;/p&gt;
&lt;pre class="c#"&gt;
// DriveInfo.cs
using System.Configuration;
using System.Management.Automation;
using System.Web.Security;
namespace ASPNETMembership
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; public class MembershipDriveInfo : PSDriveInfo
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MembershipProvider provider;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MembershipDriveInfo( PSDriveInfo driveInfo )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : base( driveInfo )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var connectionStrings = ConfigurationManager.ConnectionStrings;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var fi = typeof( ConfigurationElementCollection )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .GetField( &amp;quot;bReadOnly&amp;quot;, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fi.SetValue( connectionStrings, false );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connectionStrings.Add(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new ConnectionStringSettings(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;ProviderConnectionString&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;data source=localhost;Integrated Security=SSPI;Initial Catalog=AwesomeWebsiteDB&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; provider = new SqlMembershipProvider();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var nvc = new System.Collections.Specialized.NameValueCollection
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;connectionStringName&amp;quot;, &amp;quot;ProviderConnectionString&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;enablePasswordRetrieval&amp;quot;, &amp;quot;false&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;enablePasswordReset&amp;quot;, &amp;quot;true&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;requiresQuestionAndAnswer&amp;quot;, &amp;quot;false&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;requiresUniqueEmail&amp;quot;, &amp;quot;false&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordFormat&amp;quot;, &amp;quot;Hashed&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;maxInvalidPasswordAttempts&amp;quot;, &amp;quot;5&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;minRequiredPasswordLength&amp;quot;, &amp;quot;6&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;minRequiredNonalphanumericCharacters&amp;quot;, &amp;quot;0&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordAttemptWindow&amp;quot;, &amp;quot;10&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;passwordStrengthRegularExpression&amp;quot;, &amp;quot;&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;quot;applicationName&amp;quot;, &amp;quot;/&amp;quot; },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; provider.Initialize( &amp;quot;AspNetSqlMembershipProvider&amp;quot;, nvc );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MembershipProvider MembershipProvider
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; get
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this.provider;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p&gt;
Note that the custom drive holds a reference to the ASP.NET Membership provider that is initialized in the constructor - since PowerShell caches the drive object, it&amp;#39;s safe to save the provider reference.&amp;nbsp; The drive also exposes the membership provider through the public MembershipProvider property. 
&lt;/p&gt;
&lt;p&gt;
Build and run.&amp;nbsp; Now the fun begins - first, let&amp;#39;s create get a reference to our drive: 
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; $d = new-psdrive -P
sProvider ASPNETMembership -Name users -Root &amp;quot;&amp;quot;
&amp;gt; $d.MembershipProvider | get-member
&amp;nbsp;&amp;nbsp; TypeName: System.Web.Security.SqlMembershipProvider
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MemberType Definition
----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ---------- ----------
ValidatingPassword&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Event&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Web.Security.Membersh...
ChangePassword&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Boolean ChangePasswor...
ChangePasswordQuestionAndAnswer&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Boolean ChangePasswor...
CreateUser&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Web.Security.Membersh...
DeleteUser&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Boolean DeleteUser(St...
Equals&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Boolean Equals(Object...
FindUsersByEmail&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Web.Security.Membersh...
FindUsersByName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Web.Security.Membersh...
GeneratePassword&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String GeneratePasswo...
GetAllUsers&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Web.Security.Membersh...
GetHashCode&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Int32 GetHashCode()
GetNumberOfUsersOnline&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Int32 GetNumberOfUser...
GetPassword&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String GetPassword(St...
GetType&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Type GetType()
GetUser&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Web.Security.Membersh...
GetUserNameByEmail&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String GetUserNameByE...
Initialize&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Void Initialize(Strin...
ResetPassword&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String ResetPassword(...
ToString&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String ToString()
UnlockUser&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Boolean UnlockUser(St...
UpdateUser&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Void UpdateUser(Membe...
ValidateUser&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Method&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Boolean ValidateUser(...
ApplicationName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.String ApplicationNam...
Description&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.String Description {g...
EnablePasswordReset&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.Boolean EnablePasswor...
EnablePasswordRetrieval&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.Boolean EnablePasswor...
MaxInvalidPasswordAttempts&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.Int32 MaxInvalidPassw...
MinRequiredNonAlphanumericCharacters Property&amp;nbsp;&amp;nbsp; System.Int32 MinRequiredNonA...
MinRequiredPasswordLength&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.Int32 MinRequiredPass...
Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.String Name {get;}
PasswordAttemptWindow&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.Int32 PasswordAttempt...
PasswordFormat&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.Web.Security.Membersh...
PasswordStrengthRegularExpression&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.String PasswordStreng...
RequiresQuestionAndAnswer&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.Boolean RequiresQuest...
RequiresUniqueEmail&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Property&amp;nbsp;&amp;nbsp; System.Boolean RequiresUniqu...
&lt;/pre&gt;
&lt;p&gt;
As you can see, the membership provider is fully accessible from PowerShell via the drive object.&amp;nbsp; We can create users:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; $status = 0;
&amp;gt; $d.MembershipProvider.CreateUser( &amp;quot;myuser&amp;quot;, &amp;quot;mypassword1234&amp;quot;, &amp;quot;myuser@hotmail.com&amp;quot;, &amp;quot;what is your favorite color?&amp;quot;, &amp;quot;plaid&amp;quot;, $true, [Guid]::NewGuid(), ([ref]$status))
UserName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : myuser
ProviderUserKey&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : b6ebc2a4-2ff8-4cf8-928e-969416edd704
Email&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : myuser@hotmail.com
PasswordQuestion&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : what is your favorite color?
Comment&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :
IsApproved&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : True
IsLockedOut&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : False
LastLockoutDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 1/1/1754 12:00:00 AM
CreationDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 6/11/2009 1:13:50 PM
LastLoginDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 6/11/2009 1:13:50 PM
LastActivityDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 6/11/2009 1:13:50 PM
LastPasswordChangedDate : 6/11/2009 1:13:50 PM
IsOnline&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : True
ProviderName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : AspNetSqlMembershipProvider
&lt;/pre&gt;
&lt;p&gt;
We can get existing users:
&lt;/p&gt;
&lt;pre class="c#"&gt;
&amp;gt; $d.MembershipProvider.GetUser( &amp;quot;myuser&amp;quot;, $false )
UserName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : myuser
ProviderUserKey&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : b6ebc2a4-2ff8-4cf8-928e-969416edd704
Email&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : myuser@hotmail.com
PasswordQuestion&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : what is your favorite color?
Comment&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :
IsApproved&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : True
IsLockedOut&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : False
LastLockoutDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 12/31/1753 7:00:00 PM
CreationDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 6/11/2009 1:13:50 PM
LastLoginDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 6/11/2009 1:13:50 PM
LastActivityDate&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 6/11/2009 1:13:50 PM
LastPasswordChangedDate : 6/11/2009 1:13:50 PM
IsOnline&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : True
ProviderName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : AspNetSqlMembershipProvider 
&lt;/pre&gt;
&lt;p&gt;
We can even remove users:
&lt;/p&gt;
&lt;pre class="ps"&gt;
&amp;gt; $d.MembershipProvider.DeleteUser( &amp;quot;myuser&amp;quot;, $true )
True
&amp;gt; $d.MembershipProvider.GetUser( &amp;quot;myuser&amp;quot;,$false ) 
#note that no output is returned this time 
&lt;/pre&gt;
&lt;p class="c#"&gt;
Using the drive directly to manage users may work, but
it is a bit clumsy.&amp;nbsp; You may be asking yourself why we implemented the
drive at all, given that PowerShell could create the MembershipProvider
object directly.&amp;nbsp; The drive is the basis on which all PowerShell
provider functionality will be based; many standard cmdlets leverage the data stored in a drive object.&amp;nbsp; So things may look cumbersome at the moment, but soon we&amp;#39;ll be working
with cmdlets, pipes, and sets of user objects - all thanks to our
custom drive object. 
&lt;/p&gt;
&lt;h2&gt;Coming Up &lt;br /&gt;
&lt;/h2&gt;
&lt;p&gt;
At the moment, the ASP.NET Membership provider configuration lives
code; that should really change so this drive can be used with other
providers or for multiple sites.&amp;nbsp; The NewDriveDynamicParameters method
of the PowerShell provider object allows the provider to request
additional drive creation parameters from the new-psdrive cmdlet.&amp;nbsp; My next post will describe how to properly implement this method. 
&lt;/p&gt;
&lt;p&gt;
In upcoming posts, I&amp;#39;ll show you how to implement support for different core cmdlets, such as get-item, remove-item, new-item, and get-childitem (or &amp;#39;dir&amp;#39; for you alias users).&amp;nbsp; In addition, I&amp;#39;ll dig deep into the various path syntaxes that PowerShell expects your provider to support.&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
The code for this post is available here: 
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.beefycode.com/file.axd?file=2009%2f6%2fASPNETMembership_pt1.zip"&gt;ASPNETMembership_pt1.zip (3.24 kb)&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
As always, thanks for reading, and if you liked this post, please Kick It, Shout It, trackback, or comment. 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/rKOIK4y1T7A" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-1-Creating-the-Drive.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-1-Creating-the-Drive.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=281d070b-5957-405b-b3ad-fbc24916ecb0</guid>
      <pubDate>Tue, 09 Jun 2009 14:06:00 -1300</pubDate>
      <category>powershell</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=281d070b-5957-405b-b3ad-fbc24916ecb0</pingback:target>
      <slash:comments>2</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=281d070b-5957-405b-b3ad-fbc24916ecb0</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-1-Creating-the-Drive.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=281d070b-5957-405b-b3ad-fbc24916ecb0</wfw:commentRss>
    </item>
    <item>
      <title>Log4Net Tutorials and Resources</title>
      <description>&lt;p&gt;
&lt;img src="http://www.beefycode.com/image.axd?picture=2009%2f6%2flogflowers.jpg" alt="" /&gt;I&amp;#39;ve been meaning to create a landing page for all the log4net traffic on my blog.&amp;nbsp; A friend of mine recently told me how difficult it is to drill through all the tutorials, so I decided to organize access to all the log4net stuff in one place.&amp;nbsp; I&amp;#39;ll keep this page updated as the blog grows.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
If there are any log4net resources you think I should add to this list or blog about, please add them in the comments or send them to me via the contact link.
&lt;/p&gt;
&lt;h2&gt;
Log4Net Tutorials
&lt;/h2&gt;
&lt;p&gt;
If you&amp;#39;ve never used log4net before, you should begin with the getting started and configuration topics.&amp;nbsp; You&amp;#39;ll want to review the appender, layout, and patterns tutorials next, and use the others as you see fit. 
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-1-Getting-Started.aspx" target="_blank" title="Getting Started with Log4Net"&gt;Getting Started with Log4Net&lt;/a&gt; - never used log4net before, but need to get running quickly?&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-2-Basic-XML-Configuration.aspx" target="_blank" title="Basic XML Configuration of log4net"&gt;Basic XML Configuration&lt;/a&gt; - describes how to isolate log4net configuration to an XML file for easier modification.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-3-Appenders.aspx" target="_blank" title="log4net appenders"&gt;Appenders&lt;/a&gt; - a discussion of the catalog of appenders available in the log4net distribution.  Need to figure out how to log to the console?  Or a text file?  Or a database?  This tutorial is for you.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-4-Layouts-and-Patterns.aspx" target="_blank" title="log4net layouts and patterns"&gt;Layouts and Patterns&lt;/a&gt; - want more control over the way your log looks?&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-5-Using-Logger-Objects.aspx" target="_blank" title="log4net logger objects"&gt;Using Logger Objects&lt;/a&gt; - learn to use logger objects to organize your logging code into independently configurable parts.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-6-Log-Event-Context.aspx" target="_blank" title="log4net log event context"&gt;Log Event Context&lt;/a&gt; - need some extra data in that log message?  Maybe a call stack label, user identifier, or session moniker?&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-7-Filters.aspx" target="_blank" title="log4net filters"&gt;Filters&lt;/a&gt; - learn how to tame that log output and filter out the stuff you don&amp;#39;t want or need.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-8-Lossy-Logging.aspx" target="_blank" title="configuring log4net for lossy logging"&gt;Lossy Logging&lt;/a&gt; - learn about an advanced technique of optimizing your logging activity.  Lossy logging gives you the benefit of a verbose log with the performance of a minimal logging scenario.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Recomended Practices
&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Recommended-Practices-pt-1-Your-Code.aspx" target="_blank" title="best practices for your code"&gt;Your Code&lt;/a&gt; - learn what you should do in your code to make the most of logging.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Log4Net-Recommended-Practices-pt-2-Isolating-Bugs.aspx" target="_blank" title="using log4net to help isolate bugs"&gt;Isolating Bugs &lt;/a&gt; - see how logging can be used to isolate production bugs.
	&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Death by Logging Mantras&lt;/h3&gt;
&lt;p&gt;
These mantras discuss specific scenarios where logging can cripple your application or its performance.&amp;nbsp; These are in-process and links will become available as I write the posts. 
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Death-By-Logging-1-Logs-Consume-Space.aspx" target="_blank"&gt;Mantra #1: Logs Consume Space&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Mantra #2: Logging Code is Still Code&lt;/li&gt;
	&lt;li&gt;Mantra #3: Logging Consumes Resources&lt;/li&gt;
	&lt;li&gt;Mantra #4: Noise is not a Log&lt;br /&gt;
	&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Resources 
&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Extension-Methods-for-Deferred-Message-Formatting-in-Log4Net.aspx" target="_blank" title="using lambda expressions to defer or avoid message formatting at runtime"&gt;Extension Methods for Deferred Log4Net Message Formatting&lt;/a&gt; - a set of extensions methods that make deferring log message formatting easier.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/A-Look-at-the-Live-Labs-Simple-Logging-Framework.aspx" target="_blank" title="live labs slf"&gt;A Look at the Live Labs Simple Logging Framework&lt;/a&gt; - a quick overview of the Live Labs SLF, and some pros and cons it has with regard to log4net. &lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.beefycode.com/post/Correlating-and-Grepping-Logs-with-PowerShell.aspx" target="_blank" title="correlating log4net xml output against the event log"&gt;Correlating and Grepping Logs with PowerShell&lt;/a&gt; - an example of how to leverage the log4net XML log output to slurp and correlate a log against the system event log. &lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/x54K1sDGMug" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Log4Net-Tutorials-and-Resources.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Log4Net-Tutorials-and-Resources.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=d6cc7ffb-cf4d-42bd-8e17-1dffdecfda04</guid>
      <pubDate>Sun, 07 Jun 2009 03:57:00 -1300</pubDate>
      <category>log4net</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=d6cc7ffb-cf4d-42bd-8e17-1dffdecfda04</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=d6cc7ffb-cf4d-42bd-8e17-1dffdecfda04</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Log4Net-Tutorials-and-Resources.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=d6cc7ffb-cf4d-42bd-8e17-1dffdecfda04</wfw:commentRss>
    </item>
    <item>
      <title>MSDN Southern Fried Road Show Charlotte</title>
      <description>&lt;p&gt;
Many, many thanks to Brian Hitney, Chad Brooks, and Glen Gordon for letting me give a shotgun PowerShell presentation at the MSDN RoadShow in Charlotte today.&amp;nbsp; I appreciate the opportunity, guys!
&lt;/p&gt;
&lt;p&gt;
As promised, here is a quick summary and some links to PowerShell tutorials and developer blogs...
&lt;/p&gt;
&lt;h2&gt;The Big Three Commands &lt;/h2&gt;
&lt;p&gt;
Remember, you don&amp;#39;t need to remember all 200+ commands available in PowerShell, because PowerShell provides built-in cheat sheets! 
&lt;/p&gt;
&lt;h3&gt;Get-Command &lt;/h3&gt;
&lt;p&gt;
Example 1: list all available commands&lt;br /&gt;
&lt;/p&gt;
&lt;pre class="ps"&gt;
Get-command
&lt;/pre&gt;
Example 2: list available commands pertaining to processes&lt;br /&gt;
&lt;pre class="ps"&gt;
Get-command *process*
&lt;/pre&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;h3&gt;Get-help&lt;/h3&gt;
&lt;p&gt;
Example 1: general powershell help
&lt;/p&gt;
&lt;pre class="ps"&gt;
Get-Help
&lt;/pre&gt;
Example 2: list help topics matching a term&lt;br /&gt;
&lt;pre class="ps"&gt;
Get-help *object*
&lt;/pre&gt;
Example 3: get &amp;ldquo;short&amp;rdquo; help for a specific command&lt;br /&gt;
&lt;pre class="ps"&gt;
Get-help get-process
&lt;/pre&gt;
OR&lt;br /&gt;
&lt;pre class="ps"&gt;
get-process -?
&lt;/pre&gt;
Example 3: get &amp;ldquo;full&amp;rdquo; help (details, examples, etc) for a specific command&lt;br /&gt;
&lt;pre class="ps"&gt;
Get-help get-process -full
&lt;/pre&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;h3&gt;Get-Member&lt;/h3&gt;
&lt;p&gt;
Example 1: list members of a local variable&lt;br /&gt;
&lt;/p&gt;
&lt;pre class="ps"&gt;
$variable | get-member
&lt;/pre&gt;
OR&lt;br /&gt;
&lt;pre class="ps"&gt;
Get-member &amp;ndash;input $variable
&lt;/pre&gt;
Example 2: list members of a pipeline result &lt;br /&gt;
&lt;pre class="ps"&gt;
Get-process | get-member
&lt;/pre&gt;
&lt;h2&gt;Links &lt;br /&gt;
&lt;/h2&gt;
&lt;p&gt;
&lt;a href="http://tinyurl.com/posh2download" target="_blank"&gt;Download Powershell v2 CTP3&lt;/a&gt;
&lt;/p&gt;
&lt;h3&gt;Community Resources&lt;/h3&gt;
&lt;p&gt;
&lt;a href="http://www.powershellcommunity.org" target="_blank"&gt;PowerShellCommunity.org&lt;/a&gt; - aggregator of scripts, blogs, and resources.&amp;nbsp; A great place to start searching for powershell packages.&lt;br /&gt;
&lt;a href="http://powerscripting.wordpress.com" target="_blank"&gt;PowerScripting podcast&lt;/a&gt; &amp;ndash; weekly podcast of all things powershell.&amp;nbsp; Generally targeted at sys admins and DBAs, but often they discuss targeted technologies in powershell, such as sharepoint, sql server, exchange, VMWare, etc.&lt;br /&gt;
&lt;a href="http://www.nivot.org" target="_blank"&gt;Nivot Ink&lt;/a&gt; &amp;ndash; &lt;span class="description"&gt;Oisin Grehan&amp;#39;s blog containing &lt;/span&gt;an insane amount of detail concerning v2 CTP features, including creating modules, eventing, and remoting.&lt;br /&gt;
&lt;a href="http://www.huddledmasses.org" target="_blank"&gt;huddledmasses.org&lt;/a&gt; - Joel Bennet&amp;rsquo;s blog, author of the &lt;a href="http://huddledmasses.org/powerboots/" target="_blank"&gt;PowerBoots&lt;/a&gt; WPF extensions for powershell, and massive powershell development online presence.&lt;br /&gt;
&lt;a href="http://blogs.msdn.com/PowerShell" target="_blank"&gt;PowerShell Team Blog&lt;/a&gt; &amp;ndash; the horse&amp;rsquo;s mouth, so to speak.&lt;br /&gt;
&lt;a href="http://www.powergui.org" target="_blank"&gt;PowerGUI&lt;/a&gt; - an awesome MMC-style GUI PowerShell host, very flexible and endlessly useful.
&lt;/p&gt;
&lt;h3&gt;Open-Source Projects&lt;/h3&gt;
&lt;p&gt;
&lt;a href="http://www.codeplex.com/PowerShellCX" target="_blank"&gt;PowerShell Community Extensions&lt;/a&gt; &amp;ndash; targeted at powershell v1, the functionality offered here is largely absorbed in v2; however, this codebase is still a great resource for learning more about how powershell works.&lt;br /&gt;
&lt;a href="http://www.codeplex.com/SQLPSX" target="_blank"&gt;SQL Server PowerShell Extensions&lt;/a&gt; &amp;ndash; powershell support for managing SQL 2000 and 2005; note that SQL 2008 has powershell integration out of the box.&lt;br /&gt;
&lt;a href="http://www.codeplex.com/polymon" target="_blank"&gt;PolyMon&lt;/a&gt; &amp;ndash; an open-source system monitoring solution with powershell support.
&lt;/p&gt;
&lt;h3&gt;Module, Training, &amp;amp; Tool Vendors&lt;/h3&gt;
&lt;p&gt;
&lt;a href="http://www.quest.com/powershell/activeroles-server.aspx" target="_blank"&gt;Quest AD&lt;/a&gt; &amp;ndash; active directory support for powershell. PowerGUI.org &amp;ndash; Extensible and generic MMC-type UI front-end for powershell.&lt;br /&gt;
&lt;a href="http://www.sapien.com" target="_blank"&gt;SAPIEN&lt;/a&gt; &amp;ndash; makers of PrimalScript scripting IDE, designed specifically for sys admins; training courses in powershell, scripting, etc.&lt;br /&gt;
&lt;a href="http://www.quest.com" target="_blank"&gt;Quest&lt;/a&gt;
&amp;ndash; authors of free PowerGUI (http://www.powergui.org), a UI front-end
for powershell, as well as the ActiveRoles management shell for AD and
many, many other management products.&lt;br /&gt;
&lt;a href="http://www.softwarefx.com" target="_blank"&gt;SoftwareFX&lt;/a&gt;
&amp;ndash; vendor of .NET and Java instrumentation controls, as well as
PowerGadgets, a tool that enables you to create vista side-bar gadgets
based on PowerShell scripts - it&amp;#39;s BIG FUN!
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/Iz-MheBVtdE" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/MSDN-Southern-Fried-Road-Show-Charlotte.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/MSDN-Southern-Fried-Road-Show-Charlotte.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=b4b1d8f8-a7e6-447a-977a-16fd2afdc63b</guid>
      <pubDate>Tue, 02 Jun 2009 17:14:00 -1300</pubDate>
      <category>community</category>
      <category>powershell</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=b4b1d8f8-a7e6-447a-977a-16fd2afdc63b</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=b4b1d8f8-a7e6-447a-977a-16fd2afdc63b</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/MSDN-Southern-Fried-Road-Show-Charlotte.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=b4b1d8f8-a7e6-447a-977a-16fd2afdc63b</wfw:commentRss>
    </item>
    <item>
      <title>Creating a PowerShell Provider pt 0: Gearing Up</title>
      <description>&lt;p&gt;
&lt;img src="http://www.beefycode.com/image.axd?picture=2009%2f6%2fbounceycastle.jpg" alt="" /&gt;Last month I gave a talk at ALT.NET Charlotte on using PowerShell as a platform for support tools.&amp;nbsp; The majority of the talk revolved around the extensible provider layer available in PowerShell, and how this enables support engineers to accomplish many things by learning a few simple commands.&amp;nbsp; Creating providers isn&amp;#39;t hard, but there is a bit of &amp;quot;black art&amp;quot; to it.&amp;nbsp; Based on the lack of adequate examples and documentation available, I thought I&amp;#39;d fill the void with a few posts derived from my talk. 
&lt;/p&gt;
&lt;p&gt;
In these posts, I will be creating a PowerShell provider around the ASP.NET Membership framework that enables the management of user accounts (for more information on ASP.NET Membership, start with this &lt;a href="http://weblogs.asp.net/scottgu/archive/2006/05/07/ASP.NET-2.0-Membership-and-Roles-Tutorial-Series.aspx" target="_blank"&gt;post&lt;/a&gt; from Scott Gu&amp;#39;s blog) .&amp;nbsp; Basically, this provider will allow you to treat your ASP.NET Membership user repository like a file system, allowing you to perform heroic feats that are simply impossible in the canned ASP.NET Web Site Management Tool.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Like what you ask? &amp;nbsp; Consider this:
&lt;/p&gt;
&lt;pre class="ps"&gt;
dir users: | 
&amp;nbsp;&amp;nbsp;&amp;nbsp;where { !( $_.isApproved ) -and $_.creationDate.Date -eq ( ( get-date ).Date ) } 
&lt;/pre&gt;
&lt;p&gt;
which lists all unapproved users created today.&amp;nbsp; Or this:
&lt;/p&gt;
&lt;pre class="ps"&gt;
dir users: | where{ $_.isLockedOut } | foreach{ $_.unlockUser() } 
&lt;/pre&gt;
&lt;p&gt;
which unlocks all locked users.&amp;nbsp; Or this:
&lt;/p&gt;
&lt;pre class="ps"&gt;
dir users: | 
&amp;nbsp;&amp;nbsp;&amp;nbsp;where { ( (get-date) - $_.lastLoginDate ).TotalDays -gt 365 } |
&amp;nbsp;&amp;nbsp;&amp;nbsp;remove-item 
&lt;/pre&gt;
&lt;p&gt;
which removes users who haven&amp;#39;t authenticated in the past year.&amp;nbsp; These are things you simply can&amp;#39;t do using the clumsy built-in tools.&amp;nbsp; With a simple PowerShell provider around the user store, you can accommodate these situations and many others you don&amp;#39;t even know you need yet. 
&lt;/p&gt;
&lt;p&gt;
This first post is aimed at getting a project building, with the appropriate references in place, and with the debugger invoking powershell properly.&amp;nbsp; The next few posts will focus on implementing the proper extensibility points to have PowerShell interact with your provider.&amp;nbsp; Future posts will focus on implementing specific features of the provider, such as listing, adding, or removing users.&amp;nbsp; Eventually I will discuss packaging the provider for distribution and using the provider in freely available tools, such as &lt;a href="http://www.powergui.org" target="_blank"&gt;PowerGUI&lt;/a&gt;. 
&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;
You&amp;#39;ll need the latest PowerShell v2 CTP; at the time of this writing, the latest was CTP3 and it was available &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=c913aeab-d7b4-4bb1-a958-ee6d7fe307bc" target="_blank"&gt;here&lt;/a&gt;.&amp;nbsp; Since we&amp;#39;re working with a CTP, you should expect some things to change before the RTM; however, the PowerShell provider layer is unchanged from v1.0 in most respects.&amp;nbsp; The only major difference is with regards to deployment and installation, which I&amp;#39;ll discuss at the appropriate time. 
&lt;/p&gt;
&lt;p&gt;
If you are currently using PowerShell v1.0, you will need to uninstall it.&amp;nbsp; Instructions are available &lt;a href="http://blogs.msdn.com/powershell/archive/2006/11/14/windows-powershell-1-0-released.aspx#1187598" target="_blank"&gt;here&lt;/a&gt; and &lt;a href="http://geekzguru.wordpress.com/2008/02/16/howto-uninstall-powershell-10/" target="_blank"&gt;here&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
The &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=333325FD-AE52-4E35-B531-508D977D32A6&amp;amp;displaylang=en" target="_blank"&gt;.NET Framework 3.5&lt;/a&gt;, although not required to run PowerShell v2 CTP3, is required for some of the tools that accompany the v2 release. 
&lt;/p&gt;
&lt;p&gt;
Finally, I suggest you fire up PowerShell on its own at least once, to verify it installed correctly and to loosen up the script execution policy.&amp;nbsp; To save some grief, one of the first commands I execute in a new PowerShell install is this one:
&lt;/p&gt;
&lt;pre class="ps"&gt;
set-executionPolicy RemoteSigned
&lt;/pre&gt;
&lt;p&gt;
This will enable the execution of locally-created script files, but require downloaded script files be signed by a trusted authority before allowing their execution. 
&lt;/p&gt;
&lt;h2&gt;Setting Up the Project&lt;/h2&gt;
&lt;p&gt;
Create a new Class Library project in Visual Studio named &amp;quot;ASPNETMembership&amp;quot;.&amp;nbsp; This project will eventually contain the PowerShell provider code.
&lt;/p&gt;
&lt;p&gt;
You will need to add the following references to the project:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;System.Web - this assembly contains the MembershipUser type we will be exposing in our PowerShell provider;&lt;/li&gt;
	&lt;li&gt;System.Configuration - we&amp;#39;ll need to access the configuration subsystem to properly set up our membership provider; &lt;/li&gt;
	&lt;li&gt;System.Management.Automation - this contains the PowerShell type definitions we need to create our provider.&amp;nbsp; You may have to hunt for this assembly; try here: C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;img src="http://www.beefycode.com/image.axd?picture=2009%2f5%2fposhprov_references.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
Now that the necessary references are configured, it&amp;#39;s time to configure debugging appropriately.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Open the project properties dialog and select the Debug tab.&amp;nbsp; Under Start Action, select the &amp;quot;Start external program&amp;quot; option.&amp;nbsp; In the &amp;quot;Start external program&amp;quot; textbox, enter the fully qualified path to the PowerShell executable: c:\windows\system32\windowspowershell\v1.0\powershell.exe.
&lt;/p&gt;
&lt;blockquote&gt;
	&lt;p&gt;
	&lt;em&gt;Yes, even though you&amp;#39;re running PowerShell v2, it still lives in the v1.0 directory.&lt;/em&gt;
	&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
In the &amp;quot;Command line arguments textbox, enter the following:
&lt;/p&gt;
&lt;pre class="ps"&gt;
-noexit -command &amp;quot;[reflection.assembly]::loadFrom( &amp;#39;ASPNETMembership.dll&amp;#39; ) | import-module&amp;quot;
&lt;/pre&gt;
&lt;p&gt;
The -command argument instructs PowerShell to execute the supplied command pipe string as if it were typed in at the console.&amp;nbsp; It may not be obvious what the command pipe string is doing.&amp;nbsp; In a nutshell, the first command in the pipe loads the ASPNETMembership project output dll into the AppDomain.&amp;nbsp; The second command in the pipe causes PowerShell to load any cmdlets or providers implemented in the dll.&amp;nbsp; I&amp;#39;ll touch on the import-module command more in a future post. 
&lt;/p&gt;
&lt;p&gt;
The -noexit argument prevents PowerShell from exiting once the command has been run, which enables us to type in commands and interact with the console while debugging. 
&lt;/p&gt;
&lt;h2&gt;Test the Project Setup &lt;/h2&gt;
&lt;p&gt;
Build and Run the empty project.&amp;nbsp; A PowerShell console should launch.
&lt;/p&gt;
&lt;p&gt;
In the console, run the following command, which lists all assemblies loaded in the currently executing AppDomain whose FullName property matches &amp;#39;aspnet&amp;#39;:
&lt;/p&gt;
&lt;pre class="ps"&gt;
[appdomain]::currentDomain.GetAssemblies() | 
&amp;nbsp;&amp;nbsp;&amp;nbsp;where { $_.FullName -match &amp;#39;aspnet&amp;#39; } | 
&amp;nbsp;&amp;nbsp;&amp;nbsp;select fullname
&lt;/pre&gt;
&lt;p&gt;
Verify that the ASPNETMembership assembly is listed in the output of this command:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://www.beefycode.com/image.axd?picture=2009%2f6%2fposhprov_output_0.png" alt="" /&gt;&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
If it isn&amp;#39;t, double-check the command-line arguments specified in the project properties -&amp;gt; debug tab.&amp;nbsp; Specifically, ensure that:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;the command arguments are entered exactly as specified above, and&lt;/li&gt;
	&lt;li&gt;the working directory is set to the default (empty) value &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
That&amp;#39;s it for now - next post we will begin implementing the MembershipUsers PowerShell provider and enable the most basic pieces necessary to list the users in the membership store. 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/aWnQIw1VUls" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-0-Gearing-Up.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-0-Gearing-Up.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=b3d3e874-ea7e-4264-8522-4f04348ca4d3</guid>
      <pubDate>Tue, 26 May 2009 01:11:00 -1300</pubDate>
      <category>powershell</category>
      <category>software development</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=b3d3e874-ea7e-4264-8522-4f04348ca4d3</pingback:target>
      <slash:comments>2</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=b3d3e874-ea7e-4264-8522-4f04348ca4d3</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Creating-a-PowerShell-Provider-pt-0-Gearing-Up.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=b3d3e874-ea7e-4264-8522-4f04348ca4d3</wfw:commentRss>
    </item>
    <item>
      <title>Expanding-File Resource for Spring.NET</title>
      <description>&lt;p&gt;
I just had a fun emergency - the crew decided to redirect all configuration sources using environment variables, and while &lt;a href="http://stackoverflow.com/questions/468989/how-to-specifiy-common-application-data-folder-for-log4net" target="_blank"&gt;Log4Net supports this quite easily&lt;/a&gt;, the spring.net context resource-from-a-URI abstraction does not.&amp;nbsp; E.g., this will not work:
&lt;/p&gt;
&lt;pre class="xml"&gt;
&amp;lt;spring&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;context&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;resource uri=&amp;quot;file://%PROGRAMFILES%/myapp/ioc.config&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/context&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; ...
&lt;/pre&gt;
&lt;p&gt;
Knowing that spring.net can leverage custom resource implementations, I set out to extend the file system resource to expand environment variables.&amp;nbsp; Turns out to be easy-peasy-lemon-squeezie:
&lt;/p&gt;
&lt;pre class="c#"&gt;
public class ExpandableFileSystemResource : Spring.Core.IO.FileSystemResource
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; public ExpandableFileSystemResource()
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; : base()
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; public ExpandableFileSystemResource( string resourceName )
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; : base( Environment.ExpandEnvironmentVariables( resourceName ?? String.Empty ) )
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; public ExpandableFileSystemResource( string resourceName, bool suppressInitialize )
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; : base( Environment.ExpandEnvironmentVariables( resourceName ?? String.Empty ), suppressInitialize )
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p&gt;
Register the resource implementation via config:
&lt;/p&gt;
&lt;pre class="xml"&gt;
&amp;lt;spring&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;resourceHandlers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;handler protocol=&amp;quot;filex&amp;quot; type=&amp;quot;MyApp.ExpandableFileSystemResource, MyApp&amp;quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/resourceHandlers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;context&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;resource uri=&amp;quot;filex://%PROGRAMFILES%/myapp/ioc.config&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/context&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp; ... 
&lt;/pre&gt;
&lt;p&gt;
&amp;nbsp;And it just works.&amp;nbsp; Enjoy! 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/Z2Ys1ewdhMA" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Expaning-File-Resource-for-SpringNET.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Expaning-File-Resource-for-SpringNET.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=6bc840b8-3f93-4e08-ba43-1ae44c9a8375</guid>
      <pubDate>Wed, 13 May 2009 03:48:00 -1300</pubDate>
      <category>software development</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=6bc840b8-3f93-4e08-ba43-1ae44c9a8375</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=6bc840b8-3f93-4e08-ba43-1ae44c9a8375</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Expaning-File-Resource-for-SpringNET.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=6bc840b8-3f93-4e08-ba43-1ae44c9a8375</wfw:commentRss>
    </item>
    <item>
      <title>ALT.NET Charlotte Presentation</title>
      <description>&lt;p&gt;
I just got back from the second meeting of ALT.NET Charlotte, where I presented a talk on PowerShell as a Tools Platform.&amp;nbsp; I think it went well.&amp;nbsp; The demos centered around building tools in PowerShell to manage the ASP.NET Membership store for a website, and they seemed to drive the point home.&amp;nbsp; Try doing this:
&lt;/p&gt;
&lt;pre class="ps"&gt;
dir users: | where { $_.lastLoginDate &amp;ndash;lt ( ( get-date ).addyear( -1 ) ) } | remove-item;
&lt;/pre&gt;
&lt;p&gt;
in the ASP.NET Website Management tool! 
&lt;/p&gt;
&lt;p&gt;
As well as I feel it went, I&amp;#39;m still hoping to get some feedback from the group.&amp;nbsp; Feel free to leave a comment here if you wish, or use the contact link at the top of this page. 
&lt;/p&gt;
&lt;p&gt;
And a very special thank you to those organizations and companies that provided their support for this presentation:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.powershellcommunity.org/" target="_blank"&gt;powershellcommunity.org&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.sapien.com" target="_blank"&gt;SAPIEN&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.quest.com" target="_blank"&gt;Quest&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.softwarefx.com" target="_blank"&gt;SoftwareFX&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
and those who sposor our kick-ass group:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://unfuddle.com/" target="_blank"&gt;unFuddle&lt;/a&gt; &lt;br /&gt;
	&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.red-gate.com/" target="_blank"&gt;Red Gate&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.jetbrains.com" target="_blank"&gt;JetBrains&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.wrox.com" target="_blank"&gt;Wrox Press&lt;/a&gt;&lt;br /&gt;
	&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&amp;nbsp;Thanks to everyone who came out to support the group and hear my talk! 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/NkQYR4yiHbU" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/ALTNET-Charlotte-Presentation.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/ALTNET-Charlotte-Presentation.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=85aa1971-c646-43f9-aa9a-728de3c6d32e</guid>
      <pubDate>Thu, 07 May 2009 14:16:00 -1300</pubDate>
      <category>community</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=85aa1971-c646-43f9-aa9a-728de3c6d32e</pingback:target>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=85aa1971-c646-43f9-aa9a-728de3c6d32e</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/ALTNET-Charlotte-Presentation.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=85aa1971-c646-43f9-aa9a-728de3c6d32e</wfw:commentRss>
    </item>
    <item>
      <title>Andy Hunt's Agile Carolinas Talk</title>
      <description>&lt;p&gt;
I just got back from hearing &lt;a href="http://blog.toolshed.com/" target="_blank"&gt;Andy Hunt&lt;/a&gt; discuss his new book, &lt;a href="http://www.amazon.com/gp/product/1934356050?ie=UTF8&amp;amp;tag=beefycodecom-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1934356050"&gt;Pragmatic Thinking and Learning&lt;/a&gt;&lt;img style="border: medium none  ! important; margin: 0px ! important" src="http://www.assoc-amazon.com/e/ir?t=beefycodecom-20&amp;amp;l=as2&amp;amp;o=1&amp;amp;a=1934356050" border="0" alt="" width="1" height="1" /&gt;.&amp;nbsp; I hadn&amp;#39;t heard him speak before, and I have to admit I was not sure what to expect.
&lt;/p&gt;
&lt;p&gt;
On the one hand, I hold many of this books in very high regard.&amp;nbsp; In particular, the following books were career-altering reads for me:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/097451408X?ie=UTF8&amp;amp;tag=beefycodecom-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=097451408X"&gt;Practices of an Agile Developer: Working in the Real World (Pragmatic Programmers)&lt;/a&gt;&lt;img style="border: medium none  ! important; margin: 0px ! important" src="http://www.assoc-amazon.com/e/ir?t=beefycodecom-20&amp;amp;l=as2&amp;amp;o=1&amp;amp;a=097451408X" border="0" alt="" width="1" height="1" /&gt;;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0977616673?ie=UTF8&amp;amp;tag=beefycodecom-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0977616673"&gt;Pragmatic Unit Testing in C# with NUnit, 2nd Edition&lt;/a&gt;&lt;img style="border: medium none  ! important; margin: 0px ! important" src="http://www.assoc-amazon.com/e/ir?t=beefycodecom-20&amp;amp;l=as2&amp;amp;o=1&amp;amp;a=0977616673" border="0" alt="" width="1" height="1" /&gt;; and of course...&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/020161622X?ie=UTF8&amp;amp;tag=beefycodecom-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=020161622X"&gt;The Pragmatic Programmer: From Journeyman to Master&lt;/a&gt;&lt;img style="border: medium none  ! important; margin: 0px ! important" src="http://www.assoc-amazon.com/e/ir?t=beefycodecom-20&amp;amp;l=as2&amp;amp;o=1&amp;amp;a=020161622X" border="0" alt="" width="1" height="1" /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
And his keystone role in the Agile movement has earned my utmost respect.&amp;nbsp; However, when I saw the title of this latest book I was a bit worried.&amp;nbsp; I have a master&amp;#39;s degree in cognitive psychology, and I know a lot about learning, memory, and perception.&amp;nbsp; Most learning books outside of the field are crap, so honestly my first instinct was that this was going to be a cash-grab treatise of self-help porch psychology fuzzy-feel-goods for software developers. 
&lt;/p&gt;
&lt;p&gt;
After listening to Andy&amp;#39;s presentation, I am happy to say my instincts were way off the mark.
&lt;/p&gt;
&lt;p&gt;
First of all, the book (and Andy&amp;#39;s presentation) remains true to the pragmatic banner.&amp;nbsp; His recommendations are both practical and effective.&amp;nbsp; For example, a recurring theme during this talk was to write things down.&amp;nbsp; Carry around a small notebook and jot down every idea that pops into your head.&amp;nbsp; Maintain a personal wiki.&amp;nbsp; Mindmap as you read or think.&amp;nbsp; Solidify your thoughts into something concrete.&amp;nbsp; The point is not to be able to refer back to them later, but instead to force your brain to keep working, keep producing, keep processing.&amp;nbsp; To summarize one of his points, that&amp;#39;s the first step to an accomplishment.
&lt;/p&gt;
&lt;p&gt;
Second, a lot of what he was saying is supported by academic research.&amp;nbsp; Granted, Andy takes some license with his metaphors but his points hold water. &amp;nbsp; E.g., what Andy refers to as the &amp;quot;memory bus&amp;quot; being shared between
&amp;quot;dual cores&amp;quot; of the brain is probably more of an attention effect; however, the behavioral effect cannot be denied - the serial and holistic parts of your mind compete when trying to solve a problem.
&lt;/p&gt;
&lt;p&gt;
Based on the presentation tonight, I wouldn&amp;#39;t recommend the book to my former psychology colleges - it&amp;#39;s too macro to be useful to them.&amp;nbsp; However, for my fellow geeks, this is actually a useful introduction to becoming a more effective learner. 
&lt;/p&gt;
&lt;p&gt;
It was a great talk, a fun evening, and I plan to pick up the book next time I&amp;#39;m at the bookstore.&amp;nbsp; Oh, and I had a short chat with Andy just before the talk, and I have to say how awesome it is to meet someone you hold as a guru and detect no ego.&amp;nbsp; Awesome. 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/zD8-0QBPrnQ" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Andy-Hunts-Agile-Carolinas-Talk.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Andy-Hunts-Agile-Carolinas-Talk.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=22db5737-083d-4ecd-8707-79d54aa13850</guid>
      <pubDate>Tue, 28 Apr 2009 14:11:00 -1300</pubDate>
      <category>community</category>
      <category>psychology</category>
      <category>software development</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=22db5737-083d-4ecd-8707-79d54aa13850</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=22db5737-083d-4ecd-8707-79d54aa13850</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Andy-Hunts-Agile-Carolinas-Talk.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=22db5737-083d-4ecd-8707-79d54aa13850</wfw:commentRss>
    </item>
    <item>
      <title>Log4Net Recommended Practices pt 2: Isolating Bugs</title>
      <description>&lt;p&gt;
&lt;img src="http://www.beefycode.com/image.axd?picture=2009%2f4%2ffallpath.jpg" alt="" /&gt;I realize it&amp;#39;s been a while since I&amp;#39;ve written about logging, but my experiences this morning compelled me to share.&amp;nbsp; The muse for this post was a very simple bug located in a rat&amp;#39;s nest of complexity.
&lt;/p&gt;
&lt;p&gt;
The code in question drives the I/O layer in a device that is filled with proprietary hardware. &amp;nbsp; There are about three dozen channels of internal communication when this thing is running, which doesn&amp;#39;t include any of its networking.&amp;nbsp; So lots of asynchronous I/O and threading.&amp;nbsp; All supporting an awesome graphics and physical interface layer that is equally complex on its own.
&lt;/p&gt;
&lt;p&gt;
Except that today, it was a paperweight.&amp;nbsp; In fact, you couldn&amp;#39;t even use the operating system effectively.&amp;nbsp; Eventually task manager would pop up and show that something in the application was suffocating the CPU.&amp;nbsp; I was not looking forward to diagnosing this problem given the level of complexity involved.&amp;nbsp; I expected to spend the morning on it, but in all it took about 10 minutes to find and fix the problem. 
&lt;/p&gt;
&lt;p&gt;
I had worked with the application to know what pattern of messages to expect in the log files, but when I opened them I found some of the messages to be missing.&amp;nbsp; Specifically, the messages pertaining to just one of those three dozen communication channels I mentioned earlier.&amp;nbsp; After some initial testing, I had isolated the problem to a case where that channel was misconfigured.&amp;nbsp; I re-ran the application and grabbed the fresh logs.
&lt;/p&gt;
&lt;p&gt;
Since I mirror my class structure in &lt;a href="http://www.beefycode.com/post/Log4Net-Tutorial-pt-5-Using-Logger-Objects.aspx"&gt;logger objects&lt;/a&gt;, isolating the relevant log entries was easy using logparser.&amp;nbsp; The list was surprisingly short, ending with this message:
&lt;/p&gt;
&lt;pre class="ps"&gt;
initiating PSU polling thread 
&lt;/pre&gt;
&lt;p&gt;
I quickly found this message in my code:
&lt;/p&gt;
&lt;pre class="c#"&gt;
void PollForReports()
{
&amp;nbsp;&amp;nbsp; &amp;nbsp;Log.Info( &amp;quot;initiating PSU polling thread&amp;quot; );
&amp;nbsp;&amp;nbsp; &amp;nbsp;while( 1 == Interlocked.Read( ref suspendInterlock ) )
&amp;nbsp;&amp;nbsp; &amp;nbsp;{
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; continue;
&amp;nbsp;&amp;nbsp; &amp;nbsp;}
&amp;nbsp;
&amp;nbsp;&amp;nbsp; &amp;nbsp;RequestSerialNumber();
&amp;nbsp;&amp;nbsp; &amp;nbsp;Log.Debug( &amp;quot;requested PSU serial number ...&amp;quot; );
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;
&amp;nbsp;&amp;nbsp; &amp;nbsp;// ...
&lt;/pre&gt;
&lt;p&gt;
As you can see, a &amp;quot;requested PSU serial number...&amp;quot; log message should have been written almost immediately after the last message I found in the log.&amp;nbsp; Something in the code after the &amp;quot;initiating PSU polling thread&amp;quot; message is written and before the &amp;quot;requested PSU serial number ...&amp;quot; message is locking the CPU.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Do you see it - the improperly implemented spin lock?&amp;nbsp; It&amp;#39;s pretty obvious when you know where to look, and logging can make it a lot easier to know where to look. 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/DwYgzLa-rWk" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Log4Net-Recommended-Practices-pt-2-Isolating-Bugs.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Log4Net-Recommended-Practices-pt-2-Isolating-Bugs.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=19919f75-3fd5-4946-a72a-6b14867db45d</guid>
      <pubDate>Tue, 21 Apr 2009 04:56:00 -1300</pubDate>
      <category>log4net</category>
      <category>software development</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=19919f75-3fd5-4946-a72a-6b14867db45d</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=19919f75-3fd5-4946-a72a-6b14867db45d</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Log4Net-Recommended-Practices-pt-2-Isolating-Bugs.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=19919f75-3fd5-4946-a72a-6b14867db45d</wfw:commentRss>
    </item>
    <item>
      <title>Delegates and Native API Callbacks - Answer</title>
      <description>&lt;p&gt;
A while back I &lt;a href="http://www.beefycode.com/post/Delegates-and-Native-API-Callbacks.aspx"&gt;posted a little puzzle&lt;/a&gt; about an exception I was hitting after passing a delegate to a native API call.&amp;nbsp; In a nutshell, my application was passing an anonymous delegate to an unmanaged library call:
&lt;/p&gt;
&lt;pre class="c#"&gt;
Result Code result = ExternalLibraryAPI.Open(
&amp;nbsp;&amp;nbsp;&amp;nbsp; deviceHande,
&amp;nbsp;&amp;nbsp;&amp;nbsp; delegate( IntPtr handle, int deviceId )
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // ...
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
);
VerifyResult( result ); 
&lt;/pre&gt;
&lt;p&gt;
The call returns immediately, and the unmanaged library would eventually invoke the callback in response to a user pressing a button on a device.&amp;nbsp; After a semi-random period, pressing the button would yield an exception in my application.&amp;nbsp; The questions I asked were:
&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;What&amp;#39;s the exception?&lt;/li&gt;
	&lt;li&gt;How do you avoid it? &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
I&amp;#39;ve waited a bit to post the answers to see if anyone besides Zach would chime in.&amp;nbsp; Zach correctly identified the nut of the problem - the delegate is being garbage collected because there is no outstanding reference on the anonymous delegate once the unmanaged call returns.&amp;nbsp; With no one referencing the delegate object, the garbage collector is free to reclaim it.&amp;nbsp; When the device button is pushed, the native library invokes the callback, which no longer exists in memory.&amp;nbsp; So, to answer the first question, the specific exception that is raised is a CallbackOnCollectedDelegate: 
&lt;/p&gt;
&lt;blockquote&gt;
	&lt;p&gt;
	CallbackOnCollectedDelegate was detected.&lt;br /&gt;
	Message: A callback was made on a garbage collected delegate of type &amp;#39;Device.Interop!Device.Interop.ButtonPressCallback::Invoke&amp;#39;. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
	&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
The verbage in this exception message answers my second question.&amp;nbsp; To avoid the exception, you need to hold a reference to any delegate you pass to unmanaged code for as long as you expect the delegate to be invoked.&amp;nbsp; You need to use an intermediary reference on the delegate to maintain it&amp;#39;s life.
&lt;/p&gt;
&lt;p&gt;
Based on this, any of these examples are doomed to fail eventually, because none of them maintain a reference on the delegate object being passed to the unmanaged library:
&lt;/p&gt;
&lt;pre class="c#"&gt;
ExternalLibraryAPI.Open(
&amp;nbsp;&amp;nbsp;&amp;nbsp; deviceHande,
&amp;nbsp;&amp;nbsp;&amp;nbsp; delegate( IntPtr handle, int deviceId )
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // ...
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
);
&amp;nbsp;
ExternalLibraryAPI.Open(
&amp;nbsp;&amp;nbsp;&amp;nbsp; deviceHande,
&amp;nbsp;&amp;nbsp;&amp;nbsp; new ButtonPressCallback( this.OnButtonPress )
);
&amp;nbsp;
ExternalLibraryAPI.Open(
&amp;nbsp;&amp;nbsp;&amp;nbsp; deviceHande,
&amp;nbsp;&amp;nbsp;&amp;nbsp; this.OnButtonPress
);
&lt;/pre&gt;
&lt;p&gt;
The correct way to avoid the problem is to hold an explicit reference to the specific delegate instance being passed to unmanaged code:
&lt;/p&gt;
&lt;pre class="c#"&gt;
ButtonPressCallback buttonPressCallback = this.OnButtonPress;
ExternalLibraryAPI.Open(
&amp;nbsp;&amp;nbsp;&amp;nbsp; deviceHande,
&amp;nbsp;&amp;nbsp;&amp;nbsp; buttonPressCallback
);
// hold the reference until we&amp;#39;re sure no
// further callbacks will be made on the
// delegate, then we can release the
// reference and allow it to be GC&amp;#39;ed
buttonPressCallback = null;
&lt;/pre&gt;
&lt;p&gt;
At first I thought Zach&amp;#39;s pinning solution was correct; however, you can only pin &lt;a href="http://msdn.microsoft.com/en-us/library/75dwhxf7.aspx" target="_blank"&gt;blittable types&lt;/a&gt;, of which delegates are not, so &amp;quot;pinning a delegate&amp;quot; isn&amp;#39;t even possible or necessary.&amp;nbsp; If you&amp;#39;re interested, the details of how delegates are marshalled across the&amp;nbsp; managed/unmanaged boundary are quite interesting, as I found out from &lt;a href="http://blogs.msdn.com/cbrumme/archive/2003/05/06/51385.aspx" target="_blank"&gt;Chris Brumme&amp;#39;s blog&lt;/a&gt;: 
&lt;/p&gt;
&lt;blockquote&gt;
	&lt;p&gt;
	Along the same lines, managed Delegates can be marshaled to unmanaged code, where they are exposed as unmanaged function pointers.&amp;nbsp; Calls on those pointers will perform an unmanaged to managed transition; a change in calling convention; entry into the correct AppDomain; and any necessary argument marshaling.&amp;nbsp; Clearly the unmanaged function pointer must refer to a fixed address.&amp;nbsp; It would be a disaster if the GC were relocating that!&amp;nbsp; This leads many applications to create a pinning handle for the delegate.&amp;nbsp; This is completely unnecessary.&amp;nbsp; The unmanaged function pointer actually refers to a native code stub that we dynamically generate to perform the transition &amp;amp; marshaling.&amp;nbsp; This stub exists in fixed memory outside of the GC heap.
	&lt;/p&gt;
	&lt;p&gt;
	However, the application is responsible for somehow extending the lifetime of the delegate until no more calls will occur from unmanaged code.&amp;nbsp; The lifetime of the native code stub is directly related to the lifetime of the delegate.&amp;nbsp; Once the delegate is collected, subsequent calls via the unmanaged function pointer will crash or otherwise corrupt the process.&amp;nbsp; 
	&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Thanks again Zach, and to everyone who reads my blog! 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/VqejINzujhA" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Delegates-and-Native-API-Callbacks-Answer.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Delegates-and-Native-API-Callbacks-Answer.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=835ffe5a-6fd1-4104-ac64-fa6c437bb441</guid>
      <pubDate>Thu, 16 Apr 2009 16:35:00 -1300</pubDate>
      <category>software development</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=835ffe5a-6fd1-4104-ac64-fa6c437bb441</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=835ffe5a-6fd1-4104-ac64-fa6c437bb441</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Delegates-and-Native-API-Callbacks-Answer.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=835ffe5a-6fd1-4104-ac64-fa6c437bb441</wfw:commentRss>
    </item>
    <item>
      <title>Load-Balancing the Build Farm with CruiseControl.NET</title>
      <description>&lt;p&gt;
&lt;img src="http://www.beefycode.com/image.axd?picture=2009%2f3%2fseesawbalance.jpg" alt="" /&gt;Our CI system isn&amp;#39;t terribly complicated, thankfully.&amp;nbsp; It evolved from a batch file on a single machine to a farm of VMs running CruiseControl.NET and a single Subversion repository.&amp;nbsp; The triggering mechanism hasn&amp;#39;t changed during this transition though: it&amp;#39;s a custom beast, consisting of a post-commit hook on our repository logs revisions into a database, and a custom sourcecontrol task is used to poll the database for revisions that haven&amp;#39;t been built yet.&amp;nbsp; It works fine and naturally creates a balanced build farm: several VMs can be configured to build the same project, and the database sourcecontrol task prevents more than one VM from building the same revision.
&lt;/p&gt;
&lt;p&gt;
As well as it works, it has some problems.&amp;nbsp; First, the post-commit hook relies heavily on SVN properties, which are a pain to maintain and make it impossible to simply &amp;quot;add a project to the build&amp;quot; without going through a lot of unnecessary configuration.&amp;nbsp; Moreover, the hook is starting to seriously hold up commits, sometimes as long as 20 seconds. 
&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Second, and more irritating, the system builds every individual commit.&amp;nbsp; By that, I mean it builds each and every revision committed to the repository - which may not sound like a bad thing, except that it includes &lt;strong&gt;every&lt;/strong&gt; revision, even those that occurred before the project was added to the CI farm - all the way back to the revision that created the project or branch.&amp;nbsp; I have to manually fudge the database, adding fake build results to prevent the builds from occurring.&amp;nbsp; It&amp;#39;s not hard, but it is a pain in the ass.&amp;nbsp; And with the team&amp;#39;s overuse of branching I&amp;#39;m finding myself having to fudge more and more.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
I&amp;#39;m really trying to move the system towards what &amp;quot;everyone else does,&amp;quot; by which I mean trigger builds by polling source control for changes.&amp;nbsp; No more database, no more post-commit hook, no more SVN property configuration, just Subversion and CruiseControl.NET.&amp;nbsp; It would be easy enough to do - simply change our CC.NET project configurations to use the standard SVN source control task.&amp;nbsp; The problem is that without the database, the farm is no longer automagically load-balancing - every VM in the farm would end up building the same source, which defeats the purpose of the farm.
&lt;/p&gt;
&lt;p&gt;
I figured that I could recoup the load-balancing if I had an &amp;quot;edge&amp;quot; server between Subversion and the build farm.&amp;nbsp; This server could monitor source control and when necessary trigger a build on one of the VMs in the build farm.&amp;nbsp; So instead of each farm server determining when a build should occur, there is a single edge server making that decision.
&lt;/p&gt;
&lt;div class="block" style="text-align: center"&gt;
&lt;img src="http://www.beefycode.com/image.axd?picture=2009%2f4%2fedgeserver_diagram_1.png" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;
CC.NET ships with the ability to &lt;a href="http://confluence.public.thoughtworks.org/display/CCNET/Splitting+the+build" target="_blank"&gt;split the build&lt;/a&gt; across machines - that is, for a build on one machine (like the edge server) to trigger a build on another machine (like a farm server); however, there is no load-balancing logic available.&amp;nbsp; So I made some of my own...
&lt;/p&gt;
&lt;h2&gt;Edge Server CC.NET Plugin&lt;/h2&gt;
&lt;p&gt;
The edge server plugin operates on a very simple algorithm:
&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Get a list of farm servers from configuration;&lt;/li&gt;
	&lt;li&gt;Determine which of farm servers are not currently building the project;&lt;/li&gt;
	&lt;li&gt;Fail the build if no farm server is available for the project; &lt;/li&gt;
	&lt;li&gt;Force a build of the project on the first available farm server.&lt;br /&gt;
	&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
If all you want is the project source code, here it is: &lt;a href="http://www.beefycode.com/file.axd?file=2009%2f4%2fccnet.edgeserver.zip"&gt;ccnet.edgeserver.zip (607.87 kb)&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
Take a look at the configuration of the plugin; I think it will make the code easier to digest. 
&lt;/p&gt;
&lt;h3&gt;Edge Server Configuration&lt;/h3&gt;
&lt;p&gt;
The edge server consists of little more than a source control trigger and a list of farm servers: 
&lt;/p&gt;
&lt;pre class="xml"&gt;
&amp;lt;cruisecontrol&amp;gt;
&amp;nbsp; &amp;lt;project name=&amp;quot;MyProject&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;triggers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;intervalTrigger seconds=&amp;quot;10&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/triggers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;sourcecontrol type=&amp;quot;svn&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;trunkUrl&amp;gt;svn://sourcecontrol/Trunk&amp;lt;/trunkUrl&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;autoGetSource&amp;gt;false&amp;lt;/autoGetSource&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/sourcecontrol&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;labeller type=&amp;quot;lastChangeLabeller&amp;quot; prefix=&amp;quot;MyProject_&amp;quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;tasks&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;farmBuild&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;farmServers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;farmServer priority=&amp;quot;1&amp;quot; uri=&amp;quot;tcp://build-vm-1:21234/CruiseManager.rem&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;farmServer priority=&amp;quot;2&amp;quot; uri=&amp;quot;tcp://build-vm-2:21234/CruiseManager.rem&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;farmServer priority=&amp;quot;3&amp;quot; uri=&amp;quot;tcp://build-vm-3:21234/CruiseManager.rem&amp;quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/farmServers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/farmBuild&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/tasks&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;publishers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;nullTask /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/publishers&amp;gt;
&amp;nbsp; &amp;lt;/project&amp;gt;
&amp;lt;/cruisecontrol&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
When CC.NET is run with this configuration, it will monitor the subversion repository for changes to the &amp;quot;MyProject&amp;quot; trunk (lines 6-9); note that since &lt;a href="http://confluence.public.thoughtworks.org/display/CCNET/Subversion+Source+Control+Block" target="_blank"&gt;autoGetSource&lt;/a&gt; is false, no checkout will occur.&amp;nbsp; The edge server will never have a working copy of the source. 
&lt;/p&gt;
&lt;p&gt;
The load-balancing is configured in lines 12-18; in this example, three farm servers are configured in the farm for &amp;quot;MyProject&amp;quot;, with build-vm-1 having the highest priority for the build (meaning it will be used first when all three servers are available).&amp;nbsp; When a change is committed to the repository, the edge server will choose one of these servers based on its availability and priority, and then force it to build the project. 
&lt;/p&gt;
&lt;h3&gt;Farm Server Configuration &lt;br /&gt;
&lt;/h3&gt;
&lt;p&gt;
The farm server is configured just as a normal CC.NET build, except for two key differences: first, it is configured with no trigger; second, a remoteProjectLabeller is used to label the build.&amp;nbsp; Here&amp;#39;s a sample configuration, with mundane build tasks omitted for brevity:
&lt;/p&gt;
&lt;pre class="xml"&gt;
&amp;lt;cruisecontrol&amp;gt;
&amp;nbsp; &amp;lt;project name=&amp;quot;MyProject&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;triggers/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;sourcecontrol type=&amp;quot;svn&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;trunkUrl&amp;gt;svn://sourcecontrol/MyProject/Trunk&amp;lt;/trunkUrl&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;autoGetSource&amp;gt;true&amp;lt;/autoGetSource&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/sourcecontrol&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;labeller type=&amp;quot;remoteProjectLabeller&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;project&amp;gt;MyProject&amp;lt;/project&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;serverUri&amp;gt;tcp://edgeServer:21234/CruiseManager.rem&amp;lt;/serverUri&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/labeller&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;tasks&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ... 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/tasks&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;publishers&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ... 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/publishers&amp;gt;
&amp;nbsp; &amp;lt;/project&amp;gt;
&amp;lt;/cruisecontrol&amp;gt; 
&lt;/pre&gt;
&lt;p&gt;
Details to note here are:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;the labeller points to the edge server to obtain the build label; this is necessary because labels are generated during the build trigger, which on the farm server is always forced and won&amp;#39;t include any source revision information;&lt;/li&gt;
	&lt;li&gt;the project name on the farm server matches exactly the project name on the edge server; this is a convention assumed by the plugin.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Source Code Overview&lt;br /&gt;
&lt;/h3&gt;
&lt;p&gt;
I need a FarmServer type to support the CC.NET configuration layer:
&lt;/p&gt;
&lt;pre class="c#"&gt;
using System;
using System.Collections.Generic;
using System.Text;
using ThoughtWorks.CruiseControl.Core;
using ThoughtWorks.CruiseControl.Remote;
using Exortech.NetReflector;
using ThoughtWorks.CruiseControl.Core.Publishers;
using System.Collections;
using ThoughtWorks.CruiseControl.Core.Util;
namespace CCNET.EdgeServer
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; [ReflectorType( &amp;quot;farmServer&amp;quot; )]
&amp;nbsp;&amp;nbsp;&amp;nbsp; public class FarmServer
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [ReflectorProperty( &amp;quot;uri&amp;quot; )]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public string Uri;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [ReflectorProperty( &amp;quot;priority&amp;quot; )]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int Priority;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p&gt;
No real surprises here.&amp;nbsp; Each FarmServer instance holds a URI to a CC.NET farm server and it&amp;#39;s priority in the balance algorithm.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
The real meat is in the FarmPublisher class:
&lt;/p&gt;
&lt;pre class="c#"&gt;
using System;
using System.Collections.Generic;
using System.Text;
using ThoughtWorks.CruiseControl.Core;
using ThoughtWorks.CruiseControl.Remote;
using Exortech.NetReflector;
using ThoughtWorks.CruiseControl.Core.Publishers;
using System.Collections;
using ThoughtWorks.CruiseControl.Core.Util;
namespace CCNET.EdgeServer
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; [ReflectorType( &amp;quot;farmBuild&amp;quot; )]
&amp;nbsp;&amp;nbsp;&amp;nbsp; public class FarmPublisher : ITask
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ICruiseManagerFactory factory;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [ReflectorProperty( &amp;quot;Name&amp;quot;, Required = false )]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public string EnforcerName;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [ReflectorHash( &amp;quot;farmServers&amp;quot;, &amp;quot;uri&amp;quot;, Required = true )]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public Hashtable FarmServers;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public FarmPublisher() : this( new RemoteCruiseManagerFactory() ) { }
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public FarmPublisher( ICruiseManagerFactory factory )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.factory = factory;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.EnforcerName = Environment.MachineName;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Run( IIntegrationResult result )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // build a list of available farm servers
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //&amp;nbsp; based off of the plugin configuration
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dictionary&amp;lt;int, ICruiseManager&amp;gt; servers = new Dictionary&amp;lt;int, ICruiseManager&amp;gt;();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FindAvailableFarmServers( result, servers );
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if( 0 == servers.Count )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Log.Info( &amp;quot;No servers are available for this project at this time&amp;quot; );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result.Status = IntegrationStatus.Failure;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // sort the available servers by priority
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; List&amp;lt;int&amp;gt; keys = new List&amp;lt;int&amp;gt;( servers.Keys );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; keys.Sort();
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // force a build on the server with the highest 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //&amp;nbsp; priority
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ICruiseManager availableServer = servers[ keys[ 0 ] ];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Log.Info( &amp;quot;forcing build on server ...&amp;quot; );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; availableServer.ForceBuild( result.ProjectName, EnforcerName );
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ...
&lt;/pre&gt;
&lt;p&gt;
FarmPublisher is configured with a list of FarmServer objects (lines 20-21).&amp;nbsp; The Run method (starting on line 31) implements the simple load-balancing algorithm:
&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;a list of farm servers which are available to build the project is constructed (lines 33-36);&lt;/li&gt;
	&lt;li&gt;if no server is available to build the project, the edge server reports a build failure (line 38-43);&lt;/li&gt;
	&lt;li&gt;the list of available farm servers is sorted by priority (lines 45-47); &lt;/li&gt;
	&lt;li&gt;the project build is started on the farm server configured with the highest priority (line 53).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
Determining a list of available farm servers is pretty straightforward:
&lt;/p&gt;
&lt;pre class="c#"&gt;
void FindAvailableFarmServers( IIntegrationResult result, IDictionary&amp;lt;int, ICruiseManager&amp;gt; servers )
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; // predicate to locate a server that isn&amp;#39;t actively building 
&amp;nbsp;&amp;nbsp;&amp;nbsp; // the current project
&amp;nbsp;&amp;nbsp;&amp;nbsp; Predicate&amp;lt;ProjectStatus&amp;gt; predicate = delegate( ProjectStatus prj )
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return IsServerAvailableToBuildProject( result, prj );
&amp;nbsp;&amp;nbsp;&amp;nbsp; };
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; // check the status of each configured farm server
&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach( FarmServer server in FarmServers.Values )
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ICruiseManager manager = null;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; try
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; manager = ( ICruiseManager )factory.GetCruiseManager( server.Uri );
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // get a local copy of server&amp;#39;s current project status snapshot
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; List&amp;lt;ProjectStatus&amp;gt; projects = new List&amp;lt;ProjectStatus&amp;gt;( manager.GetProjectStatus() );
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if( null != projects.Find( predicate ) )
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // add the farm server to the list of available servers, 
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //&amp;nbsp; keyed by its configured priority
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; servers[ server.Priority ] = manager;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; catch( Exception e )
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Log.Warning( e );
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p&gt;
Available servers are saved in the servers dictionary, keyed by their configured priority.&amp;nbsp; The availability of each farm server listed in the task configuration is checked by obtaining the status of the farm server&amp;#39;s projects, and passing them to the IsServerAvaialbleToBuildProject method:
&lt;/p&gt;
&lt;pre class="c#"&gt;
bool IsServerAvailableToBuildProject( IIntegrationResult result, ProjectStatus prj )
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; if( null == prj || null == result )
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return false;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; bool status = (&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // project name must match
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; StringComparer.InvariantCultureIgnoreCase.Equals( result.ProjectName, prj.Name ) &amp;amp;&amp;amp;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // integrator status must be &amp;quot;running&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; prj.Status == ProjectIntegratorState.Running &amp;amp;&amp;amp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // build activity must be &amp;quot;sleeping&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; prj.Activity.IsSleeping()
&amp;nbsp;&amp;nbsp;&amp;nbsp; );
&amp;nbsp;&amp;nbsp;&amp;nbsp; return status;
} 
&lt;/pre&gt;
&lt;p&gt;
which simply returns true when:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;the farm server configuration contains the project, &lt;br /&gt;
	&lt;/li&gt;
	&lt;li&gt;the project is currently running, and &lt;br /&gt;
	&lt;/li&gt;
	&lt;li&gt;the project isn&amp;#39;t currently building.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Download&lt;/h2&gt;
&lt;p&gt;
This code is basically spike-quality at this point.&amp;nbsp; I fully expect
to throw this away in favor of something better (or get my manager to
splurge for &lt;a href="http://www.jetbrains.com/teamcity/" target="_blank"&gt;TeamCity&lt;/a&gt;).&amp;nbsp; There&amp;#39;s still a lot of stuff to do.&amp;nbsp; E.g., the algorithm assumes that a farm server is capable of building more than one project at a time - that is, if a farm server is busy building one project, it can still be available to build another concurrently.&amp;nbsp; My assumption is that I&amp;#39;ll manage this with the farm server priority configuration.&amp;nbsp; I&amp;#39;d like to leverage the queuing features available in CC.NET; however, I see no way of querying the queue status of a farm server in the CC.NET API.&amp;nbsp; But at least I can start disabling the post-commit hook on our repository. 
&lt;/p&gt;
&lt;p&gt;
The project contains the code, a test/demo, and just a few basic unit tests; I&amp;#39;ll update the download if/when the project matures.&amp;nbsp; If you use it, or have any suggestions, please let me know in the comments of this post.&amp;nbsp; Enjoy! 
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.beefycode.com/file.axd?file=2009%2f4%2fccnet.edgeserver.zip"&gt;ccnet.edgeserver.zip (607.87 kb)&lt;/a&gt;
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/beefycode/~4/MbvV45Vv2dU" height="1" width="1"/&gt;</description>
      <link>http://www.beefycode.com/post/Load-Balancing-the-Build-Farm-with-CruiseControlNET.aspx</link>
      <author>wheresthebeef.nospam@nospam.beefycode.com (jimbo)</author>
      <comments>http://www.beefycode.com/post/Load-Balancing-the-Build-Farm-with-CruiseControlNET.aspx#comment</comments>
      <guid>http://www.beefycode.com/post.aspx?id=76d62540-91cd-4a55-b1d7-791a9c6ddfbd</guid>
      <pubDate>Mon, 06 Apr 2009 02:25:00 -1300</pubDate>
      <category>scm</category>
      <category>software development</category>
      <dc:publisher>jimbo</dc:publisher>
      <pingback:server>http://www.beefycode.com/pingback.axd</pingback:server>
      <pingback:target>http://www.beefycode.com/post.aspx?id=76d62540-91cd-4a55-b1d7-791a9c6ddfbd</pingback:target>
      <slash:comments>5</slash:comments>
      <trackback:ping>http://www.beefycode.com/trackback.axd?id=76d62540-91cd-4a55-b1d7-791a9c6ddfbd</trackback:ping>
      <wfw:comment>http://www.beefycode.com/post/Load-Balancing-the-Build-Farm-with-CruiseControlNET.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.beefycode.com/syndication.axd?post=76d62540-91cd-4a55-b1d7-791a9c6ddfbd</wfw:commentRss>
    </item>
  </channel>
</rss>
