Use PowerShell to Work with RODC Accounts

Use PowerShell to Work with RODC Accounts

  • Comments 8
  • Likes

Summary: Microsoft premier field engineer, Ian Farr, talks about using Windows PowerShell to work with RODC accounts.

Microsoft Scripting Guy, Ed Wilson, is here. Today I would like to introduce a new guest blogger, Ian Farr. Ian tells us about himself:

I started out writing UNIX shell scripts to automate simple tasks. Then as a Windows IT pro, I discovered VBScript, and it ignited a passion for automation. Over the years, I've used batch files, KiXtart, JScript, HTAs, Perl, JavaScript, and Python. I love solving problems with scripts, and I've written code for several large enterprise environments. I now work as a premier field engineer at Microsoft, teaching Windows PowerShell and helping my customers with their own scripts.

Today, I’d like to share with you my function Get-ADRodcAuthenticatedNotRevealed.ps1.

One of my customers has a large number of Read-only domain controllers (RODCs). Each one is configured (by using password replication policies) to only store the account credentials of specific low-privileged user and computer accounts. If an “allowed” account authenticates against its designated RODC, its credentials are cached on that RODC. The account is then added to the “revealed” list. If the RODC loses connectivity to the central site, it can still authenticate accounts in its revealed list.

Of course, an RODC can authenticate accounts that are not in an applicable password replication policy. To do this, it must communicate with a Read-Write domain controller. All accounts that an RODC has authenticated, including those in the revealed list, can be found in the appropriately named authenticated list. By now, you’re probably thinking, “What does all of have to do with Windows PowerShell and scripting?”

Hang on…I’m almost there.

RODCs are most suited to branch office locations, so it’s reasonable to assume that each RODC has authenticated accounts from applicable allowed password replication policies. It’s also reasonable to assume that user and computer accounts that are not defined in a password replication policy may have been authenticated—for example, perhaps a roaming user has visited and plugged in their laptop to the LAN.

You may also see authenticated accounts that are part of a “denied” password replication policy. Built-in privileged groups and accounts, by default, do not have their credentials stored on an RODC.

Now to analyse specific RODC usage. How do you determine which accounts are stored in the “authenticated” list, but don’t appear in the “revealed” list?

At last…some Windows PowerShell!

I’ve published the Get-ADRodcAuthenticatedNotRevealed function on the TechNet Script Center Repository. It will return account objects that are authenticated but not revealed.

You could add the script to $profile, so it is loaded to the PSDrive function (see Use PowerShell to Work Easily with Dives and Paths) when Windows PowerShell opens. Or you can access it as needed by dot sourcing the script file:

. .\Get-ADRodcAuthenticatedNotRevealed.ps1

Here’s a suggested way to use the function:

Get-ADDomainController -Filter {IsReadOnly -eq $True} | Get-ADRodcAuthenticatedNotRevealed 

Here, we get all of the RODCs from the domain that the user is currently logged in to and pipe them one-by-one into the function. This line could happily be part of a script that defines or references the function.

We can just as easily pipe a hand-typed list of RODCs:

“NINJARODC01”, “ninjarodc02” | Get-ADRodcAuthenticatedNotRevealed  

Or the contents of a text file:

Get-Content –Path rodc.txt | Get-ADRodcAuthenticatedNotRevealed 

By the way, this is the geeky way to get the contents of that file:

${c:rodc.txt} | Get-ADRodcAuthenticatedNotRevealed  

The Get-ADRodcAuthenticatedNotRevealed function processes each RODC that is passed down the pipeline with a Process script block. Script blocks are the building blocks of Windows PowerShell. They are recognized by those interesting curly braces you see everywhere: { }.

Next, we compare the output of two cmdlets. The first cmdlet gets all authenticated accounts from the RODC currently in the pipeline:

$AuthenticatedAccounts = Get-ADDomainControllerPasswordReplicationPolicyUsage -Identity $Rodc -AuthenticatedAccounts

The second gets all the revealed accounts:

$RevealedAccounts = Get-ADDomainControllerPasswordReplicationPolicyUsage -Identity $Rodc -RevealedAccounts

Here’s the comparison:

Comparison = Compare-Object -ReferenceObject $AuthenticatedAccounts -DifferenceObject $RevealedAccounts

After the comparison is performed, we filter on those accounts that only appear in the reference object by using the side indicator output, “< =”, as shown here:

$Results = $Comparison | Where-Object {$_.SideIndicator -eq "<="} 

 Here’s what the $Comparison side indicators represent:

  • “<=” implies that these accounts only appear in the AuthenticatedAccounts list
  • “=>” implies that the accounts appear only in the RevealedAccounts list
  • “==” implies that the accounts are present in both lists (the IncludeEqual switch has to be supplied to the Compare-Object cmdlet)

$Results now contains authenticated accounts that have not been revealed. Let’s return them to the calling scope:


We could export the account objects to XML, or as in the following example, to a file:

$Results.InputObject | Export-CSV -Path "$($Rodc)_AuthenticatedNotRevealed.csv" -Force

We’ll end up with a .csv file of authenticated but not revealed accounts for each RODC processed. It will look something like this:


Image of command output   

Now to write a function to analyse the Get-ADRodcAuthenticatedNotRevealed output…

Here is the function: Get-ADRodcAuthenticatedNotRevealed.ps1.


Thank you, Ian, for sharing your knowledge and time.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Very nice, we just implemented a RODC so I'm looking forward to playing with this. Your function assumes that the user has run import-module activedirectory in advance, you may wish to put that in the notes section or something, otherwise very very nice.

  • Thanks for the great feedback, Jeffrey. Much appreciated. I like to put '#Requires' statements at the start of my functions or scripts. This one has the following statements to ensure that the AD module is present and is loaded automatically: #Requires -version 3 #Requires -modules ActiveDirectory You're absolutely right that this function will work just as well with an 'Import-Module ActiveDirectory' statement. It should be placed after the Param block, to avoid an error, and the two requires statements should be removed. In fact, as a recommended practice point, I would consider replacing the '#Requires -version 3' statement with '#Requires -version 2'. I'll update the notes in the repository. Thanks, again :)

  • @Ian, Then I'm misunderstanding how the #Requires works, as when I ran it on a server without the ad modules available I just got an error message the get-addomaincontroller didn't exist. Get-ADRodcAuthenticatedNotRevealed : Cannot validate argument on parameter 'Rodc'. The term 'Get-ADDomainController' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. Should there have been a different error message?

  • @Jeffrey What you're seeing is correct, because the #Requires statements should be BEFORE the Function statement - my apologies for any confusion caused! I'll update the sample now. BTW -le v2 doesn't understand the #Requires -modules so you'll get a message saying it's in the wrong format! Thanks, Ian :)

  • Updated. @Jeffrey - have fun with that RODC!

  • Just add -NoTypeInformation while exporting to csv to avoid type information on cell 1