I can’t believe I’ve been paying for this server for over three… wait four years and I haven’t posted anything!? That’s like clearly some number of dollars I am not willing to give a #%&^ about!

I’ve got a new job now. It’s not in consulting, but instead is at a large corporation. I’ve really enjoyed it so far, and apparently my skills as a barista here give me bonus points. I’ve also had to develop some really interesting and new scripts to accomplish all sorts of crazy things! CRAZY THINGS! It’s almost as if I haven’t grown at all in the five years since the last time I posted.

So today, I am going to cover some interesting Active Directory scripting I’ve done. We needed to have a script (I mean module) that could assign custom permissions to a given Organizational Unit in our crazy big business environment. I mean, you could hit the “Delegate controls” button in ADUC, but come now, where the hell is the fun in that!? So, being the reasonable sort of person I am, I spent 20 hours scripting something that might have taken me about four hours to just complete through the GUI. On the plus side, the three people who read this might benefit from my incredibly terrible use of time to be able to benefit from it. (All right, to be fair, this actually was a time saving thing, because I would have had to apply permissions around 1400 times, and so even if that took me a solid 40, it would have been quicker than anything else.)

Wow this is tangential… (I couldn’t spell that last word on my first try… THANK GOD FOR SPELL CHECK!) Anyway, to the Crayon Corner!

$ADGuidReferences = @{
    'bf967a86-0de6-11d0-a285-00aa003049e2' = "Computer"
    'bf967aba-0de6-11d0-a285-00aa003049e2' = "User"
    'bf967a9c-0de6-11d0-a285-00aa003049e2' = "Groups"
    '00299570-246d-11d0-a768-00aa006e0529' = "ResetPassword"
    'ab721a53-1e2f-11d0-9819-00aa0040529b' = "ChangePassword"
    '00000000-0000-0000-0000-000000000000' = "GenericInheritance"
    'bf967a0a-0de6-11d0-a285-00aa003049e2' = "PasswordLastSet"
    '4c164200-20c0-11d0-a768-00aa006e0529' = "UserAccountRestrictions"
}

Function Get-ADSGroupOUPermissions {

      <#
    .SYNOPSIS
    Gets privileged group membership and checks to see if they are changed. If they are 
    sends an email stating the change has occurred. 
            
    .DESCRIPTION
    Description to be added

    .PARAMETER Identity
    Use the SAMAccountName of the Object you'd like to query.

    .PARAMETER Domain
    This string allows you to specify a domain to be run against.

    .PARAMETER IncludeInheritedResults
    This boolean will grab every OU, including sub OUs that have inheritance allowed bringing down the permission.

    .PARAMETER Group
    This switch allows you to only look up a single group.

    .PARAMETER User


    .EXAMPLE

    Get-ADSGroupOUPermissions -Identity "some emea Desktop Admins" -Domain coffee.net -Group

    Get-ADSGroupOUPermissions -Identity nholmes -Domain coffee.net -User

    .NOTES
    Author: Nicholas Holmes
    Date: 05/16/2019
    Version: 0.5


    #>
    [Cmdletbinding()]
    Param(
        [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=0)]
        [string]$Identity,
        [Parameter(Mandatory=$false)]
        [string]$Domain,
        [bool]$includeInheritedResults = $false,
        [switch]$Group,
        [switch]$User
    )
    #Currently known AD References
        
    $filter = "((objectClass=organizationalUnit))"
    $ldapDomain = Get-ADDomain $Domain
    $ldapSearch = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$($ldapDomain.DNSRoot)/$($ldapDomain.DistinguishedName)")
    $netBIOSName = $ldapDomain.NetBIOSName
        
    $OUSearch = New-Object System.DirectoryServices.DirectorySearcher($ldapSearch)
    $OUSearch.SearchRoot = $ldapSearch
    $OUSearch.PageSize = 1000
    $OUSearch.Filter = $filter
    $OUSearch.SearchScope = "Subtree"

    if($Group) {
        $output = New-Object System.Collections.Generic.List[PSObject]
        foreach($ou in $OUSearch.FindAll().GetDirectoryEntry()) {
            $samAccountName = (Get-ADGroup $Identity).samAccountName
            $fullIdentity = "$netBIOSName\" + $samAccountName
            $perms = getperms -Identity $fullIdentity -includeInheritedResults $includeInheritedResults -ou $ou
            $output.Add($perms) 
        }
        $domainPerms = Get-Acl "AD:$($ldapDomain.DistinguishedName)"
        $domainResults = getpermsdomain -Identity $fullIdentity -ADDomain $domainPerms
        foreach($b in $domainResults) {
            $output.Add($b)
        }
        return $results
    }

    if($User) {
        $output = New-Object System.Collections.Generic.List[PSObject]
        $adGroups = (Get-ADUser $Identity -Properties MemberOf).MemberOf
        for($i = 0; $i -lt $adGroups.Count; $i++) {
            $samAccountName = (Get-ADGroup $adGroups[$i]).samAccountName
            $fullIdentity = "$netBIOSName\" + $samAccountName
            foreach($ou in $OUSearch.FindAll().GetDirectoryEntry()) {
                $results = getperms -Identity $fullIdentity -includeInheritedResults $includeInheritedResults -ou $ou
                foreach($j in $results) {
                    if($output.DistinguishedName -notcontains $j.DistinguishedName) {
                        $output.Add($j)
                    }
                }
            }
            $domainPerms = Get-Acl "AD:$($ldapDomain.DistinguishedName)"
            $domainResults = getpermsdomain -Identity $fullIdentity -ADDomain $domainPerms
            foreach($b in $domainResults) {
                $output.Add($b)
            }
        }
    return $output
    }
}

Function getperms {
    [Cmdletbinding()]
    Param(
        [string]$Identity,
        [bool]$includeInheritedResults,
        [System.DirectoryServices.DirectoryEntry]$ou
    )
    $permissions = $ou.PsBase.ObjectSecurity.GetAccessRules($true,$includeInheritedResults,[Security.Principal.NTAccount])
    $results = New-Object System.Collections.Generic.List[PSObject]
    $groupPermissions = $permissions | Where-Object {$_.IdentityReference -like $Identity} 
    foreach($perm in $groupPermissions) {
        $row = New-Object PSObject
        Add-Member -InputObject $row -MemberType NoteProperty -Name DistinguishedName -Value $ou.DistinguishedName[0]
        Add-Member -InputObject $row -MemberType NoteProperty -Name Group -Value $perm.IdentityReference
        Add-Member -InputObject $row -MemberType NoteProperty -Name ActiveDirectoryRights -Value $perm.ActiveDirectoryRights
        Add-Member -InputObject $row -MemberType NoteProperty -Name AccessControlType -Value $perm.AccessControlType
        Add-Member -InputObject $row -MemberType NoteProperty -Name IsInherited -Value $perm.IsInherited
        Add-Member -InputObject $row -MemberType NoteProperty -Name InheritanceType -Value $perm.InheritanceType
        Add-Member -InputObject $row -MemberType NoteProperty -Name ObjectType -Value $perm.ObjectType
        $objectTypeGuid = $perm.ObjectType.Guid
        if($ADGuidReferences.$objectTypeGuid -and $ADGuidReferences.$objectTypeGuid -ne "GenericInheritance") {
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyObjectName" -Value $ADGuidReferences.$objectTypeGuid
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyInheritedObjectName" -Value "NULL"
        }
        elseif($ADGuidReferences.$objectTypeGuid -eq "GenericInheritance") {
            $objectTypeGuid = $perm.InheritedObjectType.Guid
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyObjectName" -Value "NULL"
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyInheritedObjectName" -Value $ADGuidReferences.$objectTypeGuid
        }
        else {
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyObjectName" -Value "Unknown"
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyInheritedObjectName" -Value "Unknown"
            Write-Host "$($perm.ObjectType) is an unknown GUID, please review and see if you can identify it."
        }
        $results.Add($row)
    }
    return $results
}

Function getpermsdomain {
    [Cmdletbinding()]
    Param(
        [string]$Identity,
        [System.DirectoryServices.ActiveDirectorySecurity]$ADDomain
    )
    $permissions = $ADDomain | Select-Object -ExpandProperty access
    $results = New-Object System.Collections.Generic.List[PSObject]
    $groupPermissions = $permissions | Where-Object {$_.IdentityReference -like $Identity} 
    foreach($perm in $groupPermissions) {
        $row = New-Object PSObject
        Add-Member -InputObject $row -MemberType NoteProperty -Name DistinguishedName -Value $ADDomain.PSChildName
        Add-Member -InputObject $row -MemberType NoteProperty -Name Group -Value $perm.IdentityReference
        Add-Member -InputObject $row -MemberType NoteProperty -Name ActiveDirectoryRights -Value $perm.ActiveDirectoryRights
        Add-Member -InputObject $row -MemberType NoteProperty -Name AccessControlType -Value $perm.AccessControlType
        Add-Member -InputObject $row -MemberType NoteProperty -Name IsInherited -Value $perm.IsInherited
        Add-Member -InputObject $row -MemberType NoteProperty -Name InheritanceType -Value $perm.InheritanceType
        Add-Member -InputObject $row -MemberType NoteProperty -Name ObjectType -Value $perm.ObjectType
        $objectTypeGuid = $perm.ObjectType.Guid
        if($ADGuidReferences.$objectTypeGuid -and $ADGuidReferences.$objectTypeGuid -ne "GenericInheritance") {
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyObjectName" -Value $ADGuidReferences.$objectTypeGuid
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyInheritedObjectName" -Value "NULL"
        }
        elseif($ADGuidReferences.$objectTypeGuid -eq "GenericInheritance") {
            $objectTypeGuid = $perm.InheritedObjectType.Guid
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyObjectName" -Value "NULL"
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyInheritedObjectName" -Value $ADGuidReferences.$objectTypeGuid
        }
        else {
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyObjectName" -Value "Unknown"
            Add-Member -InputObject $row -MemberType NoteProperty -Name "FriendlyInheritedObjectName" -Value "Unknown"
            Write-Host "$($perm.ObjectType) is an unknown GUID, please review and see if you can identify it."
        }
        $results.Add($row)
    }
    return $results
}

Export-ModuleMember -Function Get-ADSGroupOUPermissions
Expand

This first one is a little script I wrote to allow us to check an individual account or a group to see their permissions. The latter half is kind of hackjobby because I realized that I wasn’t checking the actual domain level, but it still works. The help file doesn’t have any specific information that is helpful to you dear scripter, but you can change coffee.net to whatever you’d like.

The idea behind this script is to be able to look through all of Active Directory (Here forth referred to as AD, because that’s a lot to type when it’s past midnight), and find any permissions that are applied. At the top, you can see some fancy GUIDs, those can be updated to include more GUIDs if you discover them. I’m kind of lazy so the ones you get are the ones you get. (YAY!)

Now onto the real meat and broccoli! I mean potatoes, that’s what people like right? They don’t well serve broccoli at fast food do they? (And not because they are slow to cook, those things steam faster than a pig can say “Shank me.”) (Uh… I’m sorry for all the vegetarians out there… I actually don’t particularly like to eat pig, they are surprisingly intelligent, and seem rather friendly, at least the domesticated and hollywood types. +1 for anyone who remembers the line, “That’ll do pig… That’ll do.)

TANGENTIAL!!! Back to… … … the CRAYON CORNER! (It occurs to me that if someone is first reading this block for the first time, they may not understand what the Crayon Corner is. It’s a reference to the plugin I use for displaying my code, called Crayon. And the corner reference is… Well if you don’t get the subtleties of that… There really is no hope for you.)

Function Set-BlargPrivilegedGroups {
    <#
    .SYNOPSIS
    Sets the Blarg PrivilegedGroups 
            
    .DESCRIPTION
    Description to be added

    .PARAMETER Group
    This switch allows you to only look up a single group

    .EXAMPLE

    .NOTES
    Author: Nicholas Holmes
    Date: 05/20/2019
    Version: 0.1

    #>
    [Cmdletbinding()]
    Param(
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
        [string]$Group,
        [Parameter(Mandatory=$true,Position=1)]
        [ValidateLength(5,5)]
        [string]$Blarg,
        [Parameter(Mandatory=$true)]
        [ValidateSet("AccessSystemSecurity","CreateChild","Delete","DeleteChild","DeleteTrue",`
        "ExtendedRight","GenericAll","GenericExecute","GenericRead","GenericWrite","ListChildren",`
        "ListObject","ReadControl","ReadProperty","Self","WriteDacl","WriteOwner","WriteProperty")]
        [string[]]$Rights,
        [Parameter(Mandatory=$true)]
        [ValidateSet("All","Children","Descendents","None","SelfAndChildren")]
        [string]$InheritanceType,
        [Parameter(Mandatory=$false)]
        [switch]$Deny,
        [Parameter(Mandatory=$false)]
        [switch]$Database,
        [Parameter(Mandatory=$false)]
        [switch]$InheritedObjectType
    )

    #Building Dynamic parameters.
    DynamicParam {

        #Getting information for LDAP lookups
        $rootDSE = Get-ADRootDSE
        $parameterName = 'ObjectType'

        $runtimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

        $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

        $parameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $parameterAttribute.Mandatory = $true
        $parameterAttribute.Position = 3

        $attributeCollection.Add($parameterAttribute)
        #Creating a map between the GUID and the object types
        $guidmap = @{}
        Get-ADObject -SearchBase ($rootDSE.schemaNamingContext) -LDAPFilter "(schemaidguid=*)" -Properties lDAPDIsplayName,schemaIDGUID | ForEach-Object {$guidmap[$_.lDAPDIsplayName]=[System.Guid]$_.schemaIDGUID}
        $guidmap['null'] = [System.Guid]'00000000-0000-0000-0000-000000000000'
        
        $dynamicParamValue = $guidmap.Keys
        $validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($dynamicParamValue)

        $attributeCollection.Add($validateSetAttribute)

        $runtimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($parameterName, [string], $attributeCollection)
        $runtimeParameterDictionary.Add($parameterName, $runtimeParameter)


        $parameterName = 'ExtendedRight'
        $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

        $parameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $parameterAttribute.Mandatory = $false

        $attributeCollection.Add($parameterAttribute)
        
        #Creating a map between the GUID of the extended rights, and their names
        $extendedRightsMap = @{}
        Get-ADObject -SearchBase ($rootDSE.configurationNamingContext) -LDAPFilter "(&(objectclass=controlAccessRight)(rightsguid=*))" -Properties displayName,rightsGuid | ForEach-Object {$extendedRightsMap[$_.displayName]=[System.GUID]$_.rightsGuid}
        $extendedRightsMap['null'] = [System.Guid]'00000000-0000-0000-0000-000000000000'

        $dynamicParamValue = $extendedRightsMap.Keys
        $validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($dynamicParamValue)

        $attributeCollection.Add($validateSetAttribute)

        $runtimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($parameterName, [string], $attributeCollection)
        $runtimeParameterDictionary.Add($parameterName, $runtimeParameter)

        $runtimeParameterDictionary

    }
    Begin {
        if($Deny) {
            $allowDeny = "Deny"
        }
        else {
            $allowDeny = "Allow"
        }
        #Generating domain specific variables.
        $domain = Get-ADDomain

        #Getting specific information regarding this call of the cmdlet
        if($Database) {
            $organizationalUnit = Get-ADOrganizationalUnit -Identity ("OU=$Blarg DB,OU=" + $FGID + ",OU=MyPants," + $domain.DistinguishedName)
        }
        else {
            $organizationalUnit = Get-ADOrganizationalUnit -Identity ("OU=" + $Blarg + ",OU=MyPants," + $domain.DistinguishedName)
        }
        $groupSID = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup $Group).SID
        $acl = Get-ACL -Path "AD:$($organizationalUnit.DistinguishedName)"
        if(!$InheritedObjectType) {
            if(!$ExtendedRight) {
                $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $groupSID,$Rights,$allowDeny,$InheritanceType,$guidmap[$ObjectType]))
            }
            elseif($Rights -contains "ExtendedRights") {
                $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $groupSID,$Rights,$allowDeny,$extendedRightsMap[$ExtendedRight],$InheritanceType,$guidmap[$ObjectType]))
            }
        }
        elseif($InheritedObjectType) {
            if(!$ExtendedRight) {
                $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $groupSID,$Rights,$allowDeny,"00000000-0000-0000-0000-000000000000",$InheritanceType,$guidmap[$ObjectType]))
            }
            elseif($Rights -contains "ExtendedRights") {
                $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $groupSID,$Rights,$allowDeny,"00000000-0000-0000-0000-000000000000",$extendedRightsMap[$ExtendedRight],$InheritanceType,$guidmap[$ObjectType]))
            }
        }
    }
    Process {
        Set-ACL -ACLObject $acl -Path ("AD:" + ($organizationalUnit.DistinguishedName))
    }
}
Expand

Now there is some really interesting things going on in this script. The first is the DynamicParam field. This allows us to generate autocomplete parameters by making a query. As you can see, we are creating the first Parameter, ‘ObjectType’ by performing a query from AD. (Oh, FYI, this script won’t work on 3.0 PowerShell or earlier, nor will it work without the RSAT tools installed… I should put a check in for that… #TOADD!!) After it performs the query, you’ll see on line 71, we are actually adding it to the $runtimeParameterDictionary. This allows us to have this as an additional variable, that has hundreds of auto completes that are generated, so we don’t have to have them all in our ValidateSet specification.

The next thing you’ll notice is that there is the variable $Blarg. That’s just some basic cleansing of the module so that there isn’t anything easy to transcribe to the place I work for. I would heavily suggest you update lines 110 and 113. They aren’t going to make sense for this. Honestly, I should probably update the whole script to just allow an individual to specify a full DN, but man I am lazy. (Have I mentioned that yet!? Why are you reading this blog this far given all the lazy that I am talking about… Oh yeah… #TOADD!)

Once you have this set up though, you can use it to be able to query your AD, and then apply permissions exactly how you want.

Hell, in another few days I may update this post to just allow you to specify the OU so that it can be used against a CSV. But for now, I wanted to post something again. I wanted to see if I still have what it takes to make it in this crazy blog world… Do you think I am a bad enough dude to save the president from Ninjas!?

Leave a Reply

Your email address will not be published. Required fields are marked *