Skip to content

Instantly share code, notes, and snippets.

@stuartsmith78
Created July 13, 2023 15:51
Show Gist options
  • Save stuartsmith78/3f5b96e67985a1175ed8b31eb290b7ef to your computer and use it in GitHub Desktop.
Save stuartsmith78/3f5b96e67985a1175ed8b31eb290b7ef to your computer and use it in GitHub Desktop.
Custom Domain Join for MDT
<#
.SOLUTION
.FILENAME domain-join.ps1
Joins a computer to the targeted domain, using a retrieved credential, deleting the old account
.DESCRIPTION
.PARAMETER -Join
setting this flag cause the script to join the computer to the domain
.PARAMTER -Move
setting this flag causes the script to move the computer account into a new OU
.PUBLISHER
smithsm@
2021/09/20
#>
[CmdletBinding()]
param(
[Parameter(ParameterSetName="Join",Mandatory=$true)]
[Parameter(ParameterSetName="Move",Mandatory=$false)]
[Parameter(HelpMessage="Action flag to join the domain specified by the TSVariable 'JoinDomain', in the OU specified by the TSVariable 'TempOU'")]
[Alias("Join")]
[switch]
$paramActionJoin,
[Parameter(ParameterSetName="Join",Mandatory=$false)]
[Parameter(ParameterSetName="Move",Mandatory=$true)]
[Parameter(HelpMessage="Action flag to move the computer account into the OU specified by the TSVariable 'OSDDomainOUName'")]
[Alias("Move")]
[switch]
$paramActionMove
)
Function ScriptExitCleanup ($code=0)
{
# Retrieve a completion timestamp and calculate the delta
$newCur = Get-Date
$deltaCur = $newCur - $cur
Write-Host "$($myInvocation.MyCommand) Completion: " + $newCur
Write-Host "$($myInvocation.MyCommand) Runtume: " + $deltaCur
# End Powershell logging
Stop-Transcript
exit $code
}
Function ErrorLog ($err)
{
Write-Error "***AN ERROR HAS OCCURRED***";
# Get-Variable $err;
Write-Error $err.TargetObject;
Write-Error $err.ErrorDetails;
Write-Error $err.StackTrace;
}
Function Retrieve-Credential ()
{
try {
$host_url = ""
$api_path = ""
$item_id = ""
$url = $host_url + $api_path + $item_id
$result = Invoke-RestMethod -Method GET -Uri $url -Headers @{ "APIKey" = "" }
return $result
} catch {
ErrorLog ($_)
ScriptExitCleanup(-2)
}
}
$Version = 1.60;
# Load MDT Task Sequence Environment and Logs
try
{
$TSenv = New-Object -COMObject Microsoft.SMS.TSEnvironment;
$logPath = $tsenv.Value("LogPath");
$computerName = $TSenv.Value("OSDComputerName");
if (-not $computerName)
{
Write-Host "Warning-Unable to retrieve hostname from TS Environment, falling back to local environment";
$computerName = $env:COMPUTERNAME;
}
#$domainName = $TSenv.Value("OSDDomainName");
$domainName = $TSenv.Value("JoinDomain"); # this is not the correct OSD TS Variable, but a Custom TS Variable?
#this is a location with group policy inheritance blocked
$targetOU = $TSenv.Value("TempOU");
if (-not $targetOU)
{
Write-Host "Warning-Unable to retrieve temporary OU from TS Environment, falling back to hardcoded value";
$targetOU = "";
}
$finalOU = $TSenv.Value("OSDDomainOUName"); #MachineObjectOU
if ( -not $finalOU)
{
Write-Host "Warning-Unable to retrieve 'OSDDomainOUName', attempting retrieval of 'MachineObjectOU'";
$finalOU = $TSenv.Value("MachineObjectOU");
}
$caCertificate = $TSenv.Value("CACertificate");
$currentTSAction = $TSenv.Value("_SMSTSCurrentActionName");
$currentOU = $TSenv.Value("MachineAccountOU");
} catch {
ErrorLog ($_);
}
$Error.Clear();
$logFile = "$logPath\$($MyInvocation.MyCommand)-$currentTSAction.log";
# Retrieve start timestamp
$cur = Get-Date;
# Start the logging
Start-Transcript -Path $logFile;
Write-Host "Storing Transcripts in $logFile";
Write-Host "$($myInvocation.MyCommand) Version: $Version";
Write-Host $cur;
Write-Host "Current TS Step is $currentTSAction";
if ($paramActionJoin)
{
Write-Host "Flag for JoinDomain set";
}
if ($paramActionMove)
{
Write-Host "Flag for Computer Account Move set";
}
try {
Write-Host "Importing $caCertificate";
Import-Certificate -FilePath $caCertificate -CertStoreLocation 'Cert:\LocalMachine\Root' -Verbose;
$apiCredential = Retrieve-Credential;
Write-Host "Retrieved $($apiCredential.Title)" ;
Write-Host "Operating on $computerName";
Write-Host "Target Domain: $domainName";
Write-Host "Temporary OU: $targetOU";
Write-Host "Final OU: $finalOU";
Write-Host "Looking up SRV record ""_ldap._tcp.pdc._msdcs.$domainName""";
$srvResult = Resolve-DnsName -Name "_ldap._tcp.pdc._msdcs.$($domainName)" -Type "srv";
Write-Host "Retrieved $($srvResult.NameTarget) at $($srvResult.IP4Address)";
$pdc = $srvResult.IP4Address;
Write-Host "Cracking DomainName into LDAP string";
$ldapDomain = $domainName -replace "\.", ",DC=";
$ldapDomain = -join("DC=",$ldapDomain);
Write-Host "LDAP String: $ldapDomain";
Write-Host "Creating System.DirectoryServices.DirectoryEntry";
$searchRoot = New-Object System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$pdc/$ldapDomain", $apiCredential.Username, $apiCredential.Password;
Write-Host "Creating System.DirectoryServices.DirectorySearcher and binding to System.DirectoryServices.DirectoryEntry";
$adSearcher = New-Object -Type System.DirectoryServices.DirectorySearcher -ArgumentList $searchRoot, "(&(name=$computerName)(objectClass=computer))", ["Path"], "Subtree" -ErrorAction "Stop";
Write-Host "Searching active directory for existing account";
Write-Host "Executing search with LDAP Filter ""(&(name=$computerName)(objectClass=computer))""";
$adComputerResult = $adSearcher.FindOne();
if ($paramActionJoin)
{
if ($adComputerResult)
{
$adComputer = $adComputerResult.GetDirectoryEntry();
Write-Host "Located an existing account at $($adComputer.Path)";
Write-Host "Deleting object from AD";
$adComputer.DeleteTree();
} else {
Write-Host "No existing ADObject found";
}
Write-Host "Converting credentials to PSCredential for Add-Computer";
$password = $apiCredential.Password | ConvertTo-SecureString -asPlainText -Force
$username = $apiCredential.UserName
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
<#
022/05/31
The use of Remove-Computer is not successful as it seems to manipulate the computer itself rather than the
dirctory object. Windows 10 Build 21H2 runs djoin.exe against the unattended.mxl file even in the event
of a skeleton section completely devoid of values. Removing the skeleton section seems to prevent the
DirectAccess Offline Domain Join from taking place.
#>
$Error.Clear(); # attempt to prevent any outstanding errors from tripping
Write-Host "Joining $domainName in $targetOU with $($apiCredential.Title) on server $($srvResult.NameTarget)";
Add-Computer -DomainName $domainName -Credential $credential -OU $targetOU -Verbose -Server $srvResult.NameTarget -ErrorAction "Ignore" -ErrorVariable addError;
if ($addError)
{
Write-Host $addError.TargetObject;
Write-Host $addError.ErrorDetails;
Write-Host $addError.StackTrace;
}
#/*
# Not present in MDT? Maybe only SCCM
#/*
# set Reboot Required Value
# Write-Host "Setting SMSTSRebootRequested = TRUE";
# $TSenv.SMSTSRebootRequested = $true;
$TSenv.Value("SMSTSRebootRequested") = "true";
}
if ($paramActionMove)
{
Write-Host "Excpecting an account at $currentOU";
if ($adComputerResult)
{
$adComputer = $adComputerResult.GetDirectoryEntry();
Write-Host "Located an existing account at $($adComputer.Path)";
Write-Host "Binding new System.DirectoryServices.DirectoryEntry to LDAP://$pdc/$finalOU"; $deFinalOU = New-Object System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$pdc/$finalOU", $apiCredential.Username, $apiCredential.Password -ErrorAction Stop; if ($deFinalOU)
{
Write-Host "Retrieved OU at $($deFinalOU.Path)";
Write-Host "Moving computer account";
try {
$adComputer.MoveTo($deFinalOU);
}
catch [System.InvalidOperationException]
{
Write-Host "Failed to move computer account to new OU. The specified DirectoryEntry is not a container.";
ErrorLog($Error[0]);
ScriptExitCleanup(-5);
}
# search for computer account in new location
$adSearcher = New-Object -Type System.DirectoryServices.DirectorySearcher -ArgumentList $searchRoot, "(&(name=$computerName)(objectClass=computer))", ["Path"], "Subtree" -ErrorAction "Stop";
Write-Host "Searching active directory for account";
Write-Host "Executing search with LDAP Filter ""(&(name=$computerName)(objectClass=computer))""";
$adComputerResult = $adSearcher.FindOne();
if ($adComputerResult)
{
$adComputer = $adComputerResult.GetDirectoryEntry();
Write-Host "Located $computerName machine account at $($adComputer.Path)";
$sComparePath = -join("LDAP://$pdc/CN=",$computerName,",",$finalOU);
Write-Host "Checking $sComparePath against $($adComputer.Path)";
if ($sComparePath -eq $adComputer.Path)
{
Write-Host "Move Successful";
} else {
Write-Host "Failed to move computer account";
ScriptExitCleanup(-5);
}
}
} else {
# we did not retrieve the OU
Write-Host "Failed to retrieve a DirectoryEntry corresponding to the OU"
ScriptExitCleanup(-7);
}
} else {
Write-Host "No existing ADObject found";
ScriptExitCleanup(-7);
}
}
}
catch {
if ($_) {
Write-Host "Default Variable is non-null: $($_.GetType())";
Write-Host $_.ErrorDetails;
Write-Host $_.ScriptStackTrace;
ErrorLog ($_);
}
# if ($Error[0]) {
# if ($Error.Count -gt 0) {
# Write-Host "Special variable \$Error.Count is > 0";
# }
# if ($Error[0]) {
# Write-Host "Special variable \$Error[0] is non-null $($Error[0].GetType())";
# }
# ErrorLog ($Error[0]);
# }
ScriptExitCleanup(-2);
}
ScriptExitCleanup 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment