{"id":2328,"date":"2013-12-29T00:01:00","date_gmt":"2013-12-29T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/12\/29\/weekend-scripter-using-powershell-to-replace-stsadm\/"},"modified":"2013-12-29T00:01:00","modified_gmt":"2013-12-29T00:01:00","slug":"weekend-scripter-using-powershell-to-replace-stsadm","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-using-powershell-to-replace-stsadm\/","title":{"rendered":"Weekend Scripter: Using PowerShell to Replace STSADM"},"content":{"rendered":"<p><b>Summary<\/b>: Learn about a Windows PowerShell script to replace <b>STSADM &ndash;o enumallwebs<\/b> in SharePoint.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Welcome back today guest blogger, <a href=\"\/b\/heyscriptingguy\/archive\/tags\/brian+jackett\/\" target=\"_blank\">Brian Jackett<\/a>.<\/p>\n<p style=\"margin-left:30px\">Brian is a senior premier field engineer at Microsoft who has specialized in SharePoint development, Windows PowerShell, and Project Server since 2008. Brian is a steering committee member of the Buckeye SharePoint User Group and coauthor of <a href=\"http:\/\/shop.oreilly.com\/product\/0790145335920.do\" target=\"_blank\">Microsoft SharePoint 2010: Creating and Implementing Real-World Projects<\/a>. He enjoys giving back to the community through giving presentations, planning and volunteering at conferences, maintaining a SharePoint\/.NET-centric blog (<a href=\"http:\/\/www.briantjackett.com\/\" target=\"_blank\">The Frog Pond of Technology<\/a>), and contributing to the SharePoint Twitterverse (@BrianTJackett).&nbsp; He also holds several Microsoft Certified Technology Specialist (MCTS) certificates for SharePoint-related technologies.<\/p>\n<p>Just like many other Microsoft server applications, SharePoint has had a fair number of command-line interfaces for administrating the product. STSADM.exe was one of these command line tools. Since SharePoint 2010, Windows PowerShell has become the preferred command-line administration tool.&nbsp; However, there are certain instances where Windows PowerShell does not provide an exact replacement for tools such as STSADM.exe. In this post, we will explore one such command (<b>enumallwebs<\/b>) and attempt to replace that functionality with a Windows PowerShell script.<\/p>\n<h2>Problem<\/h2>\n<p>The SharePoint team that is in charge of Windows PowerShell cmdlets has done a great job of building a replacement for most of the STSADM.exe capabilities (and in many cases, greatly surpassed them). For a list of the cmdlets, see <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/ff621084.aspx\" target=\"_blank\">Stsadm to Windows PowerShell mapping in SharePoint 2013<\/a>.<\/p>\n<p>As you can see, there are a few STSADM.exe capabilities that do not have a 1 to 1 equivalent. One example is the <b>STSADM &ndash;o enumallwebs<\/b> command.&nbsp; This is a very popular command to generate an XML output of all content databases in the SharePoint farm, site collections, and sites within the databases, the template used, URL, and more. Because there is not a replacement for this command, I started looking for a way to replace portions of the functionality.<\/p>\n<h2>Research<\/h2>\n<p>Whenever I encounter a large problem or challenge, I find it best to start at the beginning and take one step at a time. To start with, I wanted to see what the Help information for enumallwebs provided.&nbsp; Knowing what the parameters are and whether they are required vs. optional is very helpful.&nbsp; Following is the output from <b>STSADM &ndash;help enumallwebs<\/b>:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM1.jpg\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM1.jpg\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>I do not have the source code for the STSADM executable, so I don&rsquo;t know how the output is generated.&nbsp; As such, I will have to rely on a bit of <a href=\"http:\/\/en.wikipedia.org\/wiki\/Black-box_testing\" target=\"_blank\">black-box testing<\/a>, and I&rsquo;ll run various sample-case executions of the command and compare what output is generated.<\/p>\n<p>I start by looking at the output when running against my SharePoint&nbsp;2013 lab farm and with no parameters specified. This gives me the basic XML hierarchy that I need to re-create, what properties to pull back, and a sample to compare my Windows PowerShell script results against. The following screenshot shows the output from the <b>enumallwebs<\/b> command with no parameters.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM2.jpg\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM2.jpg\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>&nbsp;I see that the general hierarchy of XML is as follows:<\/p>\n<p style=\"margin-left:30px\">&lt;Databases&gt;<\/p>\n<p style=\"margin-left:30px\">&lt;Database&hellip;&gt;<\/p>\n<p style=\"margin-left:30px\">&lt;Site&hellip;&gt;<\/p>\n<p style=\"margin-left:30px\">&lt;Webs&hellip;&gt;<\/p>\n<p style=\"margin-left:30px\">&lt;Web&hellip;\/&gt;<\/p>\n<p style=\"margin-left:30px\">&lt;\/Webs&gt;<\/p>\n<p style=\"margin-left:30px\">&lt;\/Site&gt;<\/p>\n<p style=\"margin-left:30px\">&lt;\/Database&gt;<\/p>\n<p style=\"margin-left:30px\">&lt;\/Databases&gt;<\/p>\n<p>This hierarchy should be fairly easy to replicate. Normally, the <b>Get-SPContentDatabase<\/b> cmdlet would be sufficient; but unfortunately, it does not include the Central Administration content databases. As a workaround, the <b>Get-SPWebApplication &ndash;IncludeCentralAdministration<\/b> command can be used to gather all web applications including Central Administration. Piping this to <b>Select-Object &ndash;ExpandProperty ContentDatabases<\/b> will then retrieve all the necessary content databases, and we can iterate over each site collection (and later site) contained within each content database.<\/p>\n<p style=\"margin-left:30px\">Get-SPWebApplication -IncludeCentralAdministration | Select-Object -ExpandProperty ContentDatabases<\/p>\n<p>Next, I break down each XML element to find which properties we need to return. There is nothing at the <b>Databases<\/b> level, so we can continue on to database. The <b>Database<\/b> XML element has three attributes:<\/p>\n<ul>\n<li>SiteCount<\/li>\n<li>Name<\/li>\n<li>DataSource<\/li>\n<\/ul>\n<p>I know what I am looking for, but not specifically if or which property on the content database object contains the corresponding data. Therefore, I use a common technique when encountering a new datatype: <b>Format-List &ndash;Property *<\/b>, or <b>fl *<\/b> if I use the alias for <b>Format-List<\/b> and don&rsquo;t include the positional parameter <b>-Property<\/b>.<\/p>\n<p>Passing the output from <b>Get-SPContentDatabase<\/b> to <b>fl *<\/b> lets us see all of the properties and their associated values for the Microsoft.SharePoint.Administration.SPContentDatabase objects that are returned.&nbsp;<\/p>\n<p>From this I can see that the <b>SPContentDatabase<\/b> properties for <b>CurrentSiteCount<\/b> matches <b>SiteCount<\/b>, <b>Server<\/b> matches <b>DataSource<\/b>, and <b>Name<\/b> matches <b>Name<\/b>. You can see the initial portion of the output here:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM3.jpg\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM3.jpg\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>Next up is the <b>Site<\/b> element. In all <b>Site<\/b> elements I find the following attributes:<\/p>\n<ul>\n<li>Id<\/li>\n<li>OwnerLogin<\/li>\n<li>InSiteMap<\/li>\n<\/ul>\n<p>However, I notice some differences in the output from one element to the next&mdash;specifically, the inclusion or exclusion of a <b>HostHeader<\/b> attribute. In the following screenshot, you can see an example of each. This means that I will have to perform an additional check for the <b>HostHeaderIsSiteName<\/b> property on the site collection to determine whether that attribute needs to be included.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM4.jpg\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM4.jpg\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>Using the same trick of piping the objects to <b>Format-List &ndash;Property *<\/b>, I find that almost all of the attributes I need are associated with a property on the site collection object. The one exception is <b>InSiteMap<\/b>.&nbsp;<\/p>\n<p>The <b>InSiteMap<\/b> attribute from the STSADM output refers to the fact that the content database may know about a site collection that exists, but the SharePoint configuration database map not have the site collection listed in its site map. This is otherwise known as an orphaned site collection.<\/p>\n<p>I could not find any documentation about the Microsoft.SharePoint.Administration.SPConfigurationDatabase object (at least none that weren&rsquo;t marked as obsolete), so I saved the configuration database object into a variable to further inspect it.<\/p>\n<p style=\"margin-left:30px\">$configDB = Get-SPDatabase | where {$_.typename -eq &quot;Configuration Database&quot;}<\/p>\n<p>I then piped the <b>$configDB<\/b> variable to <b>Get-Member<\/b> to see what was available. As it turns out, there is a <b>SiteExists()<\/b> method on the object. After running a few tests, I felt fairly confident that this was what I was looking for. If I passed the site collection, I was iterating into the <b>SiteExists()<\/b> method on the configuration database, and this would fulfill my <b>InSiteMap<\/b> attribute.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM5.jpg\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/ReplacingSTSADM5.jpg\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>Next is the <b>Webs<\/b> element which contains a single attribute:<\/p>\n<ul>\n<li>Count<\/li>\n<\/ul>\n<p>Because the site collection object has a property <b>AllWebs<\/b>, which returns a <b>SPWebCollection <\/b>object (similar to an array), I can access the <b>Count<\/b> of the <b>AllWebs<\/b> to find how many subsites are in the site collection.<\/p>\n<p style=\"margin-left:30px\">$site.AllWebs.Count<\/p>\n<p>The last element is the <b>Web<\/b> element, and it contains a number of attributes:<\/p>\n<ul>\n<li>Id<\/li>\n<li>Url<\/li>\n<li>LanguageId<\/li>\n<li>TemplateName<\/li>\n<li>TemplateId<\/li>\n<\/ul>\n<p>Once again, piping the <b>SPWeb<\/b> objects to <b>Format-List &ndash;Property *<\/b> reveals many of the properties that I will need. The one troublesome property will be <b>TemplateName<\/b>, but I can see that it is a combination of the <b>WebTemplate<\/b> and <b>Configuration<\/b> properties.<\/p>\n<h2>Solution<\/h2>\n<p>After researching all of the hierarchy components, I put them together into the following script.&nbsp; You can download it from the Script Center Repository: <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/PowerShell-replacement-for-5fece860\">PowerShell replacement for &quot;STSADM -o enumallwebs&quot; in SharePoint 2010 or 2013<\/a>. I recommend redirecting the output to a file to avoid line-wrapping issues with the faux XML output that is generated.<\/p>\n<p style=\"margin-left:30px\">Param([string]$DatabaseName,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;[string]$DatabaseServer,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [switch]$IncludeFeatures,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [switch]$IncludeEventReceivers)<\/p>\n<p style=\"margin-left:30px\">############################################################<\/p>\n<p style=\"margin-left:30px\">#SP_Enum-AllWebs_vHSG.ps1<\/p>\n<p style=\"margin-left:30px\">#<\/p>\n<p style=\"margin-left:30px\">#Author: Brian T. Jackett<\/p>\n<p style=\"margin-left:30px\">#Last Modified Date: 2013-11-08<\/p>\n<p style=\"margin-left:30px\">#<\/p>\n<p style=\"margin-left:30px\">#Script to replace functionality from STSADM -o enumallwebs<\/p>\n<p style=\"margin-left:30px\">############################################################<\/p>\n<p style=\"margin-left:30px\"># some references can be used from the below article<\/p>\n<p style=\"margin-left:30px\">#http:\/\/www.craigtothepoint.com\/Lists\/Posts\/Post.aspx?ID=11<\/p>\n<p style=\"margin-left:30px\">function ProcessContentDatabase([Microsoft.SharePoint.Administration.SPContentDatabase]$SPContentDatabase)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Verbose &quot;Begin &#8211; Processing content database: $($db.Name)&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Database SiteCount=`&quot;$($db.CurrentSiteCount)`&quot; Name=`&quot;$($db.Name)`&quot; DataSource=`&quot;$($db.Server)`&quot;&gt;&quot;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach($site in $db.Sites)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ProcessSiteCollection $site<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;\/Database&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Verbose &quot;End &#8211; Processing content database: $($db.Name)&quot;<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">function ProcessSiteCollection([Microsoft.SharePoint.SPSite]$SPSite)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Verbose &quot;Begin &#8211; Processing site collection: $($site.URL)&quot;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if($site.HostHeaderIsSiteName -eq $false)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Site Id=`&quot;$($site.Id)`&quot; OwnerLogin=`&quot;$($site.Owner.UserLogin)`&quot; InSiteMap=`&quot;$($configDB.SiteExists($($site.Id)))`&quot;&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; else<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Site Id=`&quot;$($site.Id)`&quot; OwnerLogin=`&quot;$($site.Owner.UserLogin)`&quot; InSiteMap=`&quot;$($configDB.SiteExists($($site.Id)))`&quot; HostHeader=`&quot;$($site.HostName)`&quot;&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if($IncludeEventReceivers)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;EventReceiverAssemblies&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach($eventReceiver in $site.EventReceivers)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;EventReceiverAssembly Name=`&quot;$($eventReceiver.Assembly)`&quot; \/&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;\/EventReceiverAssemblies&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if($IncludeFeatures)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Features&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach($feature in $site.Features)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Feature Id=`&quot;$($feature.DefinitionId)`&quot; Count=`&quot;1`&quot; DisplayName=`&quot;$($feature.Definition.DisplayName)`&quot; InstallPath=`&quot;$($feature.Definition.RootDirectory)`&quot; Status=`&quot;$($feature.Definition.Status)`&quot; \/&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;\/Features&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Webs Count=`&quot;$($site.AllWebs.Count)`&quot;&gt;&quot;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; foreach($web in $site.AllWebs)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ProcessSite $web<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;\/Webs&gt;&quot;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;\/Site&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Verbose &quot;End &#8211; Processing site collection: $($site.URL)&quot;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $site.Dispose()<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">function ProcessSite([Microsoft.SharePoint.SPWeb]$SPWeb)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Verbose &quot;Begin &#8211; Processing site: $($web.URL)&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if(!($IncludeFeatures -or $IncludeEventReceivers))<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Web Id=`&quot;$($web.Id)`&quot; Url=`&quot;$($web.Url)`&quot; Languauge=`&quot;$($web.Language)`&quot; TemplateName=`&quot;$($web.WebTemplate)#$($web.Configuration)`&quot; TemplateId=`&quot;$($web.WebTemplateId)`&quot; \/&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; else<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Web Id=`&quot;$($web.Id)`&quot; Url=`&quot;$($web.Url)`&quot; Languauge=`&quot;$($web.Language)`&quot; TemplateName=`&quot;$($web.WebTemplate)#$($web.Configuration)`&quot; TemplateId=`&quot;$($web.WebTemplateId)`&quot;&gt;&quot;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if($IncludeEventReceivers)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;EventReceiverAssemblies&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach($eventReceiver in $web.EventReceivers)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;EventReceiverAssembly Name=`&quot;$($eventReceiver.Assembly)`&quot; \/&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;\/EventReceiverAssemblies&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if($IncludeFeatures)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Features&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach($feature in $web.Features)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Feature Id=`&quot;$($feature.DefinitionId)`&quot; Count=`&quot;1`&quot; DisplayName=`&quot;$($feature.Definition.DisplayName)`&quot; InstallPath=`&quot;$($feature.Definition.RootDirectory)`&quot; Status=`&quot;$($feature.Definition.Status)`&quot; \/&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;\/Features&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;\/Web&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Verbose &quot;End &#8211; Processing site: $($web.URL)&quot;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $web.Dispose()<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\"># start of main portion of script<\/p>\n<p style=\"margin-left:30px\">if((Get-PSSnapin -Name Microsoft.SharePoint.PowerShell) -eq $null)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Add-PSSnapin Microsoft.SharePoint.PowerShell<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\"># check if farm was able to be referenced<\/p>\n<p style=\"margin-left:30px\">if($farm -eq $null)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Error &quot;Unable to access farm, exiting script&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; exit<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p style=\"margin-left:30px\">else<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; # get reference to configuration database to be used in site collection processing<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $configDB = Get-SPDatabase | where {$_.typename -eq &quot;Configuration Database&quot;}<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; [Microsoft.SharePoint.Administration.SPContentDatabase[]]$contentDB = Get-SPWebApplication -IncludeCentralAdministration | Select-Object -ExpandProperty ContentDatabases<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; # filter content databases based on user input (if supplied)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if(-not [string]::IsNullOrEmpty($DatabaseName))<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contentDB = $contentDB | Where-Object {$_.Name -eq $DatabaseName}<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if(-not [string]::IsNullOrEmpty($DatabaseServer))<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contentDB = $contentDB | Where-Object {$_.Server -eq $DatabaseServer}<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;Databases&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; foreach($db in $contentDB)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ProcessContentDatabase $db<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Output &quot;&lt;\/Databases&gt;&quot;<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p style=\"margin-left:30px\"><b>Note<\/b> &nbsp;I have added some additional parameters (<b>IncludeFeatures<\/b> and <b>IncludeEventReceivers<\/b>), which I omitted from this post due to length considerations. I attempted to research the <b>IncludeWebParts<\/b>, <b>IncludeSetupFiles<\/b>, and <b>IncludeCustomListView<\/b> parameters, but I ran into issues getting sample output or finding the corresponding properties necessary. I also included <b>Write-Verbose<\/b> calls for additional troubleshooting if necessary.<\/p>\n<p>In this post, I walked through attempting to replace the functionality from <b>STSADM &ndash;o enumallwebs<\/b> with a Windows PowerShell script. In the process of examining the output and researching corresponding Windows PowerShell substitutes, I learned quite a bit about the SharePoint object model. Specifically, the Microsoft.SharePoint.Administration.SPConfigurationDatabase datatype was entirely new to me. Although this script is not meant to be an official replacement for STSADM, you got to see one way to tackle an issue such as this.<\/p>\n<p>~Brian<\/p>\n<p>Thank you, Brian!<\/p>\n<p>Join me tomorrow when we begin a series of posts by guest blogger, Rohn Edwards. It is a really cool series that you will not want to miss. I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Learn about a Windows PowerShell script to replace STSADM &ndash;o enumallwebs in SharePoint. Microsoft Scripting Guy, Ed Wilson, is here. Welcome back today guest blogger, Brian Jackett. Brian is a senior premier field engineer at Microsoft who has specialized in SharePoint development, Windows PowerShell, and Project Server since 2008. Brian is a steering committee [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[177,56,3,59,61,45],"class_list":["post-2328","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-brian-jackett","tag-guest-blogger","tag-scripting-guy","tag-sharepoint","tag-weekend-scripter","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Learn about a Windows PowerShell script to replace STSADM &ndash;o enumallwebs in SharePoint. Microsoft Scripting Guy, Ed Wilson, is here. Welcome back today guest blogger, Brian Jackett. Brian is a senior premier field engineer at Microsoft who has specialized in SharePoint development, Windows PowerShell, and Project Server since 2008. Brian is a steering committee [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2328","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=2328"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2328\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=2328"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=2328"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=2328"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}