{"id":235,"date":"2007-03-12T00:00:00","date_gmt":"2007-03-12T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/powersmo-at-work-part-2\/"},"modified":"2021-08-24T13:40:45","modified_gmt":"2021-08-24T13:40:45","slug":"powersmo-at-work-part-2","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/powersmo-at-work-part-2\/","title":{"rendered":"PowerSMO at Work Part 2"},"content":{"rendered":"<h2>Controlling Logins, Activity Monitoring and Standalone Scripts<\/h2>\n<p>My previous article, <a href=\"http:\/\/www.simple-talk.com\/sql\/database-administration\/powersmo-at-work-part-i-dba-scripts-and-functions\/\">PowerSMO at Work I<\/a>, demonstrated how to create and deploy secure, signed DBA scripts and then showed how to use PowerSMO functions to manage the extended properties of SQL Server objects.<\/p>\n<p>In this article we will look how to select the identity that is used to logon to SQL Server and how to build an activity monitor. We will also look at how to create standalone PowerSMO scripts, <i>i.e.<\/i> scripts that do not require <span class=\"CodeInText\">initPowerSMO.ps1<\/span> to be run before they are used. Standalone PowerSMO scripts are much more practical for use in production environments.<\/p>\n<p>We will also be looking at some more useful features of PowerShell such as using PowerShell to write powershell scripts, regular expressions, formatting output, default arguments, and local variables.<\/p>\n<h2>Login <\/h2>\n<p>In the previous articles we ignored the fact that you do not get a free ride when you use SMO; you still have to login to SQL Server. The <span class=\"CodeInText\">Get-SMO_Server<\/span> function, as we have been using it in the previous articles, logs in to SQL Server using the credentials of the person who started PowerShell. This means that you must be running with a Windows identity that has a login on SQL Server that will allow you to access whatever you need to manage. In practice, this means that you make this login a member of the <b>sysadmin<\/b> server role.<\/p>\n<p>I have a Windows identity, named <b>SqlAdmin<\/b>, which also has a SQL login that is a member of the <b>sysadmin<\/b> role. The Windows <b>SqlAdmin<\/b> identity is an ordinary Windows user with no special Windows privileges; in fact this <b>SqlAdmin<\/b> is not even a member of the Windows Users group.<\/p>\n<p><b>SqlAdmin<\/b> does not have the Windows privileges I need to do my daily work on Windows, and I never use it to logon to Windows. Instead I use the ability of SMO to impersonate a Windows identity. The <span class=\"CodeInText\">loginServer.ps1<\/span> script, shown below, is the one I use to connect to a SQL Server instance when I need to do some management operations.<\/p>\n<pre><\/pre>\n<p class=\"Code\">param($server, $user, $password)<br \/>$conn = SMO_ServerConnection<br \/>$conn.ServerInstance = $server<br \/>$conn.ConnectAsUserName = $user<br \/>$conn.ConnectAsUserPassword = $password<br \/>$conn.ConnectAsUser = $true<br \/>$conn.ApplicationName =&#8221;Ad hoc Management&#8221;<br \/>$conn.WorkstationID = $ENV:ComputerName<br \/>$conn.MinPoolSize=1<br \/>$conn.PooledConnectionLifetime=3600<br \/>$conn.AutoDisconnectMode=&#8217;NoAutoDisconnect&#8217;<br \/>$server = SMO_Server $conn<\/p>\n<p>I run this script as follows (be sure to run <span class=\"CodeInText\">initPowerSMO.ps1<\/span> before trying this):<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk&gt;. .\\loginServer.ps1 CANOPUS5 &#8216;SqlAdmin&#8217; &#8216;P@ssw0rd&#8217;<\/p>\n<p>Be sure to include the &#8220;. &#8221; at the beginning of the command line. This script adds a <span class=\"CodeInText\">$conn<\/span> and a <span class=\"CodeInText\">$server<\/span> variable to the context in which you run it. The script uses <b>SMO_ServerConnection<\/b> make the <span class=\"CodeInText\">$conn<\/span> variable, which specifies how the connection is to be made, and then passes <span class=\"CodeInText\">$conn<\/span> to SMO_Server to create the <span class=\"CodeInText\">$server<\/span> variable.<\/p>\n<p><b>NOTE<\/b>:<br \/><i>This script seems to be using some new functions; <\/i><i><span class=\"CodeInText\">SMO_ServerConnection<\/span><\/i><i> and <\/i><i><span class=\"CodeInText\">SMO_Server<\/span><\/i><i>. These are not really new functions that are just making use of a feature of PowerShell that can save you some typing. You can skip typing the &#8220;<\/i><i><span class=\"CodeInText\">get-<\/span><\/i><i>&#8221; prefix for a command as long as it does not produce a conflict with some other command, so <\/i><i><span class=\"CodeInText\">SMO_Server<\/span><\/i><i> is the equivalent of <\/i><i><span class=\"CodeInText\">get-SMO_Server<\/span><\/i><i>.<\/i><\/p>\n<p>The <span class=\"CodeInText\">loginServer.ps1<\/span> script starts by creating an empty ServerConnection and follows this by filling out the pertinent information for the connection. It gets the server instance name, user name, and password from the command line. It fills out the <span class=\"CodeInText\">ApplicationName<\/span> and <span class=\"CodeInText\">WorkstationID<\/span> to make it easier to identify this connection; we will make use of this shortly.<\/p>\n<p>The <span class=\"CodeInText\">$conn.ConnectAsUser = $true<\/span> part of the script is what is making SMO impersonate a Windows user, using <span class=\"CodeInText\">ConnectAsUserName<\/span> and <span class=\"CodeInText\">ConnectAsUserPassword<\/span> when connecting to SQL Server. The <span class=\"CodeInText\">$true<\/span> variable is a built-in variable that returns a Boolean true value; likewise <span class=\"CodeInText\">$false<\/span> returns a Boolean false value.<\/p>\n<p>The <span class=\"CodeInText\">$ENV:ComputerName<\/span> is a special built-in variable of PowerShell. Any variable that starts with a <span class=\"CodeInText\">$ENV<\/span>: prefix accesses the Windows environment variable whose name makes up the suffix of the variable name. In this case the name of the computer the script is running on is accessed. <\/p>\n<p>When performing management operations, my preference is to make sure there is a ready-to-go connection in the pool while I am working, which is why the minimum connection pool size is set to one.<\/p>\n<p>The end of the <span class=\"CodeInText\">loginServer.ps1<\/span> creates a <span class=\"CodeInText\">$server<\/span> using <span class=\"CodeInText\">$conn<\/span> to initialize it.<\/p>\n<h2>SQL Login <\/h2>\n<p>You can also use a ServerConnection to connect using a SQL Server login. The <span class=\"CodeInText\">loginSeverSql.ps1<\/span> script, shown below, does this.<\/p>\n<pre><\/pre>\n<p class=\"Code\">param($server, $user, $password)<br \/>$conn = SMO_ServerConnection<br \/>$conn.ServerInstance = $server<br \/>$conn.LoginSecure = $false;<br \/>$conn.Login = $user<br \/>$conn.Password = $password<br \/>$conn.ConnectAsUser = $false<br \/>$conn.ApplicationName =&#8221;Ad hoc Management&#8221;<br \/>$conn.WorkstationID = $ENV:ComputerName<br \/>$conn.MinPoolSize=1<br \/>$conn.PooledConnectionLifetime=3600<br \/>$conn.AutoDisconnectMode=&#8217;NoAutoDisconnect&#8217;<br \/>$server = SMO_Server $conn<\/p>\n<p>The <span class=\"CodeInText\">$conn.LoginSecure = $false<\/span> and <span class=\"CodeInText\">$conn.ConnectAsUser=$false<\/span> part of the script is what is making SMO use a SQL login instead of a Windows login.<\/p>\n<h2>Checking activities <\/h2>\n<p>A fairly common management task is to find out which activities are running on a server. In fact SSMS (SQL Server Management Studio) has a Management-&gt;Activity Monitor for just this purpose. You can get equivalent functionality from PowerSMO using a SMO_Server to enumerate the processing running on a SQL Server instance, as shown below:<\/p>\n<pre><\/pre>\n<p class=\"MsoNormal\">PS C:\\SimpleTalk &gt;$server = SMO_Server EARTH5<br \/>PS C:\\SimpleTalk &gt;$server.EnumProcesses() <br \/>| format-table -property Spid,Program,Host,Command -autosize<br \/>&#160;<br \/>Spid Program&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Host&#160;&#160; Command<br \/>&#8212;- &#8212;&#8212;-&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#8212;-&#160;&#160; &#8212;&#8212;-<br \/>&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; RESOURCE MONITOR<br \/>&#160;&#160; 2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; LAZY WRITER<br \/>&#160;&#160; 3<br \/>&#160;&#160; 4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; LOCK MONITOR<br \/>&#160;&#160; 5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; SIGNAL HANDLER<br \/>&#160;&#160; 6&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TASK MANAGER<br \/>&#160;&#160; 7&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TRACE QUEUE TASK<br \/>&#160;&#160; 8<br \/>&#160;&#160; 9&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; BRKR TASK<br \/>&#160; 10&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160;TASK MANAGER<br \/>&#160; 11<br \/>&#160; 12&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TASK MANAGER<br \/>&#160; 13&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TASK MANAGER<br \/>&#160; 14&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TASK MANAGER<br \/>&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;TASK MANAGER<br \/>&#160; 16&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TASK MANAGER<br \/>&#160; 17&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TASK MANAGER<br \/>&#160; 18&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TASK MANAGER<br \/>&#160; 19&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;TASK MANAGER<br \/>&#160; 20&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; BRKR EVENT HNDLR<br \/>&#160; 21&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; BRKR TASK<br \/>&#160; 22&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; BRKR TASK<br \/>&#160; 23&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;TASK MANAGER<br \/>&#160; 24&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TASK MANAGER<br \/>&#160; 51 Microsoft SQL Server Management Studio EARTH5<br \/>&#160; 52 Ad hoc Management&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; EARTH5 SELECT <\/p>\n<p>A SMO_Server has a function, <span class=\"CodeInText\">EnumProcesses<\/span>, which lists all of the processes running on its instance of SQL Server. We are piping those results to the built-in cmdlet, <span class=\"CodeInText\">format-table<\/span>. This outputs a row for each process passed into it so that the results look like at table with column per property of the process. We didn&#8217;t want to see all of the properties, so we used the &#8211;<span class=\"CodeInText\">property<\/span> parameter of <span class=\"CodeInText\">format-table<\/span> to restrict the results to just the Spid, Program, Host, and Command properties of the process. The &#8211;<span class=\"CodeInText\">autosize<\/span> makes each column as narrow as possible.<\/p>\n<p><b>NOTE<\/b>:<br \/><i>The <\/i><i><span class=\"CodeInText\">Format-table<\/span><\/i><i> cmdlet, and its companion cmdlet <\/i><i><span class=\"CodeInText\">format-list<\/span><\/i><i>, are very useful. You can type <\/i><i><span class=\"CodeInText\">help format-list -full<\/span><\/i><i> to see all of their capabilities.<\/i><\/p>\n<p>We can see that there are quite a few system processes running along with two connections from my machine, EARTH5. SSMS is running on SPID 51 and our &#8220;Ad hoc Management&#8221; PowerSMO is running on SPID 52. The <span class=\"CodeInText\">ApplicationName<\/span> we set in the <span class=\"CodeInText\">loginSever.ps1<\/span> script appears as the <span class=\"CodeInText\">Program<\/span> in the properties of a process. The WorkstationID appears as the Host.<\/p>\n<p>Well, now that we have PowerSMO we won&#8217;t need SSMS anymore so let&#8217;s kill its connection to the server. That is shown next.<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk &gt;$server.KillProcess(51)<\/p>\n<p>Now let&#8217;s check to see if SSMS still has a connection, as shown below<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk &gt;$server.EnumProcesses($true) <br \/>| format-table -property Spid,Program,Host,Command -autosize<br \/>LogonUser succedded<\/p>\n<p>Spid Program&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Host&#160;&#160; Command<br \/>&#8212;- &#8212;&#8212;-&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#8212;-&#160;&#160; &#8212;&#8212;-<br \/>&#160; 51 Ad hoc Management EARTH5 SELECT<\/p>\n<p>We didn&#8217;t need to list all of the system processes to check the status of the SSMS connection. Passing a <span class=\"CodeInText\">$true<\/span> into the <span class=\"CodeInText\">EnumProcesses<\/span> function limits it to user processes. Here, we can see that Spid 52 is no longer running.<\/p>\n<h3>Monitoring activities <\/h3>\n<p>One of the nice things about the SSMS Activity Monitor is that you can configure it to poll the status of the server every few seconds to get a sort of real time display. The script, ActivityMonitor.ps1, shown next, works in a similar way.<\/p>\n<pre><\/pre>\n<p class=\"Code\">param($server, $user, $password)<br \/>$local:conn = SMO_ServerConnection<br \/>$local:conn.ServerInstance = $server<br \/>$local:conn.ConnectAsUserName = $user<br \/>$local:conn.ConnectAsUserPassword = $password<br \/>$local:conn.ConnectAsUser = $true<br \/>$local:conn.ApplicationName =&#8221;Ad hoc Management&#8221;<br \/>$local:conn.WorkstationID = $ENV:ComputerName<br \/>$local:conn.NonPooledConnection = $true<br \/>$local:server = SMO_Server $conn<br \/>while($true)<br \/>{<br \/>&#160;&#160;&#160;&#160;&#160; $local:server.EnumProcesses($true) |<br \/>&#160;&#160;&#160;&#160;&#160; format-table -property Spid,Program,Host,Command -autosize;<br \/>&#160;&#160;&#160;&#160;&#160; [System.Threading.Thread]::Sleep(10000)<br \/>&#160;&#160;&#160;&#160;&#160; if([System.Console]::KeyAvailable)<br \/>&#160;&#160;&#160;&#160;&#160; {<br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;<br \/>&#160;&#160;&#160;&#160;&#160; }<br \/>}<\/p>\n<p>The results of running it are shown below. About every ten seconds the script polls the CANOPUS5 server to see what non-system processes it is running. Between the second and third pass I started up SSMS.<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\Simpletalk &gt; .\\ActivityMonitor.ps1 EARTH5 SqlAdmin P@ssw0rd<\/p>\n<p>Spid Program&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Host&#160;&#160; Command<br \/>&#8212;- &#8212;&#8212;-&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#8212;-&#160;&#160; &#8212;&#8212;-<br \/>&#160; 51 Ad hoc Management EARTH5 SELECT<\/p>\n<p>Spid Program&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Host&#160;&#160; Command<br \/>&#8212;- &#8212;&#8212;-&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#8212;-&#160;&#160; &#8212;&#8212;-<br \/>&#160; 51 Ad hoc Management EARTH5 SELECT<\/p>\n<p>Spid Program&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Host&#160;&#160; Command<br \/>&#8212;- &#8212;&#8212;-&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#8212;-&#160;&#160; &#8212;&#8212;-<br \/>&#160; 51 Ad hoc Management&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; EARTH5 SELECT<br \/>&#160; 52 Microsoft SQL Server Management Studio EARTH5<\/p>\n<p>The <span class=\"CodeInText\">ActivityMonitor.ps1<\/span> script starts in way similar to the way that <span class=\"CodeInText\">loginServer.ps1<\/span> does, it just makes a Server that is connected to the SQL instance you want to monitor. However, there are a couple of differences. Firstly, it is creating <span class=\"CodeInText\">$local:conn<\/span> and <span class=\"CodeInText\">$local:server<\/span> variables. When a variable has a &#8220;<span class=\"CodeInText\">$local:<\/span>&#8221; prefix it is local to the PowerShell context in which it is run, which prevents it from interfering with the variables in the PowerShell that invoked it. Secondly, <span class=\"CodeInText\">$local:conn.NonPooledConnection=$true<\/span>, which means it gets its own connection rather than using one from the pool.<\/p>\n<p>After the <span class=\"CodeInText\">$local:server<\/span> variable has been created the script creates an infinite loop that is broken after you press a key on the keyboard. PowerShell can use any capability provided by .NET, so we make use of a static property of the System.Console class to see if a key has been typed. In the previous PowerSMO article we learned that when PowerShell sees text inside of square brackets it interprets it as a type definition. A type definition followed by &#8220;::&#8221; means &#8220;access a static member of the type&#8221;. So <span class=\"CodeInText\">[<span class=\"CodeInText\">System.Console<\/span>]::KeyAvailable<\/span> accesses the static <span class=\"CodeInText\">KeyAvailable<\/span> property of the <span class=\"CodeInText\">System.Console<\/span> class in .NET.<\/p>\n<p>Inside this loop, PowerSMO runs the same command we used earlier to enumerate the processes running on SQL Server. It follows the command by a ten second delay. The static <span class=\"CodeInText\">Sleep<\/span> method of the <span class=\"CodeInText\">System.Threading.Thread<\/span> class provides a delay specified in milliseconds.<\/p>\n<h3>Choosing process properties <\/h3>\n<p>The Spid, Program, Host, and Command properties of a SQL Server process are fairly common things to monitor, but you might want to choose a different set. The script, <span class=\"CodeInText\">ActivityMonitorSel.ps1<\/span>, shown below, is an enhancement to <span class=\"CodeInText\">ActivityMonitor.ps1<\/span> that allows the properties to be specified on the command line.<\/p>\n<pre><\/pre>\n<p class=\"Code\">param($server, $user, $password, $properties)<br \/>$local:conn = SMO_ServerConnection<br \/>$local:conn.ServerInstance = $server<br \/>$local:conn.ConnectAsUserName = $user<br \/>$local:conn.ConnectAsUserPassword = $password<br \/>$local:conn.ConnectAsUser = $true<br \/>$local:conn.ApplicationName =&#8221;Ad hoc Management&#8221;<br \/>$local:conn.WorkstationID = $ENV:ComputerName<br \/>$local:conn.NonPooledConnection = $true<br \/>$local:server = SMO_Server $conn<br \/>$local:conn.Connect()<br \/>while($true)<br \/>{<br \/>$local:server.EnumProcesses($true) |<br \/>&#160;format-table -property $properties -autosize<br \/>[System.Threading.Thread]::Sleep(10000)<br \/>if([System.Console]::KeyAvailable)<br \/>{<br \/>break;<br \/>}<br \/>}<\/p>\n<p>This script has modified <span class=\"CodeInText\">ActivityMonitor.ps1<\/span> in two ways; it adds the <span class=\"CodeInText\">$properties<\/span> argument to the script and it uses that argument in the format-table cmdlet.<\/p>\n<p>We use this script as follows (the first two lines are actually a single line):<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk&gt; .\\ActivityMonitorSel.ps1 CANOPUS5<br \/>                    SqlAdmin P@ssw0rd spid, cpu, status, memusage<\/p>\n<p>Spid Cpu Status&#160; MemUsage<br \/>&#8212;- &#8212; &#8212;&#8212;&#160; &#8212;&#8212;&#8211;<br \/>&#160; 51&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<br \/>&#160; 52 187&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<br \/>&#160; 53&#160;&#160; 0 running&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<\/p>\n<p>Spid Cpu Status&#160; MemUsage<br \/>&#8212;- &#8212; &#8212;&#8212;&#160; &#8212;&#8212;&#8211;<br \/>&#160; 52 187&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<br \/>&#160; 53&#160;&#160; 0 running&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<\/p>\n<p>After the password on the command line, we passed in the properties that we wished to monitor, as a comma-separated list. Up until now we have been careful to separate arguments by spaces, not commas. PowerShell treats a sequence of values separated by commas as a single argument. The argument itself is an array with an entry for each of the comma separated values.<\/p>\n<p>The <span class=\"CodeInText\">format-table<\/span> cmdlet interprets an array after -property as the list of properties it should display. In fact, if you look at how we specified the properties when we first used <span class=\"CodeInText\">format-table<\/span> they were in the form of a comma separated list. PowerShell turned this into an array and then passed it to the <span class=\"CodeInText\">format-table<\/span> cmdlet.<\/p>\n<h3>Choosing user processes <\/h3>\n<p>Let&#8217;s make one more change to our activity monitor script so that we can use another feature of PowerShell, namely default arguments. Below are the changes we need to make in order to turn <span class=\"CodeInText\">ActivityMonitorSel.ps1<\/span> into <span class=\"CodeInText\">ActivityMonitorOpt.ps1<\/span>, which allows us to select whether or not we want to monitor system processes.<\/p>\n<pre><\/pre>\n<p class=\"Code\">param($server, $user, $password, $properties, $userOnly = $true)<br \/>&#8230;<br \/>$local:server.EnumProcesses($userOnly) |<br \/>format-table -property $properties -autosize<\/p>\n<p>We have added a <span class=\"CodeInText\">$userOnly<\/span> argument and specified its default value as <span class=\"CodeInText\">$true<\/span>. If we don&#8217;t include this value on the command line then <span class=\"CodeInText\">ActivityMonitorOpt.ps1<\/span> will produce the same output as <span class=\"CodeInText\">ActivityMonitorSel.ps1<\/span>. We can see this below (the first two lines are a single line):<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk&gt; .\\ActivityMonitorOpt.ps1 CANOPUS5<br \/>                    SqlAdmin P@ssw0rd spid, cpu, status, memusage<br \/>Spid Cpu Status&#160; MemUsage<br \/>&#8212;- &#8212; &#8212;&#8212;&#160; &#8212;&#8212;&#8211;<br \/>&#160; 51&#160;&#160; 0 running&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<\/p>\n<p>However if we include the <span class=\"CodeInText\">$false<\/span> value on the command line we will get all the processes, including the system processes (the first two lines are a single line):<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk&gt; .\\ActivityMonitorOpt.ps1 CANOPUS5<\/p>\n<p class=\"Code\">                    SqlAdmin P@ssw0rd spid, cpu, status, memusage $false<br \/>Spid Cpu Status&#160;&#160;&#160;&#160; MemUsage<br \/>&#8212;- &#8212; &#8212;&#8212;&#160;&#160;&#160;&#160; &#8212;&#8212;&#8211;<br \/>&#160;&#160; 1&#160;&#160; 0 background&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0<br \/>&#160;&#160; 2&#160;&#160; 0 background&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0<br \/>&#160;&#160; 3&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0<br \/>&#8230;<br \/>&#160;&#160;20&#160;&#160; 0 background&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0<br \/>&#160; 21&#160;&#160; 0 background&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0<br \/>&#160; 22&#160;&#160; 0 background&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0<br \/>&#160; 23&#160;&#160; 0 sleeping&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0<br \/>&#160; 51&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<br \/>&#160; 52&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<br \/>&#160; 53&#160;&#160; 0 running&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<\/p>\n<h2>Standalone scripts <\/h2>\n<p>So far we have been running our scripts inside of a PowerShell that has run the <span class=\"CodeInText\">initPowerSMO.ps1<\/span> script. There are a couple of issues with this. One is that <span class=\"CodeInText\">initPowerSMO.ps1<\/span> is really just meant to be used for tutorial purposes and has not been put through the kind of testing that something used in a production environment requires. Another is that it takes quite a long time to run&#8230; if all you want to do is a quick activity check you wouldn&#8217;t want to wait for the 20 or more seconds for it to start.<\/p>\n<p>To see how to make our scripts standalone, <i>i.e.<\/i> run without first running <span class=\"CodeInText\">initPowerSMO.ps1<\/span>, we have to look at how a command like <span class=\"CodeInText\">get-SMO_Server<\/span> works. Look at the line below:<\/p>\n<pre><\/pre>\n<p class=\"Code\">$server = get-SMO_Server CANOPUS5<\/p>\n<p>In effect, when the script is run this line is converted to:<\/p>\n<pre><\/pre>\n<p class=\"Code\">$server = new-object<br \/>           &#8220;Microsoft.SqlServer.Management.Smo.Server&#8221; CANOPUS5<\/p>\n<p>The new-object command makes a new instance of a <b>Microsoft.SqlServer.Management.Smo.Server<\/b> object and passes the string &#8220;CANOPUS5&#8221; to the constructor it uses to do this. To convert the <span class=\"CodeInText\">ActivityMonitorOpt.ps1<\/span> script to a standalone version, named <span class=\"CodeInText\">ActivityMonitorSA.ps1<\/span>, the first thing we have to do is to replace the SMO_ServerConnection and SMO_Server as is shown below.<\/p>\n<pre><\/pre>\n<p class=\"Code\">$local:conn = new-object<br \/>               &#8220;Microsoft.SqlServer.Management.Common.ServerConnection&#8221;<br \/>&#8230;<br \/>$local:server = new-object<br \/>                 &#8220;Microsoft.SqlServer.Management.Smo.Server&#8221; $conn<\/p>\n<p>It turns out that is not enough. By default, PowerShell only loads a few .NET assemblies, the most common ones. In order for our standalone script to work we must load the SMO assemblies. If you look at the <span class=\"CodeInText\">initPowerSMO.ps1<\/span> script you will see that it starts by loading the SMO assemblies:<\/p>\n<pre><\/pre>\n<p class=\"Code\">[System.Reflection.Assembly]::Load(&#8220;Microsoft.SqlServer.Smo,<br \/>                                     Culture=Neutral,Version=9.0.242.0,<br \/>                                     PublicKeyToken=89845dcd8080cc91&#8243;)<br \/>                                      | out-null<br \/>[System.Reflection.Assembly]::Load(&#8220;Microsoft.SqlServer.ConnectionInfo,<br \/>                                     Culture=Neutral,Version=9.0.242.0,<br \/>\t\t\t\t     PublicKeyToken=89845dcd8080cc91&#8243;) <br \/>\t\t\t\t      | out-null <br \/>[System.Reflection.Assembly]::Load(&#8220;System.Data,Culture=Neutral,<br \/>\t\t\t\t     Version=2.0.0.0,<br \/>\t\t\t\t     PublicKeyToken=b77a5c561934e089&#8243;)<br \/>\t\t\t\t      | out-null<br \/>[System.Reflection.Assembly]::Load(&#8220;Microsoft.SqlServer.SqlEnum,<br \/>\t\t\t\t     Culture=Neutral,Version=9.0.242.0,<br \/>\t\t\t\t     PublicKeyToken=89845dcd8080cc91&#8243;)<br \/>&#160; | out-null<\/p>\n<p>We will save the details of how this loads the needed assemblies for a later article; for now all we will do is make sure that this is part of any standalone assembly. The beginning of our <span class=\"CodeInText\">ActivityMonitorSA.ps1<\/span> script now looks like:<\/p>\n<pre class=\"Reg\"><\/pre>\n<p>With this modification, <span class=\"CodeInText\">ActivityMonitorSA.ps1<\/span> will run without needing to first run <span class=\"CodeInText\">initPowerSmo.ps1<\/span>. One of the nice things about a standalone PowerSMO assembly like this is that it can be run directly from a Windows command shell command line, as shown below. You will also notice that this starts much faster than running <span class=\"CodeInText\">initPowerSMO.ps1<\/span>.<\/p>\n<pre><\/pre>\n<p class=\"Code\">C:\\SimpleTalk&gt;PowerShell .\\ActivityMonitorSA.ps1 CANOPUS5<br \/>\t\t\t   SqlAdmin P@ssw0rd spid<br \/>,program<\/p>\n<p>Spid Program<br \/>&#8212;- &#8212;&#8212;-<br \/>&#160; 51 Ad hoc Management<br \/>&#160; 52 Ad hoc Management<br \/>&#160; 53 Ad hoc Management<br \/>&#160; 54 Ad hoc Management<\/p>\n<h3>Converting to standalone scripts <\/h3>\n<p>So, the way to create your standalone scripts is first to get them working using all the PowerSMO functions like get-SMO_Server, and then to convert the script to using new-object and the class names that correspond to the PowerSMO functions. To do this you will have to know the full name of the classes that SMO functions are using. You can look these up in BOL, but you&#8217;ll find it is easier to just ask PowerShell to tell you what the full names are. For every PowerSMO function like <span class=\"CodeInText\">get-SMO_Server<\/span> there is a corresponding function named <span class=\"CodeInText\">get-SMOT_Server<\/span> that returns the type definition the function uses. This can be used to get the full name of the type, as shown in the example below.<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk&gt; (get-SMOT_Server).FullName<br \/>Microsoft.SqlServer.Management.Smo.Server<br \/>PS C:\\SimpleTalk&gt;<\/p>\n<h3>Automatic conversion to standalone scripts <\/h3>\n<p>PowerShell is a full-fledged scripting engine so we might as well just write a script that will automatically convert an <span class=\"CodeInText\">initPowerSMO.ps1<\/span> based script to a standalone one.<\/p>\n<p>To get started we will need a script that can convert something like:<\/p>\n<pre><\/pre>\n<p class=\"Reg\"><span class=\"CodeInText\">get-SMO_Server<\/span> or <span class=\"CodeInText\">SMO_Server<\/span><\/p>\n<p>into:<\/p>\n<pre><\/pre>\n<p class=\"Reg\"><span class=\"CodeInText\">new-object &#8220;Microsoft.SqlServer.Management.Smo.Server&#8221;<\/span><\/p>\n<p>The following script, in <span class=\"CodeInText\">replace.ps1<\/span>, can do that:<\/p>\n<pre><\/pre>\n<p class=\"Code\">param ([String]$smo_fcn)<br \/>$t = $smo_fcn.Insert($smo_fcn.IndexOf(&#8216;SMO&#8217;)+3, &#8216;T&#8217;)<br \/>$c = (&amp;$t).FullName<br \/>&#8220;new-object &#8220;&#8221;$c&#8221;&#8221;&#8221;<\/p>\n<p>This script inserts a &#8220;T&#8221; after the SMO in the input string, which makes the function name that returns the type definition for the function. You can execute a string in PowerShell by prefacing it with a &#8220;<span class=\"CodeInText\">&amp;<\/span>&#8221; character; think dynamic SQL&#8230; you&#8217;ve done this before. This returns the type definition from which we extract the full name. Lastly we preface the full name of the class with <span class=\"CodeInText\">new-object<\/span> and return the result. Some examples of its usage shown below:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk&gt;&#160; .\\replace.ps1 SMO_Server<br \/>new-object &#8220;Microsoft.SqlServer.Management.Smo.Server&#8221;<br \/>PS C:\\SimpleTalk&gt;&#160; .\\replace.ps1 get-SMO_Server<br \/>new-object &#8220;Microsoft.SqlServer.Management.Smo.Server&#8221;<\/p>\n<p>To do an automatic conversion we must find all of the SMO functions in a script and replace them with an equivalent <span class=\"CodeInText\">new-object<\/span>. We can use the regular expression support built into PowerShell to do this. The &#8211;<span class=\"CodeInText\">match<\/span> operator in PowerShell uses a regular expression to find patterns in strings, as shown below.<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk&gt; &#8216;$local:conn=SMO_ServerConnection&#8217; | %{$_ -match &#8220;SMO_\\w*&#8221;}<br \/>True<br \/>PS C:\\SimpleTalk&gt; $matches.Values<br \/>SMO_ServerConnection<\/p>\n<p>The &#8211;<span class=\"CodeInText\">match<\/span> returns a Boolean true if any patterns were found in the input it tested. It also fills the built-in <span class=\"CodeInText\">$matches<\/span> variable with an array of all of the patterns that matched it. As is shown above, the <b>Values<\/b> property of the <b>$matche<\/b>s variable contains the SMO functions from the input string.<\/p>\n<p>The get-content cmdlet will parse a text file into individual lines. To use it to do this, the path to the file is passed to the -path property of get-content. The <span class=\"CodeInText\">makeSA.ps1<\/span> script, shown below, uses get-content to parse a file into lines, and then replaces each SMO functions with its <span class=\"CodeInText\">new-object<\/span> equivalent, <i>i.e.<\/i> it creates a standalone version of the input script.<\/p>\n<pre><\/pre>\n<p class=\"Code\">param ($file)<br \/>$assemblies = <br \/>&#8216;<br \/>[System.Reflection.Assembly]::Load(&#8220;Microsoft.SqlServer.Smo,<br \/>\t\t\t\tCulture=Neutral,Version=9.0.242.0,<br \/>\t\t\t\tPublicKeyToken=89845dcd8080cc91&#8243;) <br \/>\t\t\t\t| out-null<br \/>[System.Reflection.Assembly]::Load(&#8220;Microsoft.SqlServer.ConnectionInfo,<br \/>\t\t\t\tCulture=Neutral,Version=9.0.242.0,<br \/>\t\t\t\tPublicKeyToken=89845dcd8080cc91&#8243;) <br \/>\t\t\t\t| out-null <br \/>[System.Reflection.Assembly]::Load(&#8220;System.Data,Culture=Neutral,<br \/>\t\t\t\tVersion=2.0.0.0,<br \/>\t\t\t\tPublicKeyToken=b77a5c561934e089&#8243;) <br \/>\t\t\t\t| out-null<br \/>[System.Reflection.Assembly]::Load(&#8220;Microsoft.SqlServer.SqlEnum,<br \/>\t\t\t\tCulture=Neutral,Version=9.0.242.0,<br \/>\t\t\t\tPublicKeyToken=89845dcd8080cc91&#8243;)&#160; <br \/>| out-null<br \/>&#8216;<br \/>$assembliesIncluded = $false;<br \/>get-content -path $file |<br \/>%{ <br \/>if($_ -match &#8220;SMO_\\w*&#8221;)<br \/>{<br \/>if(!$assembliesIncluded)<br \/>{<br \/>$assemblies<br \/>$assembliesIncluded = $true<br \/>}<br \/>foreach($m in $matches.Values)<br \/>{<\/p>\n<p>$t = $m.Insert($m.IndexOf(&#8216;SMO&#8217;)+3, &#8216;T&#8217;)<br \/>$c = (&amp;$t).FullName<br \/>$r = &#8220;new-object &#8220;&#8221;$c&#8221;&#8221;&#8221;<br \/>$_ = $_.Replace($m, $r)<br \/>}<br \/>}<br \/>$_<br \/>}<\/p>\n<p>The file path is passed into the script in the <span class=\"CodeInText\">$file<\/span> argument. The <span class=\"CodeInText\">$assemblies<\/span> variable holds the instructions that will load the assemblies that SMO requires. The <span class=\"CodeInText\">$assembliesIncluded<\/span> variable is a marker used so that the assemblies are loaded only once.<\/p>\n<p>Next the <span class=\"CodeInText\">get-content<\/span> cmdlet is used to pipe the lines of the input file into a script block. The script block uses &#8211;<span class=\"CodeInText\">match<\/span> find all the SMO functions in the line. If the line does include any SMO functions they are replaced using the script we looked at earlier, <span class=\"CodeInText\">replace.ps1<\/span>. Lastly the input line, with any SMO function replaced, is output.<\/p>\n<p>Below is an example of using <span class=\"CodeInText\">makeSA.ps1<\/span> to convert <span class=\"CodeInText\">ActivityMonitorOpt.ps1<\/span> into a standalone script named <span class=\"CodeInText\">AM_Standalone.ps1<\/span>:<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk&gt; .\\makeSA.ps1 .\\ActivityMonitorOpt.ps1 <br \/>\t\t\t\t| out-file AM_Standalone.ps1<\/p>\n<p>The <span class=\"CodeInText\">out-file<\/span> cmdlet is used to pipe the result of the <span class=\"CodeInText\">makeSA.ps1<\/span> to a file. We can test things out by running <span class=\"CodeInText\">AM_Standalone.ps1<\/span> in a new instance of PowerShell that has not had <span class=\"CodeInText\">intiPowerSMO.ps1<\/span> run in it, as shown below.<\/p>\n<pre><\/pre>\n<p class=\"Code\">PS C:\\SimpleTalk&gt; .\\AM_Standalone.ps1 CANOPUS5<br \/>\t\t     SqlAdmin P@ssw0rd spid, program<\/p>\n<p>Spid Program<br \/>&#8212;- &#8212;&#8212;-<br \/>&#160; 51 Ad hoc Management<br \/>&#160; 52 Ad hoc Management<\/p>\n<p>Here we see that <b><span class=\"CodeInText\">AM_Standalone.ps1<\/span><\/b> worked without first running <span class=\"CodeInText\">initPowerSMO.ps1<\/span> and ran immediately.<\/p>\n<h2>Conclusion <\/h2>\n<p>We started off by looking at how we can control the identity that SMO uses to login to SQL Server. Next, we built an activity monitor that gave us capabilities similar to those provided by the SSMS Activity Monitor.<\/p>\n<p>The <span class=\"CodeInText\">initPowerSMO.ps1<\/span> script creates a shell in which we can easily interact with and exploit all the features of SMO, but it really isn&#8217;t really practical for production management scripts. However, we can automatically convert any script we write that uses the PowerSMO functions to an equivalent one that does not depend on the <span class=\"CodeInText\">initPowerSMO.ps1<\/span> script.<\/p>\n<p>So, we can use a PowerShell initialized with <span class=\"CodeInText\">initPowerSMO.ps1<\/span> in an <i>ad hoc<\/i> and development environment, but we make standalone scripts for production use.<\/p>\n<p><em>Some of the topics in this article are covered in the Pluralsight.com <\/em><a href=\"http:\/\/www.pluralsight.com\/courses\/AppliedSql2005.aspx\"><em>Applied SQL Server 2005<\/em><\/a><em> course.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In part 3 of Dan Sullivan&#8217;s in-depth exploration of PowerSMO, the versatile command line utility for managing SQL Server databases, he describes how to control the identity that SMO uses to login to SQL Server, how to set up Server activity monitoring, and how to create standalone PowerSMO scripts, suitable for use in a production environment.&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":[4168,4170,4659,4658,4638,4364,4637,4657,4701,4336,4179,4150,4151,4702],"coauthors":[],"class_list":["post-235","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","tag-database","tag-database-administration","tag-dba","tag-functions","tag-managing-sql-server","tag-monitoring","tag-powersmo","tag-scripts","tag-server-activity-monitor","tag-smo","tag-source-control","tag-sql","tag-sql-server","tag-sql-server-logins"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/235","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=235"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/235\/revisions"}],"predecessor-version":[{"id":39797,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/235\/revisions\/39797"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=235"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=235"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=235"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=235"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}