FSMO Roles and PowerShell

While at work a couple a week ago, I had to move some FSMO (Flexible Single Master Operations) roles around on our network.  What are FSMO roles you say? Well, instead of getting too deep into what they are and how they work, I will reference a link for you to check out. That link is right here. Short story is that if you have issues with any of these, prepare for a fun filled day (or night) keeping your domain from taking a dive.

Finding out who owns the FSMO roles can either be accomplished by running the “netdom query fsmo” command line, which gives you info or open up the Active Directory MMC’s to find the information out.

image

image

There are a number of ways to transfer these roles from one Domain Controller to another or to seize a role if you are backed into a corner with no way out due to a DC crashing hard. One way is to use ntdsutil to make transfer/seize the roles. Another option is to use the mmc’s that you can open up to make a FSMO role transfer, but that requires that you know which mmc has which role. In the case of the schema master role, it requires that you register the schmmgmt.dll and then add the schema manager via mmc.exe.

image

image

Both ways are nice but do take a certain amount of time to go and make the changes. You could create a script using ntdsutil, but I am going a different route.

Thankfully, you can use the .NET class system.directoryservices.activedirectory.Forest to connect to a forest and list all domains and domain controllers within the forest. In this case, I am using the static method GetCurrentForest() to get my forest information.

$forest = [system.directoryservices.activedirectory.Forest]::GetCurrentForest()
$Forest

image

As you can see, there is a wealth of information to view. If you really want your mind blown, check out the members of the $forest object.

image

Just an amazing amount of methods to run here. Everything from global catalogs to raising domain or forest functionality to working with Trust relationships. And there is much more, so much more that I am going to limit myself to just working with the FSMO roles for this post.

If you look at some of the properties, you can see a couple that stand out such as SchemaRoleOwner and NamingRoleOwner, both of which are the forest level FSMO roles, meaning that if you have multiple domains, you will still only just have one set of these roles.  So lets see who owns these two roles:

$forest | Select NamingRoleOwner,SchemaRoleOwner

NamingRoleOwner   SchemaRoleOwner
—————   —————
dc1.rivendell.com dc1.rivendell.com

Pretty simple for that, but now I want to know about the rest of the FSMO role owners which are “domain specific” meaning that unlike the other two FSMO roles, each domain within a forest has the rest of the roles. So 2 domains would both have a set of roles for PDC, RID and Infrastructure.  You can view each domain by typing the following command:

$forest.Domains

Forest                  : rivendell.com
DomainControllers       : {dc1.rivendell.com}
Children                : {}
DomainMode              : Windows2000MixedDomain
Parent                  :
PdcRoleOwner            : dc1.rivendell.com
RidRoleOwner            : dc1.rivendell.com
InfrastructureRoleOwner : dc1.rivendell.com
Name                    : rivendell.com

In my case, I only have one domain to worry about. If I had more than one domain, then I would have to find out where in the collection of domains that my domain belonged in that I wished to view. As you can also see, the other three FSMO role owners are clearly listed among the other information.

My function I wrote simplifies this process by doing the legwork for you. It will give you all of the FSMO Role owners for each domain in a forest and also listing the owner of the forest level FSMO roles as well.

Get-FSMORoleOwner

image

If you want to filter by a specific domain, you can use the Where-Object cmdlet to perform that task.

Get-FSMORoleOwner | Where {$_.Domain -eq 'domain.com'}

Changing the FSMO Role Owners

I have showed you how you can use the .net class to show the FSMO Role owners, now lets take it a step forward by showing you how you can transfer and in a more extreme situation, seizing a role and moving it to a domain controller.

First thing we need to do is connect to a domain using this line:

$domain = $forest.Domains[0]

From there we need to select the Domain Controller that we want to to give a FSMO role to. In my case, I only have one Domain Controller, but I will still filter for that specific DC.

$DomainController = $domain.DomainControllers | Where {$_.Name -eq "dc1.rivendell.com"}
$DomainController.GetType().ToString()

I also decided to find out what kind of object we have once we filter for a specific Domain Controller.  The object that we get is System.DirectoryServices.ActiveDirectory.DomainController

Now lets look at the methods on this object and see if there is anything that I can use to transfer and seizing the roles.

$DomainController | Get-Member -Type Method | Select Name

Name
—-
CheckReplicationConsistency
Dispose
EnableGlobalCatalog
Equals
GetAllReplicationNeighbors
GetDirectoryEntry
GetDirectorySearcher
GetHashCode
GetReplicationConnectionFailures
GetReplicationCursors
GetReplicationMetadata
GetReplicationNeighbors
GetReplicationOperationInformation
GetType
IsGlobalCatalog
MoveToAnotherSite
SeizeRoleOwnership
SyncReplicaFromAllServers
SyncReplicaFromServer
ToString
TransferRoleOwnership
TriggerSyncReplicaFromNeighbors

Here we find two methods that match what we need:

SeizeRoleOwnership

TransferRoleOwnership

Each of these requires the System.DirectoryServices.ActiveDirectory.ActiveDirectoryRole object before it will let the Domain Controller transfer or seize the role. Looking at the properties, we find the data we are looking for:

[System.DirectoryServices.ActiveDirectory.ActiveDirectoryRole] | Get-Member -static -Type Property | Select Name

Name
—-
InfrastructureRole
NamingRole
PdcRole
RidRole
SchemaRole

Ok, now we are ready to transfer/seize the role and give it to the Domain Controller:

$DomainController.TransferRoleOwnership("NamingRole")

Warning!!!

Do not seize a FSMO role from a Domain Controller is healthy and running! Once you seize a FSMO role from a Domain Controller, that DC can never be brought back online and onto the network, otherwise you will experience a lot of fun with your domain. And by fun I mean you will have all sorts of issues to contend with. Now with that taken care of, here is the seizing (which I did not actually do on my DC):

$DomainController.SeizeRoleOwnership("NamingRole")

I also wrote a function called Set-FSMORoleOwner to wither transfer or seize a FSMO role. You can define the Domain Controller, Role and whether to transfer or seize the role. Being this is an advanced functon, I made sure to take advantage of the –WhatIf parameter so you can be sure you doing what you mean to do. Also you can specify –PassThru to show the current FSMO Role owners after making the change.

Set-FSMORoleOwner -DomainController dc1.rivendell.com -Role PdcRole -Transfer
Set-FSMORoleOwner -DomainController dc1.rivendell.com -Role PdcRole,SchemaRole -Transfer
Set-FSMORoleOwner -DomainController dc1.rivendell.com -Role PdcRole -Seize -WhatIf
Set-FSMORoleOwner -DomainController dc1.rivendell.com -Role PdcRole -Seize
Set-FSMORoleOwner -DomainController dc1.rivendell.com -Role PdcRole -Transfer -PassThru

Hope you enjoyed this posting on working with FSMO Roles and the advanced functions that I wrote to go along with this. Below are the links to where I posted the functions as well as the actual code for the functions.

Get-FSMORoleOwner Code

Technet

Poshcode

Function Get-FSMORoleOwner {  
<#    
.SYNOPSIS    
    Retrieves the list of FSMO role owners of a forest and domain    
      
.DESCRIPTION    
    Retrieves the list of FSMO role owners of a forest and domain  
      
.NOTES    
    Name: Get-FSMORoleOwner  
    Author: Boe Prox  
    DateCreated: 06/9/2011    
  
.EXAMPLE  
    Get-FSMORoleOwner  
      
    DomainNamingMaster  : dc1.rivendell.com  
    Domain              : rivendell.com  
    RIDOwner            : dc1.rivendell.com  
    Forest              : rivendell.com  
    InfrastructureOwner : dc1.rivendell.com  
    SchemaMaster        : dc1.rivendell.com  
    PDCOwner            : dc1.rivendell.com  
      
    Description  
    -----------  
    Retrieves the FSMO role owners each domain in a forest. Also lists the domain and forest.    
            
#>  
[cmdletbinding()]   
Param() 
Try {  
    $forest = [system.directoryservices.activedirectory.Forest]::GetCurrentForest()   
    ForEach ($domain in $forest.domains) {  
        $forestproperties = @{  
            Forest = $Forest.name  
            Domain = $domain.name  
            SchemaRole = $forest.SchemaRoleOwner  
            NamingRole = $forest.NamingRoleOwner  
            RidRole = $Domain.RidRoleOwner  
            PdcRole = $Domain.PdcRoleOwner  
            InfrastructureRole = $Domain.InfrastructureRoleOwner  
            }  
        $newobject = New-Object PSObject -Property $forestproperties  
        $newobject.PSTypeNames.Insert(0,"ForestRoles")  
        $newobject  
        }  
    }  
Catch {  
    Write-Warning "$($Error)"  
    }  
}

Set-FSMORoleOwner Code

Technet

Poshcode

 

 

Function Set-FSMORoleOwner { 
<#   
.SYNOPSIS   
    Performs a transfer of a FSMO role to a specified Domain Controller.  
     
.DESCRIPTION   
    Performs a transfer of a FSMO role to a specified Domain Controller. 
 
.PARAMETER DomainController 
    Fully Qualified Domain Name of the Domain Controller to take a transfer role to 
 
.PARAMETER Role 
    Name of the role to transfer to domain controller 
 
.PARAMETER Transfer 
    Transfers the specified role and give to specified domain controller.  
 
.PARAMETER Seize 
    Seize the specified role and give to specified domain controller.    
 
.PARAMETER PassThru 
    Show the FSMO role owners after performing action     
 
.NOTES   
    Name: Set-FSMORoleOwner 
    Author: Boe Prox 
    DateCreated: 06/9/2011   
 
.EXAMPLE 
    Set-FSMORoleOwner -DomainController DC1.Rivendell.com -Role RidRole 
     
    Description 
    ----------- 
    Transfers the RidRole to DC1.Rivendell.com  
 
.EXAMPLE 
    Set-FSMORoleOwner -DomainController DC1.Rivendell.com -Role PdcRole -Transfer -PassThru 
     
    NamingRole  : dc2.rivendell.com  
    Domain              : rivendell.com  
    RidRole            : dc2.rivendell.com  
    Forest              : rivendell.com  
    InfrastructureRole : dc2.rivendell.com  
    SchemaRole        : dc2.rivendell.com  
    PdcRole            : dc1.rivendell.com      
     
    Description 
    ----------- 
    Transfers the PdcRole to DC1.Rivendell.com and displays the current FSMO Role Owners. 
 
.EXAMPLE 
    Set-FSMORoleOwner -DomainController DC1.Rivendell.com -Role PdcRole,RidRole,SchemaRole -Transfer -PassThru 
     
    NamingRole         : dc2.rivendell.com  
    Domain              : rivendell.com  
    RidRole            : dc1.rivendell.com  
    Forest              : rivendell.com  
    InfrastructureRole : dc2.rivendell.com  
    SchemaRole        : dc1.rivendell.com  
    PdcRole            : dc1.rivendell.com      
     
    Description 
    ----------- 
    Transfers the PdcRole,RidRole and SchemaRole to DC1.Rivendell.com and displays the current FSMO Role Owners.   
     
.EXAMPLE 
    Set-FSMORoleOwner -DomainController DC1.Rivendell.com -Role PdcRole -Seize -PassThru 
     
    WARNING: Performing this action is irreversible! 
    The Domain Controller that originally holds this role should be rebuilt to avoid issues on the domain! 
     
    NamingRole  : dc2.rivendell.com  
    Domain              : rivendell.com  
    RidRole            : dc2.rivendell.com  
    Forest              : rivendell.com  
    InfrastructureRole : dc2.rivendell.com  
    SchemaRole        : dc2.rivendell.com  
    PdcRole            : dc1.rivendell.com      
     
    Description 
    ----------- 
    Seizes the PdcRole and places it on DC1.Rivendell.com and displays the current FSMO Role Owners.   
           
#> 
[cmdletbinding( 
    SupportsShouldProcess = 'True', 
    ConfirmImpact = 'High', 
    DefaultParameterSetName = 'Transfer' 
    )]  
Param ( 
    [parameter(Position=1,Mandatory = 'True',ValueFromPipeline = 'True', 
        HelpMessage='Enter the Fully Qualified Domain Name of the Domain Controller')] 
    [ValidateCount(1,1)] 
    [string[]]$DomainController, 
    [parameter(Position=2,Mandatory = 'True', 
        HelpMessage = "InfrastructureRole,NamingRole,PdcRole,RidRole,SchemaRole")] 
    [Alias('fsmo','fsmorole')] 
    [ValidateSet('InfrastructureRole','NamingRole','PdcRole','RidRole','SchemaRole')] 
    [ValidateCount(1,5)] 
    [string[]]$Role, 
    [parameter(Position=4,ParameterSetName='Transfer')] 
    [Switch]$Transfer,     
    [parameter(Position=4,ParameterSetName='Seize')] 
    [Switch]$Seize, 
    [parameter(Position=5)] 
    [switch]$PassThru 
    ) 
Begin {} 
Process { 
    Try { 
        Write-Verbose "Connecting to Forest" 
        $forest = [system.directoryservices.activedirectory.Forest]::GetCurrentForest() 
        Write-Verbose "Locating $DomainController"  
        $dc = $forest.domains | ForEach { 
            $_.Domaincontrollers | Where { 
                $_.Name -eq $DomainController 
                } 
            } 
        } 
    Catch { 
        Write-Warning "$($Error)" 
        Break 
        } 
    If (-NOT [string]::IsNullOrEmpty($dc)) { 
        ForEach ($r in $role) { 
            Switch ($PScmdlet.ParameterSetName) { 
               "Transfer" { 
                Write-Verbose "Beginning transfer of $r to $DomainController" 
                    If ($PScmdlet.ShouldProcess("$DomainController","Transfer Role: $($Role)")) { 
                        Try { 
                            $dc.TransferRoleOwnership($r) 
                            } 
                        Catch { 
                            Write-Warning "$($Error[0])" 
                            Break 
                            } 
                        } 
                    } 
                "Seize" { 
                    Write-Warning "Performing this action is irreversible!`nThe Domain Controller that originally holds this role should be rebuilt to avoid issues on the domain!" 
                    Write-Verbose "Seizing $r and placing on $DomainController" 
                    If ($PScmdlet.ShouldProcess("$DomainController","Seize Role: $($Role)")) { 
                        Try { 
                            $dc.SeizeRoleOwnership($r) 
                            } 
                        Catch { 
                            Write-Warning "$($Error[0])" 
                            Break 
                            } 
                        }                
                    } 
                Default { 
                    Write-Warning "You must specify either -Transfer or -Seize!" 
                    Break 
                    } 
                } 
            } 
        } 
    Else { 
        Write-Warning "Unable to locate $DomainController!" 
        Break 
        } 
    } 
End { 
    If ($PSBoundParameters['PassThru']) { 
        $forest = [system.directoryservices.activedirectory.Forest]::GetCurrentForest() 
        ForEach ($domain in $forest.domains) { 
            $forestproperties = @{ 
                Forest = $Forest.name  
                Domain = $domain.name  
                SchemaRole = $forest.SchemaRoleOwner  
                NamingRole = $forest.NamingRoleOwner  
                RidRole = $Domain.RidRoleOwner  
                PdcRole = $Domain.PdcRoleOwner  
                InfrastructureRole = $Domain.InfrastructureRoleOwner  
                } 
            $newobject = New-Object PSObject -Property $forestproperties 
            $newobject.PSTypeNames.Insert(0,"ForestRoles") 
            $newobject         
            } 
        } 
    } 
}

About Boe Prox

Microsoft Cloud and Datacenter MVP working as a SQL DBA.
This entry was posted in powershell, scripts and tagged , , . Bookmark the permalink.

3 Responses to FSMO Roles and PowerShell

  1. Jeff Patton says:

    Boe,

    Love this script but I need to be able to pass in a forest, so using [system.directoryservices.activedirectory.Forest]::GetCurrentForest() gives me the forest of my current user context, but I need to pull in information from the forest that my computer is located in. I’ve not had much luck in trying to pass something else in, do you have any thoughts or better google-fu to help me out?

    Thanks,

    • Boe Prox says:

      Hi Jeff. I am not sure of any way to look up the forest of a computer that it resides in off the top of my head. But I can do a little searching and testing to see if I can find anything that might suit what you are looking for.

    • Stefan Herman says:

      $Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain()

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s