Offline Domain Join using PowerShell and c#
Last update: 2020/07/09 - High level diagram of the ConfigMgr implementation
I wrote on Offline Domain Join (ODJ) a while back where I used PowerShell to rebuild a “djoin-compatible” file from the djoin.exe
output in a format that djoin would accept. In my scenario at the time, I couldn’t pass the file generated by djoin directly.
What is the djoin.exe
you may ask ? this is a tool provided by Microsoft that allow you to join a machine to an Active Directory domain without network connectivity. The operation happens in two phases: First you run djoin on a machine connected to Active Directory which output a very long string of text (or a file directly with that string), in a second part you provide this file to djoin on the machine that need to be joined to complete the operation.
The article I wrote at that time was focused on the second phase. I was still using djoin.exe for the first phase.
Not fast enough…
More recently, I was facing another djoin problematic scenario.
I had to create an automation to stage new device(s) in different systems, including SCCM and make the device ready to boot from SCCM OSD and start applying an OS + Configuration.
The high-level flow looked like this:
- Receive a request via Rest API with some parameters (ComputerName, MacAddress, Owner, Location, …)
- Invoke PowerShell Script to Stage the new device
- Register new device into custom systems (inventory system, monitoring system, asset management system…)
- Create a SCCM Device Association using the ComputerName, MacAddress, …
- Append the SCCM Device to the Collection
ABC00001
where the OS desired is advertised - Wait for the machine to be queryable from the
SMS00001
default collection. If the new device boot but this collection is not refreshed, it won’t pick up the SCCM Operating System Deployment (OSD) assigned on the collectionABC00001
. - Perform the first phase of Offline Domain Join using
djoin.exe
under a different account that hold the proper entitlements, - Create SCCM Device Variables to pass different values consumed during the SCCM OSD
- New machine boot for the first time, pickup DHCP Information/IP
- New machine start SCCM OSD and get all the values from SCCM Device Variables
- Apply OS and Configuration
Here is an high-level flow:
This was working really well, the whole Stage process was taking 3-5 minutes. However one of the tasks that was costly in time was the ODJ part. This automation needed to run very frequently so it started to become a bottleneck for the entire workflow where hundreds machines needed to be deployed at the same time.
Djoin was slow mostly because:
- The script had to do a bunch of pre-work before invoking djoin, the new device was not actually on the network yet. Example of task: Find the closest Domain Controllers (DC) to the new machine based on the New computer Location information or IPAddress. In some edge case if only one DC/RODC was in the location, the code had to look for closest neighbor site based on site link costs.
- PowerShell was invoked under different context, using an account that has the permissions to run djoin.
- The tool I was using was spawning a new process (when doing RunAs) which took some extra precious seconds
- Invoke Djoin against a Domain Controller close to new machine
- Capture the Output and parse it
- Cherry on the cake PowerShell 2.0 (yep…) had to be used, that came with some other limitations.
On a side note, in this scenario the second part of the djoin was handled by unattend.xml
file inside the SCCM Task Sequence, the djoin string was appended to it.
Alternatively you could still use the approach shown in my previous ODJ blogpost.
Solution using c# and PowerShell
I spent sometimes looking for a solution that could expedite the ODJ step while using the entitled AD account. I tested different approaches but ultimately ended up using a combination of PowerShell and c#
While investigating on P/Invoke around [NetJoinDomain], [NetGetJoinInformation] methods, I found the following code snippet from Ryan Ephgrave which was using PowerShell/c# to join a Nano Server during WinPE.
For my need, this was working great during my initial tests on W2008R2 but I had to update the code a bit to make it work on Windows Server 2012/2016+ versions.
- Window Client 7/ Windows 2008 are using the NetProvisionComputerAccount function
- Windows Client 8+/ Windows Server 2012+ are using the NetCreateProvisioningPackage function
Overall this approach saved a lot of time and run in seconds compared djoin.exe
.
Usage
# Define Credentials to use
$MyAccount = 'MyDomain\MyAccount'
$Passw = ConvertTo-SecureString -String '<SecretPassword>' -AsplainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($MyAccount,$Passw)
# Define Parameters
$Splatting = @{
domain = 'mydomain.com'
machinename = 'testmachine'
machineaccountou = 'OU=test,DC=mydomain,DC=com'
dcname = 'dc01.mydomain.com'
}
# Invoke the Offline Domain Join
New-ADDomainJoin -Credential $credential @splatting
This will create an object in active directory and output the blob needed to join the machine offline.
Download
The code is available on GitHub.
Hope this helps :-)
Leave a comment