{"id":219,"date":"2007-01-31T00:00:00","date_gmt":"2007-01-30T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/managing-sql-server-using-powersmo\/"},"modified":"2021-08-24T13:40:46","modified_gmt":"2021-08-24T13:40:46","slug":"managing-sql-server-using-powersmo","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/managing-sql-server-using-powersmo\/","title":{"rendered":"Managing SQL Server using PowerSMO"},"content":{"rendered":"<p>PowerSMO combines two new Microsoft technologies:<\/p>\n<ul>\n<li><b>PowerShell<\/b> &#8211; an object based scripting engine and command-line shell<\/li>\n<li><b>SMO<\/b> &#8211; SQL Server Management Objects, a .NET based object model, which can be used in the management of SQL Server.<\/li>\n<\/ul>\n<p>In this article we&#8217;ll be looking firstly at the basics of using PowerShell, and then at how to combine SMO with PowerShell to make <b>PowerSMO<\/b>, and finally at how to use PowerSMO to manage SQL Server.<\/p>\n<p>You will find that PowerSMO is in the spirit of scripting languages such as VBA or Perl. Ultimately, I think you&#8217;ll find PowerSMO easier to use and more powerful, but like any new technology it has a learning curve. The intention of this article is to help push you over the first hump of that curve.<\/p>\n<p>All of the code examples I include here are available in the zip archive that comes with this article (see the CODE DOWNLOAD link in the box to the right of the article title), as well as on my own <a href=\"http:\/\/www.pluralsight.com\/dan\/samples\/ST-PowerSMO.zip\">pluralsight.com<\/a> web site.<\/p>\n<h2>Getting started<\/h2>\n<p>Besides the zip archive, you will need two other things in order to get started using PowerSMO, both of which are free downloads from Microsoft:<\/p>\n<ul>\n<li><b>The SMO assemblies<\/b> &#8211; these form part of the SQL Server 2005 Features Pack. Go to <a href=\"http:\/\/www.msdn.com\/sql\">http:\/\/www.msdn.com\/sql<\/a> and click on &#8220;Downloads&#8221; to find the Features Pack. The &#8220;SQL Server 2005 Management Objects Collections&#8221; are about half-way down the page. Download and install them.<\/li>\n<li><b>PowerShell<\/b> &#8211; this is available in a number of languages\/platforms. Search for Knowledge Base article &#8220;KB926139&#8221; on MSDN at <a href=\"http:\/\/support.microsoft.com\/?kbid=926139\">http:\/\/support.microsoft.com\/?kbid=926139<\/a> to find a list of the various versions of PowerShell. Download and install the one appropriate for your environment.<\/li>\n<\/ul>\n<h2>A brief tour of PowerShell<\/h2>\n<p>The PowerShell installation will add a &#8220;Windows PowerShell 1.0&#8221; entry to the &#8220;All Programs&#8221; part of your start menu. It contains a &#8220;Windows PowerShell&#8221; item you can use to start PowerShell, as shown in Figure 1:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/342-Sullivan001.gif\" alt=\"342-Sullivan001.gif\" \/><br \/>\n <i>Figure 1:\u00a0PowerShell Start Menu<\/i><i><\/i><\/p>\n<p>Let&#8217;s start with some PowerShell basics. Starting PowerShell produces a command shell window, which looks a bit like the old Windows\/DOS command shell. It will even respond to some of the old DOS commands you know and love, such as <b><span class=\"CodeInText\">dir<\/span><\/b> and <b><span class=\"CodeInText\">echo<\/span><\/b>, as shown in Figure 2:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/342-Sullivan002.gif\" alt=\"342-Sullivan002.gif\" \/><br \/>\n <em>Figure 2: Command Shell<\/em><\/p>\n<p>One of the things that make PowerShell different from the Windows command shell is that it is based on <b>objects<\/b>, not text. The <b><span class=\"CodeInText\">echo<\/span><\/b> example happened to use text objects so this difference was not apparent. However, PowerShell is capable of using any object that can be created by the .NET framework and it is this ability that will later allow us to integrate SMO into PowerShell to make PowerSMO.<\/p>\n<h3>Using strings, variables and objects<\/h3>\n<p>Figure 3 shows PowerShell creating an instance of the .NET <b><span class=\"CodeInText\">Random<\/span><\/b> class, and then using it to generate some numbers. I&#8217;ll be using simple text grabs going forward rather than full screen shots of the PowerShell.<\/p>\n<pre><\/pre>\n<p>PS D:\\SimpleTalkSmo&gt; $rand = New-Object &#8220;Random&#8221; PS D:\\SimpleTalkSmo&gt; $rand.Next() 839188457 PS D:\\SimpleTalkSmo&gt; $rand.Next() 693128074 PS D:\\SimpleTalkSmo&gt; <em>Figure3: Random Object<\/em> <b><span class=\"CodeInText\">New-Object<\/span><\/b> is what PowerShell calls a <b>cmdlet<\/b>; it is basically a command, like <b><span class=\"CodeInText\">dir<\/span><\/b> is in the Windows command shell. The <b><span class=\"CodeInText\">New-Object<\/span><\/b> command creates an instance of a .NET class, in this case, the <b>Random<\/b> class. Variables in PowerShell always have the <strong>$<\/strong> character as a prefix. In Figure 3, <strong><span class=\"CodeInText\">$rand<\/span><\/strong> is a variable that, in this case, holds an instance of a Random object. PowerShell can call any method of an object using a simple dotted syntax as is shown in <strong><span class=\"CodeInText\">$rand.Next()<\/span><\/strong>. Each time you call <strong><span class=\"CodeInText\">Next()<\/span><\/strong> on a <b><span class=\"CodeInText\">Random<\/span><\/b> object it returns a different number. Whenever an object is returned, it is typically assigned to a variable or its value is printed on the screen. Figure 3 showed the object produced by <strong><span class=\"CodeInText\">New-Object<\/span><\/strong> being assigned to the variable <strong><span class=\"CodeInText\">$rand<\/span><\/strong>. You can also assign literal numbers or text to PowerShell variables:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; $n = 8<br \/>\n PS C:\\SimpleTalkSmo&gt; $s = &#8220;Dan&#8221;<br \/>\n PS C:\\SimpleTalkSmo&gt; $n<br \/>\n 8<br \/>\n PS C:\\SimpleTalkSmo&gt; $s<br \/>\n Dan<br \/>\n PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure 4:<\/i><i> Numeric and Text Values<\/i><\/p>\n<p>PowerShell supports all of the arithmetic and string operations that you would expect. Figure 5 shows some of these:<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; $n = (3+4)\/2 PS C:\\SimpleTalkSmo&gt; $n 3.5 PS C:\\SimpleTalkSmo&gt; $s = $s + &#8221; Sullivan&#8221; PS C:\\SimpleTalkSmo&gt; $s Dan Sullivan PS C:\\SimpleTalkSmo&gt;\u00a0 <i>Figure 5: Simple Operations<\/i> PowerShell also lets you ignore types if you wish. Figure 6 shows string being &#8220;added&#8221; to a number, but still producing a numeric result:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; $n = $n +&#8221;8&#8243;<br \/>\n PS C:\\SimpleTalkSmo&gt; $n<br \/>\n 11.5<br \/>\n PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure 6: Type Can Be Ignored<\/i><\/p>\n<p>You can see in Figure 7 that PowerShell is based on objects, not strings. Here we see the type of <strong><span class=\"CodeInText\">$n<\/span><\/strong> and <strong><span class=\"CodeInText\">$s<\/span><\/strong> being returned:<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; $n.GetType().Name Double PS C:\\SimpleTalkSmo&gt; $s.GetType().Name String PS C:\\SimpleTalkSmo&gt; <i>Figure 7: <\/i><i>Objects<\/i> Figure 7 also demonstrates another consequence of the fact that PowerShell works with objects, which is that we can access that object&#8217;s methods and properties. In this example we used the <span class=\"CodeInText\">GetType()<\/span> method to find the type of <span class=\"CodeInText\">$n<\/span>, then the <span class=\"CodeInText\">Name<\/span> property of its type to find its name. Back in Figure 3 we made an instance of the <b><span class=\"CodeInText\">Random<\/span><\/b> class from .NET. You might not know the documentation for the <b><span class=\"CodeInText\">Random<\/span><\/b> class so PowerShell includes a cmdlet called <span class=\"CodeInText\">get-member<\/span> that allows you to investigate what sort of features an object has. Figure 8 shows the use of <span class=\"CodeInText\">get-member<\/span> to find out what you can do with the <span class=\"CodeInText\">$rand<\/span> object.<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; $rand = New-Object &#8220;Random&#8221;<br \/>\n PS C:\\SimpleTalkSmo&gt; get-member -inputObject $rand<br \/>\n \u00a0\u00a0 TypeName: System.Random<\/p>\n<p class=\"Code\">Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 MemberType Definition<br \/>\n &#8212;-\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#8212;&#8212;&#8212;- &#8212;&#8212;&#8212;-<br \/>\n Equals\u00a0\u00a0\u00a0\u00a0\u00a0 Method\u00a0\u00a0\u00a0\u00a0 System.Boolean Equals(Object obj)<br \/>\n GetHashCode Method\u00a0\u00a0\u00a0\u00a0 System.Int32 GetHashCode()<br \/>\n GetType\u00a0\u00a0\u00a0\u00a0 Method\u00a0\u00a0\u00a0\u00a0 System.Type GetType()<br \/>\n Next\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Method\u00a0\u00a0\u00a0\u00a0 System.Int32 Next(), System.Int32 Ne<br \/>\n NextBytes \u00a0\u00a0Method\u00a0\u00a0\u00a0\u00a0 System.Void NextBytes(Byte[] buffer)<br \/>\n NextDouble\u00a0 Method\u00a0\u00a0\u00a0\u00a0 System.Double NextDouble()<br \/>\n ToString\u00a0\u00a0\u00a0 Method\u00a0\u00a0\u00a0\u00a0 System.String ToString()<\/p>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure 8: <\/i><i>get-member <\/i><\/p>\n<p>The text in Figure 8 is chopped off on the right a little bit but you get the idea. You can see the methods supported by <span class=\"CodeInText\">$rand<\/span>, including the <span class=\"CodeInText\">Next<\/span> method we used in Figure 3. Figure 9 shows the results produced when <span class=\"CodeInText\">get-member<\/span> is used with a string variable.<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; $s = &#8220;Dan&#8221; PS C:\\SimpleTalkSmo&gt; get-member -inputObject $s \u00a0\u00a0 TypeName: System.String Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0MemberType\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Definition &#8212;-\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8212;&#8212;&#8212;-\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8212;&#8212;&#8212;- Clone\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Method\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0System.Object Clone() CompareTo\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Method\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0System.Int32 CompareTo(Object value), Contains\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Method\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0System.Boolean Contains(String value) &#8230; Length\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Property\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0System.Int32 Length {get;} PS C:\\SimpleTalkSmo&gt; <i>Figure 9: <\/i><i>String Methods and Properties<\/i> In Figure 9, we see that a string has both methods and properties. Note that <span class=\"CodeInText\">get-member<\/span> applied to a string produces a lot more results than are shown. A string has a <span class=\"CodeInText\">Length<\/span> property which can be used to find out how long it is, as is shown in Figure 10:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; $s.Length<br \/>\n 12<br \/>\n PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure <\/i><i>10:<\/i><i> Using a Property<\/i><\/p>\n<p>It&#8217;s fairly easy to find out what you can do with a variable by using get-member, but what if you need to know how to use one of the cmdlets in power shell? The <span class=\"CodeInText\">get-help<\/span> cmdlet in PowerShell is used to get the documentation of a cmdlet, as is shown in Figure 11:<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; get-help New-Object NAME \u00a0\u00a0\u00a0 New-Object SYNOPSIS \u00a0\u00a0\u00a0 Creates an instance of a .Net or COM object. SYNTAX \u00a0\u00a0\u00a0 New-Object [-typeName] &lt;string&gt; [[-argumentList] &lt;Object[]&gt;] &#8230; REMARKS \u00a0\u00a0\u00a0 For more information, type: &#8220;get-help New-Object -detailed&#8221;. \u00a0\u00a0\u00a0 For technical information, type: &#8220;get-help New-Object -full&#8221;. PS C:\\SimpleTalkSmo&gt; <i>Figure <\/i><i>11:<\/i><i> get-help<\/i> Figure 11 does not show all of the documentation produced, a big chunk in the middle has been replaced with an ellipsis to reduce clutter. You can use you wildcard character, <span class=\"CodeInText\">*<\/span>, if you are not sure how to spell the cmdlet you want documentation for. For example you can use &#8220;<span class=\"CodeInText\">get-help *name*<\/span> to find all the help topics available that include the word &#8220;name&#8221;.<\/p>\n<h3>Pipelines<\/h3>\n<p>Pipelines are supported by PowerShell. Pipelines are a common feature of shells and allow the output of one segment to be used as the input in the next segment in the pipeline. Segments of a pipeline are connected together by the <span class=\"CodeInText\">|<\/span> character. A segment of the pipeline is often a script block. A script block is a PowerShell script wrapped in curly braces, prefaced by either a <span class=\"CodeInText\">%<\/span> or <span class=\"CodeInText\">?<\/span>. There is a special variable, <span class=\"CodeInText\">$_<\/span>, that is used to represent the output of the previous segment of the pipeline. Figure 12 shows an example of a simple pipeline:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; $s = &#8220;Dan&#8221;<br \/>\n PS C:\\SimpleTalkSmo&gt; $s | %{$_.Length}<br \/>\n 3<br \/>\n PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure <\/i><i>12: <\/i><i>Simple Pipeline<\/i><\/p>\n<p>In Figure 12, <span class=\"CodeInText\">$s<\/span> is passed into a script block prefaced by the <span class=\"CodeInText\">%<\/span> character. A script block prefaced by the\u00a0<span class=\"CodeInText\">%<\/span> character is executed as though it were typed in on the command line without a pipeline. When a script block has no following pipeline segment its output is sent to the screen.<\/p>\n<p>The <span class=\"CodeInText\">$_<\/span> represents the input of the previous segment, in this case the <span class=\"CodeInText\">$s<\/span> variable. The length of the input object is accessed through its <span class=\"CodeInText\">Length<\/span> property. There is no following pipeline segment so the result, the number of characters in &#8220;Dan&#8221;, is output to the screen.<\/p>\n<p>A script block can also be used to test to see whether or not further processing should be applied to the object that is passed in to it. A script block prefixed with the <span class=\"CodeInText\">?<\/span> character executes the script inside of it and interprets the result as a Boolean. If the result is <span class=\"CodeInText\">true<\/span> the object is passed onto next segment of the pipeline, otherwise it is thrown away. Figure 13 shows examples of pipelines that test an input object:<\/p>\n<pre><\/pre>\n<p><span class=\"CodeInText\">PS C:\\SimpleTalkSmo&gt; $s = &#8220;Daniel&#8221; PS C:\\SimpleTalkSmo&gt; $s | ?{$_.Length -gt 3} | %{$_} Daniel PS C:\\SimpleTalkSmo&gt; $s = &#8220;Dan&#8221; PS C:\\SimpleTalkSmo&gt; $s | ?{$_.Length -gt 3} | %{$_} PS C:\\SimpleTalkSmo&gt;<\/span> <i>Figure <\/i><i>13:<\/i><i> Testing Segment Input<\/i> Figure 13 starts off by setting the variable type <span class=\"CodeInText\">$s<\/span> to the text &#8220;Daniel&#8221;. Next it passes <span class=\"CodeInText\">$s<\/span> into a script block that is prefaced by a <span class=\"CodeInText\">?<\/span> character. This script block compares the value of the <span class=\"CodeInText\">Length<\/span> property of the object that was input to the number three. It does this comparison using a &#8220;greater than&#8221; test. PowerShell supports all the kinds of comparisons you&#8217;d expect but they are all specified using a syntax that begins with a dash followed by a few letters. The comparison is true so <span class=\"CodeInText\">$s<\/span> is passed into the code block in the next segment of the pipeline. The next segment of the pipeline evaluates the value of the object, that it executes the simple script <span class=\"CodeInText\">$_<\/span>. The result, &#8220;Daniel&#8221;, is typed out on the screen. The remainder of the example repeats the test but this time with the <span class=\"CodeInText\">$s<\/span> variable is set to &#8220;Dan&#8221;. In this case nothing is typed out on the screen because the length of the variable <span class=\"CodeInText\">$s<\/span> is not greater than three. We have barely scratched the surface of the capabilities of PowerShell, but it should be enough to get you going. The resources for learning about PowerShell are at the Microsoft Scripting Center. Browse to <a href=\"https:\/\/technet.microsoft.com\/en-us\/scriptcenter\/bb410849.aspx\">https:\/\/technet.microsoft.com\/en-us\/scriptcenter\/bb410849.aspx<\/a>\u00a0and look for the link further down the page that says &#8220;Scripting for&#8230;&#8221;, &#8220;Windows PowerShell&#8221;.<\/p>\n<h3>PowerShell and WMI<\/h3>\n<p>There is one more feature of PowerShell that we need to look at before digging into PowerSMO; and that&#8217;s its support of WMI, <b>Windows<\/b> <b>Management<\/b> <b>Instrumentation<\/b>. WMI provides a standard way to access the administrative information about a Windows system, for example what user accounts are in a system&#8230;and so on. Later we are going to use this to automate the creation of SQL logins for accounts in a particular group of Windows system users. Prior to PowerShell, WMI was typically used with a Windows Scripting Host language such VBA. The PowerShell cmdlet <span class=\"CodeInText\">get-WMIObject<\/span> removes most of the detail work you would have to do a WMI query with Windows Scripting Host. Figure 14 is a PowerShell script that uses the <span class=\"CodeInText\">get-WMIObject<\/span> cmdlet to get all the instances of the <span class=\"CodeInText\">Win32_Group<\/span> class in the PARSEC5 Common Information Model or CIM. So, in effect, we are listing all the user groups on my computer (called PARSEC5):<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; $groups = Get-WmiObject -computerName<br \/>\n PARSEC5 Win32_Group<br \/>\n PS C:\\SimpleTalkSmo&gt; $groups | %{$_.Name}<br \/>\n Administrators<br \/>\n Backup Operators<br \/>\n Guests<br \/>\n Network Configuration Operators<br \/>\n Power Users<br \/>\n Users<br \/>\n PowerSQL<br \/>\n SQLServer2005MSFTEUser$PARSEC5$MSSQLSERVER<br \/>\n SQLServer2005MSSQLServerADHelperUser$PARSEC5<br \/>\n SQLServer2005MSSQLUser$PARSEC5$MSSQLSERVER<br \/>\n SQLServer2005NotificationServicesUser$PARSEC5<br \/>\n SQLServer2005SQLAgentUser$PARSEC5$MSSQLSERVER<br \/>\n SQLServer2005SQLBrowserUser$PARSEC5<\/p>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure <\/i><i>14:<\/i><i> Finding User Groups with WMI<\/i><\/p>\n<p>The Common Information Model is the industry-standard\u00a0model of computer systems, on which WMI is based. You might recognize some of the SQL Server groups on this list and groups such as &#8220;administrators&#8221; or &#8220;users&#8221;. Note that there is a group named &#8220;PowerSQL&#8221;. We will be using that group later.<\/p>\n<h2>SMO + PowerShell = PowerSMO<\/h2>\n<p>Now that we have finished our introduction to PowerShell and a very brief introduction to WMI we can move on to using PowerSMO.<\/p>\n<h3>Configuring PowerSMO<\/h3>\n<p>In order to use PowerSMO you will need the <b>InitPowerSMO.ps1<\/b> script file which is in the .zip archive mentioned at the beginning of this article.<\/p>\n<p><strong>NOTE<\/strong>:<br \/>\n <i>This script uses a number of advanced features of PowerShell that we will not be discussing in this article, but the beginning of this script loads the SMO assemblies into PowerShell<\/i>.<\/p>\n<p>The default installation of PowerShell is locked down fairly tightly. You will have to change the configuration of PowerShell to allow the <strong>InitPowerSMO.ps1<\/strong> script to be run. You can do this using the <span class=\"CodeInText\">set-ExecutionPolicy<\/span> cmdlet as shown in Figure 15:<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; Set-ExecutionPolicy RemoteSigned PS C:\\SimpleTalkSmo&gt; <i>Figure <\/i><i>15:<\/i><i> Execution Policy<\/i> Next thing you need to do is initialize PowerSMO by running the\u00a0<strong>InitPowerSMO.ps1<\/strong> script. This is shown in Figure 16:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; . D:\\InitPowerSMO.ps1<br \/>\n PowerSMO! Copyright 2006 Danal Technology Inc<br \/>\n www.pluralsight.com<br \/>\n For Tutorial Purposes Only<br \/>\n PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure <\/i><i>16:<\/i><i> Initializing PowerSMO<\/i><\/p>\n<p>The <strong>InitPowerSMO.ps1<\/strong> script is run by typing its full path into PowerShell followed by a carriage return. Note carefully that the path is preceded by a period, &#8220;.&#8221;, and a space; this is required. Depending on the speed of your system it might take 20 or 30 seconds for this script to run. Besides loading the SMO assemblies it is also building helper functions that can be used to access SMO.<\/p>\n<h3>Using the ManagedComputer Class<\/h3>\n<p>In the SMO object model, one of the most important classes is the <a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/microsoft.sqlserver.management.smo.wmi.managedcomputer.aspx\">ManagedComputer<\/a> class. It is &#8220;<i>a top level class that represents Microsoft SQL Server services and network settings available through the WMI Provider<\/i>&#8220;.<\/p>\n<p>PowerSMO has a <span class=\"CodeInText\">get-SMO_ManagedComputer<\/span> cmdlet to give you access to the managed computer. Figure 17 shows use of <span class=\"CodeInText\">get-SMO_ManagedComputer<\/span> to get to the managed computer for the PARSEC5 the system and assign it to <span class=\"CodeInText\">$mc<\/span>.<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; $mc = get-SMO_ManagedComputer &#8220;PARSEC5&#8221; PS C:\\SimpleTalkSmo&gt; <i>Figure <\/i><i>17:<\/i><i> Managed Computer<\/i> Now, via <b><span class=\"CodeInText\">$mc<\/span><\/b>, we have access to access to all of the methods and properties supported by <b><span class=\"CodeInText\">ManagedComputer<\/span><\/b>. But how do you know which properties and methods are supported and what they do? You could have used <span class=\"CodeInText\">get-member<\/span> to find the properties and methods but that wouldn&#8217;t have told you what they did. All of this information, however, is available in the BOL. Figure 18 shows the some of the BOL documentation for the <b><span class=\"CodeInText\">ManagedComputer<\/span><\/b> class: <img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/342-Sullivan18.gif\" alt=\"342-Sullivan18.gif\" \/> <i>Figure <\/i><i>18:<\/i><i> BOL SMO Documentation<\/i> In Figure 18, we could drill into services or service instances to find out more about these properties. What this means is the documentation for PowerSMO is the same as it is for SMO, it is the BOL. PowerSMO makes helper functions, like <span class=\"CodeInText\">get-SMO_ManagedComputer<\/span> for most of the SMO classes. You can see the list of these helper functions the <span class=\"CodeInText\">dir<\/span> command as is shown in Figure 19:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; dir Function:*SMO_*<\/p>\n<p> CommandType\u00a0\u00a0\u00a0\u00a0 Name<br \/>\n &#8212;&#8212;&#8212;&#8211;\u00a0\u00a0\u00a0\u00a0 &#8212;-<br \/>\n Function\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-SMO_ExecutionStatus<br \/>\n Function\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-SMO_SmoApplication<br \/>\n &#8230;<\/p>\n<p><i>Figure <\/i><i>19:<\/i><i> SMO Functions<\/i><\/p>\n<p>The listing shown in Figure 19 is truncated to save space; the actual list you will see when you type this on you system is quite a bit longer.<\/p>\n<h4>Listing all SQL Server instances on a computer<\/h4>\n<p>Now we can start using PowerSMO to perform some SQL Server management tasks. The first thing we will do is look for the instances of SQL Server that are on PARSEC5.<\/p>\n<p>Server instances are in the <span class=\"CodeInText\">ServerInstances<\/span> property of the <span class=\"CodeInText\">ManagedComputer<\/span>, an instance of which we have saved in our <b>$mc<\/b> variable. Figure 20 shows the server instances on the PARSEC5 computer. Remember that you can always use something like <span class=\"CodeInText\">get-member -inputObject $mc<\/span> to find out what properties and methods a variable has:<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; $mc.ServerInstances ServerProtocols : {Np, Sm, Tcp, Via} Parent\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer Urn\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : ManagedComputer[@Name=&#8217;PARSEC5&#8242;]\/ServerInstance [@Name=&#8217;MSSQLSERVER&#8217;] Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : MSSQLSERVER Properties\u00a0\u00a0\u00a0\u00a0\u00a0 : {} UserData\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : State\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : Existing ServerProtocols : {Np, Sm, Tcp, Via} Parent\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer Urn\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : ManagedComputer[@Name=&#8217;PARSEC5&#8242;]\/ServerInstance [@Name=&#8217;TEST2&#8242;] Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : TEST2 Properties\u00a0\u00a0\u00a0\u00a0\u00a0 : {} UserData\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : State\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : Existing PS C:\\SimpleTalkSmo&gt; <i>Figure <\/i><i>20:<\/i><i> Server Instances<\/i> Figure 20 shows that there are two SQL Server instances on PARSEC5; one is named &#8220;MSSQLSERVER&#8221; the other is named &#8220;TEST2&#8221;. We can make use of a pipeline to filter out the information we&#8217;re not interested in. Figure 21 shows the server instances being piped to a script block that extracts the name of the server instance:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; $mc.ServerInstances | %{$_.Name}<br \/>\n MSSQLSERVER<br \/>\n TEST2<br \/>\n PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure <\/i><i>21:<\/i><i> Instance Names Only<\/i><\/p>\n<p>When a segment of a pipeline produces more than one object, as is the case here, the objects are passed one at a time to the script block that follows the segment. So the <span class=\"CodeInText\">$_.Named<\/span> in the last segment in Figure 21 is executed twice, once for each of the instances produced by the first segment.<\/p>\n<h4>Starting and Stopping SQL Server<\/h4>\n<p>Let&#8217;s first look to see if the MSSQLSERVER server instance is running. To do that we need to access the service that is running this instance. The <span class=\"CodeInText\">Services<\/span> property of the managed computer is an array of all of the services on this managed computer. We index into this array just by using the name of the service we want and look at its <span class=\"CodeInText\">ServiceStat<\/span>e property to see if it is running. Figure 22 shows how to check the state of a service:<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; $mc.Services[&#8216;MSSQLSERVER&#8217;].ServiceState Stopped PS C:\\SimpleTalkSmo&gt; <i>Figure <\/i><i>22:<\/i><i> Checking ServiceState<\/i> In Figure 22, <span class=\"CodeInText\">$mc.Services[&#8216;MSSQLSERVER&#8217;]<\/span> is accessing the service that is running MSSQLSERVER. By checking the <span class=\"CodeInText\">ServiceState<\/span> we can see that this instance is currently stopped. The next thing we can do with PowerSMO is start our instance of SQL Server. We can do this by typing <span class=\"CodeInText\">$mc.Services[&#8216;MSSQLSERVER&#8217;].Start()<\/span> into PowerShell. Likewise we can stop SQL Server by typing <span class=\"CodeInText\">$mc.Services[&#8216;MSSQLSERVER&#8217;].Stop()<\/span>. It&#8217;s worth noting at this point that a <span class=\"CodeInText\">ManagedComputer<\/span> object has all of the capabilities that the SQL Server Configuration Manager application has.<\/p>\n<h3>Using the Server Class<\/h3>\n<p>The SMO Server class is used to represent all of the objects in an instance of SQL Server, for example all of its databases. In PowerSMO we use <span class=\"CodeInText\">get-SMO_Server<\/span> to get an instance of the SMO server class as shown in Figure 23:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; $server = get-SMO_Server PARSEC5<br \/>\n PS C:\\SimpleTalkSmo&gt; $server.Databases | %{$_.Name}<br \/>\n DBDemoSample<br \/>\n master<br \/>\n model<br \/>\n msdb<br \/>\n MyDB<br \/>\n sqlcourse<br \/>\n SQLLabs<br \/>\n tempdb<br \/>\n WFPersistance<\/p>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure <\/i><i>23:<\/i><i> Databases on Server<\/i><\/p>\n<p>In Figure 23, <span class=\"CodeInText\">get-SMO_Server<\/span> creates a SMO <span class=\"CodeInText\">Server<\/span> object for the default instance of SQL Server on PARSEC5, just as it is described in the BOL. The <span class=\"CodeInText\">Databases<\/span> property of the <span class=\"CodeInText\">$server<\/span> is an array of all of the databases in the server.<\/p>\n<h4>Creating logins<\/h4>\n<p>Now we will write a script that will create logins for all of the users in the Windows PowerSQL group. We will use WMI find all the user accounts on PARSEC5 that are a members of the PowerSQL group, and then create a login for each of them. Let&#8217;s start by creating a login for a single Windows user to see how that works. I know that there is a user named &#8220;Ralph&#8221; on PARSEC5 so let&#8217;s create a login for him, as shown in Figure 24:<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; $login = get-SMO_Login $server &#8220;PARSEC5\\Ralph&#8221; PS C:\\SimpleTalkSmo&gt; $login.LoginType = &#8220;WindowsUser&#8221; PS C:\\SimpleTalkSmo&gt; $login.Create() PS C:\\SimpleTalkSmo&gt; <i>Figure <\/i><i>24: <\/i><i>Creating Logins<\/i> We start off by getting a reference to the PARSEC5 server. Then we use the <span class=\"CodeInText\">get-SMO_Login<\/span> cmdlet to create a <span class=\"CodeInText\">login <\/span>object. We pass a reference to the server and the domain\\name of the Windows user we want to create a login for, similar to what we would do using T-SQL to create a Windows login. Note that <span class=\"CodeInText\">$server<\/span> and domain\\name are separated only by a whitespace and not by a comma. Next we set the login type to be a Windows user. Lastly the <span class=\"CodeInText\">Create()<\/span> command actually adds the login to SQL Server. Now the default server on PARSEC5 has a <span class=\"ImportantWords\">Ralph<\/span> login. We could have also set Ralph&#8217;s default database and such if we had wanted to.<\/p>\n<h4>Automating login creation<\/h4>\n<p>We are at the point where we can automate creating a login for every user account in the PowerSQL group. In Figure 14 we used <b><span class=\"CodeInText\">get-WMIObject<\/span><\/b> to list all the groups on PARSEC5. We can also list all the users on PARSEC5. WMI keeps track of <b><span class=\"CodeInText\">UserAccount<\/span><\/b> objects for this purpose. Figure 25 shows the users on PARSEC5. You can see the Ralph user we used previous to demonstrate how to create a login for SQL Server.:<\/p>\n<pre><\/pre>\n<p class=\"MsoNormal\">PS C:\\SimpleTalkSmo&gt; Get-WmiObject &#8220;Win32_UserAccount&#8221; | %{$_.name}<br \/>\n Administrator<br \/>\n ASPNET<br \/>\n Dan<br \/>\n dawn<br \/>\n don<br \/>\n Gort<br \/>\n Guest<br \/>\n HelpAssistant<br \/>\n IUSR_PARSEC5<br \/>\n IWAM_PARSEC5<br \/>\n Klatuu<br \/>\n MiniDan<br \/>\n Ralph<br \/>\n SMTP_USER<br \/>\n SomeUser<br \/>\n SQLAClient<br \/>\n SqlAdmin<br \/>\n PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure <\/i><i>25:<\/i><i> Listing Users<\/i><\/p>\n<p>WMI keeps track of the relationships between users and groups with <span class=\"CodeInText\">Win32_GroupUser<\/span> objects. There is a group user object for each user, group pair. This means if Joe belongs to two different groups there will be two <b><span class=\"CodeInText\">Win32_GroupUser<\/span><\/b> objects to represent this.<\/p>\n<p>Each <b><span class=\"CodeInText\">Win32_GroupUser<\/span><\/b> contains a <b><span class=\"CodeInText\">GroupComponent<\/span><\/b> property that identifies a Windows group and a <b><span class=\"CodeInText\">PartComponent<\/span><\/b> property that identifies a user account. A typical <b><span class=\"CodeInText\">GroupCompenent<\/span><\/b> looks as follows:<\/p>\n<pre><\/pre>\n<p>\\\\PARSEC5\\root\\cimv2:Win32_Group.Domain=&#8221;PARSEC5&#8243;,Name=&#8221;PowerSQL&#8221; and a <b><span class=\"CodeInText\">PartComponent<\/span><\/b> looks like this:<\/p>\n<pre><\/pre>\n<p class=\"Code\">\\\\PARSEC5\\root\\cimv2:Win32_UserAccount.Domain=&#8221;PARSEC5&#8243;,Name=&#8221;Gort&#8221;<\/p>\n<p>The only part of these properties that is of interest to us is <span class=\"CodeInText\">id<\/span> in <span class=\"CodeInText\">Name=&#8221;id&#8221;<\/span>. Since we are going to be looking for this over and over, we will create a function that retrieves it. Figure 26 shows a PowerShell function that can crack out the name value. Type this function into the command line to create it:<\/p>\n<pre><\/pre>\n<p>function get-WMIName($component) { $component.SubString(6 + $component.LastIndexOf(&#8220;Name=&#8221;&#8221;&#8221;)).TrimEnd(&#8220;&#8221;&#8221;&#8221;) } <i>Figure <\/i><i>26:<\/i><i> Name Cracker Function<\/i> The <span class=\"CodeInText\">get-WMIName<\/span> function in Figure 26 names the input argument <span class=\"CodeInText\">$component<\/span>. It looks for the last occurrence of <span class=\"CodeInText\">Name=&#8221;<\/span>, extracts the string that comes after that, and then trims of the trailing quote character. We can test this function against an actual string, as is shown in Figure 27:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; $s = &#8220;\\\\PARSEC5\\root\\cimv2:Win32_Group.<br \/>\n Domain=&#8221;&#8221;PARSEC5&#8243;&#8221;,Name=&#8221;&#8221;PowerSQL&#8221;&#8221;&#8221;<br \/>\n PS C:\\SimpleTalkSmo&gt; get-WMIName($s)<br \/>\n PowerSQL<br \/>\n PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure<\/i><i>27:<\/i><i> Cracking Name<\/i><\/p>\n<p>Now we can use the <span class=\"CodeInText\">get-WMIName<\/span> function to build a test to find the users in the PowerSQL group as shown in Figure 28:<\/p>\n<pre><\/pre>\n<p>PS C:\\SimpleTalkSmo&gt; Get-WmiObject -computerName PARSEC5 Win32_GroupUser | ?{(get-WMIName($_.GroupComponent)) -eq &#8220;PowerSQL&#8221;} | %{get-WMIName($_.PartComponent)} Klatuu Gort PS C:&gt; <i>Figure <\/i><i>28:<\/i><i> Finding Users in PowerSQL<\/i> The script in Figure 28 uses <span class=\"CodeInText\">get-WMIObject<\/span> to pipe all of the <b><span class=\"CodeInText\">Win32_GroupUser<\/span><\/b> objects into a script block that tests to see if it <span class=\"CodeInText\">GroupComponent<\/span> is the PowerSQL group. If the <b><span class=\"CodeInText\">Win32_GroupUser<\/span><\/b> passes this test it is passed into the last script block that extracts the users name from the <span class=\"CodeInText\">PartComponent<\/span>. Here we see that both <span class=\"ImportantWords\">Gort<\/span> and <span class=\"ImportantWords\">Klatuu<\/span> are members of the PowerSQL users group. Extra points if you know the movie where they saved the Earth :). The easiest way to add logins for the users we found is to add one more segment to the pipeline shown in Figure 28 that will process the names, as shown in Figure 29:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt; Get-WmiObject Win32_GroupUser | <br \/>\n ?{(get-WMIName($_.GroupComponent)) -eq &#8220;PowerSQL&#8221;} | <br \/>\n %{get-WMIName($_.PartComponent)} | <br \/>\n %{$login = SMO_Login $server (&#8220;PARSEC5\\&#8221;+$_); $login.LoginType=<br \/>\n &#8220;WindowsUser&#8221;; $login.Create()}<\/p>\n<p class=\"Code\">PS C:\\SimpleTalkSmo&gt;<\/p>\n<p><i>Figure <\/i><i>29:<\/i><i> Adding Logins<\/i><\/p>\n<p>The script block added to the pipeline in Figure 29 prefixes each user name passed into it with &#8220;PARSEC5\\&#8221;, the domain for these users, and uses that name to create a login object. Then it makes the login a Windows login and creates it. Note that within a script block script expressions are separated by a semi-colon, &#8220;;&#8221;.<\/p>\n<p>Just to make sure things are working as we expect we can start SSMS (SQL Server Management Studio) and use it to list the logins on PARSEC5. Figure 30 shows that SSMS sees that <b>Gort<\/b> and <b>Klatuu<\/b> have been added as logins to SQL Server:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/342-Sullivan004.gif\" alt=\"342-Sullivan004.gif\" \/><\/p>\n<p><i>Figure <\/i><i>30:<\/i><i> SSMS<\/i><\/p>\n<h2>Summary<\/h2>\n<p>We started by installing the SMO assemblies and PowerShell. Then we initialized PowerSMO by running the <strong>InitPowerSMO.ps1<\/strong> script in PowerShell. We examined some of the features of PowerShell and its support of WMI. Then we looked at PowerSMO, which is in effect a set of helper functions that create SMO objects, which in turn can be used to manage SQL Server. We found that we can combine WMI with PowerSMO to help us manage SQL Server.<\/p>\n<p>This has been a really short introduction into PowerShell, WMI, and PowerSMO. I hope it is enough to entice you into using them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most DBAs don&#8217;t have time to perform routine processes manually. Everything has to be scripted, automated and scheduled. Here, Dan Sullivan describes how a combination of PowerShell and SMO (&#8220;PowerSMO&#8221;) can provide a familiar but powerful command line tool for managing common SQL Server tasks.&hellip;<\/p>\n","protected":false},"author":221818,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527],"tags":[4178,4639,4170,4638,4635,4637,4336,4150,4151,4636],"coauthors":[29488],"class_list":["post-219","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","tag-bi","tag-creating-logins","tag-database-administration","tag-managing-sql-server","tag-powershell","tag-powersmo","tag-smo","tag-sql","tag-sql-server","tag-sql-server-management-objects"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/219","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\/221818"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=219"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/219\/revisions"}],"predecessor-version":[{"id":77790,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/219\/revisions\/77790"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=219"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=219"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=219"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=219"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}