<?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>GoateePFE - Archived</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/ashleymcglone/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/feed.xml</link>
    <description>Archived blog of former Microsoft Premier Field Engineer Ashley McGlone featuring PowerShell scripts for the real world.</description>
    <lastBuildDate>Fri, 01 Sep 2017 11:33:15 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>New-TimeSpan -Start '9/1/2017'</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/new-timespan-start-912017</link>
      <pubDate>Fri, 01 Sep 2017 03:00:17 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2396</guid>
      <description><![CDATA[Dear readers of the GoateePFE blog,
It is with mixed emotion that I announce after seven years of...]]></description>
      <content:encoded><![CDATA[<h2><img width="183" height="300" class="alignright size-medium wp-image-2416" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/08/Ashley_jpg-200x0-183x300.jpg" />Dear readers of the GoateePFE blog,</h2>
It is with mixed emotion that I announce after seven years of real-world script blogging this is the final GoateePFE blog post. I have chosen to take the next step in my career with a company outside of Microsoft. My new role will involve automation, security, and customers.

When speaking at conferences it has been a pleasure and honor to meet people impacted by the GoateePFE blog posts and videos. I have enjoyed your Tweets and post comments as well. <strong>Please continue to share this content with others when it is helpful.</strong>
<h2>Thank You</h2>
You may not be aware that you have played a significant role in my career. Each review cycle I would give my manager a count of blog visits, demonstrating value delivered to the global community, to you. Sometimes I would include your tweets and comments, validating that I was connecting with real issues and providing real answers. Thanks for reading and sharing in the journey with me.
<h2>What will happen to "GoateePFE"?</h2>
My <a target="_blank" href="https://aka.ms/goateepfe" rel="noopener noreferrer" title="https://aka.ms/goateepfe">TechNet blog</a> will remain for years to come, although I will no longer be able to update it. I will keep the <a target="_blank" href="https://twitter.com/GoateePFE" rel="noopener noreferrer" title="https://twitter.com/GoateePFE">@GoateePFE Twitter</a> handle to maintain contact with the PowerShell community. My PowerShell video content will remain on <a target="_blank" href="https://mva.microsoft.com/search/SearchResults.aspx#!q=ashley%20mcglone&amp;lang=1033" rel="noopener noreferrer" title="https://mva.microsoft.com/search/SearchResults.aspx#!q=ashley%20mcglone&amp;lang=1033">Microsoft Virtual Academy</a> and the Microsoft Premier video education subscription. My <a target="_blank" href="https://www.facebook.com/GoateePFE/" rel="noopener noreferrer" title="https://www.facebook.com/GoateePFE/">Facebook</a> and <a target="_blank" href="https://github.com/GoateePFE" rel="noopener noreferrer" title="https://github.com/GoateePFE">GitHub</a> locations will also remain. My next role will contain significant automation responsibilities, so <strong>I plan to continue involvement in the PowerShell community on some level</strong>.
<h2>Career Advice</h2>
In <a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/2014/05/14/career-tip-the-power-of-a-niche/" rel="noopener noreferrer" title="https://blogs.technet.microsoft.com/ashleymcglone/2014/05/14/career-tip-the-power-of-a-niche/">this post a few years back</a> I shared some tips for career success. I hope that the content provided on the blog has helped you make your own mark on the world. I firmly believe that community participation is one of the best things you can do to advance your career and advance the industry. Join the conversation.

00100010 01000001 00100000 01100111 01101100 01101001 01110100 01100011 01101000 00111111 00100000 01001110 01101111 00101100 00100000 01110100 01101000 01100001 01110100 00100111 01110011 00100000 01101110 01101111 01110100 00100000 01110000 01101111 01110011 01110011 01101001 01100010 01101100 01100101 00101110 00100000 01001001 00100000 01110000 01110010 01101111 01100111 01110010 01100001 01101101 01101101 01100101 01100100 00100000 01101001 01110100 00100000 01101101 01111001 01110011 01100101 01101100 01100110 00101110 00100010 00100000 00101101 00100000 01000110 01110010 01100101 01100100 00100000 01010010 01100001 01101110 01100100 01100001 01101100 01101100 00100000 00101101 00100000 01010010 01101111 01100011 01101011 01100101 01110100 01101101 01100001 01101110

Cheers,

Mr. Ashley McGlone

<a target="_blank" href="https://twitter.com/GoateePFE" rel="noopener noreferrer" title="https://twitter.com/GoateePFE">@GoateePFE</a>]]></content:encoded>
    </item>
    <item>
      <title>New, Improved Group Policy Link Report with PowerShell</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/new-improved-group-policy-link-report-with-powershell</link>
      <pubDate>Thu, 31 Aug 2017 12:41:21 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2425</guid>
      <description><![CDATA[A peer asked me to update one of my classic Group Policy reporting scripts this week, so I thought I...]]></description>
      <content:encoded><![CDATA[<img width="99" height="99" class="alignright size-full wp-image-2435" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/08/GPMC.png" />A peer asked me to update one of my classic Group Policy reporting scripts this week, so I thought I would share the update with y'all.
<h2>Continuous Improvement</h2>
Over the years I have released a <a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/tag/gpo/" rel="noopener noreferrer">number of Group Policy scripts</a>. This one shows you all kinds of goodness:
<ul>
 	<li>GPOs linked to OUs</li>
 	<li>OUs where block-inheritance may be turned on</li>
 	<li>Situations where no-override is used</li>
 	<li>Forensic data about the last time a GPO was linked or updated on an OU</li>
</ul>
By popular demand the improvements in this release are:
<ul>
 	<li>Unlinked GPOs are included</li>
 	<li>Columns are added for easy filtering where User or Computer version do not match</li>
 	<li>Output no longer defaults to CSV, so that you can pipe the output wherever you like</li>
</ul>
<h2>Show me some code</h2>
Call the script like this:
<pre>.\gPLinkReport.ps1 | Out-GridView
.\gPLinkReport.ps1 | Export-Csv -Path .\GPLinkReport.csv -NoTypeInformation</pre>
You can download today's script from the <a target="_blank" href="https://www.powershellgallery.com/packages/gPLinkReport" rel="noopener noreferrer">PowerShell Gallery here</a>.]]></content:encoded>
    </item>
    <item>
      <title>Function to Create Certificate Template in Active Directory Certificate Services for PowerShell DSC and CMS Encryption</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/function-to-create-certificate-template-in-active-directory-certificate-services-for-powershell-dsc-and-cms-encryption</link>
      <pubDate>Tue, 29 Aug 2017 12:08:35 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2365</guid>
      <description><![CDATA[Today I’m cleaning out my code closet. I found this script that I have wanted to share for a while...]]></description>
      <content:encoded><![CDATA[<img width="300" height="300" class="alignright size-medium wp-image-2375" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/08/CodeCat-300x300.png" />Today I’m cleaning out my code closet. I found this script that I have wanted to share for a while now.
<h3>Problem</h3>
Active Directory Certificate Services does not include a template for Document Encryption. This is required for <a target="_blank" href="https://docs.microsoft.com/en-us/powershell/dsc/securemof#certificate-requirements" rel="noopener noreferrer" title="https://docs.microsoft.com/en-us/powershell/dsc/securemof#certificate-requirements">DSC credential encryption</a> and the <a target="_blank" href="https://docs.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Security/Protect-CmsMessage" rel="noopener noreferrer" title="https://docs.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Security/Protect-CmsMessage">CMS encryption cmdlets</a>. Current processes require manual effort to create the template. Or you must figure out how to use the less-than-friendly AD CS API from .NET. We all know I ain’t no .NET developer.
<h3>Solution</h3>
I reverse-engineered the resulting OID and certificate objects in Active Directory and wrote a function to create this template from code. This provides a <strong>fully-automated solution for creating the template in a lab or production environment</strong>.

Functionality
<ul>
 	<li>Take parameters</li>
 	<li>Generate a unique OID for the template</li>
 	<li>Create the template</li>
 	<li>Optionally permission the template with Enroll for specified group(s)</li>
 	<li>Optionally add AutoEnroll permission as well</li>
 	<li>Optionally publish the template to CA(s)</li>
 	<li>Optionally target all operations to a designated DC</li>
</ul>
Requirements:
<ul>
 	<li>Enterprise AD CS PKI</li>
 	<li>Tested on Windows Server 2012 R2 &amp; 2016</li>
 	<li>Enterprise Administrator rights</li>
 	<li>ActiveDirectory PowerShell Module</li>
</ul>
Template generated will have these properties:
<ul>
 	<li>2 year lifetime</li>
 	<li>2003 lowest compatibility level</li>
 	<li>Private key not exportable</li>
 	<li>Not stored in AD</li>
 	<li>Document Encryption</li>
 	<li>No digital signature</li>
</ul>
<h3>Sample Usage</h3>
<pre># Create only the template
# Least valuable approach
New-ADCSTemplateForPSEncryption -DisplayName PowerShellCMS</pre>
<pre># Full template creation, permissioning, and deployment
New-ADCSTemplateForPSEncryption -DisplayName PSEncryption `
-Server dc1.contoso.com -GroupName G_DSCNodes -AutoEnroll -Publish

# From a client configured via GPO for AD CS autoenrollment:
$Req = @{
    Template          = 'PSEncryption'
    Url               = 'ldap:'
    CertStoreLocation = 'Cert:\LocalMachine\My'
}
Get-Certificate @Req
# Note: If you have the Carbon module installed, it conflicts with Get-Certificate native cmdlet.

$DocEncrCert = (dir Cert:\LocalMachine\My -DocumentEncryptionCert |
 Sort-Object NotBefore)[-1]

Protect-CmsMessage -To $DocEncrCert -Content "Encrypted with my new cert from the new template!"</pre>
<h3>Get the code</h3>
I have posted this code to the <a target="_blank" href="https://www.powershellgallery.com/packages/ADCSTemplateForPSEncryption" rel="noopener noreferrer" title="https://www.powershellgallery.com/packages/ADCSTemplateForPSEncryption">PowerShell Gallery here</a>. Enjoy!]]></content:encoded>
    </item>
    <item>
      <title>Use Hash Tables To Go Faster Than PowerShell Compare-Object</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/use-hash-tables-to-go-faster-than-powershell-compare-object</link>
      <pubDate>Mon, 07 Aug 2017 14:44:10 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2356</guid>
      <description><![CDATA[Compare-Object gotcha down? Slower than my old 300 baud modem? Have no fear. Today we go faster...]]></description>
      <content:encoded><![CDATA[<img width="109" height="300" class="alignright size-medium wp-image-2337" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/08/fighter-pilot-300px-109x300.png" />Compare-Object gotcha down? Slower than my old 300 baud modem? Have no fear. Today we go faster using hash tables.

Let me state first that I love the cmdlet Compare-Object, and I have used it many times with great results. But at scale my customer had some serious performance issues.
<h2>The Problem - <em>“I feel the need. The need for speed.”</em></h2>
So my customer has employed all the tricks from <a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/2017/07/12/slow-code-top-5-ways-to-make-your-powershell-scripts-run-faster/" rel="noopener noreferrer" title="https://blogs.technet.microsoft.com/ashleymcglone/2017/07/12/slow-code-top-5-ways-to-make-your-powershell-scripts-run-faster/">the last blog post on making your scripts go faster</a>. But still the script takes hours to run. Between each command he dropped a timestamp into a log file. The culprit… Compare-Object. That single command was taking hours.

But let’s be fair. He’s comparing about 800,000 email addresses between two lists. It would take me weeks to do that by hand with a pencil and paper. Compare-Object is pretty quick at 13 hours. But let’s get this down to seconds.
<h2>The Research</h2>
First things first. What exactly is Compare-Object doing? To find out, view the <a target="_blank" href="https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Utility/commands/utility/compare-object.cs" rel="noopener noreferrer" title="https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Utility/commands/utility/compare-object.cs">source code over at the PowerShell open source GitHub location</a>. So I did that. But I’m not a .NET developer. However, I did notice the comments starting on line 120 helped me understand what it does. That is very similar to my idea.

All I know is that when I want list processing to go faster in PowerShell I use <a target="_blank" href="https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_hash_tables" rel="noopener noreferrer" title="https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_hash_tables">hash tables</a>. I’ll write my own version in native PowerShell and see if it is faster.
<h2>The Approach</h2>
We have two lists, and we need to know what is different. We want to make the most efficient use of both memory and computation.

If I compare every item in List1 against every item in List2, well, that’s going to take a while (n*m).

Each list comes in as an array. I need to look up all the items in List1 against List2. The fastest way to do lookups is with a hash table.

To find the differences, I will delete the matching entries from both List1 and List2. Arrays are slow at removing a single item, so again I will use hash tables.

After deleting all of the equal values, the only things left in each list are the unique values.

If you want to see what is equal, then I will stuff that into a third list (hash table) containing only the equal values.
<h2>The Code</h2>
I have placed the hash table comparison into a function called Compare-Object2.
<pre class="PowerShellColorizedScript"><span style="color: #006400">&lt;#
.SYNOPSIS
Faster version of Compare-Object for large data sets with a single value.
.DESCRIPTION
Uses hash tables to improve comparison performance for large data sets.
.PARAMETER ReferenceObject
Specifies an array of objects used as a reference for comparison.
.PARAMETER DifferenceObject
Specifies the objects that are compared to the reference objects.
.PARAMETER IncludeEqual
Indicates that this cmdlet displays characteristics of compared objects that
are equal. By default, only characteristics that differ between the reference
and difference objects are displayed.
.PARAMETER ExcludeDifferent
Indicates that this cmdlet displays only the characteristics of compared
objects that are equal.
.EXAMPLE
Compare-Object2 -ReferenceObject 'a','b','c' -DifferenceObject 'c','d','e' `
    -IncludeEqual -ExcludeDifferent
.EXAMPLE
Compare-Object2 -ReferenceObject (Get-Content .\file1.txt) `
    -DifferenceObject (Get-Content .\file2.txt)
.EXAMPLE
$p1 = Get-Process
notepad
$p2 = Get-Process
Compare-Object2 -ReferenceObject $p1.Id -DifferenceObject $p2.Id
.NOTES
Does not support objects with properties. Expand the single property you want
to compare before passing it in.
Includes optimization to run even faster when -IncludeEqual is omitted.
#&gt;</span>            
<span style="color: #00008b">function</span> <span style="color: #8a2be2">Compare-Object2</span> <span style="color: #000000">{</span>            
<span style="color: #00008b">param</span><span style="color: #000000">(</span>            
    <span style="color: #008080">[psobject[]]</span>            
    <span style="color: #ff4500">$ReferenceObject</span><span style="color: #a9a9a9">,</span>            
    <span style="color: #008080">[psobject[]]</span>            
    <span style="color: #ff4500">$DifferenceObject</span><span style="color: #a9a9a9">,</span>            
    <span style="color: #008080">[switch]</span>            
    <span style="color: #ff4500">$IncludeEqual</span><span style="color: #a9a9a9">,</span>            
    <span style="color: #008080">[switch]</span>            
    <span style="color: #ff4500">$ExcludeDifferent</span>            
<span style="color: #000000">)</span>            
            
    <span style="color: #006400"># Put the difference array into a hash table,</span>            
    <span style="color: #006400"># then destroy the original array variable for memory efficiency.</span>            
    <span style="color: #ff4500">$DifHash</span> <span style="color: #a9a9a9">=</span> <span style="color: #000000">@{</span><span style="color: #000000">}</span>            
    <span style="color: #ff4500">$DifferenceObject</span> <span style="color: #a9a9a9">|</span> <span style="color: #0000ff">ForEach-Object</span> <span style="color: #000000">{</span><span style="color: #ff4500">$DifHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Add</span><span style="color: #000000">(</span><span style="color: #ff4500">$_</span><span style="color: #a9a9a9">,</span><span style="color: #ff4500">$null</span><span style="color: #000000">)</span><span style="color: #000000">}</span>            
    <span style="color: #0000ff">Remove-Variable</span> <span style="color: #000080">-Name</span> <span style="color: #8a2be2">DifferenceObject</span>            
            
    <span style="color: #006400"># Put the reference array into a hash table.</span>            
    <span style="color: #006400"># Keep the original array for enumeration use.</span>            
    <span style="color: #ff4500">$RefHash</span> <span style="color: #a9a9a9">=</span> <span style="color: #000000">@{</span><span style="color: #000000">}</span>            
    <span style="color: #00008b">for</span> <span style="color: #000000">(</span><span style="color: #ff4500">$i</span><span style="color: #a9a9a9">=</span><span style="color: #800080">0</span><span style="color: #000000">;</span><span style="color: #ff4500">$i</span> <span style="color: #a9a9a9">-lt</span> <span style="color: #ff4500">$ReferenceObject</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Count</span><span style="color: #000000">;</span><span style="color: #ff4500">$i</span><span style="color: #a9a9a9">++</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>            
        <span style="color: #ff4500">$RefHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Add</span><span style="color: #000000">(</span><span style="color: #ff4500">$ReferenceObject</span><span style="color: #a9a9a9">[</span><span style="color: #ff4500">$i</span><span style="color: #a9a9a9">]</span><span style="color: #a9a9a9">,</span><span style="color: #ff4500">$null</span><span style="color: #000000">)</span>            
    <span style="color: #000000">}</span>            
            
    <span style="color: #006400"># This code is ugly but faster.</span>            
    <span style="color: #006400"># Do the IF only once per run instead of every iteration of the ForEach.</span>            
    <span style="color: #00008b">If</span> <span style="color: #000000">(</span><span style="color: #ff4500">$IncludeEqual</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>            
        <span style="color: #ff4500">$EqualHash</span> <span style="color: #a9a9a9">=</span> <span style="color: #000000">@{</span><span style="color: #000000">}</span>            
        <span style="color: #006400"># You cannot enumerate with ForEach over a hash table while you remove</span>            
        <span style="color: #006400"># items from it.</span>            
        <span style="color: #006400"># Must use the static array of reference to enumerate the items.</span>            
        <span style="color: #00008b">ForEach</span> <span style="color: #000000">(</span><span style="color: #ff4500">$Item</span> <span style="color: #00008b">in</span> <span style="color: #ff4500">$ReferenceObject</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>            
            <span style="color: #00008b">If</span> <span style="color: #000000">(</span><span style="color: #ff4500">$DifHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">ContainsKey</span><span style="color: #000000">(</span><span style="color: #ff4500">$Item</span><span style="color: #000000">)</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>            
                <span style="color: #ff4500">$DifHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Remove</span><span style="color: #000000">(</span><span style="color: #ff4500">$Item</span><span style="color: #000000">)</span>            
                <span style="color: #ff4500">$RefHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Remove</span><span style="color: #000000">(</span><span style="color: #ff4500">$Item</span><span style="color: #000000">)</span>            
                <span style="color: #ff4500">$EqualHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Add</span><span style="color: #000000">(</span><span style="color: #ff4500">$Item</span><span style="color: #a9a9a9">,</span><span style="color: #ff4500">$null</span><span style="color: #000000">)</span>            
            <span style="color: #000000">}</span>            
        <span style="color: #000000">}</span>            
    <span style="color: #000000">}</span> <span style="color: #00008b">Else</span> <span style="color: #000000">{</span>            
        <span style="color: #00008b">ForEach</span> <span style="color: #000000">(</span><span style="color: #ff4500">$Item</span> <span style="color: #00008b">in</span> <span style="color: #ff4500">$ReferenceObject</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>            
            <span style="color: #00008b">If</span> <span style="color: #000000">(</span><span style="color: #ff4500">$DifHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">ContainsKey</span><span style="color: #000000">(</span><span style="color: #ff4500">$Item</span><span style="color: #000000">)</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>            
                <span style="color: #ff4500">$DifHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Remove</span><span style="color: #000000">(</span><span style="color: #ff4500">$Item</span><span style="color: #000000">)</span>            
                <span style="color: #ff4500">$RefHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Remove</span><span style="color: #000000">(</span><span style="color: #ff4500">$Item</span><span style="color: #000000">)</span>            
            <span style="color: #000000">}</span>            
        <span style="color: #000000">}</span>            
    <span style="color: #000000">}</span>            
            
    <span style="color: #00008b">If</span> <span style="color: #000000">(</span><span style="color: #ff4500">$IncludeEqual</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>            
        <span style="color: #ff4500">$EqualHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Keys</span> <span style="color: #a9a9a9">|</span> <span style="color: #0000ff">Select-Object</span> <span style="color: #000000">@{</span><span style="color: #000000">Name</span><span style="color: #a9a9a9">=</span><span style="color: #8b0000">'InputObject'</span><span style="color: #000000">;</span><span style="color: #000000">Expression</span><span style="color: #a9a9a9">=</span><span style="color: #000000">{</span><span style="color: #ff4500">$_</span><span style="color: #000000">}</span><span style="color: #000000">}</span><span style="color: #a9a9a9">,</span><span style="color: #000000">`
</span>            <span style="color: #000000">@{</span><span style="color: #000000">Name</span><span style="color: #a9a9a9">=</span><span style="color: #8b0000">'SideIndicator'</span><span style="color: #000000">;</span><span style="color: #000000">Expression</span><span style="color: #a9a9a9">=</span><span style="color: #000000">{</span><span style="color: #8b0000">'=='</span><span style="color: #000000">}</span><span style="color: #000000">}</span>            
    <span style="color: #000000">}</span>            
            
    <span style="color: #00008b">If</span> <span style="color: #000000">(</span><span style="color: #a9a9a9">-not</span> <span style="color: #ff4500">$ExcludeDifferent</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>            
        <span style="color: #ff4500">$RefHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Keys</span> <span style="color: #a9a9a9">|</span> <span style="color: #0000ff">Select-Object</span> <span style="color: #000000">@{</span><span style="color: #000000">Name</span><span style="color: #a9a9a9">=</span><span style="color: #8b0000">'InputObject'</span><span style="color: #000000">;</span><span style="color: #000000">Expression</span><span style="color: #a9a9a9">=</span><span style="color: #000000">{</span><span style="color: #ff4500">$_</span><span style="color: #000000">}</span><span style="color: #000000">}</span><span style="color: #a9a9a9">,</span><span style="color: #000000">`
</span>            <span style="color: #000000">@{</span><span style="color: #000000">Name</span><span style="color: #a9a9a9">=</span><span style="color: #8b0000">'SideIndicator'</span><span style="color: #000000">;</span><span style="color: #000000">Expression</span><span style="color: #a9a9a9">=</span><span style="color: #000000">{</span><span style="color: #8b0000">'&lt;='</span><span style="color: #000000">}</span><span style="color: #000000">}</span>            
        <span style="color: #ff4500">$DifHash</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Keys</span> <span style="color: #a9a9a9">|</span> <span style="color: #0000ff">Select-Object</span> <span style="color: #000000">@{</span><span style="color: #000000">Name</span><span style="color: #a9a9a9">=</span><span style="color: #8b0000">'InputObject'</span><span style="color: #000000">;</span><span style="color: #000000">Expression</span><span style="color: #a9a9a9">=</span><span style="color: #000000">{</span><span style="color: #ff4500">$_</span><span style="color: #000000">}</span><span style="color: #000000">}</span><span style="color: #a9a9a9">,</span><span style="color: #000000">`
</span>            <span style="color: #000000">@{</span><span style="color: #000000">Name</span><span style="color: #a9a9a9">=</span><span style="color: #8b0000">'SideIndicator'</span><span style="color: #000000">;</span><span style="color: #000000">Expression</span><span style="color: #a9a9a9">=</span><span style="color: #000000">{</span><span style="color: #8b0000">'=&gt;'</span><span style="color: #000000">}</span><span style="color: #000000">}</span>            
    <span style="color: #000000">}</span>            
<span style="color: #000000">}</span>            
</pre>
Note that for my purposes I did not need to compare multiple properties, so this approach does not entirely duplicate functionality of the native Compare-Object. You could probably adapt this code for that purpose. I would drop each list object into a hash table value, while making the key a string representation of the one or more properties to be compared. I’ll leave that bit up to you.

Also note that, yes, I used ForEach. General consensus is that ForEach is slower than For. Feel free to adjust and see if that makes a difference in execution time for you.
<h2>The Results</h2>
<pre class="PowerShellColorizedScript"><span style="color: #006400"># Native Compare-Object</span>            
<span style="color: #0000ff">Measure-Command</span> <span style="color: #000080">-Expression</span> <span style="color: #000000">{</span>            
    <span style="color: #0000ff">Compare-Object</span> <span style="color: #000080">-ReferenceObject</span> <span style="color: #000000">(</span><span style="color: #0000ff">Get-Content</span> <span style="color: #8a2be2">.\file1.txt</span><span style="color: #000000">)</span> <span style="color: #000000">`
</span>        <span style="color: #000080">-DifferenceObject</span> <span style="color: #000000">(</span><span style="color: #0000ff">Get-Content</span> <span style="color: #8a2be2">.\file2.txt</span><span style="color: #000000">)</span> <span style="color: #000080">-IncludeEqual</span>            
<span style="color: #000000">}</span> <span style="color: #a9a9a9">|</span> <span style="color: #0000ff">Select-Object</span> <span style="color: #8a2be2">TotalMilliseconds</span>            
            
<span style="color: #006400"># Hash table comparison</span>            
<span style="color: #0000ff">Measure-Command</span> <span style="color: #000080">-Expression</span> <span style="color: #000000">{</span>            
    <span style="color: #0000ff">Compare-Object2</span> <span style="color: #000080">-ReferenceObject</span> <span style="color: #000000">(</span><span style="color: #0000ff">Get-Content</span> <span style="color: #8a2be2">.\file1.txt</span><span style="color: #000000">)</span> <span style="color: #000000">`
</span>        <span style="color: #000080">-DifferenceObject</span> <span style="color: #000000">(</span><span style="color: #0000ff">Get-Content</span> <span style="color: #8a2be2">.\file2.txt</span><span style="color: #000000">)</span> <span style="color: #000080">-IncludeEqual</span>            
<span style="color: #000000">}</span> <span style="color: #a9a9a9">|</span> <span style="color: #0000ff">Select-Object</span> <span style="color: #8a2be2">TotalMilliseconds</span></pre>
When racing the native Compare-Object against my hash table implementation here are the results:
<ul>
 	<li>For test lists of 1,000 items, Compare-Object finishes in five seconds while the hash table version finishes in &lt;1 second.</li>
 	<li>For test lists of 100,000 items, the hash table finishes in five seconds while Compare-Object had not finished after multiple minutes (so I just killed the task).</li>
 	<li>For the customer’s 800,000 items, the hash table finished in 30 minutes, as opposed to 13 hours for Compare-Object. To be fair, the script does other tasks besides this Compare-Object. Regardless that is a 25x performance improvement!</li>
</ul>
How is that for efficiency gain?!
<h2>The Moral of the Story</h2>
Learn <a target="_blank" href="https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_hash_tables" rel="noopener noreferrer" title="https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_hash_tables">hash tables</a> today! They are the single most versatile, powerful, and fun data structure in all of PowerShell. Let me know your results in the comments area below.

<em>“Goose, it’s time to buzz the tower.”</em>
<h2>Updated Code</h2>
<em>Edit - Aug 29, 2017</em> - Find an updated version of this script that takes multiple properties for comparison <a target="_blank" href="https://github.com/GoateePFE/CodeCloset/blob/master/Compare-Object2.ps1" rel="noopener noreferrer">on my GitHub here</a>.]]></content:encoded>
    </item>
    <item>
      <title>Slow Code: Top 5 Ways to Make Your PowerShell Scripts Run Faster</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/slow-code-top-5-ways-to-make-your-powershell-scripts-run-faster</link>
      <pubDate>Wed, 12 Jul 2017 12:53:41 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2315</guid>
      <description><![CDATA[Slow code?
Are you frustrated by slow PowerShell scripts? Is it impacting business processes? Need...]]></description>
      <content:encoded><![CDATA[<h2><a href="https://msdnshared.blob.core.windows.net/media/2017/07/06_emoji.gif"><img width="100" height="100" class="alignright size-full wp-image-2335" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/06_emoji.gif" /></a>Slow code?</h2>
Are you frustrated by slow PowerShell scripts? Is it impacting business processes? Need help tuning your scripts? Today's post is for you.

Can you identify with any of these customer slow PowerShell stories?
<h3 id="case-1">Case #1</h3>
Customer is scanning Active Directory Domain Controllers in multiple domains and forests scattered all over the state on slow links. This key audit data takes 62 hours to collect and impacts the business response to audit requests. After applying these techniques, the customer reports that the script now completes in 30 minutes.
<h3 id="case-2">Case #2</h3>
Customer needs to update user licensing on Office 365 for daily new user provisioning. This impacts the business due to 10 hour run time of the script. After applying these optimization tips, the script finishes in 14 minutes.
<h3 id="case-3">Case #3</h3>
Customer is parsing SCCM client log files that rotate every few minutes. But the script takes longer to run than the log refresh interval. After applying these techniques, the script is 10% of its original length and runs 10 times faster.
<h2 id="scripting-secrets">Scripting Secrets</h2>
After several years of teaching and advising PowerShell scripting I have observed some routine practices that lead to poor script performance. Often this happens with people who copy and paste their scripts from internet sources without truly understanding the language. Other times it simply comes from a lack of formal training. Regardless, today I am going to share with you the secrets I have shared with many customers to improve their script run time.

The classic programming trade-off is speed vs. memory. We want to be aware of both as we write the most efficient code.
<h2 id="problem-0-not-using-cmdlet-parameter-filters">Problem #0: Not using cmdlet parameter filters</h2>
There is an ancient PowerShell pipeline proverb: <em>Filter left, format right.</em> Disobey it, and your script will take a while. It means that you should filter the pipeline objects as far to the left as possible. And formatting cmdlets should always go at the end, never the middle.

Early on a customer reported to me, "Querying event logs over the WAN is taking days!" Study these two code samples below. Which do you think is faster and why?
<pre><code># Approach #1
Get-WinEvent -LogName System -ComputerName Server1 |
  Where-Object {$_.InstanceID -eq 1500}

# Approach #2
Get-WinEvent -FilterHashtable @{LogName='System';ID=1500} `
  -MaxEvents 50 -ComputerName Server1
</code></pre>
The first approach retrieves the <strong>ENTIRE</strong> event log over the wire and then filters the results in local memory. The second approach uses cmdlet parameters to effectively reduce the dataset coming from the remote system.

This same advice applies to any cmdlet that queries data, whether local or remote. Be sure to explore the help for all the parameters, even if they look complicated at first. It is worth your time to write the code correctly.

Yes, #2 is faster. Much faster.
<h2 id="problem-1-expensive-operations-repeated">Problem #1: Expensive operations repeated</h2>
Usually I see this manifest as a query to Active Directory, a database, Office 365 accounts, etc. The script needs to process multiple sets of data, so the script author performs a unique query each time. For example, I need to report on the license status of 10 user accounts in Office 365. Which pseudo code would be faster?
<pre><code>For Each User
    Query the account from Office 365
    Output the license data of the user
</code></pre>
Or this:
<pre><code>Construct a single query to Office 365 that retrieves all users in scope
Execute the query and store it into a variable
Pipe the variable into the appropriate loop, selection or output cmdlet
</code></pre>
Yes, the second is more efficient, because it only performs the expensive operation once. It may be a little more involved to construct the query appropriately. Or you may need to retrieve an even larger data set if you cannot precisely isolate the accounts in question. However, the end result is a single expensive operation instead of multiples.

Another expensive operation is crawling an array to search a value:
<pre><code>For ($i=0; $i -lt $array.count; $i++) {
    If ($array[$i] -eq $entry) {
        "We found $entry after $($i+1) iterations."
        $found = $true
        Break
    }
}
</code></pre>
Instead, add the items to a hash table which has blazingly fast search performance:
<pre><code>$hashtable.ContainsKey($entry)
</code></pre>
See <code>Get-Help about_Hash_Tables</code> for more information on my favorite PowerShell data structure.
<h2 id="problem-2-appending-stuff">Problem #2 &amp; #3: Appending stuff</h2>
<em>Append</em>-icitus is painful, but appending to objects is more painful. This usually comes in one of two forms:
<ol>
 	<li>Appending to files</li>
 	<li>Appending to arrays</li>
</ol>
<h3 id="appending-to-files">Appending to files</h3>
I usually see this with script logging output. Cmdlets like <code>Add-Content</code>, <code>Out-File -Append</code> and <code>Export-CSV -Append</code> are convenient to use for small files. However, if you are using these in a loop with hundreds or thousands of iterations, they will slow your script significantly. Each time you use one of these it will:
<ul>
 	<li>Open the file</li>
 	<li>Scroll to the end</li>
 	<li>Add the content</li>
 	<li>Close the file</li>
</ul>
That is heavy. Instead use a .NET object like this:
<pre><code>$sw = New-Object System.IO.StreamWriter "c:\temp\output.txt"
for ($a=1; $a -le 10000; $a++)
{
    $sw.WriteLine($BigString)
}
$sw.Close()
</code></pre>
For CSV output, this may require you to construct your own CSV delimited line of text to add to the file. However, it is still significantly faster.
<h3 id="appending-to-arrays">Appending to arrays</h3>
I used to do this one often until someone pointed it out to me.
<pre><code># Empty array
$MyReport = @()
ForEach ($Item in $Items) {
    # Fancy script processing here
    # Append to the array
    $MyReport += $Item | Select-Object Property1, Property2, Property3
}
# Output the entire array at once
$MyReport | Export-CSV -Path C:\Temp\myreport.csv
</code></pre>
Now this is almost one better, because we are not appending to a file inside the loop. However, we are appending to an array, which is an expensive memory operation. Behind the scenes .NET is duplicating the entire array in memory, adding the new item, and deleting the old copy in memory.

Here is the more efficient way to do the same thing:
<pre><code>$MyReport = ForEach ($Item in $Items) {
    # Fancy script processing here
    $Item | Select-Object Property1, Property2, Property3
}
# Output the entire array at once
$MyReport | Export-CSV -Path C:\Temp\myreport.csv
</code></pre>
You can actually assign the variable one time in memory by capturing all the output of the loop. Just make sure the loop only outputs the raw data you want in the report.

Another option is to use a hash table or .NET array list object. These data structures can dynamically add and remove items without the memory swapping of an array. See <code>Get-Help about_Hash_Tables</code> or <code>System.Collections.ArrayList</code>.
<h2 id="problem-4-searching-text">Problem #4: Searching text</h2>
The log parsing example I mentioned in Case #3 above gets a lot of people, especially if you started scripting in VBScript where string methods were quite common. Here is a quick chart comparing the three most popular text parsing methods, including links for more info.
<table>
<thead>
<tr>
<th style="text-align: left">Technique</th>
<th>Friendly</th>
<th>Power</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://msdn.microsoft.com/en-us/library/system.string(v=vs.110).aspx">String methods</a></td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td><a href="https://go.microsoft.com/fwlink/?LinkId=133231">Regular expressions</a></td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td><a href="https://blogs.technet.microsoft.com/ashleymcglone/2016/09/13/use-the-new-powershell-cmdlet-convert-string-to-parse-email-addresses/">Convert-String</a> / <a href="https://blogs.technet.microsoft.com/ashleymcglone/2016/09/14/use-the-new-powershell-cmdlet-convertfrom-string-to-parse-klist-kerberos-ticket-output/">ConvertFrom-String</a></td>
<td>Yes</td>
<td>Yes</td>
</tr>
</tbody>
</table>
Sometimes string methods (ToUpper, IndexOf, Substring, etc.) are all you need. But if the text parsing requires pattern matching of any kind, then you really need one of the other methods, which are much faster as well.

Here is a simple example of using string methods:
<pre><code>$a = 'I love PowerShell!'
# View the string methods
$a | Get-Member -MemberType Methods
# Try the string methods
$a.ToLower()
$a.ToLower().Substring(7,10)
$a.Substring(0,$a.IndexOf('P'))
</code></pre>
While string methods are easy to discover and use, their capability gets cumbersome very quickly.

Observe this comparison of three techniques:
<pre><code>$domainuser = 'contoso\alice'

# String methods
$domain = $domainuser.Substring(0,$domainuser.IndexOf('\'))
$user   = $domainuser.Substring($domainuser.IndexOf('\')+1)

# RegEx
$domainuser -match '(?&lt;domain&gt;.*)\\(?&lt;user&gt;.*)'
$Matches

# Convert-String
'contoso\alice' |
    Convert-String -Example 'domain\user=domain,user' |
    ConvertFrom-Csv -Header 'Domain','User'
</code></pre>
RegEx is used widely in PowerShell: -split, -replace, Select-String, etc. RegEx excels at parsing string patterns out of text with speed. Take some time to learn it today (<code>Get-Help about_Regular_Expressions</code>).

The new <code>Convert-String</code> and <code>ConvertFrom-String</code> cmdlets were introduced in PowerShell 5. See the links in the chart above for more detailed examples of these powerful text parsing cmdlets. <code>ConvertFrom-String</code> excels at parsing multiple values out of multi-line patterns. And that is exactly what challenged the customer in Case #3 above.
<h2 id="how-can-i-tell-how-long-my-script-runs">How can I tell how long my script runs?</h2>
Use one of these techniques to test different versions of your code for speed.

PowerShell has a cmdlet <code>Measure-Command</code> that takes an <code>-Expression</code> scriptblock parameter. This is the first way most people measure execution time.
<pre><code>Measure-Command -Expression {
    #
    # Insert body of script here
    #
}
</code></pre>
Others will do something like this:
<pre><code>$Start = Get-Date
#
# Insert body of script here
#
$End = Get-Date
# Show the result
New-Timespan -Start $Start -End $End
</code></pre>
Either method returns a TimeSpan object with properties for any desired unit of time. Just be sure to use the <em>total</em> properties for accuracy.
<h2 id="the-one-common-performance-link-loops">The One Common Performance Link: Loops</h2>
If you do a slow operation one time, maybe that is little impact. But if you do it 1,000 times, then we are all in trouble. If the data processed in each loop is a rich object with many properties, then it is even worse (ie. more memory). Review your loops carefully to identify expensive commands and optimize them.
<h2 id="disclaimer">Disclaimer</h2>
One of the challenges of sharing code publicly is that I am always learning. If you go back to my posts six years ago, you will find that I used some of these poor practices. I have re-written and re-blogged some of them. Others are still there.

Take-aways:
<ul>
 	<li>Keep learning</li>
 	<li>Review (and optimize) all code you find online before implementing it</li>
 	<li>Periodically review your most-used scripts in light of your new knowledge</li>
</ul>
Keep scripting!
<h2 id="more-tips">More Tips</h2>
You can find more tips in <em>The Big Book of PowerShell Gotchas</em> over at <a href="https://powershell.org/ebooks">PowerShell.org/ebooks</a>.]]></content:encoded>
    </item>
    <item>
      <title>Getting Started with PowerShell Core on Windows, Mac, and Linux</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/getting-started-with-powershell-core-on-windows-mac-and-linux</link>
      <pubDate>Wed, 21 Jun 2017 12:45:37 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2295</guid>
      <description><![CDATA[This is deeper than Coke vs. Pepsi or Ford vs. Chevy. We are breaking down the barriers. Cats and...]]></description>
      <content:encoded><![CDATA[<div>
<div>This is deeper than Coke vs. Pepsi or Ford vs. Chevy. We are breaking down the barriers. Cats and dogs living together. Are you ready for this?</div>
<div></div>
<div></div>
</div>
<div>This month I posted over on the PowerShell team blog about my recent experiences with PowerShell on Linux and Mac. It is a ton of fun. Check out the post here:</div>
<div></div>
<div></div>
<div><a target="_blank" href="https://blogs.msdn.microsoft.com/powershell/2017/06/09/getting-started-with-powershell-core-on-windows-mac-and-linux/">Getting Started with PowerShell Core on Windows, Mac, and Linux</a></div>
<div></div>
<div></div>
<div>Enjoy!</div>]]></content:encoded>
    </item>
    <item>
      <title>Top 10 PowerShell DSC Node Events to Monitor</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/top-10-powershell-dsc-node-events-to-monitor</link>
      <pubDate>Thu, 11 May 2017 13:31:14 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2265</guid>
      <description><![CDATA[In a previous blog post I demonstrated how to get a list of all possible PowerShell Desired State...]]></description>
      <content:encoded><![CDATA[<a href="https://msdnshared.blob.core.windows.net/media/2017/05/01_blivit.gif"><img width="100" height="100" title="01_blivit" align="right" style="float: right" alt="01_blivit" src="https://msdnshared.blob.core.windows.net/media/2017/05/01_blivit_thumb.gif" /></a>In a <a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/2017/01/12/pro-tip-powershell-dsc-events-to-monitor" title="https://blogs.technet.microsoft.com/ashleymcglone/2017/01/12/pro-tip-powershell-dsc-events-to-monitor">previous blog post</a> I demonstrated how to get a list of all possible <strong>PowerShell Desired State Configuration (DSC) events</strong> for monitoring. Admittedly, that was an overwhelming list. Today I want to narrow that down to the essentials of DSC monitoring events.
<h2>These <em>are</em> the events you’re looking for.</h2>
Recently I was working with a customer who wanted specific events for DSC monitoring. I did my testing with a Windows Server 2012 R2 node running WMF 5.1. The pull server was on the same versions. I fired up a node connected to the pull server and labbed a number of common scenarios you would want to monitor.

DSC node events are recorded in the <strong>Microsoft-Windows-DSC/Operational</strong> log. Here are the main events you want to capture. I have assigned a simple category to each of these.
<table style="border-style: solid;border-color: #aaaaaa" cellspacing="0" cellpadding="5">
<tbody>
<tr style="border-style: solid;border-color: #aaaaaa">
<td><strong>Category</strong></td>
<td><strong>Event ID</strong></td>
<td><strong>Level</strong></td>
<td><strong>Status</strong></td>
</tr>
<tr style="border-style: solid;border-color: #aaaaaa">
<td>Desired State</td>
<td>4115 / 4343</td>
<td>Information</td>
<td>Consistency scan completed (ie. in desired state if 4249 is not also present)</td>
</tr>
<tr style="border-style: solid;border-color: #aaaaaa">
<td><span style="background-color: #ffff00">Desired State</span></td>
<td><span style="background-color: #ffff00">4249</span></td>
<td><span style="background-color: #ffff00">Warning</span></td>
<td><span style="background-color: #ffff00">Failed consistency scan (ie. not in desired state). Only appears in <em>ApplyAndMonitor</em> mode.</span></td>
</tr>
<tr style="border-style: solid;border-color: #aaaaaa">
<td><span style="background-color: #ffff00">Configuration Apply</span></td>
<td><span style="background-color: #ffff00">4097</span></td>
<td><span style="background-color: #ffff00">Error</span></td>
<td><span style="background-color: #ffff00">Configuration failed to apply</span></td>
</tr>
<tr style="border-style: solid;border-color: #aaaaaa">
<td>Configuration Apply</td>
<td>4332</td>
<td>Information</td>
<td>Listing of resources applied in the configuration</td>
</tr>
<tr style="border-style: solid;border-color: #aaaaaa">
<td>Configuration Apply</td>
<td>4257</td>
<td>Information</td>
<td>LCM settings during the configuration</td>
</tr>
<tr style="border-style: solid;border-color: #aaaaaa">
<td><span style="background-color: #ffff00">Node Pull</span></td>
<td><span style="background-color: #ffff00">4252</span></td>
<td><span style="background-color: #ffff00">Error</span></td>
<td><span style="background-color: #ffff00">Node failed to download from pull server, only event 4252 with <em>Error Category 8</em> in the message</span></td>
</tr>
<tr style="border-style: solid;border-color: #aaaaaa">
<td>Node Report</td>
<td>4264 / 4266</td>
<td>Information</td>
<td>Node successfully reported to report server</td>
</tr>
<tr style="border-style: solid;border-color: #aaaaaa">
<td>Node Report</td>
<td>4260</td>
<td>Error</td>
<td>Node failed reporting to report server</td>
</tr>
</tbody>
</table>
&nbsp;

In some cases there may be other events to indicate similar status. These IDs are the least chatty. Of these ten events I have highlighted the three essential error conditions for monitoring.

Note the following points:
<ul>
 	<li>Event 4249 only shows up in <em>ApplyAndMonitor</em> configuration mode to indicate configuration drift. In my testing I could not find an event indicating configuration drift when <em>ApplyAndAutocorrect</em> actually makes a correction to the configuration.</li>
 	<li>In the message body of some events you will see <em>PerformRequiredConfigurationChecks.</em> These bit flag values are documented <a target="_blank" href="https://msdn.microsoft.com/en-us/powershell/dsc/msft-dsclocalconfigurationmanager-performrequiredconfigurationchecks" title="https://msdn.microsoft.com/en-us/powershell/dsc/msft-dsclocalconfigurationmanager-performrequiredconfigurationchecks">here</a>.</li>
 	<li>Event 4252 appears for all kinds of conditions. Differentiate the events by the message body and the <em>Error Category</em> data inside the event.</li>
</ul>
<h2>Scripting to Capture Logs</h2>
Here is some quick syntax to remotely query the events. Note that I limit the total number of events returned for performance reasons. Tweak <em>MaxEvents</em> as needed.
<pre class="PowerShellColorizedScript"><span style="color: #0000ff">Invoke-Command</span> <span style="color: #000080">-ComputerName</span> <span style="color: #8a2be2">server1</span><span style="color: #a9a9a9">,</span><span style="color: #8a2be2">server2</span><span style="color: #a9a9a9">,</span><span style="color: #8a2be2">server3</span> <span style="color: #000080">-ScriptBlock</span> <span style="color: #000000">{</span>            
  <span style="color: #0000ff">Get-WinEvent</span> <span style="color: #000080">-LogName</span> <span style="color: #8b0000">'Microsoft-Windows-DSC/Operational'</span> <span style="color: #000080">-MaxEvents</span> <span style="color: #800080">50</span><span style="color: #000000">}</span> <span style="color: #a9a9a9">|</span>            
  <span style="color: #0000ff">Select-Object</span> <span style="color: #8a2be2">PSComputerName</span><span style="color: #a9a9a9">,</span><span style="color: #8a2be2">TimeCreated</span><span style="color: #a9a9a9">,</span><span style="color: #8a2be2">LevelDisplayName</span><span style="color: #a9a9a9">,</span><span style="color: #8a2be2">Id</span><span style="color: #a9a9a9">,</span><span style="color: #8a2be2">Message</span> <span style="color: #a9a9a9">|</span>            
  <span style="color: #0000ff">Out-Gridview</span></pre>
Here is some quick syntax to export all of the DSC event logs, optional pull server details, and zip them up for analysis off-box. I use this when troubleshooting DSC.
<pre class="PowerShellColorizedScript"><span style="color: #0000ff">New-Item</span> <span style="color: #000080">-ItemType</span> <span style="color: #8a2be2">Directory</span> <span style="color: #000080">-Path</span> <span style="color: #8a2be2">C:\logs</span> <span style="color: #000080">-ErrorAction</span> <span style="color: #8a2be2">SilentlyContinue</span>            
<span style="color: #000000">(</span><span style="color: #0000ff">Get-WinEvent</span> <span style="color: #000080">-ListLog</span> <span style="color: #8a2be2">*desired*</span><span style="color: #a9a9a9">,</span><span style="color: #8a2be2">*dsc*</span><span style="color: #000000">)</span><span style="color: #a9a9a9">.</span><span style="color: #000000">LogName</span> <span style="color: #a9a9a9">|</span>             
<span style="color: #0000ff">Where-Object</span> <span style="color: #000000">{</span><span style="color: #ff4500">$_</span> <span style="color: #a9a9a9">-notlike</span> <span style="color: #8b0000">"*admin*"</span><span style="color: #000000">}</span> <span style="color: #a9a9a9">|</span>             
<span style="color: #0000ff">ForEach-Object</span> <span style="color: #000000">{</span>            
    <span style="color: #0000ff">wevtutil</span> <span style="color: #8a2be2">export-log</span> <span style="color: #8a2be2">/overwrite:true</span> <span style="color: #ff4500">$_</span> <span style="color: #8b0000">"C:\logs\$($env:COMPUTERNAME)_$($_.Replace('/','-')).evtx"</span>            
<span style="color: #000000">}</span>            
<span style="color: #8b0000">'System'</span><span style="color: #a9a9a9">,</span><span style="color: #8b0000">'Application'</span> <span style="color: #a9a9a9">|</span> <span style="color: #0000ff">ForEach-Object</span> <span style="color: #000000">{</span>            
    <span style="color: #0000ff">wevtutil</span> <span style="color: #8a2be2">export-log</span> <span style="color: #8a2be2">/overwrite:true</span> <span style="color: #ff4500">$_</span> <span style="color: #8b0000">"C:\logs\$($env:COMPUTERNAME)_$($_).evtx"</span>            
<span style="color: #000000">}</span>            
<span style="color: #00008b">If</span> <span style="color: #000000">(</span><span style="color: #000000">(</span><span style="color: #0000ff">Get-WindowsFeature</span> <span style="color: #8a2be2">DSC-Service</span><span style="color: #000000">)</span><span style="color: #a9a9a9">.</span><span style="color: #000000">Installed</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>            
    <span style="color: #0000ff">Get-ChildItem</span> <span style="color: #8b0000">'C:\Program Files\WindowsPowerShell\DscService'</span> <span style="color: #a9a9a9">&gt;</span> <span style="color: #8a2be2">C:\logs\DscService.txt</span>            
    <span style="color: #0000ff">Copy-Item</span> <span style="color: #000080">-Path</span> <span style="color: #8b0000">'C:\inetpub\wwwroot\PSDSCPullServer\web.config'</span> <span style="color: #000080">-Destination</span> <span style="color: #8a2be2">C:\logs</span>            
<span style="color: #000000">}</span>            
<span style="color: #ff4500">$PSVersionTable</span> <span style="color: #a9a9a9">&gt;</span> <span style="color: #8a2be2">C:\logs\PSVersionTable.txt</span>            
<span style="color: #0000ff">Compress-Archive</span> <span style="color: #000080">-Path</span> <span style="color: #8a2be2">C:\logs\*.evtx</span><span style="color: #a9a9a9">,</span><span style="color: #8a2be2">C:\logs\*.config</span><span style="color: #a9a9a9">,</span><span style="color: #8a2be2">C:\logs\*.txt</span> <span style="color: #000000">`
</span>    <span style="color: #000080">-DestinationPath</span> <span style="color: #8b0000">"C:\logs\$($env:COMPUTERNAME)_DSC_Logs.zip"</span> <span style="color: #000080">-Update</span></pre>
The <em><a target="_blank" href="https://www.powershellgallery.com/packages/xDscDiagnostics" title="https://www.powershellgallery.com/packages/xDscDiagnostics">xDscDiagnostics</a></em> module has a function <em>New-xDscDiagnosticsZip</em> which will get most of these things and a few other items. This code above is tailored for my own DSC troubleshooting needs. Note that my version will attempt to collect additional details from a pull server, assuming the default install paths.
<h2>Additional Resources</h2>
For more info on troubleshooting DSC and logs see the documentation here: <a href="https://msdn.microsoft.com/en-us/powershell/dsc/troubleshooting">https://msdn.microsoft.com/en-us/powershell/dsc/troubleshooting</a>

Don’t forget to check out my <a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/2017/01/12/pro-tip-powershell-dsc-events-to-monitor" title="https://blogs.technet.microsoft.com/ashleymcglone/2017/01/12/pro-tip-powershell-dsc-events-to-monitor">previous blog post</a> for more on working with DSC event logs.
<h2>Comments</h2>
What do you monitor for DSC events? Did I miss any? If so, let me know in the comments area below.]]></content:encoded>
    </item>
    <item>
      <title>PowerShell Remoting and Kerberos Double Hop: Old Problem - New Secure Solution</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/powershell-remoting-and-kerberos-double-hop-old-problem-new-secure-solution</link>
      <pubDate>Wed, 12 Apr 2017 06:00:56 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2245</guid>
      <description><![CDATA[PowerShell and DevOps Global Summit 2017
This week I enjoyed presenting at the PowerShell and...]]></description>
      <content:encoded><![CDATA[<h2 id="powershell-remoting-and-kerberos-double-hop-old-problem-new-secure-solution"><a href="https://msdnshared.blob.core.windows.net/media/2017/04/PshSummit2017.jpg"><img width="240" height="240" title="PshSummit2017" align="right" style="float: right;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-width: 0px" alt="PshSummit2017" src="https://msdnshared.blob.core.windows.net/media/2017/04/PshSummit2017_thumb.jpg" border="0" /></a>PowerShell and DevOps Global Summit 2017</h2>
This week I enjoyed presenting at the <strong>PowerShell and DevOps Global Summit 2017</strong>. If you have not attended, I highly encourage it. You will get to meet PowerShell team members from Microsoft, MVPs, and the people you follow on Twitter! Follow <a target="_blank" href="https://twitter.com/PSHSummit" title="https://twitter.com/PSHSummit">@PshSummit</a> on Twitter to get the alerts for registration. I even work for Microsoft, and I learn a ton every year from the amazing sessions. It is also great connecting with everyone in the PowerShell community.

See this <a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/2016/08/30/powershell-remoting-kerberos-double-hop-solved-securely/" title="https://blogs.technet.microsoft.com/ashleymcglone/2016/08/30/powershell-remoting-kerberos-double-hop-solved-securely/">previous blog post</a> for the benefits of <em>resource-based Kerberos constrained delegation</em> and how it can apply to PowerShell remoting. It is not a complete solution, but it works for the key scenarios described below and a few others. The article also outlines a number of other possible Kerberos double hop solutions. The PowerShell documentation team took that article, tweaked it, and turned it into a documentation page <a target="_blank" href="https://msdn.microsoft.com/en-us/powershell/scripting/setup/ps-remoting-second-hop" title="https://msdn.microsoft.com/en-us/powershell/scripting/setup/ps-remoting-second-hop">here</a>.
<h2 id="tl-dr">tl;dr</h2>
<ul>
 	<li>This is a follow up to my <a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/2016/08/30/powershell-remoting-kerberos-double-hop-solved-securely/" title="https://blogs.technet.microsoft.com/ashleymcglone/2016/08/30/powershell-remoting-kerberos-double-hop-solved-securely/">previous blog post</a> on Kerberos double hop and PowerShell remoting.</li>
 	<li>I have published some helper functions for working with <strong>resource-based Kerberos constrained delegation</strong> (<strong>RB KCD</strong>) and PowerShell remoting: <code>Enable-RBKCD</code>, <code>Disable-RBKCD</code>, <code>Get-RBKCD</code>.</li>
 	<li><span style="background-color: #ffff00">Get the files and slides </span><a target="_blank" href="https://github.com/GoateePFE/PshSummit2017" title="https://github.com/GoateePFE/PshSummit2017"><span style="background-color: #ffff00">on my GitHub here</span></a><span style="background-color: #ffff00">. </span></li>
 	<li>RB KCD works with a limited set of commands and functions running under SYSTEM account in the kernel context.</li>
 	<li>RB KCD does not support WinRM / PowerShell remoting, because that runs under the NETWORK account.</li>
 	<li>For cases where RB KCD does not work you can nest two <code>Invoke-Command</code> statements to make the double hop. See helper function <code>Invoke-DoubleHop</code>.</li>
 	<li>You can share these RB KCD articles and scripts with this short link: <a target="_blank" href="https://aka.ms/pskdh" title="https://aka.ms/pskdh">https://aka.ms/pskdh</a></li>
</ul>
<h2 id="the-problem">The Problem</h2>
<h3 id="classic-kerberos-double-hop">Classic Kerberos Double Hop</h3>
I am on <em>ServerA</em>, connected to <em>ServerB</em> where I need to reach <em>ServerC</em>. I have permissions to ServerC, but I still get <em>Access Denied</em>. Default Kerberos rules prevent ServerB from passing credentials to ServerC. The most common example is a user (ServerA) connected to a web server (ServerB/frontend) that needs to use the user's credentials to access a database server (ServerC/backend). In a <a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/2016/08/30/powershell-remoting-kerberos-double-hop-solved-securely/" title="https://blogs.technet.microsoft.com/ashleymcglone/2016/08/30/powershell-remoting-kerberos-double-hop-solved-securely/">previous blog post</a> I described multiple popular (and not-so-popular) work-arounds.
<h3 id="scenario-a-jump-server">Scenario A: Jump Server</h3>
From my workstation I connect to my jump server (tool server, etc. whatever you like to call it) via PowerShell remoting (<code>Enter-PSSession</code>, <code>Invoke-Command</code>). From that server I want to reach out and collect data from multiple other servers for a report. I am in the Administrators group on all of these servers, but I get an <em>Access Denied</em> when attempting to access them from my jump server.

Why not connect directly to the servers? Perhaps I have limited network connectivity or restricted routing. Maybe it is a DMZ or a hosted environment. There are many legitimate scenarios why you may choose this approach.
<h3 id="scenario-b-remote-software-install">Scenario B: Remote Software Install</h3>
Another popular scenario is installing software remotely. From my workstation (ServerA) I want to fan out to 50 servers (ServerB) and install an application whose source files are hosted on a file share (ServerC). Here again I will get <em>Access Denied</em> at the file share even though I know I have permissions. This is Kerberos double hop.
<h3 id="scenario-x">Scenario X</h3>
There are many more scenarios for Kerberos double hop. RB KCD will help with some of them. <code>Invoke-DoubleHop</code> should help with more of them. And some will likely have no other choice but to continue using <em>CredSSP</em> for the time being. You will need to experiment to see which commands are compatible with RB KCD (running as SYSTEM in kernel context).

For example, from your workstation you connect to your SharePoint server with PowerShell remoting. The SharePoint cmdlets need to access a backend SQL server, but they fail. Typically CredSSP is the solution. I have some peers who have not been successful yet getting RB KCD to work with this case. I suspicion that it would need to be configured on service accounts and may work then. Let me know if you figure this one out.
<h2 id="powershell-and-devops-global-summit-2017">Two Solutions, One Module</h2>
I created a helper module for quickly configuring RB KCD and for cheating with nested <code>Invoke-Command</code> cmdlets.
<pre style="font-family: monospace;color: #ffffff;margin: 10px;background-color: #012456;border: #012456 10px solid">PS&gt; Import-Module rbkcd.psm1

PS&gt; Get-Command -Module RBKCD

CommandType Name             Version Source
----------- ----             ------- ------
Function    Disable-RBKCD    0.0     RBKCD
Function    Enable-RBKCD     0.0     RBKCD
Function    Get-RBKCD        0.0     RBKCD
Function    Invoke-DoubleHop 0.0     RBKCD</pre>
Here are some examples:
<pre style="font-family: monospace;color: #ffffff;margin: 10px;background-color: #012456;border: #012456 10px solid"># Both ServerB and ServerC in the same domain.
Enable-RBKCD -ServerB sb.proseware.com -ServerC sc.proseware.com -Credential (Get-Credential)

# ServerB and ServerC in different domains.
Enable-RBKCD -ServerB sb.proseware.com -ServerC ms1.alpineskihouse.com -DomainBCred (Get-Credential) -DomainCCred (Get-Credential)

# See which identities are allowed to delegate to ServerC
Get-RBKCD -ServerC sc.proseware.com -Credential (Get-Credential proseware\adminacct)

# Remove all identities allowed to delegate to ServerC
Disable-RBKCD -ServerC sc.proseware.com -Credential (Get-Credential proseware\adminacct)

# For scenarios that do not work with RB KCD
Invoke-DoubleHop -ServerB sb -ServerC sc -DomainBCred $DomainBCred -Scriptblock {
    dir \\sc\c$
}</pre>
While these functions were written to help with RB KCD for PowerShell remoting, they could be used for any other RB KCD scenario. Note that these only work with computer accounts. You could expand the code to work with service accounts or user accounts also.
<h2 id="enjoy">Did this work for you?</h2>
This is a bit of a niche topic, but lots of people struggle with it. Hopefully this was helpful. <strong>Please use the comments below to help the community understand where this was helpful for you and where is was <em>not</em> helpful.</strong> This is an on-going research project for me, and your feedback is valuable. Thank you. Happy scripting!]]></content:encoded>
    </item>
    <item>
      <title>Practical PowerShell Security: Enable Auditing and Logging with DSC</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/practical-powershell-security-enable-auditing-and-logging-with-dsc</link>
      <pubDate>Wed, 29 Mar 2017 08:37:27 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2225</guid>
      <description><![CDATA[PowerShell Security
Almost two years ago Lee Holmes released his famous PowerShell ♥ the Blue Team...]]></description>
      <content:encoded><![CDATA[<h2><a href="https://msdnshared.blob.core.windows.net/media/2017/03/Windows_Insiders_Ninjacat-100.png"><img width="100" height="125" title="Windows_Insiders_Ninjacat-100" align="right" style="float: right;padding-top: 0px;padding-left: 0px;padding-right: 0px;border: 0px" alt="Windows_Insiders_Ninjacat-100" src="https://msdnshared.blob.core.windows.net/media/2017/03/Windows_Insiders_Ninjacat-100_thumb.png" border="0" /></a>PowerShell Security</h2>
Almost two years ago <a target="_blank" href="https://twitter.com/Lee_Holmes" title="https://twitter.com/Lee_Holmes">Lee Holmes</a> released his famous <a target="_blank" href="https://blogs.msdn.microsoft.com/powershell/2015/06/09/powershell-the-blue-team/" title="https://blogs.msdn.microsoft.com/powershell/2015/06/09/powershell-the-blue-team/">PowerShell ♥ the Blue Team</a> whitepaper. This is required reading for anyone who works with PowerShell at all in their job or who is concerned about the <strong>security of PowerShell</strong> in their environment. I outlined a number of PowerShell security-related resources in this previous post: <a target="_blank" href="https://aka.ms/pssec" title="https://aka.ms/pssec">https://aka.ms/pssec</a>.

I am not going to rehash all of the topics in the white paper, but I do want to make it easy for people to implement PowerShell auditing and logging. What better way than putting Lee’s recommendation and code samples into a <strong>Desired State Configuration</strong> (DSC)? If you would like to see Lee demonstrate these improvements you can watch the third module of this video series: <a target="_blank" href="https://aka.ms/MVAps5" title="https://aka.ms/MVAps5">https://aka.ms/MVAps5</a>.
<h2>Practical DSC</h2>
Sometimes customers ask me what should go into their baseline configurations for servers. Now that is a fun conversation. Let’s just say PowerShell auditing is an easy one to overlook. PowerShell security is not on the radar in many IT shops. With this DSC configuration sample below it can become routine.
<h2>Security Features</h2>
As Lee outlined in the whitepaper, WMF 5.x includes a number of enhancements in the area of security:
<ul>
 	<li>Script block logging</li>
 	<li>System-wide transcription</li>
 	<li>Protected event logging (used together with Windows Event Forwarding)</li>
 	<li>and more…</li>
</ul>
Lee even provided sample code to set the Group Policy registry values to enable these enhancements. In DSC it is really easy to set registry values, so let’s get to it.
<h2>Requirements</h2>
We are assuming that the target system already has WMF 5.x installed or upgraded. Each of these enhancement will require the following settings:
<ul>
 	<li><strong>Script block logging</strong>
<ul>
 	<li>HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
<ul>
 	<li>EnableScriptBlockLogging, 1</li>
 	<li>EnableScriptBlockInvocationLogging, 1</li>
</ul>
</li>
 	<li>It may also be a good idea to increase the log size. The <em>Microsoft-Windows-PowerShell/Operational</em> log is 15MB by default.</li>
</ul>
</li>
 	<li><strong>System-wide transcription</strong>
<ul>
 	<li>Create a directory to hold transcripts
<ul>
 	<li>Set permissions on the directory to prevent tampering. (I chose <a target="_blank" href="https://msdn.microsoft.com/en-us/library/cc230374.aspx" title="https://msdn.microsoft.com/en-us/library/cc230374.aspx">SDDL</a> for the shortest code here.)</li>
 	<li>Trim the transcript directory contents on an interval to avoid filling the drive (if local).</li>
 	<li>We are going to use a local directory for now. Lee recommends pointing it to a share off-box.</li>
</ul>
</li>
 	<li>HKLM:\Software\Policies\Microsoft\Windows\PowerShell\Transcription
<ul>
 	<li>EnableTranscripting, 1</li>
 	<li>IncludeInvocationHeader,1</li>
 	<li>OutputDirectory, [Path]</li>
</ul>
</li>
</ul>
</li>
 	<li><strong>Protected event logging (can be used together with Windows Event Forwarding)</strong>
<ul>
 	<li>Requires Windows 10 or Windows Server 2016</li>
 	<li>Requires a document encryption certificate</li>
 	<li>HKLM:\Software\Policies\Microsoft\Windows\EventLog\ProtectedEventLogging
<ul>
 	<li>EnableProtectedEventLogging, 1</li>
 	<li>EncryptionCertificate, [Certificate]</li>
</ul>
</li>
</ul>
</li>
</ul>
<em>Note that the logging and transcription enhancements include an option for invocation logging. This is optional and will increasing the logging volume. It basically adds start/stop header information for each command that is executed. You can omit this setting if you prefer a lower logging leverl.</em>
<h2>Desired State Configuration</h2>
Now that we have an outline of the settings required, we can move those into a DSC configuration. <span style="background-color: #ffff00">You can </span><a target="_blank" href="https://github.com/GoateePFE/BlogScripts/blob/master/PSSecDSC.ps1" title="https://github.com/GoateePFE/BlogScripts/blob/master/PSSecDSC.ps1"><span style="background-color: #ffff00">view the code on my GitHub account here</span></a><span style="background-color: #ffff00">.</span>
<ul>
 	<li>Script Block Logging
<ul>
 	<li>Two registry resources set the values.</li>
 	<li>Then for good measure we use a script resource to increase the size of the PowerShell event log.</li>
</ul>
</li>
 	<li>Transcription
<ul>
 	<li>Three registry resources set the values.</li>
 	<li>We need a file resource to create the directory to hold the transcript files.</li>
 	<li>Then two script resources set the permissions on the directory and remove any old files. We probably could have used an external resource to set the directory permissions, but I want to keep this using in-box resources for simplicity. Keeping the transcript directory clean requires that you set the DSC Local Configuration Manager (LCM) to <em>ApplyAndAutocorrect</em>.</li>
 	<li>NOTE: Remove the file and script resources if you send the transcripts to a remote UNC share path.</li>
 	<li>NOTE: If you use a local path, have fun trying to read the transcript files. Also, the trim files script resource will likely generate warnings trying to clear old files.</li>
</ul>
</li>
 	<li>Protected Event Logging
<ul>
 	<li>I’m going to skip this one due to a couple reasons:</li>
 	<li>Right now we do not have a way to request a certificate in a configuration, and then pass that data to another resource in the same configuration. Writing a custom resource for that wouldn’t be practical, because I’m trying to keep this to in-box DSC resources for now.</li>
 	<li>Most customers already have an event collection tool (<a target="_blank" href="https://en.wikipedia.org/wiki/Security_information_and_event_management" title="https://en.wikipedia.org/wiki/Security_information_and_event_management">SIEM</a>). If not, then explore the <a target="_blank" href="https://www.powershellgallery.com/packages/xWindowsEventForwarding" title="https://www.powershellgallery.com/packages/xWindowsEventForwarding">xWindowsEventForwarding</a> resource module.</li>
</ul>
</li>
 	<li>Fit and Finish
<ul>
 	<li>Notice that the configuration has the following parameters:</li>
 	<li>TranscriptPath – Directory where you want to put the transcript files. Can be local or UNC path.</li>
 	<li>TranscriptDays – How many days of transcripts do you wish to retain?</li>
 	<li>EventLogSizeInMB – Size to set for the PowerShell log to hold the additional content generated.</li>
</ul>
</li>
 	<li>BONUS
<ul>
 	<li>For completeness I threw in a configuration to disable the transcription and logging.</li>
 	<li>I also threw in a couple lines to query the event logs for your new events.</li>
</ul>
</li>
</ul>
Ideally you would take this sample code and create two configurations: one for the nodes being audited and one to set up the server with the UNC share receiving the transcripts and Windows Event Forwarding.

The <a target="_blank" href="https://github.com/GoateePFE/BlogScripts/blob/master/PSSecDSC.ps1" title="https://github.com/GoateePFE/BlogScripts/blob/master/PSSecDSC.ps1">code on my GitHub</a> account is purely sample for you to copy/paste into your own configurations. This makes a great finish for a baseline server security config.
<h2>Gotchas</h2>
The PowerShell console and ISE will cache group policy settings when they are launched. In order to see the effects of the logging and transcription you will need to open a new session.

If you have DSC set to scan the node for compliance (as most of us do), note that this will generate a significant amount of logging via each of these methods.

So if you decide to put the transcript folder on the local box, then the DSC configuration is going to error out when it goes to check the folder for old files to purge. You will see orange warning text when this happens.
<h2>Audit Thy Servers</h2>
Now you have a cool whitepaper on PowerShell security to read and some free DSC code to play with. Use the comments area below to discuss any questions or ideas.

Cheers!<!--EndFragment-->]]></content:encoded>
    </item>
    <item>
      <title>Compare Group Policy (GPO) and PowerShell Desired State Configuration (DSC)</title>
      <link>https://docs.microsoft.com/archive/blogs/ashleymcglone/compare-group-policy-gpo-and-powershell-desired-state-configuration-dsc</link>
      <pubDate>Mon, 27 Feb 2017 21:53:51 GMT</pubDate>
      <dc:creator><![CDATA[Ashley McGlone]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ashleymcglone/?p=2195</guid>
      <description><![CDATA[What is the difference between Group Policy (GPO) and PowerShell Desired State Configuration (DSC)?...]]></description>
      <content:encoded><![CDATA[<a href="https://msdnshared.blob.core.windows.net/media/2017/02/14_floppy.gif"><img width="100" height="100" title="14_floppy" align="right" style="float: right" alt="14_floppy" src="https://msdnshared.blob.core.windows.net/media/2017/02/14_floppy_thumb.gif" /></a>What is the difference between <strong>Group Policy (GPO)</strong> and <strong>PowerShell Desired State Configuration (DSC)</strong>? Why would I use one over the other? I hear these questions frequently. Today we are going to fully explore the pros and cons of each. Is GPO going the way of the floppy disk? Let’s find out.
<h2>The Contenders</h2>
<strong>Group Policy Objects (GPO)</strong> have been around since Windows 2000 for configuring Windows clients and servers. They require Active Directory, and domain controllers host the policies. Clients download the policies at login based on their location in the organizational unit (OU) hierarchy and based on filters by security groups and WMI. This is the first choice for managing Windows machines in the enterprise, and it is included in-box with the operating system. GPOs enforce system settings and facilitate compliance with corporate and industry policies.

<strong>PowerShell Desired State Configuration (DSC)</strong> was introduced with Windows Management Framework (WMF) 4.0. Like group policy, it can configure settings on Windows machines. However, DSC can leverage the automation of PowerShell to configure nearly anything on the system, beyond the preconfigured choices of group policy. DSC is also built into the operating system on Windows 8.1/Windows Server 2012 R2 and above. It is available down level by downloading and installing the latest WMF update (currently 5.1). DSC does not have a graphical management interface and requires knowledge of PowerShell scripting to build out tools for administration.
<h2>Group Policy vs. PowerShell DSC</h2>
Comparing GPO and DSC is like apples vs. oranges, or pirates vs. ninjas, or <a target="_blank" href="https://www.youtube.com/results?search_query=chuck+norris+bruce+lee" title="https://www.youtube.com/results?search_query=chuck+norris+bruce+lee">Chuck Norris</a> vs. .... Well. They each have strengths and weaknesses. And each technology was created to solve a different problem... in completely different eras of computing. 15 years after GPO we now have DSC, addressing a new set of problems that did not exist when GPO came on the scene.

I like to keep things simple, so let’s make a matrix to compare the two. This comparison will be very thorough. If you are impatient, then skip to the end of the article for guidance on when to use either technology.

If you have ever watched the show <em>Who’s Line Is It Anyway</em>, you know that “everything is made up and the points don’t matter.” I am going to give a point to each product along the categories of comparison. These points are based on my own personal bias, evaluating the capabilities of each technology in the current era computing. Do you disagree? Let me know in the comments area below.
<table width="700" border="2" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td width="100" valign="top"><strong>Category</strong></td>
<td width="250" valign="top"><strong>GPO</strong></td>
<td width="50" valign="top"><strong>Point</strong></td>
<td width="50" valign="top"><strong>Point</strong></td>
<td width="250" valign="top"><strong>DSC</strong></td>
</tr>
<tr>
<td width="100" valign="top">History</td>
<td width="250" valign="top">I started using Group Policy in 1999 on the released candidate of Active Directory in Windows Server 2000. This familiarity gives GPO an advantage as the incumbent in the room. It also has the advantage of years of feedback and refinement (and acquisitions).
GPO was an evolution of registry.pol files in the pre-Windows 2000 days.</td>
<td width="50" valign="top">1</td>
<td width="50" valign="top"></td>
<td width="250" valign="top">DSC began in 2014 with the release of Windows Server 2012 R2, earlier if you count preview releases. Currently it is approximately a “version two” product still gaining momentum with broad community support and feedback. You know what they say, when Microsoft hits version three it is ready for prime time.</td>
</tr>
<tr>
<td width="100" valign="top">Domain Environment and Authentication</td>
<td width="250" valign="top">GPO requires an Active Directory domain for traditional delivery, authentication, permissions, etc. GPO can be delivered to Nano Server via <a target="_blank" href="https://blogs.msdn.microsoft.com/powershell/2016/05/20/new-group-policy-cmdlets-for-nano-server/" title="https://blogs.msdn.microsoft.com/powershell/2016/05/20/new-group-policy-cmdlets-for-nano-server/">new cmdlets</a> that will import a policy backup, but the policy engine is not available.
Outside of this you can manually configure local GPOs.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">DSC works in both domain and non-domain environments. In fact, DSC does not even need a network. For one customer I created a thumb drive deployment script for field techs to build servers. It can be that easy. DSC can authenticate via any of the methods supported by WinRM/WsMAN.
DSC now has <a target="_blank" href="https://blogs.msdn.microsoft.com/powershell/2017/02/17/managing-security-settings-on-nano-server-with-dsc/" title="https://blogs.msdn.microsoft.com/powershell/2017/02/17/managing-security-settings-on-nano-server-with-dsc/">resources</a> to leverage the Nano Server GPO cmdlets.</td>
</tr>
<tr>
<td width="100" valign="top">Operating System Requirements (including non-Windows)</td>
<td width="250" valign="top">Wow. GPO works out-of-the-box on any Windows machine since Windows 2000. That’s huge.

Scoring this section is difficult. GPO is ubiquitous and easy. DSC is more powerful, more complicated, and not as complete. DSC will win this later with more refinement; for now I’ll call it a draw.</td>
<td width="50" valign="top">draw</td>
<td width="50" valign="top">draw</td>
<td width="250" valign="top">DSC requires WMF 4.0 or above. (You <em>REALLY</em> want to use 5.x or above.) This is available as far back as Windows 7 and Windows Server 2008 R2. Do you really need to support Windows 2000 any more? If you are still running native PowerShell 2.0 on Windows 7/2008 R2, why? Upgrade today. It’s a no-brainer. The support desk will love you for it.
(The only exceptions here are some server-tier products that don’t take kindly to a WMF upgrade. Be sure to read the <span style="background-color: #ffff00"></span><a target="_blank" href="https://msdn.microsoft.com/en-us/powershell/wmf/5.0/productincompat" title="https://msdn.microsoft.com/en-us/powershell/wmf/5.0/productincompat">release notes</a> <span style="background-color: #ffff00"></span>prior to deploying across your entire data center.)
You also will need remoting enabled on your machines, which is automatic on Windows Server 2012 and above. If InfoSec doesn’t like this, then send them to <a target="_blank" href="https://aka.ms/pssec" title="https://aka.ms/pssec">this article addressing PowerShell security.</a>
But DSC also supports Linux/Unix machines and network switches… barely. Back in 2014 we <a target="_blank" href="https://blogs.msdn.microsoft.com/powershell/tag/linux" title="https://blogs.msdn.microsoft.com/powershell/tag/linux">announced DSC support cross-platform</a>, but it is a work in progress and not really viable compared to all of the more mature solutions in that space… for now. Keep watching here. Azure is driving this, and it will grow quickly, especially since PowerShell is now open source cross-platform.</td>
</tr>
<tr>
<td width="100" valign="top">Behavior</td>
<td width="250" valign="top">GPO <em>prevents</em> changes to the operating system. When applied to GUI settings, the options are grey and simply unavailable. That is pretty strong.
Group Policy Preferences (GPP) allow greater flexibility of setting something only once or allowing it to be changed temporarily.</td>
<td width="50" valign="top">draw</td>
<td width="50" valign="top">draw</td>
<td width="250" valign="top">DSC <em>reverts</em> setting via <em>ApplyAndAutoCorrect</em> configuration mode. If something does get changed, then DSC can put it back. However, DSC can also imitate GPO by setting the exact same registry keys and values. This gives DSC the advantage of both prevention and correction.
Like GPP, DSC also has an <em>ApplyOnly</em> mode for initial configuration that can change later.</td>
</tr>
<tr>
<td width="100" valign="top">Authoring</td>
<td width="250" valign="top">Beginning in Windows Server 2003 you could click through the Group Policy Management Console (GPMC) to find and configure settings. Over time the number of settings has grown to thousands, and you need to download a <span style="background-color: #ffff00"></span><a target="_blank" href="https://www.microsoft.com/en-us/download/details.aspx?id=25250" title="https://www.microsoft.com/en-us/download/details.aspx?id=25250">spreadsheet</a> <span style="background-color: #ffff00"></span>to tell you where to find them in the interface. If you really like GUIs, then GPO is for you. “Just keep clicking; just keep clicking.”</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">DSC is authored in PowerShell, a 10-year-old scripting language that is present in all Microsoft enterprise products. This does require learning new skills, but so does everything else in IT. 15 years ago you had to learn GPO; now you have to learn DSC.
DSC gets the point here, because the power of automation is a force-multiplier when creating configurations.
Move your existing server build scripts into DSC for the win.</td>
</tr>
<tr>
<td width="100" valign="top">Workstation Management</td>
<td width="250" valign="top">GPO wins here, no contest. GPO is your tool of choice for all of the unique workstation configurations across the enterprise. The number of variables would be overwhelming to DSC.</td>
<td width="50" valign="top">1</td>
<td width="50" valign="top"></td>
<td width="250" valign="top">Yes, DSC can do workstations. I have a customer using it to apply security baselines across the enterprise, <em>where the settings are all identical</em>. DSC would also be handy for kiosks, classroom computers, and other <em>static configuration scenarios</em>.</td>
</tr>
<tr>
<td width="100" valign="top">Server Management</td>
<td width="250" valign="top">GPO ensures compliance with industry security requirements for companies all around the world. This works well, even for non-domain machines where you have to import security templates.</td>
<td width="50" valign="top">1</td>
<td width="50" valign="top"></td>
<td width="250" valign="top">DSC can do all that, too, and even easier than GPO in those non-domain environments.
If you want to manage all of the unique server configurations across the data center, then DSC is going to require more effort and/or paid tools. Stick with GPO and System Center there. DSC would facilitate a good baseline build of each server, though.</td>
</tr>
<tr>
<td width="100" valign="top">Server Build</td>
<td width="250" valign="top">GPO can apply settings and even install software for a new server build, but friends don’t let friends deploy software with GPO. You might have to wait a bit for GPO to fully kick in, even using GPUPDATE. And there is that whole non-domain environment problem as well.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">This is where DSC shines. It was made for server deployment… over and over and over. On-prem, cloud, you name it. Once the box is provisioned, DSC takes over inside the box to fully build it out. GPO was not intended for this and lacks the broad coverage of PowerShell under the covers. Yes, you could probably force GPO into this, but it would require a lot more duct tape.
Recently I helped a customer deploy non-domain joined Azure scale set VMs with GPO security baselines migrated into DSC. It was a beautiful thing. All servers came up identically and completely secured… all with just a couple PowerShell commands.</td>
</tr>
<tr>
<td width="100" valign="top">DevOps and Cloud Environments</td>
<td width="250" valign="top">The domain requirement encumbers GPO in the agile world of DevOps. In order to deploy the same settings across multiple domain environments you would have to duplicate GPOs in each. <a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/2014/08/11/gpo-migration-with-powershell-now-including-wmi-filters/" title="https://blogs.technet.microsoft.com/ashleymcglone/2014/08/11/gpo-migration-with-powershell-now-including-wmi-filters/">This is no trivial task.</a> It also requires management of OU and group memberships outside the DevOps pipeline.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">If you want to go fast with modern cloud deployments or DevOps Continuous Integration/Continuous Deployment (CI/CD), then DSC is your tool of choice. DSC can make dev / test / qa / prod all match, for real, and keep it that way.
DSC can both build the server and then ensure the configuration going forward. GPO doesn’t have the coverage to compete here.</td>
</tr>
<tr>
<td width="100" valign="top">Extensibility</td>
<td width="250" valign="top">GPO has always had the ability for you to create custom registry setting template files (ADM/ADMX). Advanced GPO customization involves creation of Client Side Extensions (CSE), which requires developer skills.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">DSC is all about roll-your-own settings, and the customization extends well beyond the registry. You can configure anything that you can script in PowerShell. DSC wins here for ease of customization.</td>
</tr>
<tr>
<td width="100" valign="top">Community Support</td>
<td width="250" valign="top">There may be a few custom items on the TechNet Script Center, but GPO is generally lacking in community extensions when compared with DSC.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">Um. DSC wins this one hands down. From the beginning Microsoft has encouraged community contributions, and we have a rich <a target="_blank" href="https://github.com/powershell" title="https://github.com/powershell">GitHub</a> and <a target="_blank" href="https://powershellgallery.com" title="https://powershellgallery.com">Gallery</a> environment full of community content. This is one of the first times in history YOU have been able to work WITH Microsoft on the product. Our DSC resources are all open source and welcoming of your improvements and fixes. Of course the community is full of other code repositories as well.</td>
</tr>
<tr>
<td width="100" valign="top">Third Party Support</td>
<td width="250" valign="top">GPO has a few vendors who have improved the functionality of the product over the years, and Microsoft acquired some of those to put in the box (ex. Group Policy Preferences, AGPM – Advanced Group Policy Management). The group policy MVPs <a target="_blank" href="https://www.gpanswers.com/" title="https://www.gpanswers.com/">Jeremy Moskowitz</a> and <a target="_blank" href="https://sdmsoftware.com/gpoguy/" title="https://sdmsoftware.com/gpoguy/">Darren Mar Elia</a> each have released a number of GPO enhancements as well.</td>
<td width="50" valign="top">draw</td>
<td width="50" valign="top">draw</td>
<td width="250" valign="top">Where DSC lacks in robust management features, third party vendors have stepped up. Chef and Puppet lend their enterprise readiness from the Linux side of the house to make DSC more administrator-friendly at scale.
Also, Azure Automation DSC has a nice dashboard experience for managing configurations and nodes. Keep an eye on this space.</td>
</tr>
<tr>
<td width="100" valign="top">Delivery Method</td>
<td width="250" valign="top">Group Policy uses a pull model. The client logs on to an Active Directory domain. Then it begins a back-and-forth conversation to discover all the policies and download their respective files. “Got any jacks?” “Go fish”
The advantage of Active Directory is that the Domain Controllers (ie pull servers) are highly available and replicate automatically.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">DSC offers push and pull. Similar to GPO, DSC can download a setting template (MOF) and the files that make it happen (resources). This pull server can be set up for SMB, HTTP, or HTTPS transport. However, the in-box pull server feature is not highly available. The Azure DSC pull server is highly available for nodes both on-prem and in the cloud, but this requires additional cost.
DSC additionally has robust support for peer-to-peer delivery of settings via push.</td>
</tr>
<tr>
<td width="100" valign="top">Firewall</td>
<td width="250" valign="top">GPO uses an <a target="_blank" href="https://technet.microsoft.com/en-us/library/jj572986(v=ws.11).aspx" title="https://technet.microsoft.com/en-us/library/jj572986(v=ws.11).aspx">assortment of ports:</a> SMB 445, RPCSS 135, RPC random ports</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">One port, encrypted by default. WinRM/WsMan 5985, optionally SMB only if the configuration leverages it.</td>
</tr>
<tr>
<td width="100" valign="top">Scalability</td>
<td width="250" valign="top">Both GPO and DSC scale to thousands of machines. GPO wins for ease-of-use here.</td>
<td width="50" valign="top">1</td>
<td width="50" valign="top"></td>
<td width="250" valign="top">DSC requires your own tooling and automation to scale. It is honestly much more work than linking a GPO to an OU. However, the automation and complexity of DSC makes it much more powerful than GPO.
If you need to encrypt credentials in DSC configurations, then scalability gets much more complicated with certificate management. It can be automated, but it is more complex.</td>
</tr>
<tr>
<td width="100" valign="top">How many can I have?</td>
<td width="250" valign="top">It is really easy to apply multiple GPOs to a single machine, and you can see the result using GPRESULT or RSOP. You might have to trace down conflicts, but there is a clear winner and clear rules.</td>
<td width="50" valign="top">1</td>
<td width="50" valign="top"></td>
<td width="250" valign="top">DSC can use partial configurations to apply multiple, unique MOF files to a single system. However, this is not best practice. Worse, this is a tedious process of configuring the LCM with specific names and publishing those via push or pull. GPO wins here for ease-of-use.</td>
</tr>
<tr>
<td width="100" valign="top">Raw Power</td>
<td width="250" valign="top">GPO owns the Windows registry and obscure security settings that are difficult to reach via scripting techniques. It is a solid work-horse in this area. And Group Policy Preferences extend configuration coverage considerably. However, it simply lacks the breadth of settings compared to DSC, especially when we factor in ease of user-extensibility.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">DSC can do just about anything GPO can do, but it has the impressive force of PowerShell behind it. Anything you can do in PowerShell can be deployed to all your nodes. The logic and extensibility of scripting is a clear winner here.</td>
</tr>
<tr>
<td width="100" valign="top">Timers</td>
<td width="250" valign="top">GPO applies every 90 minutes, plus or minus up to 30 minutes, on machines in the domain. The exception is domain controllers which apply GPO every 5 minutes.
This interval is configurable in the registry from 0 minutes to 45 days, but this is not a common practice.
We will give the point to GPO here for the smallest refresh interval, but setting a smaller timer with either technology can cause issues.</td>
<td width="50" valign="top">1</td>
<td width="50" valign="top"></td>
<td width="250" valign="top">DSC has two timers: 1. <em>ConfigurationModeFrequencyMins</em> determines how often the box is scanned for configuration drift. The minimum value is 15 minutes. 2. <em>RefreshFrequencyMins</em> is how often it polls the pull server for a new configuration to apply. This minimum is also 15 minutes.
These values must be a multiple of one another.
<em>Whatever you do, don’t put the same setting in both GPO and DSC. The timers will battle back and forth resetting the same value all day long. There is no automatic conflict detection between the two.</em></td>
</tr>
<tr>
<td width="100" valign="top">Command Line</td>
<td width="250" valign="top">GPRESULT and GPUPDATE are the most common commands. GPO also has a PowerShell module full of helpful commands like Invoke-GPUpdate and Get-GPResultantSetOfPolicy. The module is available when you install GPMC. However, GPO loses a point here, because the PowerShell cmdlets only work with registry-based settings of policies and preferences. When you try to script individual GPO settings you will hit this wall pretty fast.
To be fair, there is a decent set of cmdlets for working with whole policies (links, inheritance, backups, permissions, etc.).</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">DSC has a vast array of command line support, since it was borne out of PowerShell:
Get-DscConfiguration
Get-DscConfigurationStatus
Get-DscLocalConfigurationManager
Remove-DscConfigurationDocument
Restore-DscConfiguration
Stop-DscConfiguration
Invoke-DscResource
Publish-DscConfiguration
Set-DscLocalConfigurationManager
Start-DscConfiguration
Test-DscConfiguration
Update-DscConfiguration</td>
</tr>
<tr>
<td width="100" valign="top">Logging</td>
<td width="250" valign="top">Both GPO and DSC have their own Windows Event Logs.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">I am going to give the point here to DSC, because of the <a target="_blank" href="https://www.powershellgallery.com/packages/xDscDiagnostics" title="https://www.powershellgallery.com/packages/xDscDiagnostics">xDscDiagnostics</a> module that does some cool collation across all the log sources. Also, DSC has a second logging mechanism to view with Get-DscConfigurationStatus, and this data is available centrally in Azure Automation DSC or from the in-box reporting/pull server.</td>
</tr>
<tr>
<td width="100" valign="top">Troubleshooting</td>
<td width="250" valign="top">GPO has a generous assortment of event logs, text log files, and command line utilities for troubleshooting.
GPO cannot do the live debug like DSC, at least in a user-friendly way. Well, I suppose you could attach a Windows debugger to troubleshoot GPO. Do you know anyone who can do that (besides people at Microsoft)?</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">DSC has the dominance in command line and logging as discussed above, but it also has the ability to <em><a target="_blank" href="https://blogs.technet.microsoft.com/ashleymcglone/2016/10/26/gnarly-innards-how-to-live-debug-powershell-dsc-configurations-without-using-enable-dscdebug/" title="https://blogs.technet.microsoft.com/ashleymcglone/2016/10/26/gnarly-innards-how-to-live-debug-powershell-dsc-configurations-without-using-enable-dscdebug/">live debug remotely</a></em> with no special tools as a configuration is applying. Now that’s cool!</td>
</tr>
<tr>
<td width="100" valign="top">Version Control</td>
<td width="250" valign="top">Version control and release scheduling for GPO requires an additional license for Advanced Group Policy Management (AGPM).</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">Since DSC is configuration-as-code, any source code management tool will work. Popular options are Visual Studio Team Foundation Server (TFS), git-based solutions, and other third party products. I’ll give the point to DSC here, because some of these are free.</td>
</tr>
<tr>
<td width="100" valign="top">Updates</td>
<td width="250" valign="top">GPO combined with WSUS is an effective patching solution for many organizations. This is an established pattern that works well. “If it ain’t broke, don’t fix it.”</td>
<td width="50" valign="top">1</td>
<td width="50" valign="top"></td>
<td width="250" valign="top">Yes, DSC can apply patches or updates using the xHotfix resource. However, you really don’t want to write all of that in configuration-as-code. Even though you could automate it with scripting, it is less than ideal. Stick with your other patching solution.
The only exception here would be doing a new server build. Sometimes there are certain patches you might want to lay down first before proceeding to configure the box (like WMF 5.1). One-off patch scenarios like this are well-suited to DSC.</td>
</tr>
<tr>
<td width="100" valign="top">What-If Scenarios</td>
<td width="250" valign="top">GPO can use RSOP modeling to determine “what configuration would this user and computer combination get in these OUs?”</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">DSC has a similar feature using
Test-DscConfiguration
-ReferenceConfiguration. The advantage goes to DSC for the ability to scan multiple machines simultaneously and return object-oriented data for easy bulk reporting.</td>
</tr>
<tr>
<td width="100" valign="top">Reporting</td>
<td width="250" valign="top">How would you produce a list of machines that were in or out of compliance with GPO? There is RSOP and GPRESULT, but those are quite tedious to view the entire environment. Perhaps you have a third party product to do this, but this visibility across the enterprise is not easy for GPO. I guess you could use the RSOP cmdlets if you wanted to build your own solution.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">Enterprise-wide compliance reporting for DSC is marginally better. The current in-box reporting server leaves a lot to be desired (like high availability), and the Azure Automation DSC solution costs money. However, you can script your own reporting solution using a number of DSC cmdlets like Test-DscConfiguration, Get-DscConfigurationStatus, etc.
I have heard of one person who built their own SCOM management pack for DSC. Other customer may leverage a System Center script to collect and report this data.
DSC has a nice object model to clearly identify which parts of the configuration are in and out of desired state.</td>
</tr>
<tr>
<td width="100" valign="top">Long-term Microsoft Direction</td>
<td width="250" valign="top">Many people wonder if “GPO is dead”, but we are still releasing new policies for Office and Windows with each subsequent version. I do not expect this to go away any time soon.</td>
<td width="50" valign="top"></td>
<td width="50" valign="top">1</td>
<td width="250" valign="top">Microsoft is making significant investments in DSC and it is used widely across our modern offerings. <strong>This is not a passing fad. Learn it now.</strong> It will be here for decades to come. GPO, only time will tell. The PowerShell DSC team at Microsoft is full of ex-Group Policy product people. Maybe that is a clue.</td>
</tr>
<tr>
<td width="100" valign="top"><strong>Point Tally</strong></td>
<td width="250" valign="top"><strong>Group Policy</strong></td>
<td width="50" valign="top"><strong>7</strong></td>
<td width="50" valign="top"><strong>16</strong></td>
<td width="250" valign="top"><strong>PowerShell DSC</strong></td>
</tr>
</tbody>
</table>
&nbsp;
<h2>Summary Recommendations</h2>
Look at the score! Do these points really matter? Maybe. Depends on your goals. <strong>Clearly I am biased towards DSC. Why? <a target="_blank" href="https://www.businessinsider.com/microsoft-ceo-satya-nadella-on-growth-mindset-2016-8" title="https://www.businessinsider.com/microsoft-ceo-satya-nadella-on-growth-mindset-2016-8">Growth mindset</a>.</strong> Stay locked into the old ways of GPO, or learn the new ways of DSC. Your choice. Tell me what you think in the comments area below.

At the time of this writing each technology has its own pros and cons. Here are the scenarios where I see each as most useful:
<table width="700" border="2" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td width="350" valign="top">Group Policy</td>
<td width="350" valign="top">PowerShell DSC</td>
</tr>
<tr>
<td width="350" valign="top">
<ul>
 	<li>Enterprise workstations</li>
 	<li>Complex scenarios requiring deep layering or filtering of settings</li>
 	<li>Patching with WSUS</li>
 	<li>Staff with only GUI skills</li>
</ul>
</td>
<td width="350" valign="top">
<ul>
 	<li>Server builds and baseline configurations</li>
 	<li>Static workstations</li>
 	<li>DevOps</li>
 	<li>Cloud and nano server deployments</li>
 	<li>Non-domain environments</li>
 	<li>Configuration drift scenarios</li>
 	<li>Compliance reporting</li>
</ul>
</td>
</tr>
</tbody>
</table>
If you are the typical enterprise environment, don’t rip and replace GPO with DSC. If you are building green field, doing DevOps, or moving to the cloud, then start using DSC right away. I recommend standard DevOps advice in every case: start small with DSC in a limited scope, learn it, get some early wins, and then build out the span of control as your experience and the product grow.

If you still have not learned PowerShell, then you are at a severe disadvantage in your career. PowerShell is not a “new thing”; it is 10 years old. Similarly, Desired State Configuration is three years old now. This is <em>not</em> one of those “wait and see” technologies. It is here to stay. Learn it today.
<h2>Resources</h2>
There are several places to learn about PowerShell Desired State Configuration online:
<ul>
 	<li>Start here: <a href="https://microsoft.com/powershell" title="https://msdn.microsoft.com/powershell">https://microsoft.com/powershell</a>. This page includes links for documentation, WMF download, etc. The documentation includes many code samples for getting started.</li>
 	<li><a target="_blank" href="https://mva.microsoft.com/search/SearchResults.aspx#!q=dsc" title="https://mva.microsoft.com/search/SearchResults.aspx#!q=dsc">Microsoft Virtual Academy</a> has several good videos for training on DSC.</li>
 	<li><a target="_blank" href="https://channel9.msdn.com/Search?term=dsc#ch9Search&amp;lang-en=en&amp;pubDate=year" title="https://channel9.msdn.com/Search?term=dsc#ch9Search&amp;lang-en=en&amp;pubDate=year">Channel 9</a> also has many good training videos.</li>
 	<li>Follow <a target="_blank" href="https://twitter.com/search?q=%23psdsc&amp;src=typd" title="https://twitter.com/search?q=%23psdsc&amp;src=typd">#psdsc</a> on Twitter for all the latest news and blog posts. Connect real-time with people using DSC in the real world.</li>
</ul>]]></content:encoded>
    </item>
  </channel>
</rss>