Lock Down a ConfigMgr Task Sequence to an Active Directory Security Group

The ‘production’ Windows 10 build in our environment is v1607 while the Windows 10 1703 build is still undergoing testing and is yet to be given the green light. It was brought to the attention of the powers that be that 2L Engineers have been installing the test build for end user devices which has caused a fair amount of issues since it’s not ready for production yet.

My proposal is to lock down the Task Sequence to a select few members of staff. This is what I propose:

  • Prompt for username and password at the beginning of the Task Sequence
  • If the username is a member of an AD group then the user is authorised to install this build
  • Next step is to verify their authentication by attempting to map a network drive using their credentials
  • If the drive maps successfully then the user is authorised and authenticated and thus can proceed with installing the build

Important: I make use of the excellent Deployment Web Service by Maik Koster to carry out Active Directory related tasks during OSD in my lab. As such the web service is a pre-requisite to implementing the solution demonstrated in this post.

First, a demo on how this works

Here’s a video I uploaded to YouTube yesterday to demonstrate what my implementation looks like. It demonstrates the following scenarios:

  • Entering an incorrect username (which doesn’t exist in AD)
  • Entering a username which is not part of the AD group and hence is not authorised to install this build
  • Entering a username which IS authorised to install the build but an incorrect password was entered
  • Finally, the video shows entering a username and the correct password for an account authorised to install this build

 

Instructions on how to implement the solution

Now, let’s take a look at the instructions…

Create the Active Directory group

Create an AD security group called something like “Windows10_Build_Testers”. Add users to the group who are authorised to run the Task Sequence.

Create the shared folder

Create a shared folder called something like “TaskSequenceLockdownShare” on a server/computer and give the “Windows10_Build_Testers” group share and NTFS security permissions. This can be on any server which is guaranteed to be up and running 24/7 as it will need to be on the network when Engineers run deployments.

Edit the PowerShell script

Download the PowerShell script. Here’s what it looks like:

Clear-Host
# Temporarily close the TS progress UI
$TSProgressUI = New-Object -COMObject Microsoft.SMS.TSProgressUI
$TSProgressUI.CloseProgressDialog()
Start-Sleep 1

# Set console size and title
[console]::WindowWidth=95; [console]::WindowHeight=25; [console]::BufferWidth=[console]::WindowWidth
$host.ui.RawUI.WindowTitle = "Checking authorisation to run this build..."

# Initialise variables
$URI = "http://mni-sccm01/OSDWebService/AD.asmx?wsdl"
$WebService = New-WebServiceProxy -Uri $URI -Namespace OSDWebService
$SharedDriveToMap = "\\mni-sccm01\TaskSequenceLockdownShare$"
$ADGroupName = "Windows10_Build_Testers"
$DomainName = "EMENEYE"
$Global:TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$TaskSequenceName = $Global:TSEnv.Value("_SMSTSPackageName")

# Inform user this build is locked down
Write-Host "`n$TaskSequenceName" -BackgroundColor DarkBlue
Write-Host "`nThe build you selected has not been released to production yet" -BackgroundColor Black
Write-Host "Authentication is required to install this build..." -BackgroundColor Black

# Set registry entry to prompt for credentials at the command line instead of using a dialog box
# Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds" -Name "ConsolePrompting" -Value $True

Function Get-UserCredentials {
  # Get credentials
  Do {
    Start-Sleep 3
    $Global:TSCeds = $Host.ui.PromptForCredential("Authentication is required...", "Please enter your username", "", "")
  } Until ($Global:TSCeds.UserName -ne "")

  # Check if the domain was entered. The idea is to make this script work with or without the domain name entered by the engineer 
  If ($Global:TSCeds.UserName -like "$DomainName\*") {
    # Strip the domain from the username
    $Global:Username = ($Global:TSCeds.UserName).Split("\")[1]
  }
  Else {
    $Global:Username = $Global:TSCeds.UserName
  }
 
  # Proceed to check if user exists in AD
  Check-UserExists
}

Function Check-UserExists {
  # Get user attributes using the Web Service
  $Result = $WebService.GetUserAttribute($Global:Username, "userPrincipalName")

  # Check if the user exists in AD (the attribute should have been returned and thus the variable should be populated)
  If ($Result) {
    # User exists in AD
    # Call function to check if the user is a member of the AD group
    Check-IfUserIsInGroup
  }
  Else {
    # User does not exist in AD
    Write-Host "`nThis username $($Global:Username.ToUpper()) does not exist in Active Directory" -ForegroundColor Red -BackgroundColor Black
    Write-Host "Please try again..." -BackgroundColor Black
    Write-Host ""
 
    # Call function to get user to try again
    Get-UserCredentials
  }
}

Function Check-IfUserIsInGroup {
  # Call Web Service to get list of user's group memberships
  $Result = $WebService.GetUserGroupsByName($Global:Username)

  # Check if user is in SCCM_Admins group in AD
  If ($Result -contains $ADGroupName) {
    # User is a SCCM admin
    Write-Host "`n$($Global:Username.ToUpper()) is authorised to install this build..." -BackgroundColor Black
    # Call function to check their authentication
     Check-Authentication
  }
  Else {
    # User is not in AD group
    Write-Host "`nThe username $($Global:Username.ToUpper()) is not authorised to install this build" -ForegroundColor Red -BackgroundColor Black
    Write-Host "Please try again or speak to the EUC team if you believe you are authorised" -BackgroundColor Black
    Write-Host ""
 
    #Call function to get user to try again
    Get-UserCredentials
  }
}

Function Check-Authentication {
  # Attempt to map a drive
  # If the drive maps successfully then the user is authenticated
  # Map drive using credentials
  $Drive = New-PSDrive -Name L -PSProvider FileSystem -Root $SharedDriveToMap -Credential $Global:TSCeds -ErrorAction SilentlyContinue

  # Check if drive was mapped successfully
  If (($Drive).Name -eq "L") {
    Write-Host "User authentication successful. Proceeding to pre-flight checks..." -ForegroundColor Green -BackgroundColor Black
    Remove-PSDrive -Name "L"
    Start-Sleep 4
 
    # Set the AllowInstall task sequence variable to TRUE
    $Global:TSEnv.Value("AllowInstall") = $true
  }
  Else {
    Write-Host "Error: Unable to authenticate $($Global:Username.ToUpper())" -ForegroundColor Red -BackgroundColor Black
    Write-Host "Please try again..." -BackgroundColor Black
    Write-Host ""
  
    #Call function to get user to try again
    Get-UserCredentials
  }
}

Function Check-IsWebServiceOnline {
  # Full credit for this function https://gallery.technet.microsoft.com/scriptcenter/Script-to-verify-web-ceb46109
  # Create a HTTP request to the Web Service
  $Request = [Net.HttpWebRequest]::Create($URI)
 
  #Try to get the response from the Web Service. If a response is received then return true. If some error occurs then return false.
  Try { 
    #Get the response from the request
    $Response = [Net.HttpWebResponse]$Request.GetResponse() 
    return $true
  } 
  Catch { 
    return $false
  }
  # Abort the request now
  $Request.Abort()
}



########################################################
# Only run this script if the Web Service is online
If (Check-IsWebServiceOnline -eq $true) {
 Get-UserCredentials
}
Else {
 Write-Warning "Unable to contact the Web Service. Cannot proceed with this script. Please contact the EUC team."
}

a) Change the variables in lines 12-16 to suit your own set up. This includes the web service URI, the shared drive to map (to authenticate the user against), the name of the Active Directory group and your domain name.

b) If you’d like to prompt for the credentials in the command line instead of a pop up then uncomment line 26.

c) The mapped drive is given the name “L” (for Lockdown). You can change this in lines 95, 98 and 100 if you want.

d) The name of the team to contact should something goes wrong is referred to as the “EUC team”. You can change this to suit your environment in lines 83 and 142.

Create a Package in SCCM

a) Create a folder called “Task Sequence Lockdown” and save the CheckForAuthorisation.ps1 PowerShell script.

b) From a computer/server with MDT installed, copy ServiceUI.exe (from C:\Program Files\Microsoft Deployment Toolkit\Templates\Distribution\Tools\x64) and rename it to ServiceUI_x64.exe

c) Copy ServiceUI.exe (from C:\Program Files\Microsoft Deployment Toolkit\Templates\Distribution\Tools\x86) and rename it to ServiceUI_x64.exe

d) Copy the folder to your software sources repository. Open up your SCCM console and create a Package called something like “Check for Authorisation to run Task Sequence”. Point the source to where you copied the above files. Do not create a program.

e) Once the package has been created, distribute it to your Distribution points.

Configure the Task Sequence

In my case the Task Sequence I want to lock down is called “*** Test Build *** – Windows 10 – x64 (1703)”. Right-click on the task sequence and click on Edit.

a) Under Execute Task Sequence:

Click on Add > New Group. Name it “Lock down Task Sequence”.

b) Click on Add > General > Connect to Network Folder. In the ‘path’ field enter the full path to the source folder for the package you created earlier. Important: if the package source folder and the shared folder to be mapped in the script are both located on the same server then use the IP address of the server in the path field in this step (instead of the hostname).

Optional: I renamed the step to “Preparing…” instead of leaving it the default

Picture1

c) Choose a drive letter. I chose drive letter M. Enter a user account which has permission to map the drive.

d) Click on Add > General > Run Command Line. Enter the following line in step Run Command Line. Include the following line:

M:\ServiceUI_x64.exe -process:TSProgressUI.exe %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File M:\CheckForAuthorization.ps1

Change the drive letter if you chose a different one in the previous step.

image

e) Select the group immediately after the “Lock down Task Sequence” group and click on the Options tab. Click on Add Condition > If statement > All conditions.

Again, click on Add condition > Task Sequence Variable. Enter the variable name “AllowInstall” and the value “TRUE”.

image

If authorisation and authentication is passed then the PowerShell script creates a “AllowInstall” task sequence variable and assigns the value “TRUE”. Therefore, checking for this condition in the next group means installation will not proceed without it. If the PowerShell console window is closed then the Task Sequence will fail.

Suitability of this solution in a live environment

I mentioned at the outset that this solution is intended to keep 2L Engineers from installing a test build. I know there are ways of deploying hidden task sequences but that requires editing the boot image. Personally, I find editing a task sequence simpler.

Also, some university departments have their own IT reps or admins who can be given the go-ahead to install or re-image computers. Because access is controlled via an Active Directory group, giving users that right and removing the right is as simple as adding and removing them from the AD group.

I hope someone, somewhere will find this useful. Remember that the Deployment Web Service by Maik Koster is required for this to work which makes very easy to do checks against an Active Directory domain. If any part of the instructions is not clear do let me know.

Advertisements