Introduction: "When Ransomware Eats Your Forest"
Active Directory (AD) is like the central nervous system of an organization. One wrong move, and the body seizes up. In my case, ransomware fried the old forest, leaving me with busted domain controllers and zero trust in what was left.
Fortunately, I still had my synced objects in Entra ID (formerly Azure AD). The challenge: could I rebuild a brand-new forest using nothing but the cloud?
Spoiler: yes, but it was part archaeology, part neurosurgery, and part comedy of errors.
Step 1: Exporting from Entra
I quickly realized that Export-Clixml
wasn't the silver bullet. Entra objects don't translate neatly into AD. Instead, I leaned on the Microsoft Graph PowerShell SDK and custom scripts to dump:
- Users (UPN, ImmutableID, proxyAddresses, departments, etc.)
- Groups (including memberships)
- Exchange Online attributes (mail, alias, targetAddress, etc.)
⚡ Tip: Don't export everything at once. My first attempt gave me ~900 users, half of them disabled. I refined my export to a specific group of ~200 users who actually needed to exist in AD again. This kept my test cycles fast and my mistakes contained.
Step 2: Seeding the New Forest
With a new forest in place, I wrote a script (Seed-NewAD.ps1
) to generate user objects from my CSVs.
Discoveries along the way:
- PowerShell 5 hates inline
?:
syntax. My script broke until I rewrote ternary logic as properif
blocks. - Some attributes (like
mailNickname
) aren't valid forNew-ADUser
. I had to check the schema and only populate what AD actually accepts. - PostalCode and ProxyAddresses are available, but only on update, not creation.
⚡ Tip: Build a schema support cache in your script (Get-ADObject -SearchBase $schemaNamingContext
). That way, you can programmatically test if an attribute exists before writing it.
Step 3: Hard Match Magic (msDS-ConsistencyGuid)
This was the breakthrough. To sync existing Entra users with my new AD users, I needed hard match:
- Entra uses
ImmutableID
(a base64-encoded GUID). - AD uses
msDS-ConsistencyGuid
(a raw byte array). - I converted Entra's ImmutableID back to a byte array and stamped it into AD.
💡 Gotcha: PowerShell exposes this attribute as mS-DS-ConsistencyGuid
(note the casing). You can only update it reliably with Set-ADObject
and the literal name. After that, Entra Connect was able to map users one-to-one.
Step 4: Groups, Memberships, and Alternative Approaches
Groups were trickier. My first attempt to re-import them created new groups in Entra instead of mapping to the old ones. That's because I hadn't stamped the msDS-ExternalDirectoryObjectId
.
Real-World Plot Twist
While I was planning the Group Writeback approach below, my customer needed a faster solution for managing orphaned groups. We ended up using the sync disable/enable method instead (see the Project Update section above). However, I'm keeping the Group Writeback information here as it's still a valid technical approach for other scenarios.
Group Writeback Approach (Alternative Method):
- Enable Group Writeback in Entra Connect.
- Let Entra push existing groups down into a dedicated OU in AD.
- Flip the source-of-authority (SOA) later, so I manage them from AD again.
⚡ Tip: Always stage group work in a separate OU. If you mess up, you can roll it back without touching production groups.
Step 5: Password Hash Sync (PHS) Confusion
This was the rabbit hole. Every blog I found swore there had to be a DLL called AzureADPasswordProtectionDCP.dll
in System32
. I tore my servers apart looking for it. It wasn't there.
Turns out: modern Entra Connect Sync doesn't use that DLL anymore. The password sync logic lives entirely inside the sync engine's assemblies (Microsoft.Online.PasswordSynchronization.*
).
Real blockers I hit:
- "User must change password at next logon" flag → If that's set, PHS ignores the password. Resetting with that flag disabled fixed it.
- Missing replication rights → The ADSync service account needs Replicating Directory Changes (All). Without it, no passwords.
- Wrong logs → Password events are under
Applications and Services Logs → Microsoft → AzureADConnect
, not the oldDirectory Synchronization → PasswordSync
.
⚡ Tip: Enable verbose password sync logging
reg add "HKLM\SOFTWARE\Microsoft\Azure AD Sync\PasswordHashSync" /v VerboseLogging /t REG_DWORD /d 1 /f
Restart-Service ADSync
Step 6: Password Write-Back
Getting PHS working was half the battle. Write-back (cloud → on-prem) had its own requirements:
- Enabled both in AADC wizard and Entra tenant password reset settings.
- Connector account delegated
Reset password
,Write lockoutTime
,Write pwdLastSet
,Write userAccountControl
. - Write-back doesn't work in Staging Mode.
- AD password policy is enforced → if Entra accepts a password but AD rejects it, write-back fails.
⚡ Tip: If you get "Access Denied" errors, check with dsacls
. The fastest way is to delegate control on the OU → "Reset user passwords and force password change at next logon." Then clear the flag with Set-ADUser -ChangePasswordAtLogon $false
.
Lessons Learned (a.k.a. "Don't Do What I Did")
- Don't chase ghost DLLs — check the product version before believing blog posts.
- Always pilot with a handful of users in a controlled OU. CSV subsets saved me from syncing garbage.
- Remember that Exchange attributes (aliases, targetAddress) must be exported from Exchange Online separately.
- Sometimes the simplest solution wins — Group Writeback was technically correct, but the sync disable/enable method was faster and met the customer's urgent needs.
- The "must change password" flag silently kills PHS. Check it first.
- Customer timelines matter — don't get too attached to a technical approach if business needs require a different solution.
- Document both approaches — what didn't get implemented this time might be perfect for the next project.
Project Update: The Faster Path
While the Group Writeback approach I outlined above was technically sound, my customer needed a faster solution to manage orphaned groups in the cloud. Before we had a chance to fully implement and test Group Writeback, they decided to go with a more direct approach.
The Solution We Actually Implemented
Instead of using Group Writeback, we went with the sync disable/enable method I describe in my Active Directory to Entra ID Migration article:
- Temporarily disabled synchronization in Entra ID
- Re-enabled synchronization
- Cleaned up connector space errors
Result: Objects that don't exist in on-premises AD became manageable cloud-only objects in Entra ID and Exchange Online.
⚡ Why this worked better: It was faster to implement, required no additional Entra Connect configuration changes, and gave immediate results. The customer could start managing their orphaned groups right away instead of waiting for Group Writeback testing and configuration.
The End: Rising from the Ashes
In the end, I was able to rebuild a dead forest using Entra as my source of truth:
- Users recreated with ImmutableIDs matching cloud identities.
- Groups and orphaned objects converted to cloud-only management using the sync disable/enable method.
- Password hash sync and write-back working like new.
- A new appreciation for how much misinformation exists about AADC internals.
- Sometimes the simplest solution (temporarily disabling sync) works better than complex workarounds.
I went in thinking this would be a "click, export, import" weekend job. Instead, it was an adventure full of scripting, schema spelunking, and a few laughs when I realized the DLL I'd been hunting for doesn't even exist anymore.
But hey — the forest is back, my users are happy, orphaned groups are manageable again, and I have a story worth telling.