Monthly Archives: April 2016

Active Directory GUID to Friendly Name using just Powershell

Let’s drill down to the methods we can use within Powershell to retrieve Active Directory permissions and translate the GUID:

  1. How can we get the name of a property or (extended) right in the Active Directory that a specific user has on a specific OU (path)?
  2. How can we do this without having to install anything on our server, like Active Directory Remote Management Powershell Extensions?
  3. How can we do this without using .NET code and without features/extensions?

How can we get the name of a property or (extended) right in the Active Directory that a specific user has on a specific OU (path)?
The first question could be answered with the advise to use the QAD Powershell cmdlets, specifically the cmdlet Get-QADPermission. An alternative was to use Get-ACL on Active Directory objects, but this needs the ActiveDirectory Powershell Module (Add Feature: “Active Directory module for Windows PowerShell” at “Remote Server Administration Tools” > “Role Administration Tools” > “AD DS and AD LDS Tools”).

How can we do this without having to install anything on our server, like Active Directory Remote Management Powershell Extensions?
The second question lead me the way to this book: The .NET Developer’s Guide to Directory Services Programming, through this link http://flylib.com/books/en/1.434.1.83/1/. The code is found at “Listing 8.4. GUID-to-Friendly-Name Conversion” and the BuildFilterOctetString from “Listing 4.2. Converting Binary to String for Search Filters”.
We could include .NET code in our Powershell script/function/module, this requires to add the .NET code from the book using the here-string method and use the functions in that .NET code in our Powershell script.

I implemented this using the code from the book and a reference to the DirectoryServices assembly:

The key functions are:

How can we do this without using .NET code and without features/extensions?
This meant I had to find or build the right Powershell code to do the same as the .NET code above. It seemed difficult but the key was to follow the .NET code and replicate it in Powershell.

The basics are:

 

 
Our context for which we needed this:

In our customer environment we are installing SharePoint which relies heavily on several OS configurations and permissions. We have built an entire Powershell script for the automated installation and configuration of the whole A to Z process including OS firewall and updating AppFabric. The installation script needs to be able to run smoothly and the end result needs to be the same for every environment (all-in-one development image on laptop, 3 server set-up, 8 server set-up, etc.). We do not want to install anything (OS Roles/Features, third party tools) on our servers that is not used by our application (SharePoint/SQL/OfficeWebApps/ADFS/etc).
Since the administration of OS, AD, policies, rights, etc is done by others (except for our laptop) and mistakes happen, we script checks to test all conditions, permissions, settings and versions.
One of these checks is to test the required rights in the Active Directory. We have a password reset and password change webpart, which require specific Active Directory permissions, like “User-Force-Change-Password”.

We can acquire an access rule collection for the LDAP Path to the User Accounts OU in this way:
(New-Object System.DirectoryServices.DirectoryEntry(“LDAP://$UserPath”)).PSBase.ObjectSecurity.Access

This will return an array of ActiveDirectoryAccessRule objects, like this:
ActiveDirectoryRights : CreateChild, Self, WriteProperty, ExtendedRight, Delete, GenericRead, WriteDacl, WriteOwner
InheritanceType : All
ObjectType : 00000000-0000-0000-0000-000000000000
InheritedObjectType : 00000000-0000-0000-0000-000000000000
ObjectFlags : None
AccessControlType : Allow
IdentityReference : BUILTIN\Administrators
IsInherited : True
InheritanceFlags : ContainerInherit
PropagationFlags : None

Above corresponds to “All” objects and properties. In case of specific permissions, the ObjectType and InheritedObjectType values are GUID’s for the property, object or special permisson.

In our case, we could expect something like this:
ActiveDirectoryRights : ExtendedRight
InheritanceType : Descendents
ObjectType : 00299570-246d-11d0-a768-00aa006e0529
InheritedObjectType : bf967aba-0de6-11d0-a285-00aa003049e2
ObjectFlags : ObjectAceTypePresent, InheritedObjectAceTypePresent
AccessControlType : Allow
IdentityReference : DOMAIN\AccountManagement
IsInherited : False
InheritanceFlags : ContainerInherit
PropagationFlags : InheritOnly

Somehow we want to translate these GUID’s to something useful and understandable.

The functions we created with our Powershell code to find and translate GUID’s to rights:

Using a directory array for a choice menu inside a function

Usage of [System.Management.Automation.Host.ChoiceDescription[]] and $host.ui.PromptForChoice() with an array of data for choices.
Default behavior when using functions is that the return value is every output, so when you want to return a value, using Write-Host inside the function will mess the return value up.
The scenario is I wanted the user to provide a name, which should match a directory with configuration, when the name is not provided I want to provide all directories as options.

To provide a simple and familiar choice menu, I use the PromptForChoice function. A choice is defined in this way: New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Do not carry out this action". All choices must be declared with an array inside [System.Management.Automation.Host.ChoiceDescription[]].

First I determine my script directory in this way:
$InstallerLiteralPath = ([System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition))
This works both for local and network UNC paths.

Next I determine the subdirectories, we need this as an array because we need the count attribute, so enclose it with @(…):
$ApplicationDirs = @(Get-ChildItem "$InstallerLiteralPath\configuration" | ?{ $_.PSIsContainer } | Select-Object Name)

Considering I want the choices to be subdirectories of a predefined directory, this proved to be a little bit tougher.
The PromptForChoice function is build up of a Title, Description, the choices, and the default choice.
We need a Cancel option also, which we want to be the default choice, so the choice array needs a cancel option and the default choice needs to be the number of directories, because the default choice count starts at zero and the cancel option is an extra choice.

Set up the Title and Description:
$caption = "Application Selection"
$message = "$($message)The following application configuration directories were found.rnWhich application would you like to configure/install?"

Set up the choices with the directory name as choice option for each directory plus the Cancel option:
$choices = [System.Management.Automation.Host.ChoiceDescription[]]@(($ApplicationDirs | %{$_.Name});"&Cancel")

Set up the default choice:
[int]$defaultChoice = $ApplicationDirs.Count

Display the Choice Menu with the variables defined above, $choiceRTN is the returned choice number the user has entered:
$choiceRTN = $host.ui.PromptForChoice($caption,$message, $choices,$defaultChoice)

Exit with a throw when the default choice (cancel) is chosen.
If ($choiceRTN -eq $defaultChoice){
Throw "[$($myInvocation.MyCommand)] : Application selection cancelled by user."
}

The directoryname of the chosen folder can be determined by the choice number in the array of directories:
$SelectedApplicationFolder = $ApplicationDirs[$choiceRTN].Name

The whole function would look like this: