Using ConfigMgr Compliance to Manage Security Configuration Baselines (Part 3)

If you haven’t already done so, below are links to the previous posts in this series to give you a chance to go back and read them to get caught up.

And now… on to part 3!

Where we are so far…

In the previous post (part 2) we did the following:

  • Exported (backed up) the Group Policies we wanted to create ConfigMgr Configuration Items from.
  • Generated all of the INF files using the exported Group Policies.

Next, we need to generate two things that we will need for the ConfigMgr Configuration Items.

  1. Generate the scripts
    1. Setting discovery scripts
    2. Setting remediation scripts

Generating the Scripts

We have some choices here, we can manually create each PowerShell script by hand for each setting we need to monitor and enforce compliance on, or we can be smart about it and leverage a single script with functions in it to write the scrips we need for us. Since I dislike having to type so much, we opted for the latter.

For our script, it needs to do the following actions:

  1. Get all of the INF files we generated
  2. Process each one
  3. Output a PowerShell script for setting discovery
  4. Output a PowerShell script for setting remediation

If we ran the script from part 2, all of our INF files should reside in a directory structure in the same path as the script. This makes it very easy to get all of our INF files. We can simply add these three lines at the top of our script.

This will get all of the files with the INF file extension under the directory ‘Custom-INF-Files’ and store them in the variable ‘$CustomINFFiles’.

Next, we need to process all of the custom INF files which means we will need a ‘Foreach’ loop… like this.

So far we have the INF files, and the ‘Foreach’ loop, now we need to add functionality that will actually do what we need. The easiest way would be to leverage functions (or snippets of code already produced and easily located on the internet. Other ways might be to see if people we know have already done something close to what we want to do. Thankfully, we didn’t need to reinvent the wheel in this area.

Since we can’t do file copies from within Configuration Items, we needed a way to copy the INF file for the setting we are checking. To do this we read the INF file into a base64 encoded string and write the string back to a file like this.

With a little help from DR Scripto’s Microsoft Dev Blog post ‘Use PowerShell to Work with Any INI File‘ we get the following function.

With some assistance from @theznerd, we were able to modify the function from Dr Scripto to create the code needed for our discovery scripts. Sample below…

Now we need to perform the remediation. Since we’ve chosen to use ‘secedit.exe’ that is what our PowerShell script will run to import the setting that we’ve determined to be “Compliant” with our organization’s security policy. We’ll first write the file from base64 and then run ‘secedit.exe’ to import it into the ‘secedit.sdb’ (local security policy database) on the target system.

Now we just need to pull the function together while being careful to escape special characters where needed so that our discovery and remediation scripts will be written and function properly. (In the interest of maintaining focus, I’m not going to get into the explanation of escaping special characters in PowerShell at this time.) Our final function for generation of our discover and remediation scripts looks like this. (Use the toolbar above the snippet to expand or copy the code)

We needed to actually output the generated script files now, so I just used a 3 line function that can be used to write text into any file that accepts text or strings as content.

Putting it together

All we need to do now is take our code snippets, add them to the Foreach loop and add a few things to make it all work for us, then save the file as ‘Generate-CMConfigItemScripts.ps1’

(Use the toolbar above the snippet to expand or copy the code)

After running the script we should now have all of the pieces needed to create our ConfigMgr Configuration Items and the settings within them.

Generate Scripts

In the next and final part, we’ll go through creating the Configuration Items and the Configuration Baselines with remediation enabled…with PowerShell.

Have a great week! See you soon!

Using ConfigMgr Compliance to Manage Security Configuration Baselines (Part 2)

In part 1, we set the stage for the work we are about to do. We briefly went over the items that led up to our decisions. In the next parts, we’ll walk you through what we did. If you would like, you can go back and read Using ConfigMgr Compliance to Manage Security Configuration Baselines (Part 1) to get caught up.

Active Directory Group Policy

We need to get the settings that were already configured within the domain so that we can create the needed INF file templates for the non-registry policy settings.

To do this, let’s fire up an elevated PowerShell session and do the following:

If you know the name of the GPO you are looking for, you can simply export it to the desired location of your choice. Like this…

If you don’t know the name of the policy you are looking for, you can get the names using the following…

Or, if we only know part of the GPO name, we can search for all of those that have the portion of the name we remember in it. Example – to get all GPOs that contain the word ‘Default’ in the name…

But what if we want to have a choice of exporting ALL Group Policies, or just those with a specific word or term in their name? Well, we would script that. The script might look something like this (The script below is the same script we used for our customer. I’m just placing it here for others to use if they wish.) By the way, you can also copy the code below and save it as ‘Export-GroupPolicyObjects.ps1’. It can be used to backup GPOs in the future as well.

(The code below can be expanded and copied using the snippet toolbar at the top)

If we run the above script with the following command-line

We see this output…

Output example from Export-GroupPolicyObjects.ps1 script

OK, so now we have our GPO exported to “C:\Temp\GPOExports\Default Workstation Policy”. Let’s go take a look at the INF file. The file we need from the GPO is ‘GptTmpl.inf’. It should be located in “C:\Temp\GPOExports\<GPOName>\<GPOGuid>\DomainSysvol\GPO\Machine\Microsoft\Windows NT\SecEdit”.

First, let’s tackle the User Rights Assignments since they won’t be converted to ConfigMgr Configuration Items with the script.

Open the ‘GptTmpl.inf’ file with notepad to edit it. We need to remove all of the lines we don’t need.

You’ll notice that the INF is broken up into sections with each section header specified between the square brackets “[ ]”. There are three (3) sections we are interested in. They are:

  • Unicode
  • Version
  • Privilege Rights

All of the other sections can be removed. Once this is done, you should have a file that looks similar to below.

GptTmpl.inf Example 1

Depending upon your organization’s security requirements, there may be more or less entries. Save the file as ‘UserRights.inf’ in the folder “C:\Temp\INF Files” so that we can create our individual custom INF files for use in our compliance remediation scripts.

Another way to create the custom INF files for our use is to leverage a script that will go through and generate them for us. The script code below will do just that. Just copy and save the code as “Generate-AllCustomINFFiles.ps1”

(The code below can be expanded and copied using the snippet toolbar at the top)

When you run the above script, the screen output should look something like this…

Generate-AllCustomINFFiles.ps1 script output screen

Now that we have this much ready to go, we can move on to generating our discovery scripts for the ConfigMgr Configuration Items.

Below are links to the other posts in this series.

Using ConfigMgr Compliance to Manage Security Configuration Baselines (Part 1)

You want me to do what?

The alarm clock is screaming at me to get the heck out of bed. I snooze it twice, then finally get up. Once I’ve dressed, I cruise to the kitchen for some much needed coffee because I stayed up waaay too late the night before. After coffee, I walk to my home office where my laptop awaits, along with a likely plethora of emails to respond to before continuing work on a client’s project. It seems like just another day, nothing new… on cruise control.

As soon as I sit down, I check Teams to see what’s going on and if anybody needs any immediate assistance with some technical issue they are experiencing. Wouldn’t you know it, my manager is pinging me to discuss an upcoming project for one of our clients.

Manager: “Hey! We have a project slated to start next week, and I think it’s right up your alley!”

I’ve heard this before

Me: “Awesome! What needs to be done?”

Manager: “Our customer needs to have some ConfigMgr work done around Compliance Settings.”

Compliance Settings in ConfigMgr? This is going to be another quick and easy engagementnothing too difficult or strange to worry about

Me: “Great! I’m in.”

famous last words

There was much more conversation than that, but I will spare you the details and just cut to the kick-off call with the customer…

Customer: “We have a security audit approaching, and we need to apply security settings to our servers and workstations. As well as be able to document and convey our strategy moving forward according to our security specifications.”

Me: “No problem, what exactly are we looking at doing? Don’t you already have Active Directory GPO to set these?”

Customer: “We do… but moving forward as an organization we are moving away from GPO for many of the ‘STIG’ settings and want to leverage ConfigMgr to accomplish our goals.”

Me: “STIG settings?”

Customer: “Yup.”

I’ve not had to do much with STIG settings in the past because these were always handled by the various security teams. Surely I was selected for this project by mistake, right? But I like to think that I’m an intelligent guy, I can figure this out..let’s roll with it

Me: “OK, do you have the settings rules identified and your organizational settings documented?”

Customer: “We do, in total there are close to 3 to 5 HUNDRED security settings that need to be checked, monitored, and remediated when needed on an ongoing basis for Windows Server 2016 alone. We need the same done for Windows Server 2008, 2012, 2012 R2, 2016, 2019, and Windows 10.”

Did he just say “3 to 5 HUNDRED security settings for Windows Server 2016 alone”??… holy cow that’s a lot

Me (as if it didn’t even phase me): “No problem, when does it need to be done?”

Customer: “We’d like to have this done yesterday or as soon as possible, because the Federal audit begins in about a month.”

HmmmThat’s 3 to 5 hundred settings for each for 6 operating systemsThat means a potential of 3,000 settings to check, remediate, and report on in a month for a Federal auditWhat have I gotten myself into here?

Me: “I’m not sure that we can get up to 3,000 settings done in under 4 weeks, but we’re going to try. If we don’t make it by the time the audit starts, we definitely will have a documented strategy in place that we can communicate.”

Customer: “Excellent! That will work. Is Monday good for you as a start day?”

Me: “Works for me!”

The whole time we were on the call, I was searching the internet for everything that I could find about applying STIG settings with ConfigMgr Compliance. Let me just say this…there is a serious lack of documentation around this…

I made the commitment, I better deliver. Time to get to work.

What are some of the ways that I can do this?

Below are simply a few of the ways that we can get this job done. These are in no way intended to be the final word on the subject.

Active Directory Group Policy

We can also create a backup of the policies and then grab the settings we need from the ‘GptTmpl.inf’ file. With the settings we need, we can create INF files to be used to effect the settings desired using ‘secedit.exe’.

During my searches, I found the Microsoft Security Compliance Toolkit. The description of the toolkit from Microsoft says that it is a set of tools that allow administrators to “download, analyze, test, edit and store Microsoft-recommended security configuration baselines for Windows and other Microsoft products, while comparing them against other security configurations.”

After having downloaded the toolkit, I found that it is preconfigured Group Policy Objects that can be imported into the domain. Once imported, we are able to modify the policies to match what internal security teams have determined to be the best for their organizations. This is all well and good, except that many of these settings may not be tested in an environment and carry with them the potential to cause widespread issues upon deployment if they aren’t vetted first. Remember, each environment is different. Different applications, jurisdictions, requirements, work styles, people, and cultures. Also these preconfigured policies may have far more settings configured than what is needed by a particular organization.

Further, if an organization already has a large number of policy objects and filters within the domain that are deployed . The aggregation of these policies and filters can conflict (if they are not planned and modeled properly), and cause performance issues in applying the policies. This can result in policy processing timeouts that are seen by incomplete application of the settings, or policies not being applied at all; as reflected by the errors reported in client event logs.

Tattoo the Local Registry

The term is exactly as it sounds. It means to manual create or import registry settings that remain static on a destination system.

Registry settings will work for a great number of things except policies like renaming guest accounts, renaming administrator accounts, password complexity, and user rights assignments to name a few. If your organization decides that a change or modification is needed, all that needs to be done is to push out the settings with a deployment tool or script that applies the changes through PowerShell remoting, or WinRM. However, if the setting is determined to need removal, we might run into some difficulties if we don’t have a toolset that allows us to perform this action in bulk. The recommendation is to avoid registry tattooing; if it cannot be avoided, only tattoo settings that we know will NEVER change after they are initially set.

Manually Edit the Local Security Policy

Using the command ‘secpol.msc /s’ at the command prompt (elevated), will open up the “Local Security Policy” editor. From here we can go through each of the desired settings and make the changes necessary to effect the security posture as defined by our organization. Of note here is the ability to set a configuration as needed, then export that configuration from the command-line using ‘secedit.exe’. Once we have the configuration exported, we can then import that same configuration onto other systems using different command-line options for ‘secedit.exe’.

This is great if we only have a few dozen systems to manage. When we begin talking about hundreds or even thousands of machines, we need to be looking for a better way (or combining this with other methods).

Use PowerShell Desired State Configuration (DSC)

In Microsoft Docs, there is an article called “Quickstart: Convert Policy into DSC” that describes how to convert Group Policy backups into DSC. This works great if we are converting the canned policies from the Microsoft Security Compliance Toolkit. Mileage may vary when we try to use it on a GPO backup from and existing policy within our domain. The command ‘ConvertFrom-GPO’ within the ‘BaselineManagement’ PowerShell module doesn’t seem to handle the quotes and apostrophes within the legal banner and header text in the GPO correctly. As of January 2021, this has been an issue for almost three years with no movement toward getting it fixed. To get around the issue, you’ll need to modify the ‘GptTmpl.inf’ file within the GPO backup or remove the settings from a copy of the GPO prior to making the backup.

Once we are able to successfully run the ‘ConvertFrom-GPO’ command, a MOF file is created with all of the settings contained in the GPO. This MOF file can then be used to apply our DSC configuration to our target systems.

Please note that to successfully apply the configuration, two additional modules will be needed on the target systems:

Of course, this method is not without its potential issues. If your environment leverages FIPS policies as part of the security configuration, the PowerShell modules needed will not be able to be installed onto target systems. We’ll need to figure out a way to download and install those modules offline in order to have success.

OK, so what now? What’s my path?

Pick a Pony and Ride That Thing!

Ultimately, we chose to do a combination of the following:

  • Active Directory Group Policy
    • Grab the ‘GptTmpl.inf’ files from GPO backups
    • Create custom INF files for each of the non-registry policy settings we need
  • Create PowerShell discovery scripts to determine compliance with the settings
  • Create PowerShell remediation scripts that run ‘secedit.exe’ to import the custom INF files we created
  • Create ConfigMgr Configuration Items for registry policies using a PowerShell script “Convert-GPOtoCI
  • Create ConfigMgr Configuration Items for non-registry policies and leverage the custom discovery & remediation scripts we’ve created

We decided to do the work this way because:

  • In order to install PowerShell modules onto the target systems, we would have needed a rollout plan for that including change management because of the potential impact on the environment
  • FIPS policies were in place and modules could not be downloaded from the gallery
  • ‘Secedit.exe’ was on every target Operating System within scope
  • We had time constraints that didn’t allow addition of project effort and scope at the time

With the decision made, we needed to get moving. Time was not our friend.

Continue the series…

Windows 10: In-place upgrade w/ PGP Desktop Encryption

Overview/ Intro

Recently, I’ve had a few customers who use full disk encryption from Symantec (Symantec PGP, Symantec Endpoint Encryption, or Symantec Desktop Encryption) on client computers instead of Microsoft BitLocker. “Why?”… well… it’s most likely that they have a really good Symantec sales rep. Either that or they implemented prior to BitLocker being ready for prime time and never bothered to change the solution. And yes, I prefer the Microsoft solution for its ease of management and integration points.

The most recent customer was running Windows 7 with Symantec Desktop Encryption (complete with the server component for management) for full disk encryption. Their goal was to upgrade all Windows 7 clients to Windows 10 (Current Branch) without decrypting the volume, if possible. They also use SCCM for endpoint management, software deployment and OSD. Our solution needs to leverage SCCM and an In-Place Upgrade Task Sequence.

To achieve the goal, we have some options available to us (there may be more than I’ve listed here, but you get it):

  1. Nuke and Pave (treat it like bare metal or replacement scenario)
    • Capture user files and data to a network location (USMT)
    • Delete all partitions on the physical disk
    • Install/ Apply Operating System
    • Install applications
    • Install drivers
    • Restore user files and data from network location (USMT)
  2. Include the encryption drivers by modifying the setup command-line
    • Leverage the “/ReflectDrivers” command-line option
  3. Suspend disk encryption
    • Suspend Bitlocker (if used)
  4. Decrypt the drive
  5. Use vendor supplied upgrade scripts
  6. Use a varying combinations of the above options

Ultimately, we chose option 6 (a combination of options 2 and 5). This was because of the following:

  • “Nuke and Pave” might take additional dev time to determine “What is user data?” for USMT to be right.
  • Symantec Encryption Server didn’t have an option to suspend encryption.
  • Decryption takes waaayyyy too long, and isn’t a valid option with the requirements given by the customer.


Based upon the customer requirements and the options chosen, we will need the following items to build our solution:

  • A healthy and functional Configuration Manager Hierarchy
  • In-Place Upgrade Task Sequence
  • Vendor supplied upgrade scripts from Symantec
    • A google search for the correct scripts for your version of PGP is needed. Simply find the version(s) you need and download the appropriate zip file(s).
    • SUGGESTED GOOGLE SEARCH TEXT: “Win10 In-Place upgrade PGP <version> “
      • <version> = the version of PGP you are using.
      • Ours was 10.4.1 located here
      • The specific zip file for version 10.4.1 is here
  • A few guinea pigs (Windows 7 machines with disk encrypted). Three (3) or Four (4) should do it.


Review the Upgrade Script(s) from Symantec

  1. Unzip the files in the package, then open the “Readme.txt” file.
  2. Look for the “Usage:” line in the readme file. It should have something like “WinRS3-upgrade-SED1041.cmd “
    • “WinRS3-upgrade-SED1041.cmd” would be the name of the upgrade script to examine (CMD file).
  3. Open the CMD file in notepad.
  4. Note the actions being performed; these will be the actions we perform using PowerShell.
    • For PGP 10.4.1 (our version), the CMD file does the following:
      1. Create a temp folder at “C:\PGPTemp”
      2. Stop running PGP processes
      3. Copy PGP Drivers from “C:\Windows\System32\Drivers” to “C:\PGPTemp” if present
      4. Copy support files from script folder to “C:\PGPTemp”
      5. Sets the upgrade command-line options “/ReflectDrivers” and “/PostOOBE”
      6. Initiate the upgrade to Windows 10

Mimic the script actions with PowerShell

Now that we know the steps (from Symantec) to perform our upgrade on an encrypted system, we can create our PowerShell script. Please note that these steps are not in the exact same order as the CMD file from Symantec; the end result is the same though.

  • We’ll start our script with a line to reference the location (path) of the current running script that will help us later on when we add it to the Task Sequence.
  • Then, we’ll stop any running PGP related processes with this bit of code..
  • We need to create the “C:\PGPTemp” folder and copy the encryption drivers next, but only if the drivers exist in the first place. Here’s how we do that…
  • Now we need to copy the support files from the script folder. We’ll add some code to our script to perform that action.
  • Since SCCM uses the “setupcompletetemplate.cmd” (located in ‘C:\Windows\CCM‘)as a template to create the “%WINDIR%\Setup\Scripts\SetupComplete.cmd” file, we will need to add the content from the PGP version of “setupcomplete.cmd” to our template. Yup… more PowerShell…
  • Since our SCCM Upgrade task sequence will set the “/PostOOBE” setup option for us, we only need to set the “/ReflectDrivers” option as our last step of the script… like this.

Here is the whole thing cleaned up as a single PowerShell script. Copy the code below and save it as “Set-PGPUpgradeDrivers.ps1


  • Set up bypass of Symantec BootGuard
    • Enable bypass for up to 4 reboots. Information on how to do this is located here.
  • Create a package in SCCM called “OSD-SetPGPUpgradeDrivers”
  • Add the step to your task sequence

Later on as time allows, I’ll try to add more information to this post about:

  • Accommodating other PGP versions
  • Adding support for upgrading from other Windows Client Operating Systems
    • Window 8, and 8.1
    • Windows 10

Use PowerShell to Create SCCM 2012 Site Boundaries

I needed a script to create IPRange Boundaries within SCCM 2012. One of my coworkers suggested that I use a PowerShell CMDLet  to get this done for me.

So I opened the CM2012 console and ran the PowerShell Session from the menu in the upper left corner. Then I ran ‘get-help boundary’ to see what was available.

One of the Cmdlets returned was ‘new-cmboundary’. The script reads each line of a csv file in and parses it for the needed information to construct the Boundaries.

Here is the script I came up with.

To run this script you will need to do three things.

  • Run the SCCM 2012 Console as administrator
  • Open the PowerShell session from the menu in the upper left corner of the SCCM Console.
  • Run this command: Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process

CSV File format:

Hyper-V Lab IPRANGE,IPRange,

CreateBoundaries.ps1 <pathtofile>.csv

This script can actually be used to create all of the boundary types in CM2012. To do so, you would need to change the Boundary type and respective value in your CSV file.

Available boundary types are:

  • IPSubnet (Example corresponding value: “”)
  • ADSite (Example corresponding value: “Default-First-Site-Name”)
  • IPv6Prefix (Example corresponding value: “FE80::/64”)
  • IPRange (Example Corresponding value: “”)

For more information about the commands used in the script, open PowerShell from within the SCCM console and type the following commands:

  • Get-Help Import-CSV
  • Get-Help New-CMBoundary