Created
July 13, 2023 15:51
-
-
Save stuartsmith78/3f5b96e67985a1175ed8b31eb290b7ef to your computer and use it in GitHub Desktop.
Custom Domain Join for MDT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.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