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