Modifying cross forest members of Active Directory groups

Adding users to groups within the same domain using Powershell is quite simple – there is a cmdlet Add-ADGroupMember (and removing them is just as easy !), but how we accomplish when one domain contains groups and has a one way trust with a domain in another forest that contains the users ?

This is a rhetorical question 😉 Assuming we are running from the domain containing the groups, the other domain needs to be mapped to a PSDrive. Once done we can search for the user and use the Add-ADGroupMember cmdlet to add them.

Removing users is nearly as straight forward, though I only had success using Remove-ADPrincipalGroupMembership to remove the remote user.

We will need to know:

  • The name of the remote domain.
  • A credential for the remote domain.
  • The name of the group.
  • The samaccountname of the users to add or remove.

I’ve written a script below, which is able to add and remove users to a group in single call. There is no need to specify the domain controller for the remote user domain as this can be discovered (I’m assuming your DNS is configured correctly and healthy !). However this has not been tested:

  • In a two-way trust.
  • Running in the user domain/forest.
param([Parameter(Mandatory=$true)][string] $GroupName = $(Read-Host -Prompt "Please enter a group name"),
      [string[]] $Add = $(Read-Host -Prompt "(optional) Please enter the names of any users to add to the group"),
      [string[]] $Remove = $(Read-Host -Prompt "(optional) Please enter the names of any users to remove from the group"),
      [Parameter(Mandatory=$true)][string] $RemoteDomainName = $(Read-Host -Prompt "Please enter the remote domain name"),
      [Parameter(Mandatory=$true)][PSCredential] $RemoteCredential = $(Get-Credential),
      [string] $RemoteDC,
      [switch] $Confirm)

# Ensure that this is loaded. Fail if not.
if ($null -eq $(Get-Module ActiveDirectory)) {
	Write-Host "Attempting to load ActiveDirectory module.";
	try {
		Import-Module ActiveDirectory;
		Write-Host "Loaded ActiveDirectory module." -ForegroundColor Green;
	} catch {
		Write-Host "Could not load ActiveDirectory module" -Foreground Red;
		exit;
	}
}

# Try and connect to the remote domain.
$domain = Get-ADDomain $RemoteDomainName -Credential $RemoteCredential -ErrorAction SilentlyContinue;
if ($null -eq $domain) {
    Write-Host "There was an error retrieving domain information for $RemoteDomainName" -ForegroundColor Red;
    exit;
}

# Get the group in the local domain.
$group = Get-ADGroup $GroupName -ErrorAction SilentlyContinue -Properties *
if ($null -eq $group) {
    exit;
}

if ($group.GroupScope -ne "DomainLocal") {
    Write-Host "The group $($group.Name) has scope $($group.GroupScope)";
    Write-Host "The group is not configured to contain remote users." -ForegroundColor Red;
    exit;
}

# Get the remote domain details.
$RemoteNetBIOS = $domain.NetBIOSName;
$RemoteDN = "//RootDSE/$($domain.DistinguishedName)";

if ($null -eq $RemoteDC -Or $RemoteDC.Length -eq 0) {
    $dc = Get-ADDomainController -Discover -DomainName $RemoteNetBIOS
    $RemoteDC = $dc.HostName;
}

# Try and map the remote domain to a PS drive.
try {
    New-PSDrive -Name $RemoteNetBIOS -Credential $RemoteCredential -Root $RemoteDN -PSProvider ActiveDirectory -Server $RemoteDC | out-null

} catch {
    Write-Host "There was an error connecting to the $RemoteDomainName domain." -ForegroundColor Red;
    exit;
}

foreach ($username in $Add) {
    if ($null -eq $username -Or $username.Length -eq 0) {
        continue;
    }

    # Get the user and add to the group.
    $user = Get-ADUser -Filter {SamAccountName -eq $username} -Server $RemoteNetBIOS -Credential $RemoteCredential;
    if ($null -ne $user) {           
        $group | Add-ADGroupMember -Members $user;
        Write-Host "$username was added to the group '$GroupName' successfully." -ForegroundColor Green;

    } else {
        Write-Host "Could not find a user with the username '$username'" -ForegroundColor Red;

    }
}

foreach ($username in $Remove) {
    if ($null -eq $username -Or $username.Length -eq 0) {
        continue;
    }

    $user = Get-ADUser -Filter {SamAccountName -eq $username} -Server $RemoteNetBIOS -Credential $RemoteCredential;
    if ($null -ne $user) {
        Remove-ADPrincipalGroupMembership -Server $RemoteNetBIOS -Credential $RemoteCredential $user -MemberOf $group -Confirm:$Confirm;
        Write-Host "$username was successfully removed from the group '$GroupName'" -ForegroundColor Green;

    } else {
        Write-Host "Could not find a user with the username '$username'" -ForegroundColor Red;

    }
}

# Unmap the remote domain
if ($null -ne $(Get-PSDrive -Name $RemoteNetBIOS -ErrorAction SilentlyContinue)) {
    Remove-PSDrive $RemoteNetBIOS;
}

The script doesn’t take into account if the user is already a member of the group – the way to do this is to retrieve the groups “member” property and use the SID to check against the user.

As usual, let me know if this is useful or you have any other corrections/comments. I’ve put this into a simple script located here as the script “Set-ADGroupTrustedForestMembership.ps1”.