Exporting and importing user roles

I have a customer who has many management groups and wants to synchronize the user roles between them.  There is no easy way to do this in the UI, but it can be done via PowerShell.  One of my co-workers, Jonathan Almquist, blogged about this some time ago.  His script allows one to export the user roles, along with any associated user accounts, and import them in another management group.  It, however, doesn't export any associated Group Scopes, Tasks, or Views.  The script I have created exports everything associated with the user role and is a great example of creating generic types in PowerShell.

The script takes two parameters.  The first is the name of the MS you want to connect to and the second is either "export" or "import".  The export task creates a file called userroles.xml in the same directory as the PS script ran from.  To use the "import" task you must place the userroles.xml in the same directory if it isn't already there.  The script then looks for any user roles in the userroles.xml that don't exist and creates them.  If the user role does exist, but is missing certain users, group scopes, views, or tasks then it will add them.

I have pasted the script below and also attached a copy to the blog.

#FileName: UserRoleExporter.ps1
#Created by: Russ Slaten (http://blogs.msdn.com/rslaten)
#Created on: 07/24/2008
#Modified by: xxx
#Modified on: xx/xx/xxxx

#===========================FUNCTIONS BEGIN===========================

function Main
{
#Constants
$MSPARAM = "MS"
$TASKPARAM = "TASK"
$EXPORT = "EXPORT"
$IMPORT = "IMPORT"
$XMLFILE = "userroles.xml"
#Get each of the command line parameters passed by the caller
$managementServer = GetParameter $MSParam
$task = GetParameter $TASKPARAM
Write-Host "Task =" $task
Write-Host "Management Server =" $managementServer
#Configure OpsMgr powershell environment
ValidateSnapIn
$drive = SetDrive
if (!$drive){throw("Error configuring OpsMgr Environment")}
#Connect to the Management Server
$ms = GetManagementServer $managementServer
if (!$ms){throw("Error connecting to Management Server")}
#Start the applicable task
if ($task.ToString() -eq $EXPORT.ToString()) {ExportRoles $XMLFILE} #Export Roles
elseif ($task.ToString() -eq $IMPORT.toString()) {ImportRoles $XMLFILE} #Import Roles
trap [Exception]{Write-Error $_.Exception.Message;ShowHelp;exit;}
} #Main

#Exports Roles from Management Group
function ExportRoles([string]$s)
{
#Get all non-system user roles
$userroles = get-userRole | where {$_.IsSystem -eq $False}
#Make sure some custom user roles exist
if ($userroles.count -eq 0) {Write-host "No custom user roles found";return 0;}
#Create the XML object
$doc = New-Object "System.Xml.XmlDocument"
$doc.LoadXml("<?xml version='1.0' encoding='utf-8'?><UserRoles xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'></UserRoles>")

    #Loop through each user role instance
foreach ($mo in $userRoles)
{
#Add single instance data to XML
$elem = $doc.CreateElement("UserRole")
$elem.SetAttribute("Name",$mo.Name)
$elem.SetAttribute("DisplayName",$mo.DisplayName)
$elem.SetAttribute("Description",$mo.Description)
$elem.SetAttribute("Profile",$mo.MonitoringProfile)

        #Add users to XML if any are defined
if ($mo.Users.count -ne 0)
{
foreach ($u in $mo.Users)
{
$elem2 = $doc.CreateElement("User")
$elem2.SetAttribute("UserName",$u.ToString())
$temp = $elem.AppendChild($elem2)
}
}
#Loop through the groups if the user has explicitly defined group scopes
if ($mo.scope.MonitoringObjects.count -gt 0)
{
foreach ($grp in $mo.scope.MonitoringObjects)
{
$elem2 = $doc.CreateElement("Group")
$elem2.SetAttribute("GroupScope",$grp.ToString())
$temp = $elem.AppendChild($elem2)
}
}
#Loop through the views if the user has explicitly defined views
if ($mo.scope.MonitoringViews.count -gt 0)
{
foreach ($view in $mo.scope.MonitoringViews)
{
$elem2 = $doc.CreateElement("MonitoringView")
$elem2.SetAttribute("View",$view.First.ToString())
$elem2.SetAttribute("Bool",$view.Second.ToString())
$temp = $elem.AppendChild($elem2)
}
}
#Loop through the non-credential tasks if the user has explicitly defined tasks
if ($mo.scope.NonCredentialMonitoringTasks.count -gt 0)
{
foreach ($task in $mo.scope.NonCredentialMonitoringTasks)
{
$elem2 = $doc.CreateElement("NonCredentialMonitoringTask")
$elem2.SetAttribute("Task",$task.First.ToString())
$elem2.SetAttribute("Bool",$task.Second.ToString())
$temp = $elem.AppendChild($elem2)
}
}
#Loop through the credential tasks if the user has explicitly defined tasks
if ($mo.scope.CredentialMonitoringTasks.count -gt 0)
{
foreach ($task in $mo.scope.CredentialMonitoringTasks)
{
$elem2 = $doc.CreateElement("CredentialMonitoringTask")
$elem2.SetAttribute("Task",$task.First.ToString())
$elem2.SetAttribute("Bool",$task.Second.ToString())
$temp = $elem.AppendChild($elem2)
}
}
#Write this new element to the XML document
$temp = $doc.get_ChildNodes().Item(1).AppendChild($elem)
Write-Host "---" $mo.DisplayName "-> exported"
}
#Save XML to a file
$doc.save((Join-path $SCRIPTPATH $s))
} #ExportRoles

#Imports Roles from XML in Management Group
function ImportRoles([string]$s)
{
#Get existing Non-System User Roles
$existingUserRoles = get-userRole | where {$_.IsSystem -eq $False}
#Open XML file
$doc = New-Object "System.Xml.XmlDocument"
$doc.load((Join-Path $SCRIPTPATH $s))
#Loop through each user role
$userRoles = $doc.SelectNodes("UserRoles/UserRole")
foreach ($mo in $userRoles)
{
#Check to see if user already exists
$bFound = $false
foreach ($u in $existingUserRoles)
{
if ($mo.Name -eq $u.Name)
{
Write-Host $mo.Name ":Role already exists, adding permissions"
$bFound = $true
ReplicateUserRoleRights $mo
}
}
if (!$bFound)
{
#Create new role
CreateNewUserRole $mo
}
}
} #ImportRoles

function ReplicateUserRoleRights([System.Object]$xml)
{
#Get the user
$obj = get-userRole | where {$_.Name -eq $xml.Name}
Write-Host $obj.Name ":Adding rights"
#First add users
foreach ($xmlConsoleUser in $xml.User)
{
$bFound = $false
foreach ($consoleUser in $obj.Users)
{
if ($xmlConsoleUser.UserName -eq $consoleUser)
{
$bFound = $true
}
}
if (!$bFound)
{
if ($xmlConsoleUser.UserName.length -gt 1)
{
$obj.Users.Add($xmlConsoleUser.UserName)
$obj.Update()
Write-Host $obj.Name "-User:" $xmlConsoleUser.UserName "->added"
}
}
else
{
Write-Host $obj.Name "-User:" $xmlConsoleUser.UserName "->already exists"
}
}

    #Now Add Group Scopes
foreach ($xmlGroupScope in $xml.Group)
{
$bFound = $false
foreach ($consoleScope in $obj.Scope.MonitoringObjects)
{
if ($xmlGroupScope.GroupScope -eq $consoleScope.ToString())
{
$bFound = $true
}
}
if (!$bFound)
{
$ret = $true
[string]$sGuid = $xmlGroupScope.GroupScope
if ($sGuid.length -ne 0)
{
$obj.Scope.MonitoringObjects.Add($sGuid)
$ret = $obj.Update()
trap [Exception]{continue}
if (!$ret)
{
Write-Host $obj.Name "-GroupScope:" $xmlGroupScope.GroupScope "->added"
}
else
{
Write-Host $obj.Name "-GroupScope:" $xmlGroupScope.GroupScope "->no matching group"
}
}
}
else
{
Write-Host $obj.Name "-GroupScope:" $xmlGroupScope.GroupScope "->already exists"
}
}
#Create generic type (used for views and tasks if there are any)
$genericType = [Type] "Microsoft.EnterpriseManagement.Common.Pair``2"
$typeParameters = "System.Guid","System.Boolean"
[type[]] $typedParameters = $typeParameters
$closedType = $genericType.MakeGenericType($typedParameters)

    #Now Add Views
foreach ($xmlView in $xml.MonitoringView)
{
$bFound = $false
foreach ($consoleView in $obj.Scope.MonitoringViews)
{
if ($xmlView.View.ToString() -eq $consoleView.First.ToString())
{
$bFound = $true
}
}
if (!$bFound)
{
$ret = $true
if ($xmlView.View.length -gt 1)
{
if ($xmlView.bool -eq $false)
{
$second = $false
}
$params = [guid]$xmlView.View,$second
$pair = [Activator]::CreateInstance($closedType, $params)
$obj.Scope.MonitoringViews.Add($pair)
$ret = $obj.Update()
trap [Exception]{continue}
if (!$ret)
{
Write-Host $obj.Name "-View:" $xmlView.View "->added"
}
else
{
Write-Host $obj.Name "-View:" $xmlView.View "->no matching group"
}
}
}
else
{
Write-Host $obj.Name "-View:" $xmlView.View "->already exists"
}
}
#Now Add noncredentialmonitoringtasks
foreach ($xmlNonCred in $xml.NonCredentialMonitoringTask)
{
$bFound = $false
foreach ($consoleNonCred in $obj.Scope.NonCredentialMonitoringTasks)
{
if ($xmlNonCred.Task.ToString() -eq $consoleNonCred.First.ToString())
{
$bFound = $true
}
}
if (!$bFound)
{
$ret = $true
if ($xmlNonCred.Task.length -gt 1)
{
if ($xmlNonCred.bool -eq $false)
{
$second = $false
}
$params = [guid]$xmlNonCred.Task,$second
$pair = [Activator]::CreateInstance($closedType, $params)
$obj.Scope.NonCredentialMonitoringTasks.Add($pair)
$ret = $obj.Update()
trap [Exception]{continue}
if (!$ret)
{
Write-Host $obj.Name "-NonCredTask:" $xmlNonCred.Task "->added"
}
else
{
Write-Host $obj.Name "-NonCredTask:" $xmlNonCred.Task "->no matching group"
}
}
}
else
{
Write-Host $obj.Name "-NonCredTask:" $xmlNonCred.Task "->already exists"
}
}
#Now Add credentialmonitoringtasks
foreach ($xmlCred in $xml.CredentialMonitoringTask)
{
$bFound = $false
foreach ($consoleCred in $obj.Scope.CredentialMonitoringTasks)
{
if ($xmlCred.Task.ToString() -eq $consoleCred.First.ToString())
{
$bFound = $true
}
}
if (!$bFound)
{
$ret = $true
if ($xmlCred.Task.length -gt 1)
{
if ($xmlCred.bool -eq $false)
{
$second = $false
}
$params = [guid]$xmlCred.Task,$second
$pair = [Activator]::CreateInstance($closedType, $params)
$obj.Scope.CredentialMonitoringTasks.Add($pair)
$ret = $obj.Update()
trap [Exception]{continue}
if (!$ret)
{
Write-Host $obj.Name "-CredTask:" $xmlCred.Task "->added"
}
else
{
Write-Host $obj.Name "-CredTask:" $xmlCred.Task "->no matching group"
}
}
}
else
{
Write-Host $obj.Name "-CredTask:" $xmlCred.Task "->already exists"
}
}
} #ReplicateUserRoleRights

function CreateNewUserRole([System.Object]$xml)
{
#Create a new User Role Object
$obj = new-object Microsoft.EnterpriseManagement.Monitoring.Security.MonitoringUserRole
#Populate the common fields for the userrole
$obj.Name = $xml.Name
$obj.DisplayName = $xml.DisplayName
$obj.Description = $xml.Description
$profile = $mg.GetMonitoringProfiles() | where {$_.Name -eq $xml.Profile}
$obj.MonitoringProfile = $profile
$mg.InsertMonitoringUserRole($obj)
#Now Replicate the rights associated with this role
Write-Host $xml.Name ":New user role created"
ReplicateUserRoleRights $xml
} #CreateNewUserRole

#Validates the parameters passed by the caller
#Pass either "MS" or "TASK" depending on which parameter you want
function GetParameter([string]$s)
{
#First, make sure the caller passed at least two parameters to the script
if ($cmdLineArgs.count -ne 2) {throw("Error getting command line parameters")}
#Now, validate the contents of the parameter
if ($s -eq "MS"){return $cmdLineArgs[0]}
elseif ($s -eq "TASK")
{
if ($cmdLineArgs[1] -eq "EXPORT"){return "EXPORT"}
elseif ($cmdLineArgs[1] -eq "IMPORT") {return "IMPORT"}
else {throw("Error matching 2nd command line parameter")}
}
else {throw("Error getting command line parameters")}
} #GetParameters

#This function tests whether the opsmgr snap-in has been added
function ValidateSnapIn
{
$snapins = PsSnapIn | select-Object name
$added = $false
foreach ($o in $snapins)
{
if ($o -like "*Microsoft.EnterpriseManagement.OperationsManager.Client*")
{
$added = $true
break
}
}
if (!$added)
{
add-PsSnapIn "Microsoft.EnterpriseManagement.OperationsManager.Client"
write-Host "OpsMgr Snap-in added."
}
else
{
write-Host "OpsMgr Snap-in already added."
}
} #ValidateSnapIn

function SetDrive
{
#Sets location
set-location "OperationsManagerMonitoring::"

    $drv = psdrive | select-Object name
$added = $false
foreach ($d in $drive)
{
if ($d -like "*Monitoring*")
{
$added = $true
}
}
if (!$added)
{
New-PSDrive -Name: Monitoring -PSProvider: OperationsManagerMonitoring -Root: \
write-Host "Monitoring Drive added."
}
else
{
write-Host "Monitoring Drive already added."
}
return $psdrive
} #SetDrive

function GetManagementServer([string]$s)
{
New-ManagementGroupConnection -ConnectionString: $s
cd Monitoring:\$s
$mg = (get-item .).ManagementGroup
return $mg
} #GetManagementServer

function ShowHelp
{
Write-Host "-----UserRoleExporter.ps1 Help-----"
Write-Host "This is an example script for exporting and importing user roles from OpsMgr"
Write-Host ""
Write-Host "UserRoleExporter.ps1 Usage:"
Write-Host "Parameter 1: <Management Server Name>"
Write-Host "Parameter 2: import or export"
Write-Host "Example: UserRoleExporter.ps1 myRMS export"
Write-Host ""
} #ShowHelp

#===========================FUNCTIONS END===========================

#Get CmdLine Args and set global
$CmdLineArgs = $Args

#Get path script was called from and set global
$SCRIPTPATH = $MyInvocation.Mycommand.Path | Split-Path -Parent

#Get Management Group Connection
$mg = (get-item .).ManagementGroup

#Calls the main program
Main

# End of Script

UserRoleExporter ps1_.txt