SCCM OSD Computer Naming

Computer naming during operating system deployment varies based on which method is being used. Using Legacy deployment options like WDS, which is integrated into Active Directory, relies on querying Active Directory during the imaging process to search for a computer object that has been prestaged using the MAC address set to the netbootGUID. If a prestaged computer object cannot be found a randomized name is given to the computer and is joined to the domain in the Unassigned Computers OU.

SCCM operating system deployment is not integrated into Active Directory like WDS. SCCM is only aware of the computer names and objects available in the SCCM database. In a traditional environment there would be steps in the Task Sequence that name the computer based upon a standardized naming convention and joins the domain in a predetermined OU. Using that method could entail having a Task Sequence per College, department, OU, and naming convention. Trying to accommodate that at scale would be impossible.

SCCM provides a number of ways to prestage a computer object in SCCM, but all of those processes are cumbersome and time consuming especially when trying to image hundreds of machines.

The most logical solution would be to have the computer query AD during the imaging process to find its name. This is not a built in feature and requires customized scripting.

The first step is adding the Active Directory PowerShell module to WinPE. PowerShell modules consist of several files located in specific directories. There are several blog post that talk about this process. We have settled on one that has been the most successful for us.

Because sites move or are taken down the relevant PowerShell is listed below.

Mount wim file

[powershell]Mount-WindowsImage -Path “MOUNTDIR” -ImagePath “WIMIMAGEPATH” -Index 1;[/powershell]

Save the following PowerShell code as AddADPowerShellModule.ps1 script

[powershell]Param (
[Parameter(Mandatory=$true)] [string]$sourceWinDir,
[Parameter(Mandatory=$true)] [string]$destinationWinDir
)

$dirs = @(
“System32\WindowsPowerShell\v1.0\Modules\ActiveDirectory”,
“SysWOW64\WindowsPowerShell\v1.0\Modules\ActiveDirectory”,
“Microsoft.NET\assembly\GAC_32\Microsoft.ActiveDirectory.Management”,
“Microsoft.NET\assembly\GAC_32\Microsoft.ActiveDirectory.Management.Resources”,
“Microsoft.NET\assembly\GAC_64\Microsoft.ActiveDirectory.Management”,
“Microsoft.NET\assembly\GAC_64\Microsoft.ActiveDirectory.Management.Resources”,
“WinSxS\amd64_microsoft.activedir..anagement.resources_*”,
“WinSxS\amd64_microsoft.activedirectory.management_*”,
“WinSxS\msil_microsoft-windows-d..ivecenter.resources_*”,
“WinSxS\x86_microsoft.activedir..anagement.resources_*”,
“WinSxS\x86_microsoft.activedirectory.management_*”
);
$tempFileName = ([System.IO.Path]::GetTempFileName());
Write-Host “Saving acl of "$($destinationWinDir)\WinSxS” to "$($tempFileName)“”;
Start-Process -FilePath icacls -ArgumentList “"$($destinationWinDir)\WinSxS” /save "$($tempFileName)“” -Wait -NoNewWindow;
Write-Host “Set administrators owner on "$($destinationWinDir)\WinSxS“”;
Start-Process -FilePath takeown -ArgumentList “/F "$($destinationWinDir)\WinSxS” /A” -Wait -NoNewWindow;
Write-Host “Grant administrators full rights on "$($destinationWinDir)\WinSxS” with inheritance”;
Start-Process -FilePath icacls -ArgumentList “"$($destinationWinDir)\WinSxS” /grant BUILTIN\Administrators:(F) /C /inheritance:e” -Wait -NoNewWindow;

$dirs | % {
$source = “$($sourceWinDir)\$($_)”;
$destination = “$($destinationWinDir)\$($_)”;
if($_.Substring(0,6) -eq “WinSxS”) {
Get-ChildItem -Path $source | ? {$_.PSIsContainer} | % {
Write-Host “Copying "$($_.FullName)” to "$($destination | Split-Path)\$($_.Name)“”;
$_ | Copy-Item -Destination “$($destination | Split-Path)\$($_.Name)” -Recurse;
Write-Host “Set "NT SERVICE\TrustedInstaller” owner on "$($destination | Split-Path)\$($_.Name)” recursively”;
Start-Process -FilePath icacls -ArgumentList “"$($destination | Split-Path)\$($_.Name)” /setowner "NT SERVICE\TrustedInstaller” /T /C ” -Wait -NoNewWindow;
}
}
else {
Write-Host “Copying "$($source)” to "$($destination)“”;
Copy-Item -Path $source -Destination $destination -Recurse;
}
};

Write-Host “Set "NT SERVICE\TrustedInstaller” owner on "$($destinationWinDir)\WinSxS“”;
Start-Process -FilePath icacls -ArgumentList “"$($destinationWinDir)\WinSxS” /setowner "NT SERVICE\TrustedInstaller” /C” -Wait -NoNewWindow;
Write-Host “Removing inheritance on "$($destinationWinDir)\WinSxS“”;
Start-Process -FilePath icacls -ArgumentList “"$($destinationWinDir)\WinSxS” /C /inheritance:d” -Wait -NoNewWindow;
Write-Host “Restoring acl of "$($destinationWinDir)\WinSxS” from "$($tempFileName)“”;
Start-Process -FilePath icacls -ArgumentList “"$($destinationWinDir)“\ /restore "$($tempFileName)” /C” -Wait -NoNewWindow;[/powershell]

Execute the script using for example

[powershell]AddADPowerShellModule.ps1 -sourceWinDir $ENV:windir -destinationWinDir “MOUNTDIRWINDIR”;[/powershell]

Dismount the wim and save changes

[powershell]Dismount-WindowsImage -Path “MOUNTDIR” -Save;[/powershell]

You now have a WinPE image that has the Active Directory PowerShell module available.

Now, we need a custom PowerShell script to query Active Directory.

[powershell]

Set-ExecutionPolicy -ExecutionPolicy Unrestricted

Import-Module ActiveDirectory -ErrorAction Continue

$username = “domain\accountname”
$hash = Get-Content file.txt
$hash = powershell.exe -encodedCommand $hash
$hashSecure = ConvertTo-SecureString $hash -AsPlainText -Force
$sessionKey = New-Object System.Management.Automation.PSCredential($username,$hashSecure)

$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$TSComputerName = $tsenv.value(“OSDComputerName”)

$NIC = Get-WMIObject Win32_NetworkAdapterConfiguration -Filter “NOT MacAddress LIKE ”” -Property MacAddress | select MacAddress
$NICMacs = $NIC.MacAddress

ForEach($NICMac in $NICMacs)
{

$CompDetails = ”
$MacString = $NICMac -replace “:”, “”
$MactoGUID = “00000000000000000000” + $MacString
$MactoGUID = $MactoGUID -replace ” “, ”
$NBG = [GUID]$MactoGUID
$CompDetails = Get-ADComputer -Filter ‘netbootGUID -like $NBG’ -Properties netBootGUID -Server wolftech.ad.ncsu.edu -Credential $sessionKey

If($CompDetails)
{
#Write-Host $CompDetails.Name
$TSComputerName = $CompDetails.Name
$tsenv.Value(“OSDComputerName”) = $TSComputerName

}

}

$ComputerName = $tsenv.Value(“OSDComputerName”)

If(!$ComputerName)
{

$CSProd = Get-WmiObject -Class Win32_ComputerSystemProduct -Property UUID
$UUID = [GUID]$CSProd.UUID
$CompDetailsUUID = Get-ADComputer -Filter ‘netbootGUID -eq $UUID’ -Properties netBootGUID -Server wolftech.ad.ncsu.edu -Credential $sessionKey
$TSComputerName = $CompDetailsUUID.Name
$tsenv.Value(“OSDComputerName”) = $TSComputerName
#Write-Host $CompDetailsUUID.Name

}[/powershell]

I know I should comment my code better, but I will explain what it does.

Line 1 sets the PowerShell execution policy. This is probably unnecessary, but by default the execution policy is local scripts

Line 3 Imports the Active Directory PowerShell Module

Lines 5-9 specifies a service account and sets the password as a PSCredential object. The service account is a standard user in AD and has no special permissions.

Lines 11-12 Creates a new SCCM Task Sequence variable called OSDComputerName that will hold the name of the computer retrieved from AD

Lines 14-15 Gets a list of all NIC’s and their MAC address

Lines 17-24 Takes each MAC address converts it into a GUID and searches AD for a computer object that has that GUID as the netbootGUID. If it finds a computer object with that netbootGUID it stores that computer object in $CompDetails.

Lines 26-32 If the variable $CompDetails has a value it sets the name attribute as the value of the Task Sequence Variable OSDComputerName

Lines 36-48 Sets the value of the Task Sequence variable OSDComputerName to a PowerShell variable called $ComputerName. If the variable $ComputerName does not have a value it queries the computer for the UUID and searches AD for a computer objects whose netbootGUID equals the UUID. If a computer object is found it sets the name of that computer object as the Task Sequence variable OSDComputerName.

OSDComputerName is a built in SCCM Task Sequence variable. If that variable has a value it is used to name the computer during the operating system deployment.

To use this script you need to add a step to your Task Sequence.

Right after the Partition Disk steps, you are going to Add -> General -> Run Powershell Script and use the package NCSU-Get Host Name and the name of the script is sccm_get_computer_name.ps1.

There is a second optional step where you can dump a list of all Task Sequence variables to a log file to be used for troubleshooting later if the computer is named incorrectly. Like the previous example add a Run PowerShell Script step, use the package NCSU-Get Host Name and the name of the script is TSVarsSafeDump.ps1

The sccm_get_computer_name.ps1 will be available in github.