{"id":1370,"date":"2012-07-19T00:00:00","date_gmt":"2012-07-19T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/powershell-smo-just-writing-things-once\/"},"modified":"2026-05-14T09:14:35","modified_gmt":"2026-05-14T09:14:35","slug":"powershell-smo-just-writing-things-once","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/powershell-smo-just-writing-things-once\/","title":{"rendered":"PowerShell SMO for SQL Server: Running Scripts and Reports Across Multiple Databases and Servers"},"content":{"rendered":"\n<p><b>PowerShell SMO (SQL Server Management Objects) exposes SQL Server&#8217;s object model to PowerShell &#8211; databases, tables, files, jobs, users, permissions, backups, essentially everything accessible through SSMS. The practical value for a DBA is that operations written once against SMO can run unchanged across every database on every registered server, rather than being copy-pasted into SSMS sessions or script files per instance. <\/b><b>This article demonstrates the pattern through several progressively more complex examples.<\/b><\/p>\n\n\n\n<p><strong>These examples include:<\/strong><\/p>\n\n\n\n<p><b>(1) listing file groups and files across all databases on all registered servers; <br>(2) checking log file sizes and last-autogrow events across the estate; <br>(3) verifying backup dates (finding databases that haven&#8217;t been backed up recently); <br>(4) running arbitrary T-SQL against multiple databases and aggregating results into a single report or per-database files; <br>(5) generating full source-control-ready script output for all objects in all databases. <\/b><\/p>\n\n\n\n<p><b>Each example is a few lines of PowerShell plus a filter for which servers or databases to target. The DRY principle (Don&#8217;t Repeat Yourself) applies to DBA automation as strongly as to application code &#8211; writing the operation once against SMO means the logic lives in one place, can be tested in one place, and runs consistently across the estate.<\/b><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-introduction-to-powershell-smo-for-sql-server\">Introduction to PowerShell SMO for SQL Server<\/h2>\n\n\n\n<div id=\"pretty\">\n<p>In this article, I&#8217;ll be showing you how to perform a variety of jobs on databases in a number of servers via PowerShell. Some involve simple things like getting database properties, but others involve executing SQL in all databases, or getting entire database-build-scripts. Generally, they create a single report for all your servers and databases that can then be imported into Excel or Word. You should be able to build on these examples to create a variety of other tasks. These sample jobs and reports include:<\/p>\n<ul>\n<li>Checking on database sizes<br \/><br \/><br \/><\/li>\n<li>Checking on the file groups and their files<br \/><br \/><br \/><\/li>\n<li>Checking on database sizes<br \/><br \/><br \/><\/li>\n<li>Checking on Transaction Log autogrowth<br \/><br \/><br \/><\/li>\n<li>Checking the number and size of Virtual Log Files (VLF)<br \/><br \/><br \/><\/li>\n<li>Finding out when each database was last backed up (if ever!)<br \/><br \/><br \/><\/li>\n<li>Examining table sizes<br \/><br \/><br \/><\/li>\n<li>Creating Build scripts<br \/><br \/><br \/><\/li>\n<li>Putting databases into source control<br \/><br \/><br \/><\/li>\n<li>Using SQL to find all the tables in all your databases that don&#8217;t have any indexes<br \/><br \/><br \/><\/li>\n<li>Using SQL to find all the tables without primary keys<\/li>\n<\/ul>\n<h2>DRY (Don&#8217;t repeat yourself)<\/h2>\n<p class=\"start\">When a database developer uses PowerShell, it is often to perform a routine operation via SMO on a number of databases on several servers or instances. This applies to a whole range of tasks. Just speaking for myself, I&#8217;ll be creating scripts for individual tables and routines, and I&#8217;d be doing entire build scripts. I&#8217;ll probably also be doing some sort of documentation, or cataloguing, of what there is in each database, or maybe the individual database settings.<\/p>\n<p class=\"start\">I&#8217;ll also be setting up a test run, or checking a status with a DMV. I might be doing checks to make sure that indexes weren&#8217;t duplicated, or that there was a primary key. I might just be searching for a string, or checking to see which users had rights of access. All these tasks require a PowerShell SMO script that accesses all or some of the databases on all or some of my instances.<\/p>\n<p class=\"MsoNormal\">After a while, the scripts will seem horribly familiar. It will probably consist of a pipeline that takes a list of servers, often the registered servers in my case, and finds all the non-system databases. It will either pick databases from a list (a whitelist), find those with a pattern in the name, or do every database that is not in a list (a blacklist). I might want to just do databases with a particular prefix or suffix. Then it will do things with each database. All that really changes is the action you want to perform with each database.<\/p>\n<p class=\"MsoNormal\">It always irritates me to have the same code in more than one place. You&#8217;ll really only want to write this once, and then reuse it. The only problem is how.<\/p>\n<p class=\"MsoNormal\">If you are doing a long background job serially in PowerShell, it makes sense to do this in a pipeline, rather than creating potentially huge collections of large objects, because this is kinder to the memory management of .NET. Each server object is passed down the pipeline, each to have its collection of databases passed on, one at a time to be filtered and worked on and tidied out of the way.<\/p>\n<p class=\"MsoNormal\">All you need to do is to inject the action you specify into the right part of the pipeline. To demonstrate how to do this with anonymous functions that we assign to delegates, we&#8217;ll step back slightly from the problem of accessing servers and their databases via SMO, and we&#8217;ll forget any fancy stuff like functions. We&#8217;ll demonstrate a pipeline rather differently by creating a PowerShell script to generate the lyrics for &#8216;Ten Green Bottles&#8217;. Then, we&#8217;ll change it slightly without altering the pipeline itself in order to generate &#8216;There were ten in the bed&#8217;. Believe me, this will help you understand the meaty stuff later on.<\/p>\n<h3>Ten green bottles<\/h3>\n<p class=\"MsoNormal\">We&#8217;ll introduce a filter that decides the number less than, or equal to, ten at which to start the great poem. Then we&#8217;ll make the whole pipeline do something else without changing the code in the pipeline itself.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t#The basic sequence\n\t$Sequence=\"ten\",\"nine\",\"eight\",\"seven\",\"six\",\"five\",\"four\",\"three\",\"two\",\"one\"\n\t\n\t$StartAt='Ten' #The number we want to start at. Try it out with other values.\n\t\n\t$action={param ($x,$Isfirst); #the scriptblock called for every item in the list\n\tif (-not $Isfirst) {\"there would be $x green bottles hanging on the wall.\"}\n\t@\"\n\t\n\tThere were $x green bottles hanging on the wall, \n\t$x green bottles hanging on the wall\n\tand if one green bottle should accidentally fall\n\t\"@\n\t}\n\t\n\t\n\t$Start={ \"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $StartAt Green Bottles.`n\"} #Scriptblock called at start \n\t#and the one called after all items are processed\n\t$Finish={ \"there would be no green bottles hanging on the wall`n`n\u00a0\u00a0\u00a0 The End`n\" } \n\t#the scriptblock that decides whether the current item should be processed\n\t$Filter={ param ($x, $status); if ($x -eq $startAt) {$status = $true}; $status }\n\t\n\t#the actual pipeline. We will save the scriptblock literal in a variable which we can execute\n\t$pipeline={\n\t$Sequence|\n\t&amp; { BEGIN{$ShouldOutput = $false} PROCESS{$ShouldOutput = $filter.invoke( $_, $ShouldOutput); if ($shouldOutput) {$_}} } |\n\t\u00a0\u00a0\u00a0 &amp; {BEGIN{$first=$true; $Start.invoke($_) } PROCESS{$action.invoke($_, $first); $first=$false} END{$Finish.invoke($_) } }\n\t}\n\t$pipeline.invoke() #and we just invoke the pipeline\n\t\n\t&lt;# Now, we can change the poem to be 'There were ten in the bed' without touching the pipeline at all, but just changing the contents of the variables holding three of the four scriptblocks #&gt;\n\t\n\t$action={param ($x,$Isfirst); #the scriptblock called for every item in the list\n\tif (-not $Isfirst) { \"Single beds were only made for $x\"}\n\t\"`nThere were $x in the bed and the little one said,\"\n\tif ($x -ne 'one') { @\" \n\t'Roll over, roll over!'.\n\tSo they all rolled over and one fell out\n\tAnd he gave a little scream and he gave a little shout\n\t'Please remember to tie a knot in your pajamas'\n\t\"@\n\t}\n\telse {for($ii = 1;$ii -le 3; $ii++) {\"I've got the whole mattress to myself\"}\n\t\"I've got the mattress to myself.\"}\n\t}\n\t\n\t$Start={ \"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 There were $StartAt in the Bed.`n\"} #Scriptblock called at start \n\t#and the one called after all items are processed\n\t$Finish={ \"`n`n\u00a0\u00a0\u00a0 The End\" }\n\t#and we then just call the pipeline again!\n\t$pipeline.invoke() \n\u00a0\n<\/pre>\n<p class=\"MsoNormal\">What we&#8217;re actually doing is using delegates. We are using those scriptblocks that we assign to variables as anonymous functions. We&#8217;re injecting functionality into the pipeline. We haven&#8217;t changed the code of the pipeline itself.<\/p>\n<h2>\u00a0<\/h2>\n<p class=\"MsoNormal\">PowerShell can be adapted very well to the sort of programming that works by injecting functionality into a pre-existing structure. It is, after all, a dynamic language. In our case, this structure might be a pipeline that selects all your databases, one after another you and allows you to do what you need to each database.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t$DataSources | # our list of servers\n\t&amp; {PROCESS{$ServerFilter.invoke($_)}} | # choose which servers from the list\n\t\u00a0\u00a0 Foreach-object {new-object (\"$My.Server\") $_ } | # create an SMO server object\n\t\u00a0\u00a0\u00a0\u00a0 Where-Object {$_.ServerType -ne $null} | # did you positively get the server?\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 Foreach-object {$_.Databases } | #for every server successfully reached \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Where-Object {$_.IsSystemObject -ne $true} | #not the system objects\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp; {PROCESS{$Databasefilter.invoke($_)}} | # do all,avoid blacklist or do a whitelist etc\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp; {PROCESS{$JobToDo.invoke($_)}} #and do whatever you want for the database\n<\/pre>\n<p class=\"MsoNormal\">This does most routine tasks, but we need to add a few things, just as we had to with the poem. The <code>$Serverfilter<\/code> and <code>$DatabaseFilter<\/code> are variables containing a scriptblock. They have a parameter, which is <code>$_<\/code>, meaning the current object in the pipeline. This parameter isn&#8217;t strictly necessary, since it would work by using the <code>$_<\/code> parameter within the scriptblock, but it makes things easier for debugging, or if you have a complex script. This is equivalent to a C# delegate, but without the rigorous type-checking for the parameter you pass. With the poems, the filter didn&#8217;t change. It decided how many bottles we start with, or how many children in the bed. In this case having the filter code in a variable now allows us to place the actual filter to let us choose the database when we are running the script. We might want to specify a &#8216;white&#8217; list of databases that we want to operate on, or we might want to operate on all but a &#8216;black&#8217; list of databases. We might want to select only those that conform to a wildcard or regex string.<\/p>\n<p class=\"MsoNormal\">In our case, we&#8217;ll just insert a whitelist or a blacklist if one has been specified. Logically, you can&#8217;t specify a whitelist if you have a blacklist: it is one or the other, or neither.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t$DatabaseFilter=$TheDatabaseFilter\n\tif ($blacklist.count -gt 0) {$DatabaseFilter= { param($x); if ($blacklist -notcontains $x.name) {$x} }} \n\t# followed by the ones you don't want, listed in your blacklist\n\tif ($whitelist.count -gt 0) {$DatabaseFilter= { param($x); if ($whitelist -contains $x.name) {$x} }} \n\t# and one that just selects the files you specify in your whitelist\n\t<\/pre>\n<p class=\"MsoNormal\">If you then invoke this in the process part of a scriptblock, it will filter out all the unwanted database objects as they appear, and only pass on the ones you want.<\/p>\n<p class=\"MsoNormal\">You may have already categorised your databases with special prefix such a &#8216;Test&#8217;. In this case, you can use a different <code>$Databasefilter<\/code> filter to get only these,<\/p>\n<pre>\t{ param($x); if ($x.name -like 'Adv*'){$x}} <\/pre>\n<p>Or if you want to avoid them then<\/p>\n<pre>\t{ param($x); if ($x.name -notlike 'Adv*'){$x}}<\/pre>\n<p class=\"MsoNormal\">You might want to just do part of a list of databases, specifying the start and end database, just like the poems. Who knows? You can also use blacklists or whitelists for the list of servers in your care, or your registered servers, if you prefer.<\/p>\n<p class=\"MsoNormal\">Why do it this way? It is because you do not need to alter the code for the pipeline, you are &#8216;injecting&#8217; your filter into it. You have your code in just one place, without duplication. With a delegate we can specify the functionality, and therefore what actually happens, at script execution-time. You can have a different filter depending on the specific parameters . To do it using conventional conditional logic would end up with a rats nest of &#8216;if&#8217; statements. One can, if necessary, be even cleverer and add as many tasks as you wish to perform once the database is selected, but we won&#8217;t overdo things at this point.<\/p>\n<p class=\"MsoNormal\">So, we&#8217;ll create the pipeline <a href=\"http:\/\/www.simple-talk.com\/sql\/sql-tools\/the-posh-dba-grown-up-powershell-functions\/\">as an &#8216;advanced&#8217; function<\/a>, so that we can provide help and allow &#8216;debug-printing&#8217; and &#8216;voluble&#8217; messages for debugging via <code>Write-Debug<\/code> and <code>Write-Verbose<\/code>. This means that even your anonymous functions will\u00a0 be able to use these features as they are used within the same overall function. We&#8217;ll save this function in the same directory as all our code that actually does things.<\/p>\n<\/div>\n<div id=\"pretty\">\n<p class=\"MsoNormal\">So, without actually doing anything harmful to any database, lets\u00a0 test it out.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t$DataSources=@('Dave','Dee','Dosy','Beaky','Mick','Tich') # server name and instance\n\tForeach-DatabaseInServers $Datasources | foreach-object{\"Found database '$($_.name)' on Server $($_.parent.name) \"} #test that you can cannect to your datasources \n\t<\/pre>\n<p>Once you&#8217;ve got this working to your satisfaction, and you&#8217;ve added any extra filters or other functionality you need and tested it, then you can start doing something useful. In the following examples, I&#8217;m just checking for AdventureWorks (I&#8217;ve put in a filter to select only those databases starting with &#8216;ADV&#8217;, but your requirements will be different!)<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tImport-Module sqlps -DisableNameChecking #load the SQLPS functionality for getting the registered servers\n\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t#now fetch the list of all our registered servers\n\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\t\n\tForeach-DatabaseInServers $Servers\u00a0 `\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 -TheDatabasefilter { param($x); if ($x.name -like 'Adv*'){$x}} |\n\t\u00a0\u00a0 Select-object @{Name=\"Server\"; Expression={$_.parent.name}}, Name, DataSpaceUsage, SpaceAvailable, IndexSpaceUsage\u00a0 |ConvertTo-HTML \n\t<\/pre>\n<p class=\"MsoNormal\">This gives you a report that gives you the space available to each &#8216;Adventureworks&#8217; database (or whatever you specify \u00a0on the servers you specify, broken down by data and index usage.<\/p>\n<table>\n<tbody>\n<tr>\n<th>Server<\/th>\n<th>Name<\/th>\n<th>DataSpaceUsage<\/th>\n<th>SpaceAvailable<\/th>\n<th>Index SpaceUsage<\/th>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2005<\/td>\n<td>AdventureWorks<\/td>\n<td>84160<\/td>\n<td>15280<\/td>\n<td>78008<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2005<\/td>\n<td>AdventureworksClone<\/td>\n<td>66 824<\/td>\n<td>43320<\/td>\n<td>70696<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2005<\/td>\n<td>AdventureWorksDW<\/td>\n<td>43632<\/td>\n<td>15992<\/td>\n<td>23096<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2008<\/td>\n<td>AdventureWorks<\/td>\n<td>104744<\/td>\n<td>1040<\/td>\n<td>78992<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2008<\/td>\n<td>AdventureWorksCopy<\/td>\n<td>869 36<\/td>\n<td>4992<\/td>\n<td>77464<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2012<\/td>\n<td>AdventureWorksDW<\/td>\n<td>43744<\/td>\n<td>16200<\/td>\n<td>22536<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2012<\/td>\n<td>AdventureWorksDW2008R2<\/td>\n<td>45544<\/td>\n<td>16168<\/td>\n<td>26944<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2014<\/td>\n<td>AdventureWorksLT<\/td>\n<td>3112<\/td>\n<td>168<\/td>\n<td>1632<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2014<\/td>\n<td>AdventureWorksLT2008R2<\/td>\n<td>3192<\/td>\n<td>16488<\/td>\n<td>1632<\/td>\n<\/tr>\n<tr>\n<td>Robyn.Page.com\\sql2005<\/td>\n<td>AdventureWorks<\/td>\n<td>86032<\/td>\n<td>2468856<\/td>\n<td>80656<\/td>\n<\/tr>\n<tr>\n<td>Robyn.Page.com\\sql2005<\/td>\n<td>AdventureWorksDW<\/td>\n<td>43616<\/td>\n<td>0<\/td>\n<td>23088<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>the file groups and their files<\/h3>\n<p>Here we drill down on detail to find out (for all our databases on all our registered servers this time)\u00a0 how large each MDF file is in each group for each database. We see what group they are in, whether they are allowed to auto-grow &#8211; and if so the amount of growth, the type of growth, size of file. amount of space used and so on. This should help you to spot those MDF files where things are getting tight.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tImport-Module sqlps -DisableNameChecking #load the SQLPS functionality for getting the registered servers\n\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t#now fetch the list of all our registered servers\n\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\tForeach-DatabaseInServers $Servers\u00a0 | \n\t\u00a0\u00a0 foreach-object{$_.FileGroups} | foreach-object{$_.files} |\n\t\u00a0\u00a0\u00a0\u00a0 Select-object @{Name=\"Server\"; Expression={$_.parent.parent.parent.name}}, @{Name=\"Database\"; Expression={$_.parent.parent.name}}, @{Name=\"Group\"; Expression={$_.parent.name}}, Name, AvailableSpace, Filename, Growth, GrowthType, size, UsedSpace, MaxSize |\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 format-table \n<\/pre>\n<h3>Checking on the log files<\/h3>\n<h4>AutoGrowth<\/h4>\n<p>We can also check on log files on all our databases for all our registered servers (or whatever you wish, of course).\u00a0 We see whether they are allowed to auto-grow, and if so the amount of growth, the type of growth, size of file. amount of space used and so on. This should alert you to such problems as long-running transactions.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tImport-Module sqlps -DisableNameChecking #load the SQLPS functionality for getting the registered servers\n\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t#now fetch the list of all our registered servers\n\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\tForeach-DatabaseInServers $Servers\u00a0 | \n\t\u00a0\u00a0 foreach-object{$_.Logfiles} |\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 Select-object @{Name=\"Server\"; Expression={$_.parent.parent.name}}, @{Name=\"Database\"; Expression={$_.parent.name}},\u00a0 Name, Filename, Growth, GrowthType, size, UsedSpace, MaxSize |\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 format-table \n<\/pre>\n<h4>VLFs<\/h4>\n<p>We can also very simply look at the Virtual Log files for all of the databases&#8217; transaction logs, using a DBCC LogInfo directly.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tImport-Module sqlps -DisableNameChecking #load the SQLPS functionality for getting the registered servers\n\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t#now fetch the list of all our registered servers\n\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\tForeach-DatabaseInServers $Servers\u00a0 | \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Foreach-object {\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $Db=$_\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $_.ExecuteWithResults('dbcc loginfo').Tables[0] |\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Measure-Object -Minimum -Maximum -Average FileSize |\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Select-object @{Name=\"Server\"; Expression={$Db.parent.name}}, \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{Name=\"Database\"; Expression={$Db.name}},\u00a0 \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{Name=\"No.VLFs\"; Expression={$_.Count}}, \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{Name=\"MaxVLFSize(mb)\"; Expression={\"{0:n2}\" -f ($_.Maximum\/1MB)}}, \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{Name=\"MinVLFSize(mb)\"; Expression={\"{0:n2}\" -f ($_.Minimum\/1MB)}}, \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{Name=\"AverageVLF(mb)\"; Expression={\"{0:n2}\" -f ($_.Average\/1MB)}}\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } | Format-Table * -AutoSize\u00a0 \n<\/pre>\n<h3>Checking when your databases were last backed up<\/h3>\n<p class=\"MsoNormal\">With a few alterations it becomes a check on when they were last backed up. After all, the function is returning the databases as SMO objects and so you can pick anything from\u00a0 <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sqlserver.management.smo.database_properties.aspx\">SMO: Database Properties<\/a> or even (I&#8217;ve just shown you how!) from the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sqlserver.management.smo.server_properties.aspx\">SMO: Server Properties<\/a>.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tImport-Module sqlps -DisableNameChecking #load the SQLPS functionality for getting the registered servers\n\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t#now fetch the list of all our registered servers\n\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\tForeach-DatabaseInServers $Servers\u00a0 `\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 -TheDatabasefilter { param($x); if ($x.name -like 'Adv*'){$x}} |\n\t\u00a0\u00a0 Select-object @{Name=\"Server\"; Expression={$_.parent.name}}, Name, RecoveryModel, LastBackupDate, LastLogBackupDate, LastDifferentialBackupDate\u00a0 |ConvertTo-HTML \n\t<\/pre>\n<p>\u00a0<\/p>\n<table>\n<tbody>\n<tr>\n<th>Server<\/th>\n<th>Name<\/th>\n<th>Recovery<\/th>\n<th>LastBackupDate<\/th>\n<th>LastLogBackupDate<\/th>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2005<\/td>\n<td>AdventureWorks<\/td>\n<td>Full<\/td>\n<td>15\/11\/2012 14:26:00<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2005<\/td>\n<td>AdventureworksClone<\/td>\n<td>Full<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2005<\/td>\n<td>AdventureWorksDW<\/td>\n<td>Simple<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2008<\/td>\n<td>AdventureWorks<\/td>\n<td>Simple<\/td>\n<td>01\/12\/2012 09:14:59<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2008<\/td>\n<td>AdventureWorksCopy<\/td>\n<td>Full<\/td>\n<td>01\/12\/2012 09:15:02<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2008<\/td>\n<td>AdventureWorksDW<\/td>\n<td>Simple<\/td>\n<td>01\/12\/2012 09:15:04<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2008<\/td>\n<td>AdventureWorksDW2008R2<\/td>\n<td>Simple<\/td>\n<td>01\/12\/2012 09:15:06<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2008<\/td>\n<td>AdventureWorksLT<\/td>\n<td>Simple<\/td>\n<td>01\/12\/2012 09:15:06<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Phil.Factor.com\\sql2008<\/td>\n<td>AdventureWorksLT2008R2<\/td>\n<td>Simple<\/td>\n<td>01\/12\/2012 09:15:07<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Robyn.Page.com\\sql2005<\/td>\n<td>AdventureWorks<\/td>\n<td>Full<\/td>\n<td>20\/09\/2012 13:12:25<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<tr>\n<td>Robyn.Page.com\\sql2005<\/td>\n<td>AdventureWorksDW<\/td>\n<td>Simple<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<td>01\/01\/0001 00:00:00<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">Well, that gets a lot of standard database reports out of the way, and with\u00a0 <code>Select-Object,<\/code> you can do a lot with calculated fields. \u00a0<\/p>\n<h3>\u00a0<\/h3>\n<pre class=\"theme:ssms2012 lang:tsql\">\t\tImport-Module sqlps -DisableNameChecking #load the SQLPS functionality for getting the registered servers\n\t\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t\t#now fetch the list of all our registered servers\n\t\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\t\t\n\t\t&lt;# here we\u00a0 want to find out the data space used for each row, along with the rowcount. We then can calculate the average size of each row #&gt;\n\t\tForeach-DatabaseInServers $servers\u00a0 -whitelist @('AdventureWorks')\u00a0 -jobToDo {\n\t\t \u00a0\u00a0 param($database)\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 write-verbose \"Accessing the database '$($database.name)' on server $($database.parent.name)\"\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $database.tables| select-object (@{Name=\"Server\"; Expression={$database.parent.name}}, @{Name=\"Database\"; Expression={$database.name}}, @{Name=\"table\"; Expression={$_.name}}, @{Name=\"Data Space Used (Kb)\"; e={$_.DataSpaceUsed}}, @{Name=\"Data Rows\"; e={$_.RowCount}}, @{Name=\"no. columns\"; e={$_.columns.count}}, @{Name=\"Average Row size (Bytes)\"; e={\"{0:n2}\" -f (($_.DataSpaceUsed\/$_.RowCount)*1024)}}) \n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } | format-table \n\t<\/pre>\n<h2>\u00a0<\/h2>\n<p class=\"MsoNormal\">How about generating a build script for all the databases of your registered servers, that is saved to a directory corresponding to the server?<\/p>\n<p class=\"MsoNormal\">In this case, we&#8217;ll use <code>$JobToDo<\/code>, which is a parameter that comes in handy if you like using such things as <code>Write-Debug<\/code> and <code>Write-Verbose<\/code> or if we want to do entirely different things depending on some condition. This is because the scriptblock is now executed in the context of the function. We also need a variable to specify where to save the files.\u00a0 In this case, I want a blacklist because I don&#8217;t want to script things like Adventureworks (In this example, I&#8217;ve taken out a lot of specific databases, ovbiously)<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tImport-Module sqlps -DisableNameChecking #load the SQLPS functionality for getting the registered servers\n\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t#now fetch the list of all our registered servers\n\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\t#and a list of the databases we don't want to be scripted\n\t$blacklist='Pubs','NorthWind','AdventureWorks','AdventureWorksDW','ReportServer','ReportServerTempDB' #the databases I don't want to do\n\t#where we want to store the scripts (each server\/instance a separate directory\n\t$Filepath='E:\\MyScriptsDirectory' # local directory to save build-scripts to\n\t#and do it\n\tForeach-DatabaseInServers -verbose $servers -blacklist $blacklist -jobToDo {param($database)\n\t\u00a0\u00a0 $directory=\"$($FilePath)\\$( $database.Parent.URN.GetAttribute('Name','Server') -replace '[\\\\\\\/\\:\\.]','-' )\";\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $transfer = new-object (\"$My.Transfer\") $database\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions = new-object (\"$My.ScriptingOptions\") \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions.ExtendedProperties= $true # yes, we want these\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions.DRIAll= $true # and all the constraints \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions.Indexes= $true # Yup, these would be nice\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions.Triggers= $true # This should be included when scripting a database\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions.ScriptBatchTerminator = $true # this only goes to the file\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions.IncludeHeaders = $true; # of course\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions.ToFileOnly = $true #no need of string output as well\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions.IncludeIfNotExists = $true # not necessary but it means the script can be more versatile\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $CreationScriptOptions.Filename = \"$directory\\$($Database.name)_Build.sql\"; \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $transfer.options=$CreationScriptOptions # tell the transfer object of our preferences\n\t\u00a0\u00a0 write-verbose \"scripting '$($database.name)' in server '$($database.parent.name)' to $($CreationScriptOptions.Filename)\"\n\t\u00a0\u00a0 if (!(Test-Path -path \"$directory\"))\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Try { New-Item \"$directory\" -type directory | out-null } \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Catch [system.exception]{\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Error \"error while creating '$directory' \"\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \n\t\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\t\u00a0\u00a0 Try {$transfer.ScriptTransfer()}\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Catch [system.exception]{\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Error \"couldn't script to '$directory\\$($Database.name)_Build.sql' because of error (possibly an encrypted stored procedure\"\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \n\t\u00a0\u00a0 }\n\t'did that go well?'\n\t<\/pre>\n<h2>Running a SQL Script on rather a lot of databases<\/h2>\n<p class=\"MsoNormal\">That was pretty easy. \u00a0What about executing a SQL Script in a number of databases in a number of servers?\u00a0 Right away, sir. \u00a0<\/p>\n<h3>Generating a single report from running the query<\/h3>\n<p class=\"MsoNormal\">Let&#8217;s start with a simple example that just gives you a report of all the tables in all your databases that don&#8217;t have any \u00a0indexes. (here, we&#8217;ve just restricted it to Adventureworks)<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tImport-Module sqlps -DisableNameChecking #load the SQLPS functionality for getting the registered servers\n\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t#now fetch the list of all our registered servers\n\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\t$SQL=@'\n\t--Which of my tables don't have any indexes at all?\n\tSELECT @@Servername as [Server],DB_NAME() as [Database], \n\tDB_NAME()+'.'+Object_Schema_name(t.object_ID)+'.'+t.name AS [Tables without any index]\n\tFROM sys.tables t WHERE OBJECTPROPERTY(object_id, 'TableHasIndex')=0\n\torder by [Tables without any index]\u00a0 \n\t'@\n\t\n\t&lt;#So, we can make it call some sql and get back a result. In this case we are only looking at the various AdventureWorks databases in all the servers, just to illustrate the different filters you can specify.#&gt;\n\tForeach-DatabaseInServers $servers -TheDatabasefilter { param($x); if ($x.name -like 'Adv*' ){$x}}\u00a0 -jobToDo {\n\t\u00a0\u00a0 param($database)\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result=$database.ExecuteWithResults(\"$SQL\") #execute the SQL\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result.Tables[0]\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } | select-object ('Server','Database' ,'Tables without any index') | convertTo-html \n\t<\/pre>\n<h3>Generating a report for each database<\/h3>\n<p class=\"MsoNormal\">Now, we&#8217;ll do a slightly more complicated routine. We&#8217;ll write some files, one for each database, listing in CSV all the tables in each database that are heaps, without primary keys. You&#8217;ll see that it would be dead easy to use this for a variety of reports, just by changing the SQL, (and SQL Title). I use this method for testing code on a variety of different databases, servers and collations.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\t\t#now fetch the list of all our registered servers\n\t\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\t\t$Filepath='E:\\MyScriptsDirectory' # local directory to save the reports to\n\t\t$SQLTitle='All_Heaps_In_'\n\t\t$SQL=@'\n\t\t--Which of my tables don't have primary keys?\n\t\tSELECT @@Servername as [Server],DB_NAME() as [Database], --we'll do it via information_Schema\n\t\tTheTables.Table_Catalog+'.'+TheTables.Table_Schema+'.'\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 +TheTables.Table_Name AS [tables without primary keys]\n\t\tFROM\n\t\tINFORMATION_SCHEMA.TABLES TheTables\n\t\tLEFT OUTER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TheConstraints\n\t\t\u00a0\u00a0\u00a0 ON TheTables.table_Schema=TheConstraints.table_schema\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AND TheTables.table_name=TheConstraints.table_name\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AND constraint_type='PRIMARY KEY'\n\t\tWHERE table_Type='BASE TABLE'\n\t\tAND constraint_name IS NULL\n\t\tORDER BY [tables without primary keys]\n\t\t'@\n\t\t\n\t\t&lt;#So, we can make it call some sql and get back a result. In this case we are only looking at the various AdventureWorks databases in all the servers, just to illustrate the different filters you can specify.#&gt;\n\t\tForeach-DatabaseInServers $servers -TheDatabasefilter { param($x); if ($x.name -like 'Adv*'){$x}} -jobToDo {\n\t\t \u00a0\u00a0 param($database)\n\t\t\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0 $databaseName=$database.name\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ServerName=$database.Parent.Name\n\t\t \u00a0\u00a0 $directory=\"$($FilePath)\\$($database.parent.name -replace '[\\\\\\\/\\:\\.]','-' )\";#create a directory\n\t\t \u00a0\u00a0 #and a handler for warnings and PRINT messages\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] {param($sender, $event) Write-Host $event.Message};\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $database.parent.ConnectionContext.add_InfoMessage($handler);\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result=$database.ExecuteWithResults(\"$SQL\") #execute the SQL\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $database.parent.ConnectionContext.remove_InfoMessage($handler);\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (!(Test-Path -path \"$directory\")) #create the directory if necessary\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Try { New-Item \"$directory\" -type directory | out-null } \n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Catch [system.exception]{\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Error \"error while creating '$directory' \"\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\t\t&lt;# you might want to save these in a central monitoring server, or put them all in one file, of course, but that is what powershell is all about #&gt;\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result.Tables[0]| convertto-csv -NoTypeInformation &gt;\"$directory\\$SQLTitle$databasename.csv\"\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\t<\/pre>\n<h2>Popping plenty of databases into source control<\/h2>\n<p>fine. Let&#8217;s do something slightly trickier. There is a script that generates the source, one file per object, of the databases of your registered servers. Actually, as it stands, I&#8217;ve only included those databases that start with the word &#8216;Test&#8217;. I suspect you&#8217;ll want something slightly different. I&#8217;ve put each server in its own directory, of which each database is in its own subdirectory. Each object type (e.g. TABLE) is in its own directory within this. From this, it is a mere bagatelle to pop these into source control, but we&#8217;re not scripting that for you since I don&#8217;t know what Source Control Manager (SCM) you&#8217;re using.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t\n\t$DirectoryToSaveTo='E:\\MyScriptsDirectory' # the directory where you want to store them\n\t$TheDatabasefilter = { param($x); if ($x.name -like 'Test*'){$x}} # just those starting with 'Phil'.\n\t\n\t. \".\\ForAllDatabaseServers.ps1\" # pull in our pipeline function 'Foreach-DatabaseInServers'\n\tImport-Module sqlps -DisableNameChecking #load the SQLPS functionality for getting the registered servers\n\t#get a list of the servers we want to scan\n\t$servers= dir 'SQLSERVER:\\sqlregistration\\Database Engine Server Group' | foreach-object{$_.name}\n\t\n\t$ServiceBrokerTypes=@( 'MessageType','ServiceBroker','ServiceContract','ServiceQueue','ServiceRoute','RemoteServiceBinding')\n\t\n\t$JobToDo= {\n\t\u00a0\u00a0 $database=$_ \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $databaseName=$_.name\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ServerName=$_.Parent.URN.GetAttribute('Name','Server')\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 write-verbose \"scripting $databasename in $serverName\"\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ScriptOptions = new-object (\"Microsoft.SqlServer.Management.Smo.ScriptingOptions\")\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ScriptOptions.ExtendedProperties= $true # yes, we want these\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ScriptOptions.DRIAll= $true # and all the constraints\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ScriptOptions.Indexes= $true # Yup, these would be nice\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ScriptOptions.ScriptBatchTerminator = $true # this only goes to the file\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ScriptOptions.IncludeHeaders = $true; # of course\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ScriptOptions.ToFileOnly = $true # no need of string output as well\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ScriptOptions.IncludeIfNotExists = $true # not necessary but makes script more versatile\n\t\u00a0\u00a0 $scrp=new-object (\"$My.Scripter\") $Database.parent\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $scrp.options=$ScriptOptions\n\t\u00a0\u00a0 $database.EnumObjects([long]0x1FFFFFFF -band [Microsoft.SqlServer.Management.Smo.DatabaseObjectTypes]::all) | `\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 Where-Object {('sys','information_schema') -notcontains $_.Schema} | Foreach-Object { \n\t\u00a0\u00a0 $urn=\u00a0\u00a0\u00a0\u00a0\u00a0 [Microsoft.SqlServer.Management.Sdk.Sfc.Urn] $_.URN\u00a0\u00a0\u00a0 \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (('ExtendedStoredProcedure','ServiceBroker') -notcontains $urn.type)\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $currentPath=\"$DirectoryToSaveTo\\$($ServerName -replace '[\\\\\\\/\\:\\.]','-' )\\$($urn.GetAttribute('Name','Database') -replace '[\\\\\\\/\\:\\.]','-')\"\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ( $ServiceBrokerTypes -contains $urn.type)\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {$fullPath=\"$currentPath\\ServiceBroker\\$($urn.type)\"}\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 else\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {$fullPath=\"$currentPath\\$($urn.type)\"}\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (!(Test-Path -path $fullPath ))\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Try { New-Item $fullPath -type directory | out-null } \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Catch [system.exception]{\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Error \"error while creating '$fullPath' \"\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $scrp.options.FileName = \"$fullPath\\$($urn.GetAttribute('Schema')-replace '[\\\\\\\/\\:\\.]','-')-$($urn.GetAttribute('Name') -replace '[\\\\\\\/\\:\\.]','-').sql\"\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $UrnCollection = new-object ('Microsoft.SqlServer.Management.Smo.urnCollection')\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $URNCollection.add($urn)\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 write-verbose \"writing script to $($scrp.options.FileName)\"\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $scrp.Script($URNCollection)\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\t\u00a0\u00a0 }\n\t\u00a0\u00a0\u00a0\u00a0 \n\t}\n\t\n\t$params = @{DataSources=$Servers;TheDatabaseFilter=$TheDatabasefilter;JobToDo=$JobToDo;verbose=$true}\n\tForeach-DatabaseInServers @Params\n\t\"done them, Master.\" \n<\/pre>\n<h2>\u00a0<\/h2>\n<pre class=\"theme:ssms2012 lang:tsql\">\t\t\n\t\tFunction Foreach-DatabaseInServers {\n\t\t \u00a0\u00a0\u00a0 &lt;#\n\t\t\u00a0\u00a0\u00a0 .SYNOPSIS\n\t\t\u00a0\u00a0\u00a0 Does the whatever scriptblock you wish for the databases in list of SQL server instances\n\t\t\u00a0\u00a0\u00a0 .DESCRIPTION\n\t\t\u00a0\u00a0\u00a0 This takes the scriptblock you define\u00a0 and executes it against every database that you specify, either by using a whitelist or a blacklist, or just does them all.\n\t\t\u00a0\u00a0\u00a0 .EXAMPLE\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $DataSources='Dave', 'Dee', 'Dozy', 'Beaky', 'Mick', 'Titch'\u00a0 # server name and instance\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $JobToDo ={param($database)\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $databaseName=$database.name\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ServerName=$database.Parent.Name\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"Do stuff to $databasename on $Servername\"}\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $whitelist=''#the only databases I want to do, if they're there(Leave empty otherwise)\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $blacklist='Pubs','NorthWind','AdventureWorks','AdventureWorksDW','ReportServer','ReportServerTempDB' #the databases I don't want to do\n\t\t\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Foreach-DatabaseInServers $Datasources\u00a0 $jobToDo $whitelist $blacklist\n\t\t\u00a0\u00a0\u00a0 .PARAMETER DataSources\n\t\t\u00a0\u00a0\u00a0 The list of servers that you want the files from \n\t\t\u00a0\u00a0\u00a0 .PARAMETER JobToDo\n\t\t\u00a0\u00a0\u00a0 The script of the job you want done\n\t\t\u00a0\u00a0\u00a0 .PARAMETER Whitelist\n\t\t\u00a0\u00a0\u00a0 The list of databases you want the action performed on, leaving out all others\n\t\t\u00a0\u00a0\u00a0 .PARAMETER Blacklist\n\t\t\u00a0\u00a0\u00a0 The list of databases you don't want the action performed on\n\t\t\u00a0\u00a0\u00a0 .PARAMETER TheServerFilter\n\t\t\u00a0\u00a0\u00a0 Any filter you specify for servers\n\t\t\u00a0\u00a0 .PARAMETER TheDatabaseFilter\n\t\t\u00a0\u00a0\u00a0 Any filter you specify for databases\n\t\t\u00a0\u00a0\u00a0 .PARAMETER Initialisation\n\t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0any action that needs to be done first before the pipeline\n\t\t\u00a0\u00a0 #&gt;\n\t\t \u00a0\u00a0 param([CmdletBinding()]\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # The list of databases\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [Parameter(Mandatory=$True,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0Position=0,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HelpMessage='the list of one or more SQL Server instances you would like to target')]\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [array] $DataSources,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # The Job To Do \n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [Parameter(Mandatory=$false,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0Position=1,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HelpMessage='The actual job you want to do in each database')]\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [scriptblock] $JobToDo ={ param($x); $x},\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # The WhiteList\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [Parameter(Mandatory=$false,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0Position=2,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HelpMessage='the databases in each instance that you want to select')]\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [array] $Whitelist=$null,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # The BlackList\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [Parameter(Mandatory=$false,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0Position=3,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HelpMessage='the databases in each instance that you dont want to select')]\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [array] $Blacklist=$null,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # The custom database filter you want to use \n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [Parameter(Mandatory=$false,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0Position=4,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HelpMessage='if the whitelist or blacklist is no good for what you want')]\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [scriptblock] $TheServerFilter={ param($x); $x }, # define a filter that does nothing by default\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # The custom server filter you want to use \n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [Parameter(Mandatory=$false,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0Position=5,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HelpMessage='if you wish to select from all of the databases')]\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [scriptblock] $TheDatabaseFilter={ param($x); $x },# define a filter that does nothing by default\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # The custom server filter you want to use \n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [Parameter(Mandatory=$false,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0Position=6,\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HelpMessage='any initialisation you want (with SMO loaded)')]\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [scriptblock] $Initialisation={}# define an initialisation routine that does nothing by default\n\t\t \u00a0 \n\t\t\n\t\t \u00a0\u00a0\u00a0\u00a0 )\n\t\t# set \"Option Explicit\" to catch subtle errors\n\t\tset-psdebug -strict\n\t\t\n\t\t\u00a0\n\t\t# Load SMO assembly, and if we're running SQL 2008 DLLs load the SMOExtended and SQLWMIManagement libraries\n\t\t$ms='Microsoft.SqlServer'\n\t\t$v = [System.Reflection.Assembly]::LoadWithPartialName( \"$ms.SMO\")\n\t\tif ((($v.FullName.Split(','))[1].Split('='))[1].Split('.')[0] -ne '9') {\n\t\t[System.Reflection.Assembly]::LoadWithPartialName(\"$ms.SMOExtended\") | out-null\n\t\t \u00a0\u00a0 }\n\t\t[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SmoEnum') | out-null\n\t\t\n\t\t$My=\"$ms.Management.Smo\" #\n\t\t$DatabaseFilter=$TheDatabaseFilter\n\t\tif ($blacklist.count -gt 0) {$DatabaseFilter= { param($x); if ($blacklist -notcontains $x.name) {$x} }} \n\t\t# followed by the ones you don't want, listed in your blacklist\n\t\tif ($whitelist.count -gt 0) {$DatabaseFilter= { param($x); if ($whitelist -contains $x.name) {$x} }} \n\t\t# and one that just selects the files you specify in your whitelist\n\t\t\n\t\twrite-verbose \"The database filter is '$DatabaseFilter'\"\n\t\t\n\t\t$Initialisation.invoke() &lt;# just in case you have a once-off routine you wish to execute (if you are doing SMO, since the function loads SMO.) #&gt;\n\t\twrite-verbose \"Using server list $DataSources\";\n\t\t$DataSources | # our list of servers\n\t\t \u00a0 &amp; {PROCESS{$TheServerFilter.invoke($_)}}\u00a0 | # choose which servers from the list\n\t\t \u00a0\u00a0 Foreach-object { write-verbose \"Accessing $($_.ToString())\"; new-object (\"$My.Server\") $_ } | # create an SMO server object\n\t\t \u00a0\u00a0\u00a0\u00a0 Where-Object {$_.ServerType -ne $null} | # did you positively get the server?\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0 Foreach-object { write-verbose \"reached $($_.ToString())\";$_.Databases } | #for every server successfully reached \n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Foreach-object { write-verbose \"found database $($_.name)\"; $_} | #display each database \n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Where-Object {$_.IsSystemObject -ne $true} | #not the system objects\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp; {PROCESS{$DatabaseFilter.invoke($_)}}\u00a0 | # do all,avoid blacklist or do a whitelist etc\n\t\t \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp; {PROCESS{$JobToDo.invoke($_)}}\u00a0 #and do whatever you want for the database\n\t\t}<\/pre>\n<p class=\"MsoNormal\">So there you have it; a simple pipeline for serving up whatever databases you specify, into which you can inject whatever functionality you need. Your requirements might be different. Your servers could be outside the windows domain and therefore accessible only by SQL Server credentials. You may need to do slightly different things to different databases. You may need to specify a process to be done at the sever level as well as the database level. In which case, hack it about and come up with something that fits your requirements. That&#8217;s the joy of a scripting language like PowerShell..<\/p>\n<h2>In case things don&#8217;t work&#8230;<\/h2>\n<ul>\n<li>Unless you have servers called Dave, Dee, Dosy, Beaky, Mick and Tich, you&#8217;ll need to change a list like &#8230;<br \/><code>$DataSources=@('Dave','Dee','Dosy','Beaky','Mick','Tich') # server name and instance<\/code><br \/>into a list of your servers.<\/li>\n<li>If your servers are outside your domain, then you will need to change the foreach-DatabaseInServers code suitably.<\/li>\n<li>If you haven&#8217;t got the latest SQLPS module, get it from Chad Miller&#8217;s <a href=\"http:\/\/sev17.com\/2010\/07\/10\/making-a-sqlps-module\">&#8216;Making A SQLPS Module&#8217;<\/a> blog<\/li>\n<li>Make sure that you have the SMO library installed!<\/li>\n<li>in some cases I&#8217;ve just selected AdventureWorks, just for the demo. You won&#8217;t want that when you come to use it.<\/li>\n<li>Use the code in the speechbubble at the top of the page<\/li>\n<li>if you have only one item in the servers, blacklist or whitelist, still use the array syntax <code>\u00a0e.g. @('Adventureworks')<\/code><\/li>\n<\/ul>\n<\/div>\n\n\n\n<section id=\"my-first-block-block_8982e0f759d36cb08fc5b6fc068c6169\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Simple Talk is brought to you by Redgate Software<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Take control of your databases with the trusted Database DevOps solutions provider. Automate with confidence, scale securely, and unlock growth through AI.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/solutions\/overview\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Discover how Redgate can help you: Simple Talk is brought to you by Redgate Software\">Discover how Redgate can help you<\/a>\n                    <\/div>\n    <\/div>\n<\/section>","protected":false},"excerpt":{"rendered":"<p>Using PowerShell SMO (SQL Server Management Objects) to run scripts, collect reports, and automate DBA tasks across multiple databases and multiple registered servers. Covers filegroup and log-file checks, backup-date audits, source-control generation, and patterns for writing each operation once and applying it everywhere.&hellip;<\/p>\n","protected":false},"author":154613,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527],"tags":[4168,4170,4635,4336,4150],"coauthors":[6813],"class_list":["post-1370","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","tag-database","tag-database-administration","tag-powershell","tag-smo","tag-sql"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1370","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/154613"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1370"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1370\/revisions"}],"predecessor-version":[{"id":110628,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1370\/revisions\/110628"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1370"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}