Now when more and more employees work from home, companies are forced to start supporting Operating System Deployment (OSD) outside their on-premise environment. This can be easily managed by using SCCM and CMG. But there is another important thing to solve and that is joining installed computer to the company Active Directory domain during OSD process.
This would be trivial in on-premise OSD, but when running OSD from the Internet (CMG) where there is no visibility to your DCs, it can be tricky.
In this post, I will show you my semi-automated PowerShell solution for Offline Domain Join during OSD. The solution consists of one PowerShell function and a special Task Sequence step. So in a nutshell, the PowerShell function creates Offline Domain Join blob, saves that blob in SCCM Computer Variable and Task Sequence step will then use this variable to do the actual Domain Join.
Our use case is that some employee needs to reinstall his computer. So someone from IT runs the PowerShell function to prepare the AD and SCCM environments and let the user know that he can run the installation from now on.
Table of Contents
Requirements
- AD account, that has right to join computers to your domain
- Account, that has right, to customize existing SCCM Task Sequence
- My Connect-SCCM PowerShell function for creating PSSession to your SCCM server
What is Offline Domain Join?
Offline Domain Join is a Windows feature available since Windows 8 and Windows Server 2012 that can be used to join a computer to your Active Directory without any requirement like visibility to your Domain Controllers or existing network connection. You can also think about it as an async domain join.
This process is managed by djoin.exe and has two phases:
- You start the provisioning process in your domain by creating a special information blob that can be later used for offline Domain Join and by setting/creating a corresponding AD computer object.
- Besides various information about your domain, this blob contains also new computer password and at the same time, this password is set to the appropriate AD computer object as well.
djoin.exe /provision /domain "Contoso" /machine "PC-1" /savefile djoinBlob.txt
- Joining the computer to your domain using generated blob
- When the computer connects to any DC in your AD, the password will get reset. Which invalidates the blob information as a side effect.
djoin.exe /requestodj /loadfile C:\djoinBlob.txt /windowspath C:\Windows /localos
Consider djoin blob as a high sensitive piece of information! It can be used to join any computer to your domain! Of course, to exploit this somehow, such computer still needs to have network access to your AD network.
So how the heck are we gonna join that computer to our AD?
Download exported SCCM Task Sequence OFFLINE DOMAIN JOIN USING DJOIN and import it to your list of SCCM Task Sequences
OFFLINE DOMAIN JOIN USING DJOIN task look like this
And will run only if variables OSDComputerName and DJoinBlob are defined and the computer is not joining to the domain already.

Add the 'OFFLINE DOMAIN JOIN USING DJOIN' Task Sequence as a step in your own OSD Task Sequence

- It's a good idea to run it as the last step so you decrease the possibility of break anything because of the domain join process

Download my PowerShell functions Set-CMDeviceDJoinBlobVariable and Connect-SCCM and imports them to your PowerShell console (which has to run under account with enough privileges (check Requirements))
Run function Set-CMDeviceDJoinBlobVariable like
Set-CMDeviceDJoinBlobVariable -computerName <nameOfComputerToOfflineJoin>
And this should be the result

That's it ๐
Now when you run your Task Sequence on <nameOfComputerToOfflineJoin>, it will be automatically joined to your AD.
BEWARE!
Set-CMDeviceVariable is CASE SENSITIVE!
On contrary, Get-CMDeviceVariable isn't.

If the computer Variables tab looks like this
Variables won't be retrieved! Not sure what is the correct solution to this state. The only thing that worked for me, was to remove and reimport the device to SCCM.
FYI PowerShell code, that is used in 'OFFLINE DOMAIN JOIN USING DJOIN' Task Sequence
$errorActionPreference = "Stop"
if ((Get-WmiObject -Class Win32_ComputerSystem).PartOfDomain) { return "Already connected to the domain" }
function New-DjoinFile {
<
.SYNOPSIS
Function to generate a blob file accepted by djoin.exe tool (offline domain join)
.DESCRIPTION
Function to generate a blob file accepted by djoin.exe tool (offline domain join)
This function can create a file compatible with djoin with the Blob initially provisionned.
.PARAMETER Blob
Specifies the blob generated by djoin
.PARAMETER DestinationFile
Specifies the full path of the file that will be created
Default is c:\temp\djoin.tmp
.EXAMPLE
New-DjoinFile -Blob $Blob -DestinationFile C:\temp\test.tmp
.NOTES
Francois-Xavier.Cat
LazyWinAdmin.com
@lazywinadmin
github.com/lazywinadmin
.LINK
https://github.com/lazywinadmin/PowerShell/tree/master/TOOL-New-DjoinFile
.LINK
https://lazywinadmin.com/2016/07/offline-domain-join-copying-djoin.html
.LINK
https://msdn.microsoft.com/en-us/library/system.io.fileinfo(v=vs.110).aspx
[Cmdletbinding()]
PARAM (
[Parameter(Mandatory = $true)]
[System.String]$Blob,
[Parameter(Mandatory = $true)]
[System.IO.FileInfo]$DestinationFile = "c:\temp\djoin.tmp"
)
PROCESS {
TRY {
$bytechain = New-Object -TypeName byte[] -ArgumentList 2
$bytechain[0] = 255
$bytechain[1] = 254
$FileStream = $DestinationFile.Openwrite()
$bytechain += [System.Text.Encoding]::unicode.GetBytes($Blob)
$bytechain += 0
$bytechain += 0
$FileStream.write($bytechain, 0, $bytechain.Length)
$FileStream.Close()
} CATCH {
$Error[0]
}
}
}
"STARTING OFFLINE DOMAIN JOIN"
$tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment
$djoinBlob = $tsenv.Value("DJoinBlob")
$computerName = $tsenv.Value("OSDComputerName")
if (!$computerName) { throw "OSDComputerName variable is missing" }
if (!$djoinBlob) { throw "DJoinBlob variable is missing" }
try {
$string = [System.Convert]::FromBase64String($djoinBlob)
$decodedBlob = [System.Text.Encoding]::Unicode.GetString($string)
} catch {
throw "Unable to decode blob $djoinBlob"
}
if (!($decodedBlob -match [regex]::Escape($computerName))) {
throw "This blob isn't meant for this computer ($computerName)"
}
$blobPath = "$env:temp\blob.txt"
New-DjoinFile -blob $djoinBlob -DestinationFile $blobPath
djoin.exe /requestodj /loadfile "$blobPath" /windowspath $env:windir /localos
Remove-Item $blobPath -Force
Summary
Both Task Sequence and PowerShell script can be found at my GitHub. This solution is I think very easy to use.
Enjoy ๐