FolderPermissionGroups.psm1

<#
.SYNOPSIS
TODO
 
.EXAMPLE
TODO#>



function Add-FolderPermissions {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
        [Parameter(Mandatory=$true,Position=0,HelpMessage="Permission (RW,RO,LO, or a FileSystemRights Enumeration value, see http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights.aspx)")] 
            [String]$Permission,
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=1,HelpMessage="Path to set permission on.")] 
            [String]$Path,
        [Parameter(Mandatory=$true,Position=2,HelpMessage="Name of user or group to assign permission to.")] 
            [String]$AssignedTo,
        [Parameter(Mandatory=$false,Position=3,HelpMessage="Determine how this rule is inherited by child objects. Values are None, ContainerInherit, ObjectInherit or some combination of these values in a comma seperated string. Default is ContainerInherit, ObjectInherit. See http://msdn.microsoft.com/en-us/magazine/cc163885.aspx#S3")] 
            [String]$InheritanceFlags="ContainerInherit, ObjectInherit",
        [Parameter(Mandatory=$false,Position=4,HelpMessage="Determine how inheritance of this rule is propagated to child objects. Values are None, NoPropagateInherit and InheritOnly or some combination of these values in a comma seperated string. Default is None. See http://msdn.microsoft.com/en-us/magazine/cc163885.aspx#S3")] 
            [String]$PropagationFlags="None",
        [Parameter(Mandatory=$false,Position=5,HelpMessage="Whether to Allow or Deny the permission, defaults to Allow")] [ValidateSet("Allow","Deny")] 
            [String]$Grant="Allow",
        [Parameter(Mandatory=$false,Position=6,HelpMessage="An existing ACL object to modify")]
            $ACLObject
    )
    Begin {
        switch ($Permission) {
                "RW" {  #Modify shorthand
                    $_FileSystemRights = [System.Security.AccessControl.FileSystemRights] "Modify"
                    break
                }
                "RO" {  #ReadAndExecute shorthand
                    $_FileSystemRights = [System.Security.AccessControl.FileSystemRights] "ReadAndExecute"
                    break
                }
                "LO" {  #This is used for folders only and makes only the folder appear to the user
                    $_FileSystemRights = [System.Security.AccessControl.FileSystemRights] "Read"
                    $InheritanceFlags = "None"
                    $PropagationFlags = "None"
                    break
                }
                default {
                    $_FileSystemRights = $Permission
                    break
                }
            }
            Write-Verbose "Checking for shorthand permissions, setting rights to $_FileSystemRights"
    }
    Process {
        Write-Verbose "Retrieving SID for AssignedTo $AssignedTo"
        try {
            [Microsoft.ActiveDirectory.Management.ADObject] $_object = Get-ADGroup -Identity $AssignedTo -ErrorAction SilentlyContinue
        } catch {
            [Microsoft.ActiveDirectory.Management.ADObject] $_object = Get-ADUser -Identity $AssignedTo -ErrorAction Stop
        }

        Write-Verbose "Testing path $Path"
        if ((Test-Path $Path) -eq $false) {
            throw "Folder $Path not found"
        }

        Write-Verbose "Changing ACL"
        if ($_object) {
            Write-Verbose "Retrieving ACL for $Path"
            #[System.Security.AccessControl] $acl = Get-Acl -Path $Path
            if (!$ACLObject) {
                $acl = Get-Acl -Path $Path
            } else {
                $acl = $ACLObject
            }
            #$acl.SetAccessRuleProtection($True, $False)
            Write-Verbose "Adding new rule to ACL"
            [System.Security.AccessControl.FileSystemAccessRule] $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($_object.SID ,$_FileSystemRights, $InheritanceFlags, $PropagationFlags, $Grant)
            $acl.AddAccessRule($rule)
            Write-Verbose "Setting ACL for $Path to modified ACL"
            Set-Acl $Path $acl
            
            return $acl
        } else {
            return $null
        }
    }
}

function Get-FolderPermissionsGroupName {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
        [Parameter(Mandatory=$true,Position=0,HelpMessage="Permission (RW,RO,LO, or a FileSystemRights Enumeration value, see http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights.aspx)")] 
            [String]$Permission,
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=1,HelpMessage="Path to set permission on.")] 
            [String]$Path,
        [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=0,HelpMessage="A hash table of path prefixs to be replaced with a friendly names in the group name. Often used to replace a share UNC path with a friendly name. Example, \\server\share = Users")] 
            [System.Collections.Hashtable]$PathCommonNames
    )
    Begin {
        switch ($Permission) {
                "RW" {  #Modify shorthand
                    $_PermissionAbreviation = "RW"
                    break
                }
                "RO" {  #ReadAndExecute shorthand
                    $_PermissionAbreviation = "RO"
                    break
                }
                "LO" {  #This is used for folders only and makes only the folder appear to the user
                    $_PermissionAbreviation = "LO"
                    break
                }
                default {
                    $_PermissionAbreviation = "SP"  # for special
                    break
                }
        }
        Write-Verbose "Checking for shorthand permissions, setting rights to $_PermissionAbreviation"
    }
    Process {
        $_permissiongroup = $Path
        #Calculate group name
        foreach ($_commonPath in $PathCommonNames.keys) {
            if ($Path.StartsWith($_commonPath) -eq $true) {
                $_permissiongroup = $PathCommonNames.$_commonPath + "-" + $_permissiongroup.Replace($_commonPath, "")
            }
        }
        $_permissiongroup = $_permissiongroup.Replace("\\", "\").Replace("\", "-").Replace(" ", "_").Replace(":", "-")
        $_permissiongroup = "FLDR-" + $_permissiongroup + "-" + $_PermissionAbreviation
        return $_permissiongroup
        #need to handle paths greater then X length
    }
}


<#
 
OUTPUTS
None or Microsoft.ActiveDirectory.Management.ADGroup
         
        Returns the new group object
#>


function New-FolderPermissionsGroup {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
        [Parameter(Mandatory=$true,Position=0,HelpMessage="Permission (RW,RO,LO, or a FileSystemRights Enumeration value, see http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights.aspx)")] 
            [String]$Permission,
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=1,HelpMessage="Path to set permission on.")] 
            [String]$Path,
           [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=0,HelpMessage="OU (complete path) to place newly created Folder Permissions Groups in.")] 
            [String]$OU,
        [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=0,HelpMessage="A hash table of path prefixs to be replaced with a friendly names in the group name. Often used to replace a share UNC path with a friendly name. Example, \\server\share = Users")] 
            [System.Collections.Hashtable]$PathCommonNames
    )
    Process {
        #Calculate group name
        $_groupName = Get-FolderPermissionsGroupName -Permission $Permission -Path $Path -PathCommonNames $PathCommonNames
        #See if group already exists
        try {
            $_group = Get-ADGroup -Identity $_groupName
        } catch {
            #Create group
            [Microsoft.ActiveDirectory.Management.ADGroup] $_group = New-ADGroup -DisplayName $_groupName -SAMAccountName $_groupName -Path $OU -Name $_groupName -GroupCategory Security -Description $Path -GroupScope Global -PassThru
        }

        return $_group
    }
}

function Add-FolderPermissionsGroup {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
        [Parameter(Mandatory=$true,Position=0,HelpMessage="Permission (RW,RO,LO, or a FileSystemRights Enumeration value, see http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights.aspx)")] 
            [String]$Permission,
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=1,HelpMessage="Path to set permission on.")] 
            [String]$Path,
           [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=0,HelpMessage="OU (complete path) to place newly created Folder Permissions Groups in.")] 
            [String]$OU,
        [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=0,HelpMessage="A hash table of path prefixs to be replaced with a friendly names in the group name. Often used to replace a share UNC path with a friendly name. Example, \\server\share = Users")] 
            [System.Collections.Hashtable]$PathCommonNames
    )
    Process {
        #TODO - Test to see if it already exists
        #Create group
        [Microsoft.ActiveDirectory.Management.ADGroup] $_group = New-FolderPermissionsGroup -Permission $Permission -Path $Path -OU $OU -PathCommonNames $PathCommonNames
        #Set folder permissions using group
        Add-FolderPermissions -Permission $Permission -Path $Path -AssignedTo $_group.SAMAccountName
        return $_group
    }
}

function Get-FolderPermissionsGroupOrphans {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param()
    Process {
        #Get array of Folder Permission Groups
        $_groups = Get-ADGroup -Filter "Name -like 'FLDR-*'" -Properties Description
        foreach ($_group in $_groups) {
            #There are two tests to determine if the group should be removed, if either pass then the group can be deleted
            $_delete = $false
            #Test one - Does the path still exist
            if ($_group.Description) {
                if (!(Test-Path $_group.Description)) {
                    $_delete = $true
                }
            }

            #Test two - Are there any members
            if (($_group | Get-ADGroupMember | Measure-Object).Count -eq 0) {
                $_delete = $true
            }

            if ($_delete) {
                $_group
            }
        }
    }
}

function Update-FolderPermissionGroups {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
        [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=1,HelpMessage="Path to set permission on.")] 
            [String]$Path,
           [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=2,HelpMessage="OU (complete path) to place newly created Folder Permissions Groups in.")] 
            [String]$OU,
        [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=3,HelpMessage="A hash table of path prefixs to be replaced with a friendly names in the group name. Often used to replace a share UNC path with a friendly name. Example, \\server\share = Users")] 
            [System.Collections.Hashtable]$PathCommonNames,
        [Parameter(Mandatory=$false,ValueFromPipeline=$false,Position=4,HelpMessage="Recursively process sub-folders?")]
            [Switch] $Recurse,
        [Parameter(Mandatory=$false,ValueFromPipeline=$false,Position=5,HelpMessage="Leave the existing rights acl rule in place, otherwise it removes them.")]
            [Switch] $NoRemove
    )
    Process {

        [String[]] $results = @()

        [Boolean] $MadeChange = $false

        # check folder permissions
        $acl = Get-Acl -Path $Path

        # If permissions exist
        $AccessToMove = $acl.Access | where -Property IsInherited -eq -Value $false | where -Property IdentityReference -NE -Value "CREATOR OWNER" | where -Property IdentityReference -NE -Value "BUILTIN\Administrators" | where -Property IdentityReference -NE -Value "NT AUTHORITY\SYSTEM" | where -Property IdentityReference -NotLike "WHATCOMTRANS\FLDR*"
        $AccessFLDR = $acl.Access | where -Property IsInherited -eq -Value $false | where -Property IdentityReference -Like "WHATCOMTRANS\FLDR*"
        if ($AccessFLDR.Count -gt 0) {
            [String []] $ExistingADGroups = ($AccessFLDR.IdentityReference.ToString().Split("\")[1])
        } else {
            [String []] $ExistingADGroups = @()
        }

        # Get permission type
        ForEach ($Access in $AccessToMove) {
            $Rights = ($Access.FileSystemRights.ToString()).split(",").Trim() | where -FilterScript {$_ -ne "Synchronize"}
            $NeededADGroupName = FolderPermissionsGroupName -Permission (Get-RightsShortName -Right $Rights) -Path $Path -PathCommonNames $PathCommonNames
            if ($access.IdentityReference.Value -eq "BUILTIN\Users") {
                $member = Get-ADGroup "All-Users"    #This is a WTA hack
            } else {
                $LDAPFilter = "(samaccountname=$($Access.IdentityReference.Value.Split("\")[1]))"
                $member = (Get-ADObject -LDAPFilter $LDAPFilter ).DistinguishedName
            }
            if ($NeededADGroupName -icontains $ExistingADGroups) {
                # Move existing permissions into the group
                Add-ADGroupMember -Identity $NeededADGroupName -Members $member
            } else {
                # If group with permission type does not aleady exist
                # create a group the permisson

                $Permission = (Get-RightsShortName $Rights)
                [Microsoft.ActiveDirectory.Management.ADGroup] $_group = New-FolderPermissionsGroup -Permission $Permission -Path $Path -OU $OU -PathCommonNames $PathCommonNames
                
                # Set folder permissions using group
                $acl = Add-FolderPermissions -Permission $Permission -Path $Path -AssignedTo $_group.SAMAccountName -ACLObject $acl

                # Add member
                $_group | Add-ADGroupMember -Members $member
            }
            
            if (!$NoRemove) {
                # Remove ACL entry
                #$acl.RemoveAccessRule($Access)
            }

            $MadeChange = $true
        }
        #Update the ACL if needed
        if ($MadeChange) {
            Set-Acl $Path $acl
            $results += $Path
        }

        if ($Recurse) {
            # For each child folder, call this method recursively
            Get-ChildItem -Path $Path -Directory | %{
                $results += Update-FolderPermissionGroups -Path ($_.FullName) -OU $OU -PathCommonNames $PathCommonNames -Recurse
            }
        }

        return $results
    }
}

function Get-RightsShortName {
    [CmdletBinding(SupportsShouldProcess=$false)]
    param (
        [System.Security.AccessControl.FileSystemRights] $Right
    )
    switch ($Right) {
        "Modify" {
            return "RW"
            break
        }
        "ReadAndExecute" {
            return "RO"
        }
        default {
            return $Right
        }
    }
}


Export-ModuleMember -Function "Add-FolderPermissions", "Get-FolderPermissionsGroupName", "New-FolderPermissionsGroup", "Set-FolderPermissionsOU", "Get-FolderPermissionsOU", "Get-FolderPermissionsGroupOrphans", "Add-FolderPermissionsGroup", "Update-FolderPermissionGroups"