﻿<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>PowerSQL</title>
    <atom:link
      href="https://review.docs.microsoft.com/archive/blogs/powersql/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://review.docs.microsoft.com/archive/blogs/powersql/feed.xml</link>
    <description>Bringing PowerShell and SQL together for the best of both worlds.</description>
    <lastBuildDate>Tue, 16 Apr 2019 05:23:10 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>SYSTEM Permissions in SQL Server</title>
      <link>https://review.docs.microsoft.com/archive/blogs/powersql/system-permissions-in-sql-server</link>
      <pubDate>Sun, 09 Sep 2018 20:04:51 GMT</pubDate>
      <dc:creator><![CDATA[randomnote_1]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/powersql/?p=86</guid>
      <description><![CDATA[Okay folks, we need to have a conversation about SYSTEM permissions in SQL Server. SYSTEM is...]]></description>
      <content:encoded><![CDATA[Okay folks, we need to have a conversation about <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms684190(v=vs.85).aspx">SYSTEM</a> permissions in SQL Server. SYSTEM is constantly over-permissioned in SQL Server . Some of the reasons given for why SYSTEM is over-permissioned are:
<ul>
 	<li>My backup application runs as SYSTEM and needs to log into SQL.</li>
 	<li>This application or service runs as SYSTEM and needs access to SQL.</li>
 	<li>Windows Server Failover Clustering (WSFC) needs access to SQL, therefore I need to grant SYSTEM access.</li>
 	<li>Always On Availability Groups use WSFC, therefore SYSTEM requires access to SQL.</li>
</ul>
While these reasons are legitimate, there are better ways to grant permissions. This article will discuss what SYSTEM is, why granting SYSTEM additional permissions in SQL Server is bad, and the appropriate methods to grant permission to system resources.
<h1>What is SYSTEM?</h1>
<a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms684190">SYSTEM</a> (NT AUTHORITY\SYSTEM in en-us) is the account the operating system runs as. Any process that is executed as SYSTEM is running in the highest level permissions that can be granted.
<h1>The History of SYSTEM in SQL Server</h1>
Prior to SQL Server 2012, SYSTEM was a member of the sysadmin server-level role. SQL Server 2012 introduced drastic changes to the permissions granted to SYSTEM. SYSTEM has the following permissions and group memberships in SQL Server version 2012 through current (SQL 2017 as of this writing):
<ul>
 	<li>Server Role Memberships
<ul>
 	<li>public</li>
</ul>
</li>
 	<li>Server Permission Grants
<ul>
 	<li>Alter any availability group</li>
 	<li>Connect SQL</li>
 	<li>View Server State</li>
</ul>
</li>
</ul>
As you can see, the only changes SYSTEM can make in SQL is to Availability Groups. This is far more restrictive than it used to be.
<h1>Why Were the Permissions Changed?</h1>
A large portion of the running processes on a server are executing with SYSTEM as the owner. When a process is executing as SYSTEM, the process can do anything on the server.

Members of the Local Administrators group can easily configure workloads to execute as SYSTEM. Some examples include <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms685141">Services</a>, <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa383614">Scheduled Tasks</a>, and utilities like <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/psexec">PsExec</a>. An Administrator could unintentionally or intentionally configure a rogue workload to execute as SYSTEM.

When a workload is running as SYSTEM, it is difficult (if not impossible) to trace the actions taken by SYSTEM back to the workload because all the actions show they were run as SYSTEM in the audit logs.
<h1>Demos</h1>
The following demos will demonstrate the permissions differences between SQL Server 2008 R2 and SQL Server 2017.
<h2>Setup</h2>
The following items were used in the demos:
<ol>
 	<li>A SQL Server 2017 Instance. I spun up a VM in Azure using the <a href="https://azuremarketplace.microsoft.com/en-us/marketplace/apps/Microsoft.SQLServer2017EnterpriseWindowsServer2016">SQL Server 2017</a> template from the <a href="https://azuremarketplace.microsoft.com/en-us/marketplace">Azure Marketplace</a>.</li>
 	<li>A SQL Server 2008 R2 Instance. I used the <a href="https://azuremarketplace.microsoft.com/en-us/marketplace/apps/Microsoft.WindowsServe">Windows Server 2008 R2 SP1</a> template from the <a href="https://azuremarketplace.microsoft.com/en-us/marketplace">Azure Marketplace</a> and installed SQL Server 2008 R2 RTM.</li>
 	<li>Configure a <a href="#SqlServerAudit">SQL Server Audit</a> on both instances.</li>
 	<li>Obtain a copy of <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/psexec">PsExec64</a>.</li>
</ol>
<h2 id="SqlServerAudit">SQL Server Audit</h2>
The following query can be used to create a server audit and specify which events to log. Ensure the SQL Server service has full control to the audit log directory.

[sql]USE [master]
GO

CREATE SERVER AUDIT [TestAudit]
TO FILE
(
    FILEPATH = N'C:\AuditLogs\'
    ,MAXSIZE = 20 MB
    ,MAX_ROLLOVER_FILES = 10
    ,RESERVE_DISK_SPACE = OFF
)
WITH
(
    QUEUE_DELAY = 1000
    ,ON_FAILURE = CONTINUE
)

CREATE SERVER AUDIT SPECIFICATION [TestAuditSpecification]
FOR SERVER AUDIT [TestAudit]
	ADD (APPLICATION_ROLE_CHANGE_PASSWORD_GROUP), 
	ADD (AUDIT_CHANGE_GROUP),
	ADD (BACKUP_RESTORE_GROUP),
	ADD (DATABASE_CHANGE_GROUP),
	ADD (DATABASE_OBJECT_CHANGE_GROUP),
	ADD (DATABASE_OBJECT_OWNERSHIP_CHANGE_GROUP),
	ADD (DATABASE_OBJECT_PERMISSION_CHANGE_GROUP),
	ADD (DATABASE_OPERATION_GROUP),
	ADD (DATABASE_OWNERSHIP_CHANGE_GROUP),
	ADD (DATABASE_PERMISSION_CHANGE_GROUP),
	ADD (DATABASE_PRINCIPAL_CHANGE_GROUP),
	ADD (DATABASE_PRINCIPAL_IMPERSONATION_GROUP),
	ADD (DATABASE_ROLE_MEMBER_CHANGE_GROUP),
	ADD (DBCC_GROUP),
	ADD (FAILED_LOGIN_GROUP),
	ADD (LOGIN_CHANGE_PASSWORD_GROUP),
	ADD (LOGOUT_GROUP),
        ADD (SCHEMA_OBJECT_ACCESS_GROUP),
	ADD (SCHEMA_OBJECT_CHANGE_GROUP),
	ADD (SCHEMA_OBJECT_OWNERSHIP_CHANGE_GROUP),
	ADD (SCHEMA_OBJECT_PERMISSION_CHANGE_GROUP),
	ADD (SERVER_OBJECT_CHANGE_GROUP),
	ADD (SERVER_OBJECT_OWNERSHIP_CHANGE_GROUP),
	ADD (SERVER_OBJECT_PERMISSION_CHANGE_GROUP),
	ADD (SERVER_OPERATION_GROUP),
	ADD (SERVER_PERMISSION_CHANGE_GROUP),
	ADD (SERVER_PRINCIPAL_CHANGE_GROUP),
	ADD (SERVER_PRINCIPAL_IMPERSONATION_GROUP),
	ADD (SERVER_ROLE_MEMBER_CHANGE_GROUP),
	ADD (SERVER_STATE_CHANGE_GROUP),
	ADD (SUCCESSFUL_LOGIN_GROUP),
	ADD (TRACE_CHANGE_GROUP)
WITH (STATE = ON)

ALTER SERVER AUDIT [TestAudit] WITH (STATE = ON)
GO[/sql]

<h2>Get SYSTEM Permissions</h2>
Execute the following query to get the permissions granted to SYSTEM in each instance:

[sql]EXECUTE AS LOGIN = 'NT AUTHORITY\SYSTEM'
SELECT * FROM fn_my_permissions(NULL,NULL)
REVERT[/sql]

Note that SQL Server 2008 R2 grants everything to SYSTEM by default and SQL Server 2017 instance only grants CONNECT SQL and VIEW ANY DATABASE to SYSTEM by default.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2008-R2-SYSTEM-Default-Permissions.png"><img width="300" height="178" class="size-medium wp-image-105 alignnone" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2008-R2-SYSTEM-Default-Permissions-300x178.png" /></a><a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2017-SYSTEM-Default-Permissions.png"><img width="300" height="179" class="size-medium wp-image-115 alignnone" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2017-SYSTEM-Default-Permissions-300x179.png" /></a><a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2008-R2-SYSTEM-Default-Permissions.png"></a>
<h2>Create a Database</h2>
<ul>
 	<li>Log into the instance using SQL Server Management Studio (SSMS).</li>
 	<li>Open a new query editor.</li>
 	<li>Execute the following query to create a database:</li>
</ul>

[sql]USE master;
GO

CREATE DATABASE AccessDemo;
GO

USE AccessDemo;
GO
CREATE TABLE dbo.Employee
(
    EmployeeID int 
    PRIMARY KEY CLUSTERED
);
GO[/sql]

<ul>
 	<li>Query the audit log by executing the following query:</li>
</ul>

[sql]SELECT  event_time
        ,session_server_principal_name
        ,server_principal_name
        ,database_name
        ,schema_name
        ,object_name
        ,statement
FROM    sys.fn_get_audit_file('C:\AuditLogs\*.sqlaudit',default,default)
WHERE   statement LIKE 'CREATE%';
GO[/sql]

<ul>
 	<li>Note the session_server_principal_name and the server_principal_name are the SQL Server login account used to log into the instance.</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/08/Query-Audit-Log-for-CREATE-Statements.png"><img width="1005" height="77" class="alignnone size-full wp-image-125" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/Query-Audit-Log-for-CREATE-Statements.png" /></a>
<h2>Delete a Table Using SYSTEM</h2>
Run PowerShell as Administrator and change directories to the directory where PsExec64.exe is stored. Launch PowerShell as SYSTEM using the following command. A new PowerShell window will open.

[powershell].\PsExec64.exe -i -d -s powershell.exe[/powershell]

In the new PowerShell window, execute the following command:

[powershell]sqlcmd -Q 'SELECT name FROM sys.databases'[/powershell]

This will return a list of all the databases in both SQL Server 2008 R2 and SQL Server 2017.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQLCMD-SELECT-Database-Names.png"><img width="526" height="206" class="alignnone size-full wp-image-135" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/SQLCMD-SELECT-Database-Names.png" /></a>

Now try dropping the Employee table from the AccessDemo database

[powershell]sqlcmd -d AccessDemo -Q 'DROP TABLE Employee[/powershell]

SQL Server 2008 R2 provides no feedback, indicating a successful operation.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2008-R2-SYSTEM-DROP-TABLE.png"><img width="550" height="24" class="alignnone size-full wp-image-155" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2008-R2-SYSTEM-DROP-TABLE.png" /></a>

SQL Server 2017, on the other hand, throws a "Login failed for user" error message.<a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2017-SYSTEM-DROP-TABLE.png"><img width="643" height="74" class="alignnone size-full wp-image-145" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2017-SYSTEM-DROP-TABLE.png" /></a>

To validate the table has been deleted from the AccessDemo database in SQL Server 2008 R2, execute the following query:

[sql]USE AccessDemo;
GO

SELECT  name
FROM    sys.tables;
GO[/sql]

Note the Employee table is not returned in SQL Server 2008 R2 and it is returned in SQL Server 2017.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2008-R2-SELECT-tables.png"><img width="300" height="178" class="alignnone size-medium wp-image-175" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2008-R2-SELECT-tables-300x178.png" /></a><a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2017-SELECT-tables.png"><img width="300" height="179" class="alignnone size-medium wp-image-165" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2017-SELECT-tables-300x179.png" /></a>

Execute the following query to see what actions SYSTEM performed:

[sql]SELECT TOP 2
        event_time
        ,session_server_principal_name
        ,server_principal_name
        ,database_name
        ,schema_name
        ,object_name
        ,succeeded
        ,statement
FROM    sys.fn_get_audit_file('C:\AuditLogs\*.sqlaudit',default,default)
WHERE   server_principal_name = 'NT AUTHORITY\SYSTEM'
ORDER   BY event_time DESC;
GO[/sql]

The audit log clearly shows in SQL Server 2008 R2 that SYSTEM was able to drop the table where in SQL Server 2017, SYSTEM was denied access to the AccessDemo database.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2008-R2-SYSTEM-Audit-Result.png"><img width="300" height="179" class="alignnone size-medium wp-image-185" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2008-R2-SYSTEM-Audit-Result-300x179.png" /></a><a href="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2017-SYSTEM-Audit-Result.png"><img width="300" height="179" class="alignnone size-medium wp-image-195" style="margin: 1em;border: .1em solid black" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/08/SQL-Server-2017-SYSTEM-Audit-Result-300x179.png" /></a>

Note, if the investigation into the audit logs is done forensically after-the-fact, the investigator would not know who or what initiated the DROP TABLE command when executed as SYSTEM.
<h1>A Better Way to Grant Permissions</h1>
SQL Server 2008 introduced the use of <a href="https://support.microsoft.com/en-us/help/2620201/sql-server-uses-a-service-sid-to-provide-service-isolation">per-service Security Identifiers (SID)</a> to allow permissions to be granted directly to a specific service. This is the method used by SQL Server to grant permissions to the engine and agent services (e.g. NT SERVICE\MSSQL$&lt;InstanceName&gt; and NT SERVICE\SQLAGENT$&lt;InstanceName&gt; respectively). Using this method, those services can access the database engine only when the services are running.

This same method can be utilized when granting permissions to other services. Some examples I have used include:
<ul>
 	<li>NetBackup services (<em>NT SERVICE\NetBackup Client Service</em> and <em>NT Service\NetBackup Legacy Client Service</em>)</li>
 	<li>System Center Operations Manager (SCOM) Health Service (<em>NT SERVICE\HealthService</em>)</li>
 	<li>Windows Server Failover Clustering (WSFC) service (<em>NT SERVICE\ClusSvc</em>)</li>
</ul>
Some services will require the creation of the service SID using <a href="https://docs.microsoft.com/en-us/windows/desktop/services/configuring-a-service-using-sc">SC.exe</a>. <a href="https://blogs.technet.microsoft.com/kevinholman/2016/08/25/sql-mp-run-as-accounts-no-longer-required-2/">This method</a> has been adopted by Microsoft System Center Operations Manager (SCOM) administrators to grant permission to the HealthService within SQL server.
<h2>Create a Service SID</h2>
The following command will create a service SID on the NetBackup Client Service.

[powershell]sc.exe --% sidtype &quot;NetBackup Client Service&quot; unrestricted[/powershell]

<em>Note: <strong>--%</strong> tells PowerShell to stop parsing the rest of the command. This is particularly useful when using legacy commands and applications.</em>
<h2>Query a Service SID</h2>
To check a service SID or to ensure a service SID exists, execute the following command.

[powershell]sc.exe --% qsidtype &quot;NetBackup Client Service&quot;[/powershell]

<i>Note: <strong>--%</strong> tells PowerShell to stop parsing the rest of the command. This is particularly useful when using legacy commands and applications.</i>
<h2>Add a Service SID as a Login</h2>
Once the service SID has been created and confirmed, it must be granted permission within SQL Server. This is accomplished by creating a Windows login using either SSMS or a query. The following example creates a login for the NetBackup Client Service using T-SQL:

[sql]CREATE LOGIN [NT SERVICE\NetBackup Client Service] FROM WINDOWS
GO[/sql]

Once the login is created, it can be granted permissions, added to roles, and mapped to databases just like any other login.

Note: If the service SID has been created on the service and login failures are still occurring for SYSTEM, ensure the login for the service SID was created and has been permissioned correctly.
<h1>Conclusion</h1>
In the world we live in it is imperative we limit access to our data, and can track who is accessing it. By limiting SYSTEM's access to the data and utilizing service SIDs, a DBA can accomplish both of these tasks with little to no effort.]]></content:encoded>
    </item>
    <item>
      <title>Decode PowerShell Command from Running Processes</title>
      <link>https://review.docs.microsoft.com/archive/blogs/powersql/decode-powershell-command-from-running-processes</link>
      <pubDate>Thu, 14 Dec 2017 20:14:41 GMT</pubDate>
      <dc:creator><![CDATA[randomnote_1]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/powersql/?p=35</guid>
      <description><![CDATA[There are times when I've found a PowerShell process running that is taking up a bunch of resources....]]></description>
      <content:encoded><![CDATA[There are times when I've found a PowerShell process running that is taking up a bunch of resources. Sometimes they've even been my own scripts running in the context of a SQL Server Agent Job with a PowerShell job step. Because I can have multiple PowerShell job steps running at a time, sometimes it's difficult to know what the offending job is. The following method can be used to decode a script block that a PowerShell process is currently running.

<hr />

<h1>Create a Long Running Process</h1>
To demonstrate this capability, create the following Agent Job. It executes a PowerShell job step that outputs a number every minute for 10 minutes.
<pre>USE [msdb]
GO
DECLARE @jobId BINARY(16)
EXEC msdb.dbo.sp_add_job @job_name=N'PowerShell Job', 
    @enabled=1,
    @notify_level_eventlog=0,
    @notify_level_email=2,
    @notify_level_page=2,
    @delete_level=0,
    @category_name=N'[Uncategorized (Local)]',
    @owner_login_name=N'sa'
GO

EXEC msdb.dbo.sp_add_jobserver @job_name=N'PowerShell Job', @server_name = @@SERVERNAME
GO

EXEC msdb.dbo.sp_add_jobstep @job_name=N'PowerShell Job', @step_name=N'PowerShell',
    @step_id=1,
    @cmdexec_success_code=0,
    @on_success_action=1,
    @on_fail_action=2,
    @retry_attempts=0,
    @retry_interval=0,
    @os_run_priority=0,
    @subsystem=N'PowerShell',
    @command=N'
        powershell.exe -Command {
            $i = 1
            while ( $i -le 10 )
            {
                Write-Output -InputObject $i
                Start-Sleep -Seconds 60
                $i++
            }
        }
    ', 
    @database_name=N'master', 
    @flags=0
GO

EXEC msdb.dbo.sp_update_job @job_name=N'PowerShell Job', 
    @enabled=1, 
    @start_step_id=1, 
    @notify_level_eventlog=0,
    @notify_level_email=2, 
    @notify_level_page=2, 
    @delete_level=0, 
    @description=N'', 
    @category_name=N'[Uncategorized (Local)]', 
    @owner_login_name=N'sa', 
    @notify_email_operator_name=N'', 
    @notify_page_operator_name=N''
GO</pre>
Execute the Agent Job that was just created.
<pre>USE msdb;
GO
EXEC dbo.sp_start_job N'PowerShell Job';
GO</pre>

<hr />

<h1>View the Process</h1>
<h2>Using Task Manager</h2>
<ul>
 	<li>Launch the Task Manager and select the <strong>Details</strong> tab.</li>
 	<li>Scroll down the list to powershell.exe.</li>
</ul>
<p style="padding-left: 60px"><img width="499" height="410" class="alignnone wp-image-55" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/12/TaskManagerDetails-1024x841.png" /></p>

<ul>
 	<li>Right-click the column headers in Task Manager and click <strong>Select Columns</strong>.</li>
</ul>
<p style="padding-left: 60px"><img width="499" height="410" class="alignnone wp-image-56" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/12/TaskManagerRightClick-1024x841.png" /></p>

<ul>
 	<li>Check <strong>Command Line</strong> in the Select columns dialogue and click <strong>OK</strong>.</li>
</ul>
<p style="padding-left: 60px"><img width="297" height="300" class="alignnone wp-image-65" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/12/TaskManagerSelectColumns.png" /></p>

<ul>
 	<li>Now the parameters that were passed into powershell.exe are visible. However, the command is still obfuscated as an encoded command.</li>
</ul>
<p style="padding-left: 60px"><img width="1024" height="206" class="alignnone size-large wp-image-75" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/12/TaskManagerWithCommandLine-1024x206.png" /></p>


<hr />

<h2>Using PowerShell</h2>
<ul>
 	<li>Start PowerShell as Administrator. It is vital that PowerShell is running as administrator, otherwise no results will be returned when querying the running processes.</li>
 	<li>Execute the following command to obtain all of the PowerShell processes that have an encoded command:</li>
</ul>
<pre style="padding-left: 60px">$powerShellProcesses = Get-CimInstance -ClassName Win32_Process -Filter 'CommandLine LIKE "%EncodedCommand%"'</pre>
<ul>
 	<li>The following command creates a custom PowerShell object that contains the process ID and the encoded command.</li>
</ul>
<div>
<div>
<pre style="padding-left: 60px"><span>$</span><span>commandDetails</span><span> </span><span>=</span><span> </span><span>$</span><span>powerShellProcesses</span><span> </span><span>|</span><span> </span><span>Select-Object</span><span> </span><span>-</span><span>Property ProcessId</span><span>,</span>
<span>@{</span>
<span>    name       </span><span>=</span><span> </span><span>'EncodedCommand'</span>
<span>    expression </span><span>=</span><span> {</span>
<span>        </span><span>if</span><span> </span><span>(</span><span> </span><span>$</span><span>_</span><span>.CommandLine</span><span> </span><span>-match</span><span> </span><span>'encodedCommand (.*) -inputFormat'</span><span> </span><span>)</span>
<span>        {</span>
<span>            </span><span>return</span><span> </span><span>$</span><span>matches</span><span>[</span><span>1</span><span>]</span>
<span>        }</span>
<span>    }</span>
<span>}</span></pre>
</div>
</div>
<ul>
 	<li>Now the encoded command can be decoded. The following snippet iterates over the command details object, decodes the encoded command, and adds the decoded command back to the object for further investigation.</li>
</ul>
<pre style="padding-left: 60px"><span>$</span><span>commandDetails</span><span> </span><span>|</span><span> </span><span>ForEach-Object</span><span> </span><span>-</span><span>Process</span><span> {</span>
<span>    </span><span>$</span><span>currentProcess</span><span> </span><span>=</span><span> </span><span>$</span><span>_</span>
<span>    </span><span>$</span><span>decodedCommand</span><span> </span><span>=</span><span> </span><span>[System.Text.Encoding]</span><span>::Unicode.GetString</span><span>(</span><span>[System.Convert]</span><span>::FromBase64String</span><span>($</span><span>currentProcess</span><span>.EncodedCommand</span><span>))</span>
<span>    </span><span>$</span><span>commandDetails</span><span> </span><span>|</span>
<span>        </span><span>Where-Object</span><span> </span><span>-</span><span>FilterScript { </span><span>$</span><span>_</span><span>.ProcessId</span><span> </span><span>-eq</span><span> </span><span>$</span><span>_</span><span>.ProcessId</span><span> } </span><span>|</span>
<span>        </span><span>Add-Member</span><span> </span><span>-</span><span>MemberType NoteProperty </span><span>-</span><span>Name DecodedCommand </span><span>-</span><span>Value </span><span>$</span><span>decodedCommand</span>
<span>}</span></pre>
<ul>
 	<li>The decoded command can now be reviewed by selecting the decoded command property.</li>
</ul>
<pre style="padding-left: 60px">$commandDetails[0].DecodedCommand</pre>

<hr />

There it is! Just another neat trick to add to the toolbox.]]></content:encoded>
    </item>
    <item>
      <title>Install SQL Server 2017 Using PowerShell Desired State Configuration and SqlServerDsc</title>
      <link>https://review.docs.microsoft.com/archive/blogs/powersql/install-sql-server-2017-using-powershell-desired-state-configuration-and-sqlserverdsc</link>
      <pubDate>Wed, 13 Dec 2017 16:44:35 GMT</pubDate>
      <dc:creator><![CDATA[randomnote_1]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/powersql/?p=46</guid>
      <description><![CDATA[How many times have you clicked through the SQL Server installation interface just clicking the same...]]></description>
      <content:encoded><![CDATA[How many times have you clicked through the SQL Server installation interface just clicking the same old buttons, entering the same old information, not giving it much of a second thought? Then the installation finishes and the realization of "I forgot to specify the DBA group in the sysadmins role" hits. Now you have to spend precious time dropping into single-user mode, adding the appropriate users/groups, bringing SQL back up in multi-user mode, and testing. What's worse is now the confidence of the entire installation is shaken. "What else did I forget?" I for one have been in that situation more than once.

Enter PowerShell Desired State Configuration (DSC). Using DSC I can build one configuration template that can be reused over hundreds and thousands of servers. Depending on the build, I may have to tweak a few of the setup parameters, but that's not a big deal because I can still keep all of the standard settings in place. The beautiful thing is it eliminates the possibility that will forget to enter an important parameter after spending a sleepless night caring for my kids.

In this article I will explore the initial setup of a standalone instance of SQL Server 2017 on Windows Server 2016 using the SqlServerDsc DSC resource. Some prior knowledge of DSC will be helpful as I will not explore the hows and whys of how DSC works.

The following items are required for this walkthrough:
<ul>
 	<li>A machine running Windows Server 2016</li>
 	<li><a href="https://www.microsoft.com/en-us/sql-server/sql-server-2017">SQL Server 2017</a> installation media</li>
 	<li>The <a href="https://www.powershellgallery.com/packages/SqlServerDsc">SqlServerDsc</a> DSC resource (version 10.0.0.0 is the current release at the time of this writing)</li>
</ul>

<hr />

<h1></h1>
<h1>Prerequisites</h1>
In most cases DSC will be used to handle the prerequisite requirements. However, for the purposes of this demo, I will handle the prerequisites manually.
<h2>Install the SqlServerDsc DSC Resource</h2>
The <a href="https://www.powershellgallery.com/packages/SqlServerDsc">SqlServerDsc</a><span style="float: none;background-color: transparent;color: #333333;cursor: text;font-family: Georgia,'Times New Roman','Bitstream Charter',Times,serif;font-size: 16px;font-style: normal;font-variant: normal;font-weight: 400;letter-spacing: normal;line-height: 24px;text-align: left;text-decoration: none;text-indent: 0px"> DSC resource</span> can be downloaded from the <a href="https://www.powershellgallery.com/">PowerShell Gallery</a> using the <a href="https://docs.microsoft.com/en-us/powershell/module/powershellget/Install-Module?view=powershell-5.1">Install-Module</a> cmdlet. <em>Note: Ensure PowerShell is running "As Administrator" to install the module.</em>
<pre>Install-Module -Name SqlServerDsc</pre>
<h2>Obtain the SQL Server 2017 Installation Media</h2>
Download the SQL Server 2017 installation media to the server. I downloaded SQL Server 2017 Enterprise from my Visual Studio subscription and copied the ISO to "C:\en_sql_server_2017_enterprise_x64_dvd_11293666.iso".

Now the ISO must be extracted to a directory.
<pre>New-Item -Path C:\SQL2017 -ItemType Directory
$mountResult = Mount-DiskImage -ImagePath 'C:\en_sql_server_2017_enterprise_x64_dvd_11293666.iso' -PassThru
$volumeInfo = $mountResult | Get-Volume
$driveInfo = Get-PSDrive -Name $volumeInfo.DriveLetter
Copy-Item -Path ( Join-Path -Path $driveInfo.Root -ChildPath '*' ) -Destination C:\SQL2017\ -Recurse
Dismount-DiskImage -ImagePath 'C:\en_sql_server_2017_enterprise_x64_dvd_11293666.iso'</pre>

<hr />

<h1>Create the Configuration</h1>
<h2>Configuration</h2>
Create the configuration function which will be called to generate the MOF document(s).
<pre>Configuration SQLInstall
{...}</pre>
<h2>Modules</h2>
Import the modules into the current session. These tell the configuration document how to build the MOF document(s) and tells the DSC engine how to apply the MOF document(s) to the server.
<h2>Resources</h2>
<h3>.NET Framework</h3>
SQL Server relies on the .NET framework, therefore we need to ensure it is installed prior to installing SQL Server. In order to accomplish this, the WindowsFeature resource is utilized to install the Net-Framework-45-Core Windows Feature.
<pre>WindowsFeature 'NetFramework45'
{
     Name = 'Net-Framework-45-Core'
     Ensure = 'Present'
}</pre>
<h3>SqlSetup</h3>
The SqlSetup resource is used to tell DSC how to install SQL Server. The parameters required for a basic installation are:
<ul>
 	<li><strong>InstanceName</strong> - The name of the instance. Utilize MSSQLSERVER for a default instance.</li>
 	<li><strong>Features</strong> - The features to install. In this example I am only installing the SQLEngine feature.</li>
 	<li><strong>SourcePath</strong> - The path to the SQL installation media. In this example I stored the SQL installation media in "C:\SQL2017". A network share can be utilized to minimize the space used on the server.</li>
 	<li><strong>SQLSysAdminAccounts</strong> - The users or groups who are to be a member of the sysadmin role. In this example I am granting the local Administrators group sysadmin access. <em>Note: This configuration is not recommended in a high security environment.</em></li>
</ul>
A full list and description of the parameters available on SqlSetup are available on the <a href="https://github.com/PowerShell/SqlServerDsc/tree/master#sqlsetup">SqlServerDsc GitHub respository</a>.

The SqlSetup resource is odd because it only installs SQL and DOES NOT maintain the settings that are applied. For example, if the SQLSysAdminAccounts are specified at installation time, an admin could add or remove logins from to/from the sysadmin role and the SqlSetup resource wouldn't care. If it is desired that DSC enforces the membership of the sysadmin role, the <a href="https://github.com/PowerShell/SqlServerDsc/tree/master#sqlserverrole">SqlServerRole</a> resource should be utilized.
<h3>Complete Configuration</h3>
<pre>Configuration SQLInstall
{
     Import-DscResource -ModuleName SqlServerDsc
     node localhost
     {
          WindowsFeature 'NetFramework45'
          {
               Name   = 'NET-Framework-45-Core'
               Ensure = 'Present'
          }

          SqlSetup 'InstallDefaultInstance'
          {
               InstanceName        = 'MSSQLSERVER'
               Features            = 'SQLENGINE'
               SourcePath          = 'C:\SQL2017'
               SQLSysAdminAccounts = @('Administrators')
               DependsOn           = '[WindowsFeature]NetFramework45'
          }
     }
}</pre>

<hr />

<h1>Build and Deploy</h1>
<h2>Compile the Configuration</h2>
Dot-source the configuration script:
<pre>. .\SQLInstallConfiguration.ps1</pre>
Execute the configuration function:
<pre>SQLInstall</pre>
A directory will be created in the working directory called "SQLInstall" and will contain a file call "localhost.mof". Examining the contents of the MOF will show the compiled DSC configuration.
<h2>Deploy the Configuration</h2>
To start the DSC deployment of SQL Server, call the Start-DscConfiguration cmdlet. The parameters provided to the cmdlet are:
<ul>
 	<li>Path - The path to the folder containing the MOF documents to deploy. (eg. "C:\SQLInstall")</li>
 	<li>Wait - Wait for the configuration job to complete.</li>
 	<li>Force - Override any existing DSC configurations.</li>
 	<li>Verbose - Show the verbose output. This is handy when pushing a configuration for the first time to aid in troubleshooting.</li>
</ul>
<pre>Start-DscConfiguration -Path C:\SQLInstall -Wait -Force -Verbose</pre>
As the configuration applies, the Verbose output will show you what is happening and give you a warm and fuzzy feeling that SOMETHING is happening. As long as no errors (red text) are thrown, when "Operation 'Invoke CimMethod' complete." is displayed on the screen, SQL should be installed.

<hr />

<h1>Validate Installation</h1>
<h2>DSC</h2>
The Test-DscConfiguration cmdlets can be utilized to determine if the current state of the server, in this case the SQL installation, meets the desired state. The result of Test-DscConfiguration should be "True".
<pre>PS C:\&gt; Test-DscConfiguration
True</pre>
<h2>Services</h2>
The services listing should now return SQL Server services
<pre>PS C:\&gt; Get-Service -Name *SQL*
Status  Name           DisplayName
------  ----           -----------
Running MSSQLSERVER    SQL Server (MSSQLSERVER)
Stopped SQLBrowser     SQL Server Browser
Running SQLSERVERAGENT SQL Server Agent (MSSQLSERVER)
Running SQLTELEMETRY   SQL Server CEIP service (MSSQLSERVER)
Running SQLWriter      SQL Server VSS Writer</pre>
<h2>SQL Server</h2>
<pre>PS C:\&gt; &amp; 'C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\SQLCMD.EXE' -S $env:COMPUTERNAME
1&gt; SELECT @@SERVERNAME
2&gt; GO
1&gt; quit</pre>
<h2>That's a Wrap</h2>
And there you have it. Installing SQL Server in a consistent manner has never been easier.

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Dynamically Determine a DSC Configuration Name Inside a Configuration</title>
      <link>https://review.docs.microsoft.com/archive/blogs/powersql/dynamically-determine-a-dsc-configuration-name-inside-a-configuration</link>
      <pubDate>Tue, 31 Oct 2017 08:47:11 GMT</pubDate>
      <dc:creator><![CDATA[randomnote_1]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/powersql/?p=36</guid>
      <description><![CDATA[I ran across a scenario where I needed to reference the name of the configuration inside the...]]></description>
      <content:encoded><![CDATA[I ran across a scenario where I needed to reference the name of the configuration inside the configuration. I was looking for an automatic variable that contained this information, however I was unable to locate one searching online or by asking colleagues. The suggestions I received were to call the <code>Get-PSCallStack</code> cmdlet or use the <code>$MyInvocation</code> automatic variable.

To test these options, I created a small config and wrote the output of the options using <code>Write-Verbose</code>.
<h1>Get-PSCallStack</h1>
<pre>Configuration MyConfig
{
    Write-Verbose -Message ( Get-PSCallStack | Format-Table -AutoSize | Out-String )
}

MyConfig -Verbose

VERBOSE:
Command       Arguments
-------       ---------
              {}
Configuration {ArgsToBody=System.Collections.Hashtable, Name=MyConfig, ResourceModuleTuplesToImport=, Body=...
MyConfig      {Verbose=True}
&lt;ScriptBlock&gt; {}</pre>
The configuration name "MyConfig" is the third item in the array, and therefore would be referenced like `(Get-PSCallStack)[2].Command`. Not terribly bad, but not as simple as I would like.
<h1>$MyInvocation<strong></strong></h1>
<pre>Configuration MyConfig
{
    Write-Verbose -Message ( $MyInvocation | Format-List -Property * | Out-String )
}

MyConfig -Verbose

VERBOSE: 

MyCommand             : 
BoundParameters       : {}
UnboundArguments      : {}
ScriptLineNumber      : 7
OffsetInLine          : 1
HistoryId             : 14
ScriptName            : 
Line                  : MyConfig -Verbose
PositionMessage       : At line:7 char:1
                        + MyConfig -Verbose
                        + ~~~~~~~~~~~~~~~~~
PSScriptRoot          : 
PSCommandPath         : 
InvocationName        : 
PipelineLength        : 0
PipelinePosition      : 0
ExpectingInput        : False
CommandOrigin         : Internal
DisplayScriptPosition :</pre>
The "Line" property of $MyInvocation contains the full command that was executed. I could use some string manipulation to retrieve the name of the configuration, however <code>Get-PSCallStack</code> is simpler at this point.
<h1>$MyTypeName</h1>
At this point, <code>Get-PSCallStack</code> looks like the best option, however I was still curious if there is an automatic variable that contains only the configuration name. To investigate the variables available inside the configuration, I executed the following:
<pre>Configuration MyConfig
{
    Write-Verbose -Message ( Get-Variable | Where-Object -FilterScript { $_.Value -match '^MyConfig$' } | Out-String )
}

MyConfig -Verbose

VERBOSE:
Name       Value 
----       -----
MyTypeName MyConfig</pre>
Success! There is an automatic variable called <code>$MyTypeName</code> that contains only my configuration name. This is the easy button option I was looking for.]]></content:encoded>
    </item>
    <item>
      <title>Install SQL Server 2017 Using PowerShell Desired State Configuration</title>
      <link>https://review.docs.microsoft.com/archive/blogs/powersql/install-sql-server-2017-using-powershell-desired-state-configuration</link>
      <pubDate>Tue, 10 Oct 2017 05:00:12 GMT</pubDate>
      <dc:creator><![CDATA[randomnote_1]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/powersql/?p=25</guid>
      <description><![CDATA[Update: The xSQLServer DSC resource has been renamed to SqlServerDsc. An updated version of this...]]></description>
      <content:encoded><![CDATA[<em>Update: </em><em>The <a href="https://www.powershellgallery.com/packages/xSQLServer">xSQLServer</a> DSC resource has been renamed to <a href="https://www.powershellgallery.com/packages/SqlServerDsc">SqlServerDsc</a>. An updated version of this article has been created and published <a href="https://blogs.msdn.microsoft.com/powersql/2017/12/13/install-sql-server-2017-using-powershell-desired-state-configuration-and-sqlserverdsc/">here</a>.</em>

<hr />

How many times have you clicked through the SQL Server installation interface just clicking the same old buttons, entering the same old information, not giving it much of a second thought? Then the installation finishes and the realization of "I forgot to specify the DBA group in the sysadmins role" hits. Now you have to spend precious time dropping into single-user mode, adding the appropriate users/groups, bringing SQL back up in multi-user mode, and testing. What's worse is now the confidence of the entire installation is shaken. "What else did I forget?" I for one have been in that situation more than once.

Enter PowerShell Desired State Configuration (DSC). Using DSC I can build one configuration template that can be reused over hundreds and thousands of servers. Depending on the build, I may have to tweak a few of the setup parameters, but that's not a big deal because I can still keep all of the standard settings in place. The beautiful thing is it eliminates the possibility that will forget to enter an important parameter after spending a sleepless night caring for my kids.

In this article I will explore the initial setup of a standalone instance of SQL Server 2017 on Windows Server 2016 using the xSQLServer DSC resource. Some prior knowledge of DSC will be helpful as I will not explore the hows and whys of how DSC works.

The following items are required for this walkthrough:
<ul>
 	<li>A machine running Windows Server 2016</li>
 	<li><a href="https://www.microsoft.com/en-us/sql-server/sql-server-2017">SQL Server 2017</a> installation media</li>
 	<li>The <a href="https://www.powershellgallery.com/packages/xSQLServer">xSQLServer </a>DSC resource (version 8.1.0.0 is the current release at the time of this writing)</li>
</ul>
<h1>Prerequisites</h1>
In most cases DSC will be used to handle the prerequisite requirements. However, for the purposes of this demo, I will handle the prerequisites manually.
<h2>Install the xSQLServer DSC Resource</h2>
The <a href="https://www.powershellgallery.com/packages/xSQLServer">xSQLServer </a>DSC Resource can be downloaded from the <a href="https://www.powershellgallery.com/">PowerShell Gallery</a> using the <a href="https://docs.microsoft.com/en-us/powershell/module/powershellget/Install-Module?view=powershell-5.1">Install-Module</a> cmdlet. <em>Note: Ensure PowerShell is running "As Administrator" to install the module.</em>
<pre>Install-Module -Name xSQLServer</pre>
<h2>Obtain the SQL Server 2017 Installation Media</h2>
Download the SQL Server 2017 installation media to the server. I downloaded SQL Server 2017 Enterprise from my Visual Studio subscription and copied the ISO to "C:\en_sql_server_2017_enterprise_x64_dvd_11293666.iso".

Now the ISO must be extracted to a directory.
<pre>New-Item -Path C:\SQL2017 -ItemType Directory
$mountResult = Mount-DiskImage -ImagePath 'C:\en_sql_server_2017_enterprise_x64_dvd_11293666.iso' -PassThru
$volumeInfo = $mountResult | Get-Volume
$driveInfo = Get-PSDrive -Name $volumeInfo.DriveLetter
Copy-Item -Path ( Join-Path -Path $driveInfo.Root -ChildPath '*' ) -Destination C:\SQL2017\ -Recurse
Dismount-DiskImage -ImagePath 'C:\en_sql_server_2017_enterprise_x64_dvd_11293666.iso'</pre>
<h1>Create the Configuration</h1>
<h2>Configuration</h2>
Create the configuration function which will be called to generate the MOF document(s).
<pre>Configuration SQLInstall
{...}</pre>
<h2>Modules</h2>
Import the modules into the current session. These tell the configuration document how to build the MOF document(s) and tells the DSC engine how to apply the MOF document(s) to the server.
<h2>Resources</h2>
<h3>.NET Framework</h3>
SQL Server relies on the .NET framework, therefore we need to ensure it is installed prior to installing SQL Server. In order to accomplish this, the WindowsFeature resource is utilized to install the Net-Framework-45-Core Windows Feature.
<pre>WindowsFeature 'NetFramework45'
{
     Name = 'Net-Framework-45-Core'
     Ensure = 'Present'
}</pre>
<h3>xSQLServerSetup</h3>
The xSQLServerSetup resource is used to tell DSC how to install SQL Server. The parameters required for a basic installation are:
<ul>
 	<li><strong>InstanceName</strong> - The name of the instance. Utilize MSSQLSERVER for a default instance.</li>
 	<li><strong>Features</strong> - The features to install. In this example I am only installing the SQLEngine feature.</li>
 	<li><strong>SourcePath</strong> - The path to the SQL installation media. In this example I stored the SQL installation media in "C:\SQL2017". A network share can be utilized to minimize the space used on the server.</li>
 	<li><strong>SQLSysAdminAccounts</strong> - The users or groups who are to be a member of the sysadmin role. In this example I am granting the local Administrators group sysadmin access. <em>Note: This configuration is not recommended in a high security environment.</em></li>
</ul>
A full list and description of the parameters available on xSQLServerSetup are available on the <a href="https://github.com/PowerShell/xSQLServer/tree/master#xsqlserversetup">xSQLServer GitHub respository</a>.

The xSQLServerSetup resource is odd because it only installs SQL and DOES NOT maintain the settings that are applied. For example, if the SQLSysAdminAccounts are specified at installation time, an admin could add or remove logins from to/from the sysadmin role and the xSQLServerSetup resource wouldn't care. If it is desired that DSC enforces the membership of the sysadmin role, the <a href="https://github.com/PowerShell/xSQLServer/tree/master#xsqlserverrole">xSQLServerRole</a> resource should be utilized.
<h3>Complete Configuration</h3>
<pre>Configuration SQLInstall
{
     Import-DscResource -ModuleName xSQLServer
     node localhost
     {
          WindowsFeature 'NetFramework45'
          {
               Name = 'NET-Framework-45-Core'
               Ensure = 'Present'
          }

          xSQLServerSetup 'InstallDefaultInstance'
          {
               InstanceName = 'MSSQLSERVER'
               Features = 'SQLENGINE'
               SourcePath = 'C:\SQL2017'
               SQLSysAdminAccounts = @('Administrators')
               DependsOn = '[WindowsFeature]NetFramework45'
          }
     }
}</pre>
<h1>Build and Deploy</h1>
<h2>Compile the Configuration</h2>
Dot-source the configuration script:
<pre>. .\SQLInstallConfiguration.ps1</pre>
Execute the configuration function:
<pre>SQLInstall</pre>
A directory will be created in the working directory called "SQLInstall" and will contain a file call "localhost.mof". Examining the contents of the MOF will show the compiled DSC configuration.
<h2>Deploy the Configuration</h2>
To start the DSC deployment of SQL Server, call the Start-DscConfiguration cmdlet. The parameters provided to the cmdlet are:
<ul>
 	<li>Path - The path to the folder containing the MOF documents to deploy. (eg. "C:\SQLInstall")</li>
 	<li>Wait - Wait for the configuration job to complete.</li>
 	<li>Force - Override any existing DSC configurations.</li>
 	<li>Verbose - Show the verbose output. This is handy when pushing a configuration for the first time to aid in troubleshooting.</li>
</ul>
<pre>Start-DscConfiguration -Path C:\SQLInstall -Wait -Force -Verbose</pre>
As the configuration applies, the Verbose output will show you what is happening and give you a warm and fuzzy feeling that SOMETHING is happening. As long as no errors (red text) are thrown, when "Operation 'Invoke CimMethod' complete." is displayed on the screen, SQL should be installed.
<h1>Validate Installation</h1>
<h2>DSC</h2>
The Test-DscConfiguration cmdlets can be utilized to determine if the current state of the server, in this case the SQL installation, meets the desired state. The result of Test-DscConfiguration should be "True".
<pre>PS C:\&gt; Test-DscConfiguration
True</pre>
<h2>Services</h2>
The services listing should now return SQL Server services
<pre>PS C:\&gt; Get-Service -Name *SQL*
Status Name DisplayName
------ ---- -----------
Running MSSQLSERVER SQL Server (MSSQLSERVER)
Stopped SQLBrowser SQL Server Browser
Running SQLSERVERAGENT SQL Server Agent (MSSQLSERVER)
Running SQLTELEMETRY SQL Server CEIP service (MSSQLSERVER)
Running SQLWriter SQL Server VSS Writer</pre>
<h2>SQL Server</h2>
<pre>PS C:\&gt; &amp; 'C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\SQLCMD.EXE' -S $env:COMPUTERNAME
1&gt; SELECT @@SERVERNAME
2&gt; GO
1&gt; quit</pre>
<h2>That's a Wrap</h2>
And there you have it. Installing SQL Server in a consistent manner has never been easier.

&nbsp;]]></content:encoded>
    </item>
  </channel>
</rss>