Category Archives: Powershell

SCCM – Automatically Protecting Windows File Servers From Ransomware Using FSRM

Hi folks,

Perhaps the title is a bit misleading.  This is by no means the only protection to potentially have for a file server at risk of ransomware.  Not even close!  This is just one option to work in conjunction with existing security and, as always, should be regularly revisited.

This was prepared in response to WannaCry as an additional layer of protection and I’ve meant to post it for a while and am just getting to it now.

First I will post the code then some explanation…

The Code:

####
# FSRM_POWERSHELL_FINAL.PS1
# Created By: Neal Britton
#
#
# Credit where credit is due: http://www.scconfigmgr.com/2017/03/21/protect-file-servers-from-ransomware-with-sccm-cicb/
# Credit where credit is due: https://fsrm.experiant.ca/
# Credit where credit is due: https://github.com/nexxai/CryptoBlocker
#
#
####

################################ USER CONFIGURATION ################################

# Names to use in FSRM
$fileGroupName = "CryptoBlockerGroup"
$fileTemplateName = "CryptoBlockerTemplate"
# set screening type to
# Active screening: Do not allow users to save unathorized files
$fileTemplateType = "Active"
# Passive screening: Allow users to save unauthorized files (use for monitoring)
#$fileTemplateType = "Passive"

################################ END USER CONFIGURATION ################################

################################ FUNCTIONS ################################

Function ConvertFrom-Json20
{
 # Deserializes JSON input into PowerShell object output
 Param (
 [Object] $obj
 )
 Add-Type -AssemblyName System.Web.Extensions
 $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
 return ,$serializer.DeserializeObject($obj)
}

Function New-CBArraySplit
{
 <# 
 Takes an array of file extensions and checks if they would make a string >1Kb, 
 if so, turns it into several arrays
 #>
 param(
 $Extensions
 )

$Extensions = $Extensions | Sort-Object -Unique

$workingArray = @()
 $WorkingArrayIndex = 1
 $LengthOfStringsInWorkingArray = 0

# Take the items from the input array and build up a 
 # temporary workingarray, tracking the length of the items in it and future commas
 $Extensions | ForEach-Object {

if (($LengthOfStringsInWorkingArray + 1 + $_.Length) -gt 1023) 
 { 
 # Adding this item to the working array (with +1 for a comma)
 # pushes the contents past the 1Kb limit
 # so output the workingArray
 [PSCustomObject]@{
 index = $WorkingArrayIndex
 FileGroupName = "$Script:FileGroupName$WorkingArrayIndex"
 array = $workingArray
 }
 
 # and reset the workingArray and counters
 $workingArray = @($_) # new workingArray with current Extension in it
 $LengthOfStringsInWorkingArray = $_.Length
 $WorkingArrayIndex++

}
 else #adding this item to the workingArray is fine
 {
 $workingArray += $_
 $LengthOfStringsInWorkingArray += (1 + $_.Length) #1 for imaginary joining comma
 }
 }

# The last / only workingArray won't have anything to push it past 1Kb
 # and trigger outputting it, so output that one as well
 [PSCustomObject]@{
 index = ($WorkingArrayIndex)
 FileGroupName = "$Script:FileGroupName$WorkingArrayIndex"
 array = $workingArray
 }
}

function CryptoBlocker
{

$SMTPServer = "server name"
 $SMTPFrom = "$env:COMPUTERNAME@domain name"
 $SMTPTo = "help desk email"
 $AdminEmail = "help desk email"

# Get all drives with shared folders, these drives will get FRSRM protection
 $DrivesContainingShares = @(Get-WmiObject Win32_Share | # all shares on this computer, filter:
 Where-Object { $_.Type -eq 0 } | # 0 = disk drives (not printers, IPC$, C$ Admin shares)
 Select-Object -ExpandProperty Path | # Shared folder path, e.g. "D:\UserFolders\"
 ForEach-Object { 
 ([System.IO.DirectoryInfo]$_).Root.Name # Extract the driveletter, as a string
 } | Sort-Object -Unique) # remove duplicates

if ($drivesContainingShares.Count -eq 0)
 {
 # No drives containing shares were found
 exit
 }

#Write-Host "`n####"
 #Write-Host "The following shares needing to be protected: $($drivesContainingShares -Join ",")"

# Identify Windows Server version, and install FSRM role
 $majorVer = [System.Environment]::OSVersion.Version.Major
 $minorVer = [System.Environment]::OSVersion.Version.Minor

#Write-Host "`n####"
 #Write-Host "Checking File Server Resource Manager.."

Import-Module ServerManager

if ($majorVer -eq 10)
 {
 $checkFSRM = Get-WindowsFeature -Name FS-Resource-Manager

if ($minorVer -ge 0 -and $checkFSRM.Installed -ne "True")
 {
 #Server 2016

$install = Install-WindowsFeature -Name FS-Resource-Manager -IncludeManagementTools
 if ($? -ne $True)
 {
 #Install of FSRM failed
 exit
 }
 }
 }
 elseif ($majorVer -eq 6)
 {
 $checkFSRM = Get-WindowsFeature -Name FS-Resource-Manager

if ($minorVer -ge 2 -and $checkFSRM.Installed -ne "True")
 {
 # Server 2012 or 2012 R2

$install = Install-WindowsFeature -Name FS-Resource-Manager -IncludeManagementTools
 if ($? -ne $True)
 {
 #Install of FSRM failed
 exit
 }
 }
 elseif ($minorVer -ge 1 -and $checkFSRM.Installed -ne "True")
 {
 # Server 2008 R2
 $install = Add-WindowsFeature FS-FileServer, FS-Resource-Manager
 if ($? -ne $True)
 {
 #Install of FSRM failed
 exit
 }
 
 }
 elseif ($checkFSRM.Installed -ne "True")
 {
 # Server 2008
 $install = &servermanagercmd -Install FS-FileServer FS-Resource-Manager
 if ($? -ne $True)
 {
 #Install of FSRM failed
 exit
 }
 }
 }
 else
 {
 # Assume Server 2003
 return
 }

# Download list of CryptoLocker file extensions
 $webClient = New-Object System.Net.WebClient
 $jsonStr = $webClient.DownloadString("https://fsrm.experiant.ca/api/v1/get")
 $monitoredExtensions = @(ConvertFrom-Json20 $jsonStr | ForEach-Object { $_.filters })

# Process SkipList.txt
 If (Test-Path .\SkipList.txt)
 {
 $Exclusions = Get-Content .\SkipList.txt | ForEach-Object { $_.Trim() }
 $monitoredExtensions = $monitoredExtensions | Where-Object { $Exclusions -notcontains $_ }

}
 Else 
 {
 #
 # Add one filescreen per line that you want to ignore
 #
 # For example, if *.doc files are being blocked by the list but you want 
 # to allow them, simply add a new line in this file that exactly matches 
 # the filescreen:
 #
 # *.doc
 #
 # The script will check this file every time it runs and remove these 
 # entries before applying the list to your FSRM implementation.
 #
 # In our environment we have a large number of false positives on *.one and WindowsApplication1.exe files
 #
 $emptyFile = @'
*.acc
*.one
WindowsApplication1.exe
'@
 Set-Content -Path .\SkipList.txt -Value $emptyFile
 }

# Split the $monitoredExtensions array into fileGroups of less than 4kb to allow processing by filescrn.exe
 $fileGroups = @(New-CBArraySplit $monitoredExtensions)
 $screenArgs = @()

# Perform these steps for each of the 1KB limit split fileGroups
 ForEach ($group in $fileGroups)
 {
 $FGExists = Get-FsrmFileGroup | Where-Object { $_.Name -eq "$($group.fileGroupName)" }
 if ($FGExists -gt $null)
 {
 Remove-FSRMFileGroup -name $($group.fileGroupName) -Confirm:$false | Out-Null
 }
 
 New-FSRMFileGroup -name $($group.fileGroupName) -IncludePattern $($group.array) -Description "Crypto Extension Detection" | Out-Null

$screenArgs += $($group.fileGroupName)
 }

# Create File Screen Template with Notification
 # Remove existing if found and create new
 $FSTExists = Get-FsrmFileScreenTemplate | Where-Object { $_.Name -eq "CryptoExtensions" }

if ($FSTExists -gt $null)
 {
 Remove-FsrmFileScreenTemplate -Name CryptoExtensions -Confirm:$false | Out-Null
 }

$screenArgList = [String[]]@($screenArgs)

$EmailNotification = New-FSRMAction -Type Email -Subject "Crypto File Activity Detected - $env:COMPUTERNAME" -Body "User [Source IO Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in violation of the [Violated File Group] file group. This file could be a marker for malware infection, and should be investigated immediately." -RunLimitInterval 30 -MailTo $SMTPTo
 $EventNotification = New-FSRMAction -Type Event -EventType Error -Body "User [Source IO Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in violation of the [Violated File Group] file group. This file could be a marker for malware infection, and should be investigated immediately." -RunLimitInterval 30

New-FsrmFileScreenTemplate -Name CryptoExtensions -Description "Known CryptoLocker File Extesions" -IncludeGroup($($screenArgList)) -Active:$true -Notification $EmailNotification,$EventNotification | Out-Null


# Create File Screens for every drive containing shares
 $drivesContainingShares | ForEach-Object {
 # Skip C:
 # All actual shares on our File Servers are on other drives. We only care about these.
 if ($_ -ne "C:\")
 {
 $drive = $_

# Remove File Screen is exists and create new
 $FSExists = Get-FsrmFileScreen | Where-Object { $_.Path -eq $drive }
 
 if ($FSExists -gt $null)
 {
 Remove-FsrmFileScreen -Path $_ -Confirm:$false | Out-Null
 }
 
 New-FSRMFileScreen -Path $_ -Active: $true -Description "Crypto Extension Monitoring" -Template CryptoExtensions -Notification $EmailNotification,$EventNotification | Out-Null
 }
 }

# Check for FSRM File Screen and report TRUE or FALSE for Configuration Item/Baseline Reporting
 $CryptoScreen = Get-FSRMFileScreen | Where-Object { $_.Description -eq "Crypto Extension Monitoring" }
 
 if ($CryptoScreen -gt $null)
 {
 $CryptoCICompliant = $true
 }
 else
 {
 $CryptoCICompliant = $false
 }
 Return $CryptoCICompliant
}

################################ END FUNCTIONS ################################

CryptoBlocker

 

The Explanation:

So, the above is just my implementation after finding similar items and expanding on them to meet my need.  It is not comprehensive and should definitely not stand alone.

Pros of this approach:

  • Automatic, just add a server to be protected to the collection this is deployed to
  • Can be regularly updated
  • The source list of ransomware files/extensions is somewhat trustworthy(?)
  • The protection groups are removed before they are rebuilt to ensure protection lists are up to date
  • Fast
  • No manual changes

Cons:

  • The source list is somewhat trustworthy
  • Is updated by manual submissions
  • Code has to be edited to exclude a certain file name or file extension (but only has to be done in one place)

 

The Implementation:

In your SCCM console in the Assets and Compliance work space, expand ‘Compliance Settings’ and in ‘Configuration Items’ create a new configuration item:

  1. In my example, give it a name of “CryptoBlocker FSRM Script” and hit next
  2. In Support Platforms, deselect all and the manually select your server platforms.  for me it was Server 2012, Server 2012 R2, and Server 2016
  3. Create a new setting
    1. Name = “CryptoBlocker FSRM Script”
    2. Setting Type: Script
    3. Data Type: Boolean
    4. Discovery Script (Powershell): Use above script
  4. Create a new compliance rule
    1. Name = “Crypto FSRM Protection
    2. Selected setting = The one we just created
    3. Set value returned by the specified script to equals true
    4. Check to enable “Remote noncompliance if this setting instance is not found”

Next, in Configuration Baselines, create a new baseline:

  1. Name = “Crypto File Block”
  2. Evaluation Conditions: Add the Configuration Item we created earlier

Deploy to a test collection first!  Pictures of the finished configuration item are as follows:

fsrm_ci1fsrm_ci2fsrm_ci3fsrm_ci4fsrm_ci5

Advertisements

SCCM – Enabling ‘Easy’ Local Login on Domain Computers During OSD Part 2 of 2: Applying The Info

Hello folks!

Link to Part 1: SCCM – Enabling ‘Easy’ Local Login on Domain Computers During OSD Part 1 of 2: Getting The Info

In my organization we have need for the occasional machine to be configured with local login for such things as display computers or book sign-out machines in a library.  Local login is easy, but having to revisit the machine after imaging to set it up is annoying and, frankly, a waste of time.  Unfortunately we hadn’t been successful or too interested in solving this.  Until now that is.

WARNING: This will be highly specific (yet generalized) to our implementation.  It is my hope that somehow you find some use out of this.

Part 1 will cover getting the info at the start of your task sequence while Part 2 will cover the application of the info to the machine.

Credit where credit is due: https://www.scconfigmgr.com/2014/06/06/prompt-for-ou-location-during-osd-with-powershell/ was where the start of this all came from.

Here we go…

The code (Set-LocalUserInfo.ps1)….

Import-Module -name .\policyfileeditor.psm1

$u2 = New-Object -COMObject Microsoft.SMS.TSEnvironment
$u = $u2.Value("LocalUserName")

$l2 = New-Object -COMObject Microsoft.SMS.TSEnvironment
$l = $l2.Value("LocalUserLocation")

# Set simple password of username x2 if username is short
if ($u -ne "long user name")
{
 $pass = $u + $u
}
else
{
 $pass = $u
}

$n = switch ($l)
{
 "A location" {"123"}
 "Another location" {"456"}
 "One more" {"789"}
}

# Website variable to be set
$website = "https://mywebsite.whatev?page=" + $n

# Create the local user
NET USER $u $pass /ADD /expires:never

# Set the above local user to not have an expiring password
Get-WmiObject Win32_UserAccount -filter "LocalAccount=True"|?{$_.name -eq $u} |Set-WmiInstance -Arguments @{PasswordExpires=$false}

# Create registry keys for local login
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultUserName" -PropertyType "String" -Value $u
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultPassword" -PropertyType "String" -Value $pass
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultDomainName" -PropertyType "String" -Value '.\'
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "AutoAdminLogon" -PropertyType "String" -Value '1'

Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "AutoLogonSID"

# If this computer is going to be for display, we don't want to show a website automatically when it logs on
# However, if the other alternative is a book lookup website, then we do want to fire up IE on login
if ($u -ne "display computer or whatever")
{
 New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "IEXPLORE" -PropertyType "String" -Value '"C:\Program Files\Internet Explorer\iexplore.exe"'
}

# Set the power scheme to High Performance on logon to ensure the system stays on (which we want in this case)
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "SetMaximumPower" -PropertyType "String" -Value "C:\Windows\System32\powercfg.exe /SETACTIVE 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c"

# Specify local group policy file locations
$UserDir = "C:\windows\system32\GroupPolicy\User\registry.pol"
$MachineDir = "C:\windows\system32\GroupPolicy\Machine\registry.pol"

# Specify variables for setting screen saver (user setting)
$ScrnSvrPath = 'Software\Policies\Microsoft\Windows\Control Panel\Desktop';
$ScrnSvrName = 'SCRNSAVE.EXE'
$ScrnSvrData = 'scrnsave.scr'
$ScrnSvrType = 'String'
$ScrnSvrTimeoutName = 'ScreenSaveTimeOut'
$ScrnSvrTimeoutData = '120'

# Specify variables for disabling password prompt on computer wake (machine setting)
$RegPath = 'Software\Policies\Microsoft\Power\PowerSettings\0e796bdb-100d-47d6-a2d5-f7d2daa51f51'
$RegName = 'ACSettingIndex'
$RegName2 = 'DCSettingIndex'
$RegData = '0'
$RegType = 'DWord'

# Write settings to the local group policy
Set-PolicyFileEntry -Path $MachineDir -Key $RegPath -ValueName $RegName -Data $RegData -Type $RegType
Set-PolicyFileEntry -Path $MachineDir -Key $RegPath -ValueName $RegName2 -Data $RegData -Type $RegType

# Force a specific screen saver
# Sets screensaver to blank
# ONLY IF THE LOCAL USER IS *NOT* A DISPLAY USER
if ($u -ne "display")
{
 #Set screen saver to blank
 Set-PolicyFileEntry -Path $UserDir -Key $ScrnSvrPath -ValueName $ScrnSvrName -Data $ScrnSvrData -Type $ScrnSvrType
 
 #Set screen saver timeout to 2 minutes (120 seconds)
 Set-PolicyFileEntry -Path $UserDir -Key $ScrnSvrPath -ValueName $ScrnSvrTimeoutName -Data $ScrnSvrTimeoutData -Type $ScrnSvrType
}

# Prevent power plan turning off display after x minutes
# Sets value (in seconds) to 0 (disabled)
# ONLY IF THE LOCAL USER IS A DISPLAY USER
if ($u -eq "display")
{
 New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "AC_TurnOffDisplayAfter" -PropertyType "String" -Value "C:\Windows\System32\powercfg.exe -setacvalueindex 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c 7516b95f-f776-4464-8c53-06167f40cc99 3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e 0"
 New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "DC_TurnOffDisplayAfter" -PropertyType "String" -Value "C:\Windows\System32\powercfg.exe -setdcvalueindex 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c 7516b95f-f776-4464-8c53-06167f40cc99 3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e 0"
}

# Load ntuser.dat (default user)
reg load "HKEY_LOCAL_MACHINE\defuser" "C:\users\default\ntuser.dat"
# Create a new key to control local user website, close the handle, and trigger garbage collection
Set-ItemProperty -Path "HKLM:\defuser\SOFTWARE\Microsoft\Internet Explorer\Main" -Name "Start Page" -Value $website
[gc]::Collect()
#Unload ntuser.dat
reg unload "HKEY_LOCAL_MACHINE\defuser"

 

It is ugly, I know.  Truth be told, I’m hacking and slashing my way to a working solution just like I do in C# and after it works you don’t really want to mess with it.

Part of the code above is setting local group policy options that were, for one reason or another, difficult or otherwise impossible to set programmatically.  I found http://brandonpadgett.com/powershell/Local-gpo-powershell/ which pointed me to a module that would allow me to set local group policy objects using Powershell.  Highly recommended and without the module my script would have been sunk and half-done.

Save the script in the same location as the script from Part 1 and include the Powershell module accessible from the above link in there as well.  Oh, as with the SConfigMgr link above, include the ServiceUI.exe executable from MDT.  My folder ended up looking like this:

folder

Add a “Run Powershell Script” step to the end of your task sequence and configure:

settsstep

and

settsstepsettings

Presto!

So far it is working flawlessly in my Windows 8.1 deployment and am in the process of testing for Windows 10.

SCCM – Enabling ‘Easy’ Local Login on Domain Computers During OSD Part 1 of 2: Getting The Info

Hello folks!

Link to Part 2: SCCM – Enabling ‘Easy’ Local Login on Domain Computers During OSD Part 2 of 2: Applying The Info

In my organization we have need for the occasional machine to be configured with local login for such things as display computers or book sign-out machines in a library.  Local login is easy, but having to revisit the machine after imaging to set it up is annoying and, frankly, a waste of time.  Unfortunately we hadn’t been successful or too interested in solving this.  Until now that is.

WARNING: This will be highly specific (yet generalized) to our implementation.  It is my hope that somehow you find some use out of this.

Part 1 will cover getting the info at the start of your task sequence while Part 2 will cover the application of the info to the machine.

Credit where credit is due: https://www.scconfigmgr.com/2014/06/06/prompt-for-ou-location-during-osd-with-powershell/ was where the start of this all came from.

Here we go…

The code (Get-LocalUserInfo.ps1)…

param(
[parameter(Mandatory=$true)]
[string]$RBOptionFirst,
[parameter(Mandatory=$true)]
[string]$RBOptionSecond,
[parameter(Mandatory=$true)]
[string]$RBOptionThird
)
 
function Load-Form {
 $LocationList = @("A location", "Another location", "One more")
 $Form.Controls.AddRange(@($RBOption1, $RBOption2, $RBOption3, $ComboBox, $Button, $GBSystem, $GBLocation))
 $ComboBox.Items.AddRange($LocationList)
 $Form.Add_Shown({$Form.Activate()})
 [void]$Form.ShowDialog()
}
 
function Set-UserLocation {
 param(
 [parameter(Mandatory=$true)]
 $Location
 )
 if ($RBOption1.Checked -eq $true) {
 $UserName = "$($RBOptionFirst)"
 }
 if ($RBOption2.Checked -eq $true) {
 $UserName = "$($RBOptionSecond)"
 }
 if ($RBOption3.Checked -eq $true) {
 $UserName = "$($RBOptionThird)"
 }
 $TSEnvironment = New-Object -COMObject Microsoft.SMS.TSEnvironment 
 $TSEnvironment.Value("LocalUserName") = "$($UserName)"

$TSEnvironment2 = New-Object -COMObject Microsoft.SMS.TSEnvironment 
 $TSEnvironment2.Value("LocalUserLocation") = "$($Location)"

$Form.Close()
}
 
# Assemblies
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
 
# Form
$Form = New-Object System.Windows.Forms.Form 
$Form.Size = New-Object System.Drawing.Size(260,220) 
$Form.MinimumSize = New-Object System.Drawing.Size(360,220)
$Form.MaximumSize = New-Object System.Drawing.Size(360,220)
$Form.SizeGripStyle = "Hide"
$Form.StartPosition = "CenterScreen"
$Form.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($PSHome + "\powershell.exe")
$Form.Text = "Choose user details"
$Form.ControlBox = $false
$Form.TopMost = $true
 
# Group boxes
$GBSystem = New-Object System.Windows.Forms.GroupBox
$GBSystem.Location = New-Object System.Drawing.Size(10,10)
$GBSystem.Size = New-Object System.Drawing.Size(320,60)
$GBSystem.Text = "Select user"
$GBLocation = New-Object System.Windows.Forms.GroupBox
$GBLocation.Location = New-Object System.Drawing.Size(10,80)
$GBLocation.Size = New-Object System.Drawing.Size(220,60)
$GBLocation.Text = "Select location"
 
# Radio buttons
$RBOption1 = New-Object System.Windows.Forms.RadioButton
$RBOption1.Location = New-Object System.Drawing.Size(20,33)
$RBOption1.Size = New-Object System.Drawing.Size(100,20)
$RBOption1.Text = "$($RBOptionFirst)"
$RBOption1.Add_MouseClick({$ComboBox.Enabled = $true})
$RBOption2 = New-Object System.Windows.Forms.RadioButton
$RBOption2.Location = New-Object System.Drawing.Size(120,33)
$RBOption2.Size = New-Object System.Drawing.Size(100,20)
$RBOption2.Text = "$($RBOptionSecond)"
$RBOption2.Add_MouseClick({$ComboBox.Enabled = $true})
$RBOption3 = New-Object System.Windows.Forms.RadioButton
$RBOption3.Location = New-Object System.Drawing.Size(220,33)
$RBOption3.Size = New-Object System.Drawing.Size(100,20)
$RBOption3.Text = "$($RBOptionThird)"
$RBOption3.Add_MouseClick({$ComboBox.Enabled = $true})
 
# Combo Boxes
$ComboBox = New-Object System.Windows.Forms.ComboBox
$ComboBox.Location = New-Object System.Drawing.Size(20,105)
$ComboBox.Size = New-Object System.Drawing.Size(200,30)
$ComboBox.DropDownStyle = "DropDownList"
$ComboBox.Add_SelectedValueChanged({$Button.Enabled = $true})
$ComboBox.Enabled = $false
 
# Buttons
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(140,145)
$Button.Size = New-Object System.Drawing.Size(80,25)
$Button.Text = "OK"
$Button.Enabled = $false
$Button.Add_Click({Set-UserLocation -Location $ComboBox.SelectedItem.ToString()})
 
# Load Form
Load-Form

 

As the above link mentions, using this script requires that Microsoft .NET (WinPE-NetFx) and Windows PowerShell (WinPE-PowerShell) be added to your boot wim and (re)ristributed to your distribution points.

Save the above script to your package source location and create a package for it.  In my case, the name is ‘Set Local user Info’ and includes some other scripts and components which will be discussed in Part 2.

Create a “Run Command Line” task sequence step early in the TS list:

TSStep

And configure it as follows:

TSStep2

If all goes well, the following will appear when you begin your task sequence…

TSStep3

Selecting either of the three radio buttons allows the location drop down list to appear.  Select the name/type of user you passed via the command line and then pick the location (why this is offered is covered in Part 2).

Stay tuned for Part 2!

SCCM – Remove “Contact Support” From Windows 10 1607 During OSD

EDIT 03/22/2017:  As blogged HERE by Michael Niehaus it is possible to remove this pesky program via powershell with the following:

Get-WindowsCapability -online | ? {$_.Name -like '*ContactSupport*'} | Remove-WindowsCapability –online

 

Hi everyone,

It has been quite a while since my last blog post and a lot has been going on.  I’m going to slowly start posting new items as I find the time to show some of the things I’ve created (or found) for SCCM as I have been working towards creating a master Windows 10 image.

In this post, I will be talking about removing the questionable “Contact Support” app that Microsoft for some reason saw fit to include in the Enterprise and Education editions of Windows 10.

I was able to remove it with a simple powershell script running in a normal task sequence in a “Run Command Line” step.  I am opting to simply rename the folder it lies in instead of outright removing it because hey, who knows, one day it might be needed.

The script:

powershell.exe -executionpolicy bypass Rename-Item C:\Windows\SystemApps\ContactSupport_cw5n1h2txyewy BLOCKED_ContactSupport_cw5n1h2txyewy

That’s it!  This is what it fully looks like in the task sequence and the result:

sccm_cmdremovecontactsupportbyebyecontactsupport

Until next time…