FIM, XPath, PowerShell

Hello Everyone.

I am working with FIM 2010 and need some help with a script.
Most of this is from the online community as I am trying to learn as fast as I can.

If you could review the script and provide any suggestions or feedback on lines 448 through 512.

Basically the goal is to search FIM via PowerShell and XPath and search for groups that do not meet a specific length in their name. For example, any group that has a name that is 8 characters or less in length, I would like to export the:

  1. Owner
  2. Security Group Name
  3. UUID (URL to FIM Managed Security Group)
    a. This will be concatenated (ex: iam.jones.com/identitymanagement/xx_P1 )
  4. Description of the Security Group

Please note that on lines…
370 #Script starts
470 #Get Owner, DisplayName and Description based on ObjectID
472 #If DisplayName is less than 8, If Owner is not null - iterate each Owner
474 #Print Owner, DisplayName, Url, Description to CSV file.

The current script is listed below:

.\Update-FIMConfiguration.ps1 -ConfigurationFile .\Update-FIMConfiguration-20140108changes.ps1

.NOTES

#>

[CmdletBinding(SupportsShouldProcess=$True)]
param(
[string]$ConfigurationFile = “.\FIM-GetGroups-Cfg.ps1”,
[string]$Uri = “http://localhost:5725”,
[string]$VerboseLogPath
)
set-psdebug -strict

#=============================================================================
#
FUNCTION LISTINGS
#*=============================================================================

function CreateOutput($MyText) {
If ($VerboseLogPath -eq “”) {
Write-Output ($MyText)
} else {
Write-Output ($MyText)
Out-File -filepath $VerboseLogPath -Encoding “ASCII” -Append -inputObject $MyText
}
} #CreateOutput

function Get-FIMObjectWithComparisonResults
{

param
( 

[parameter(Mandatory=$true)] 
[String]
$ObjectType,


[parameter(Mandatory=$false)] 
[String]
$XPathPredicate,


[parameter(Mandatory=$false)] 
$SourceObjectIdentifier = [Guid]::Empty,


[parameter(Mandatory=$false)] 
$TargetObjectIdentifier = [Guid]::Empty,


[parameter(Mandatory=$true)]
[ValidateScript({$_ -is [Hashtable]})]
$KeyValuePair,


[parameter(Mandatory=$false)] 
[ValidateScript({$_ -is [Microsoft.ResourceManagement.Automation.ObjectModel.ExportObject]})]
$CachedExportObject = $null,



[parameter(Mandatory=$false)] 
[Switch]
$MultiValued = $false,



[String]
$Uri = "http://localhost:5725"
        
) 
end
{
    $Results = @{}
    $Results.CompareResult = $false
    $Results.LogData = @()
    $Results.ExportObject = $null
    
    $importObject = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject
	
	$XPathFilter = "/" + $ObjectType + $XPathPredicate
    
    If ($CachedExportObject -eq $null) {
        $RetrievalType = "authoritative"
        $Results.ExportObject = Export-FIMConfig -Uri $Uri -OnlyBaseResources -CustomConfig $XPathFilter -ErrorAction SilentlyContinue
    } else {
        $RetrievalType = "cached"
        $Results.ExportObject = $CachedExportObject
    }
    
    if ($Results.ExportObject -eq $null) {
        Write-Error ("An object was not found with this criteria: $XPathFilter")
        $Results.LogData += ("An object was not found with this criteria: $XPathFilter")
    }
    else {
        $Results.LogData += ("Retrieved $RetrievalType object: $XPathFilter")

        $AttributeName = @($KeyValuePair.Keys)[0]
        
        $exportedKVP = $Results.ExportObject.ResourceManagementObject.ResourceManagementAttributes | Where-Object { $_.AttributeName -eq $AttributeName }
		
		If ($MultiValued) {
			If ($exportedKVP -eq $null) {
	            $Results.LogData += ("Current values of {0} is NULL" -f $AttributeName)
			} else {
	            $Results.LogData += ("Current values of {0} is {1}" -f $AttributeName, [string]::Join(",", $exportedKVP.Values))
			}
			If ($exportedKVP -ne $null){
				ForEach ($value in $exportedKVP.Values) {
					If ($value -eq $KeyValuePair[$AttributeName]) {
	                	$Results.LogData += ("This contains value {0}" -f $KeyValuePair[$AttributeName])
	                    $Results.CompareResult = $true
					} ElseIf ($value.StartsWith("urn:uuid:")) {
						If ($value.Substring(9) -eq $KeyValuePair[$AttributeName]) {
	                		$Results.LogData += ("This contains value {0}" -f $KeyValuePair[$AttributeName])
	                    	$Results.CompareResult = $true
						}
					}
				}
			}
			If (-not $Results.CompareResult) {
                $Results.LogData += ("This does not contain value {0}" -f $KeyValuePair[$AttributeName])
			}
		} else {
            If ($exportedKVP -eq $null) {
                $Results.LogData += ("Current value of {0} is [null]" -f $AttributeName)
				If ($KeyValuePair[$AttributeName] -eq $null) {
                    $Results.CompareResult = $true
				}
			} else {
                $Results.LogData += ("Current value of {0} is {1}" -f $AttributeName, $exportedKVP.Value)
                If ($exportedKVP.Value -eq ($KeyValuePair[$AttributeName] -Split "`r`n" -join "`n")) {
                    $Results.CompareResult = $true
                } ElseIf ($exportedKVP.Value.StartsWith("urn:uuid:")) {
	                If ($exportedKVP.Value.Substring(9) -eq $KeyValuePair[$AttributeName]) {
	                    $Results.CompareResult = $true
					}
				}
			}
		}
    }
    Write-Output $Results
}

} #Get-FIMObjectWithComparisonResults

function Get-FIMObjectIDUsingXPath($ObjectType, $XPathPredicate) {

$ObjectID = $null
CreateOutput ("In FIMObjectIDXpath " +$ObjectType + ","+$XPathPredicate)
#$XPathFilter = "/" + $ObjectType + $XPathPredicate
$XPathFilter = "/Group" + $XPathPredicate
  CreateOutput ("In FIMObjectIDXpath " +$XPathFilter)
$ExportResults = Export-FIMConfig -Uri $Uri -OnlyBaseResources -CustomConfig $XPathFilter -ErrorAction SilentlyContinue
if ($ExportResults -eq $null) {
    Write-Error ("An object was not found with this criteria: $XPathFilter")
}
else {
	$exportedKVP = $ExportResults.ResourceManagementObject.ResourceManagementAttributes | Where-Object { $_.AttributeName -eq 'ObjectID' }
	$ObjectID = $exportedKVP.Value
}

$ObjectID

} #Get-FIMObjectIDUsingXPath

function Update-FIMObjectIfNecessary($ObjectType, $XPathPredicate, $KeyValuePair) {
$ReachedDesiredState = $false
[string]$Key = $KeyValuePair.Keys
If ($KeyValuePair[$Key] -ne $null) {
[string]$Value = $KeyValuePair.Values
$KeyValuePair = @{$Key=$Value.Replace(“%FORESTROOTDN%”, $ForestRootDN).Replace(“%FORESTROOTNETBIOS%”, $ForestRootNetBIOS)}
}
Do {
CreateOutput (get-date -UFormat “%Y.%m.%d.%H.%M.%S”)
$ReturnValues = Get-FIMObjectWithComparisonResults -Uri $Uri -ObjectType $ObjectType -XPathPredicate $XPathPredicate -KeyValuePair $KeyValuePair
CreateOutput ($ReturnValues.LogData)
If ($ReturnValues.CompareResult) {
$ReachedDesiredState = $true
} else {
$ObjectID = $ReturnValues.ExportObject.ResourceManagementObject.ObjectIdentifier.Replace(‘urn:uuid:’,‘’)
CreateOutput (“Updating /$ObjectType$XPathPredicate”)
CreateOutput ($KeyValuePair)
#See if we’re updating to a value or null
$AttributeName = @($KeyValuePair.Keys)[0]
If ($KeyValuePair[$AttributeName] -eq $null) {
$Change = New-FimImportChange -Uri $Uri -Operation ‘Replace’ -AttributeName $AttributeName
} else {
$Change = New-FimImportChange -Uri $Uri -Operation ‘Replace’ -AttributeName $AttributeName -AttributeValue $KeyValuePair[$AttributeName]
}
$ImportObject = New-FimImportObject -Uri $Uri -ObjectType $ObjectType -State ‘Put’ -SourceObjectIdentifier $ObjectID -TargetObjectIdentifier $ObjectID -Changes $Change -PassThru
If ($pscmdlet.ShouldProcess(“/$ObjectType$XPathPredicate”)) {
$ImportObject | Import-FIMConfig -Uri $Uri
} else {
$ReachedDesiredState = $true
}
}
} While (-not $ReachedDesiredState)
} #Update-FIMObjectIfNecessary

function Update-FIMObjectMultiValueIfNecessary($ObjectType, $XPathPredicate, $ChangeType, $KeyValuePair) {
$ReachedDesiredState = $false
[string]$Key = $KeyValuePair.Keys
If ($KeyValuePair[$Key] -ne $null) {
[string]$Value = $KeyValuePair.Values
$KeyValuePair = @{$Key=$Value.Replace(“%FORESTROOTDN%”, $ForestRootDN).Replace(“%FORESTROOTNETBIOS%”, $ForestRootNetBIOS)}
}
Do {
CreateOutput (get-date -UFormat “%Y.%m.%d.%H.%M.%S”)
$ReturnValues = Get-FIMObjectWithComparisonResults -Uri $Uri -ObjectType $ObjectType -XPathPredicate $XPathPredicate -KeyValuePair $KeyValuePair -MultiValued $true
CreateOutput ($ReturnValues.LogData)

	If ($ChangeType -eq 'Add') {
		$DesiredComparisionResult = $true
	} else {
		$DesiredComparisionResult = $false
	}
	
	If ($ReturnValues.CompareResult -eq $DesiredComparisionResult) {
		$ReachedDesiredState = $true
	} else {
		$ObjectID = $ReturnValues.ExportObject.ResourceManagementObject.ObjectIdentifier.Replace('urn:uuid:','')
		CreateOutput ("Updating /$ObjectType$XPathPredicate $ChangeType")
		CreateOutput ($KeyValuePair)
		$AttributeName = @($KeyValuePair.Keys)[0]
		$Change = New-FimImportChange -Uri $Uri -Operation $ChangeType -AttributeName $AttributeName -AttributeValue $KeyValuePair[$AttributeName]
		$ImportObject = New-FimImportObject -Uri $Uri -ObjectType $ObjectType -State 'Put' -SourceObjectIdentifier $ObjectID -TargetObjectIdentifier $ObjectID -Changes $Change -PassThru
		If ($pscmdlet.ShouldProcess("/$ObjectType$XPathPredicate")) {
			$ImportObject | Import-FIMConfig -Uri $Uri
		} else {
			$ReachedDesiredState = $true
		}
	}
} While (-not $ReachedDesiredState)

} #Update-FIMObjectMultiValueIfNecessary

function Add-FIMObjectIfNecessary($ObjectType, $XPathPredicate, $CreationCommand) {
$ReachedDesiredState = $false
Do {
CreateOutput (get-date -UFormat “%Y.%m.%d.%H.%M.%S”)
$ReturnValues = Get-FIMObjectWithComparisonResults -Uri $Uri -ObjectType $ObjectType -XPathPredicate $XPathPredicate -KeyValuePair @{‘TestingForCreation’=‘IgnoreAttributeFailure’}
CreateOutput ($ReturnValues.LogData | Where-Object { $_ -ne ‘Current value of TestingForCreation is [null]’ })
If ($ReturnValues.ExportObject -ne $null) {
$ReachedDesiredState = $true
} else {
CreateOutput (“Creating /$ObjectType$XPathPredicate as $CreationCommand”)
If ($pscmdlet.ShouldProcess(“/$ObjectType$XPathPredicate”)) {
Invoke-Expression -Command $CreationCommand
} else {
$ReachedDesiredState = $true
}
}
} While (-not $ReachedDesiredState)
} #Add-FIMObjectIfNecessary

function Remove-FIMObjectIfNecessary($ObjectType, $XPathPredicate) {
$ReachedDesiredState = $false
Do {
CreateOutput (get-date -UFormat “%Y.%m.%d.%H.%M.%S”)
$ReturnValues = Get-FIMObjectWithComparisonResults -Uri $Uri -ObjectType $ObjectType -XPathPredicate $XPathPredicate -KeyValuePair @{‘TestingForCreation’=‘IgnoreAttributeFailure’}
CreateOutput ($ReturnValues.LogData | Where-Object { $_ -ne ‘Current value of TestingForCreation is [null]’ })
If ($ReturnValues.ExportObject -eq $null) {
$ReachedDesiredState = $true
} else {
CreateOutput (“Deleting /$ObjectType$XPathPredicate”)
$AttributeName = ($XPathPredicate.Split(“=”))[0].Replace(“[”, “”)
$AttributeValue = ($XPathPredicate.Split(“=”))[1].Replace(“]”, “”).Replace(“'”,“”)
If ($pscmdlet.ShouldProcess(“/$ObjectType$XPathPredicate”)) {
$deleteRequest = New-FimImportObject -Uri $Uri -ObjectType $ObjectType -State Delete -AnchorPairs @{$AttributeName=$AttributeValue} -PassThru
$deleteRequest | Import-FIMConfig
} else {
$ReachedDesiredState = $true
}
}
} While (-not $ReachedDesiredState)
} #Remove-FIMObjectIfNecessary

function Test-VariableDefinedAndNotNull ($MyVar) {
If (Test-Path variable:$MyVar) {
If ($MyVar -eq $null) {
$false
} else {
$true
}
} else {
$false
}
}

#=============================================================================
#
SCRIPT BODY
#*=============================================================================

$ScriptVersion = “Date - Name”
$ThisScript = Get-Item $MyInvocation.MyCommand.Definition

If ($VerboseLogPath.Contains(“?”)) {
get-help $MyInvocation.MyCommand.Definition -detailed
exit
}

#The add-pssnapin in FimPowerShellModule.psm1 breaks the FIMAutomation cmdlets.
#The snapin MUST be added BEFORE the module is imported
if(-not (get-pssnapin | Where-Object {$.Name -eq ‘FIMAutomation’})) {
add-pssnapin FIMAutomation
}
if((get-pssnapin | Where-Object {$
.Name -eq ‘FIMAutomation’})) {
}

$Imported = $false
If (-not $Imported) {
CreateOutput ("In Powrshell import module before " +$Imported)
Import-Module FimPowerShellModule -ErrorAction SilentlyContinue

If ((get-module | where-object { $_.Name -eq 'FimPowerShellModule' }) -ne $null) {
	$Imported=$true
}

}

If (-not $Imported) {
	Import-Module .\FimPowerShellModule.psm1 -ErrorAction SilentlyContinue
If ((get-module | where-object { $_.Name -eq 'FimPowerShellModule' }) -ne $null) {
	$Imported=$true
}

}

If (-not $Imported) {
	Import-Module FimPowerShellModule
	Import-Module .\FimPowerShellModule.psm1
	Write-Error "Module Import Failed"
	exit 1

}

#Establish our replacement variable values
#%FORESTROOTDN% = $ForestRootDN
#%FORESTROOTNETBIOS% = $ForestRootNetBIOS

$RootDSE = ADSI
$ForestRootDN = $RootDSE.rootDomainNamingContext
$SearchConfigForObject = New-Object DirectoryServices.DirectorySearcher
$SearchConfigForObject.SearchRoot = “LDAP://” + $RootDSE.configurationNamingContext
$SearchConfigForObject.filter = “(&(objectclass=crossref)(ncname=$ForestRootDN))”
$SearchConfigForObject.PropertiesToLoad.Add(“netbiosname”) | out-Null
$CrossRef = $SearchConfigForObject.FindOne()
If ($CrossRef -ne $null) {
$ForestRootNetBIOS = $CrossRef.Properties.netbiosname.item(0)
}

#Read in and execute external configuration

If (-not (Test-Path -Path $ConfigurationFile -PathType Leaf)) {
exit 1

}

Load configuration file - comment out on 4/2/15

#. $ConfigurationFile

$GroupName= ‘SampleGroupName’
$ObjectType = ‘Group’
$XPathPredicate = “”
$XPathPredicate = “[DisplayName=‘$GroupName’]”
#$XPathPredicate = “[DisplayName.Length -eq 8]”
#ak-$XPathPredicate = “[(Type = ‘Security’) and ((DisplayName.Length 8) or (AccountName.Length -le 8))]”
#ak-$XPathPredicate = "[(Type = ‘Security’) and ((string-length(DisplayName()) < 8) or (string-length(AccountName()) < 8))]"
#$XPathPredicate = "[(Type = 'Security') and ((string-length(DisplayName()) <= 8) or (string-length(AccountName()) <= 8))]"

$AllGroupObjects = Get-FIMObjectIDUsingXPath $ObjectType $XPathPredicate

foreach ($GroupObject in $AllGroupObjects)
{
If ($GroupObject.XPathFilter.Length &lt 8)
{
{$objItem = $GroupObject.Properties
"Name: " + $objItem.name
"Title: " + $objItem.jobTitle
"Description: " + $objItem.description
"URI: " + $objItem.Uri
If($objItem.name not eq $null)
{
If ($FileExists -eq $True) {Write-Output $objItem "True"}
Else {Write-Output $objItem "False"}
| export-csv C:\scripts\output.csv -Append
}
else {} #else for the objItem.name not being null
}
}
else {} #else for the container being more than 8 characters

#Get Owner, DisplayName and Description based on ObjectID

#If DisplayName is less than 8, If Owner is not null - iterate each Owner

#Print Owner, DisplayName, Url, Description to CSV file.

https://powershell.org/forums/topic/ad-export-group-members-but-with-a-twist/

This will allow to you put all seven group names in and run it as a scheduled task.

It'll plop the timestamped csv file out onto a share of your choice for whoever you want to share it with.

Note: $ADUser.Manager returns a super long distinguished name so I used some crude string manipulation tricks

to get just the alias so I didn't have to do another Get-ADUser query.

$Groups = 'Group1','Group2','Group3','Group4','Group5','Group6','Group7'
$FileName = "ADGroupMem_$(Get-Date -Format "yyyyMMddmmss")"
$SavePath = "c:\ps$FileName.csv"

Foreach ($Group in $Groups){

$ADGroup = Get-ADGroup -Identity $Group
$ADGroupMem = Get-ADGroupMember -Identity $ADGroup

ForEach ($Member in $ADGroupMem){

    $ADUser = $Member | Get-ADUser -Properties SamAccountName,Department,Title,Manager
    if ($ADUser.Manager){
         #$ManagerName = ($ADUser.Manager).Split(&quot;,&quot;) | Select-Object -Index 0 | ForEach-Object {$_ -replace &quot;CN=&quot;,&quot;&quot;}
		 $ManagerName = Get-ADUser -LDAPFilter &quot;(DistinguishedName=$($ADUser.Manager))&quot; -Properties DisplayName | Select-Object -Expand DisplayName
    } else {
        $ManagerName = &quot;None listed&quot;
    }
    [PSCustomObject]@{
            GroupName = $ADGroup.Name
            SAMAccountName = $ADUser.SamAccountName
            Department = $ADUser.Department
            Title = $ADUser.Title
            Manager = $ManagerName

    } | Export-Csv -Path $SavePath -Append -NoTypeInformation
}

}

}

#Boolean Expressions listed below https://msdn.microsoft.com/en-us/library/ms256081(v=vs.90).aspx
#< * Less than

* Greater than

#<= * Greater than or equal

#//*[string-length(DisplayName()) < 8]
#XPath Tutorial

#Get-FimObjectID -ObjectType 'Group' -Uri $Uri -AttributeName -AttributeName 'DisplayName' -AttributeValue 'SampleGroupName' -AttributeName 'Owner' -AttributeValue '

Sometimes it is just easier to dump ALL the objects then use a PowerShell Where-Object command to do the filtering.

For example:

### Get all the FIM Groups
$AllFimGroups = Export-FimConfig -Only -Custom "/Group" | Convert-FimExportToPSObject

### Get just the offenders
$OffensiveGroups = $AllFimGroups | Where-Object {$_.AccountName.Length -lt 8}

NOTE: Convert-FimExportToPSObject is from the FimPowerShellModule on CodePlex.