PSGISP.ActiveDirectory.psm1

#---------------------------------------------
#------------------ Classes ------------------
#---------------------------------------------

#---------------------------------------------
#------------- Private Functions -------------
#---------------------------------------------

#---------------------------------------------
#-------------- Public Functions -------------
#---------------------------------------------
Function Import-BulkGPO {
    <#
        .SYNOPSIS
        Import multiple GPO
 
        .DESCRIPTION
        If you export multiple GPOs they want have have the correct and again import, they wan't have the correct GPO Name.
        This function is able to get the correct Name out of the Manifest XML File, which is an automatically created and hidden File.
 
        .PARAMETER Path
        Path to GPOs
 
        .INPUTS
        System.String[]
 
        .OUTPUTS
        None
 
        .EXAMPLE
        Import-BulkGPO -Path .\Desktop\GPO
 
        .LINK
        https://github.com/gisp497/psgisp
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(
            Mandatory = $true,
            HelpMessage = "Path of the GPO folder"
        )]
        [string]$Path
    )
    Begin{
        Write-Verbose "Install GroupPolicy module"
        if(Get-Module -ListAvailable GroupPolicy){
            Import-Module GroupPolicy
        }else{
            Try{
                Install-WindowsFeature -Name "GPMC"
                Import-Module GroupPolicy
            }catch{
                Throw "Cant import module GroupPolicy. Error: $_"
            }
        }

        Write-Verbose "Test if the GPO path exist."
        try {
            $null = Test-Path $Path -ErrorAction Stop
        }
        catch {
            Throw "The GPO path does not exist. Error: $_"
        }

        Write-Verbose "Test if the manifest.xml file exist."
        try {
            $pathmanifest = $Path.Trim('\') + '\manifest.xml'
            $null = Test-Path $pathmanifest -ErrorAction Stop
        }
        catch {
            Throw "The GPO manisfest does not exist. Error: $_"
        }

        #Create Write-Progress Variable
        $gpocount = (Get-ChildItem -Path $Path | Where-Object Name -ne manifest.xml).count
        $loopcount = 0
    }
    Process{
        #Import GPO loop
        Get-ChildItem -Path $Path -Directory | ForEach-Object{
            Write-Verbose "Create new PSObject"
            $folderid = New-Object PSObject -Property @{id=$_.BaseName}

            Write-Verbose "Get content from manifest."
            [xml]$xml = Get-Content $pathmanifest
            $manifest = Foreach ($x in $xml.Backups.BackupInst) {
                New-Object PSObject -property @{id=$($x.ID | Select-Object -expand "#cdata-section");name=$($x.GPODisplayName | Select-Object -expand "#cdata-section");}
            }
            $gponame = $manifest | Where-Object {$folderid.id -eq $_.id} | Select-Object name

            #Create counter
            $loopcount++
            Write-Progress -Activity 'GPO Import' -PercentComplete (($loopcount / $gpocount) * 100)

            Write-Verbose "Import GPO"
            try {

                $null = Import-GPO -BackupId $_.BaseName -Path $Path -TargetName $gponame.name -CreateIfNeeded -ErrorAction Stop
            }
            catch {
                Throw "The GPOs can't be imported. Error: $_"
            }
        }
    }
    End{
    }
}
Function Import-PrintGPO {
    <#
        .SYNOPSIS
        Import printer to print gpo
 
        .DESCRIPTION
        This function can import printer to the print gpo.
        It is important, to add one printer manually to the print gpo!
        Otherwise the function will not work.
 
        .PARAMETER GPO
        The GPO which will be updated.
 
        .PARAMETER Printer
        The Shared path of the printer.
 
        .PARAMETER Action
        Execution of the GPO (create,replace,update)
 
        .PARAMETER DefaultPrinter
        Sets printer as default printer
 
        .PARAMETER GroupFilter
        Is used to deploy the printer only to specific groups.
 
        .INPUTS
        system.string[]
 
        .OUTPUTS
        none
 
        .EXAMPLE
        Import-PrintGPO -GPO usr-print-gpo -Printer "\\printserver\printer1","\\printserver\printer2" -Action create -DefaultPrinter -GroupFilter "group1"
 
        .LINK
        https://github.com/gisp497/psgisp
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Mandatory = $true,
            HelpMessage = "The GPO which will be updated.")]
        [string]$GPO,

        [Parameter(Mandatory = $true,
            HelpMessage = "The Shared path of the printer.")]
        $Printer,

        [Parameter(Mandatory = $false,
            HelpMessage = "GPO action (create, replace or update)")]
        [ValidateSet('create', 'replace','update')]
        [string]$Action = 'update',

        [Parameter(Mandatory = $false,
            HelpMessage = "Sets printer as default printer")]
        [switch]$DefaultPrinter,

        [Parameter(Mandatory = $false,
            HelpMessage = "Is used to deploy the printer only to specific groups.")]
        $GroupFilter = $null
    )
    Begin{
        Write-Verbose "Install ActiveDirectory module"
        if(Get-Module -ListAvailable ActiveDirectory){
            Import-Module ActiveDirectory
        }else{
            Try{
                Install-WindowsFeature -Name "RSAT-AD-PowerShell"
            }catch{
                Throw "Cant import module ActiveDirectory. Error: $_"
            }
        }

        Write-Verbose "Install GroupPolicy module"
        if(Get-Module -ListAvailable GroupPolicy){
            Import-Module GroupPolicy
        }else{
            Try{
                Install-WindowsFeature -Name "GPMC"
            }catch{
                Throw "Cant import module GroupPolicy. Error: $_"
            }
        }

        Write-Verbose "Get date"
        $date = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'

        Write-Verbose "Get gpo action type"
        switch ($Action) {
            create {
                $printgpoimage = 0
                $printgpoaction = 'C'
            }
            replace {
                $printgpoimage = 1
                $printgpoaction = 'R'
            }
            update {
                $printgpoimage = 2
                $printgpoaction = 'U'
            }
        }

        Write-Verbose "Get default printer option"
        if($DefaultPrinter){
            $printgpo_defaultprinter = 1
        }else{
            $printgpo_defaultprinter = 0
        }

        Write-Verbose "Get groupfilter option"
        if($null -ne $GroupFilter){
            try {
                $printgpogroupfilter = '<Filters><FilterGroup bool="AND" not="0" name="' + (Get-ADGroup -Filter 'Name -eq $GroupFilter').Name + '" sid="' + (Get-ADGroup -Filter 'Name -eq $GroupFilter').SID + '" userContext="1" primaryGroup="0" localGroup="0"/></Filters>'
            }
            catch {
                Write-Error "The ad group can't be found."
            }
        }

        Write-Verbose "Get GPO"
        try {
            $printgpo_id = (Get-GPO -DisplayName $GPO -ErrorAction Stop).id
        }
        catch {
            Throw "GPO does not exist. Create GPO and add at least 1 printer to it."
        }
    }

    Process{
        Write-Verbose "Create backup from GPO"
        $gpobackupfolder = $env:APPDATA + '\printgpo'
        try {
            if (Test-Path -Path $gpobackupfolder) {
                Remove-Item -Path $gpobackupfolder -Force -Recurse
            }
            $null = New-Item -Path $gpobackupfolder -ItemType Directory -Force -ErrorAction Stop
            $null = Backup-GPO -Id $printgpo_id -Path $gpobackupfolder -ErrorAction Stop
        }
        catch {
            Throw "The GPO could not be backed up."
        }

        Write-Verbose "Check if printer.xml file exists"
        $printerxml = (Get-ChildItem -Path $gpobackupfolder).FullName + '\DomainSysvol\GPO\User\Preferences\Printers\Printers.xml'
        if(Test-Path -Path $printerxml) {
            $printerxmlcontent = Get-Content $printerxml
        }else {
            Throw "One printer must be already added to the gpo!"
        }

        Write-Verbose "Remove end of printers.xml file"
        $printerxmlcontent = $printerxmlcontent -replace '</Printers>',''
        $printerxmlcontent = $printerxmlcontent | Where-Object {$_.trim() -ne "" }
        $null = Set-Content -Path $printerxml -Value $printerxmlcontent

        Write-Verbose "Add Printers to printers.xml file"
        $Printer | ForEach-Object {
            $uid = New-Guid
            $printername = $_.substring($_.lastindexof("\")+1)
            $printgpo_xmldata = '<SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="' + $printername + '" status="' + $printername + '" image="' + $printgpoimage + '" changed="' + $date + '" uid="' + $uid + '" userContext="1" bypassErrors="1"><Properties action="' + $printgpoaction + '" comment="" path="' + $_ + '" location="" default="' + $printgpo_defaultprinter + '" skipLocal="0" deleteAll="0" persistent="0" deleteMaps="0" port=""/>' + $printgpogroupfilter + '</SharedPrinter>'
            $null = Add-Content -Path $printerxml -Value $printgpo_xmldata
        }

        Write-Verbose "Add End of printers.xml file again"
        $null = Add-Content -Path $printerxml -Value '</Printers>'

        Write-Verbose "Restore GPO with new printers"
        try {
            $null = Restore-GPO -Guid $printgpo_id -Path $gpobackupfolder
        }
        catch {
            Throw "The GPO could not be restored. Error: $_"
        }
    }
    End{
        Write-Verbose "Remove old files"
        $null = Remove-Item -Path $gpobackupfolder -Force -Recurse
    }
}
Function New-NetworkDrive {
    <#
        .SYNOPSIS
        Create folder an ad group for network drive
 
        .DESCRIPTION
        This function creates folders and groups for network drives.
        The groups can be used to give access via gpo.
 
        .PARAMETER Name
        Array object of names which will be the new network drives.
 
        .PARAMETER Path
        Path in which the folders will be created.
 
        .PARAMETER GroupDriveOrganizationalUnit
        AD Organizationl Unit to safe new AD group. Those groups can be used to give users the folder as network drive (GPO).
 
        .PARAMETER GroupAccessOrganizationalUnit
        AD Organizationl Unit to safe new AD group. Those groups can be used to give users access to the folder.
 
        .INPUTS
        Array
        String
 
        .OUTPUTS
        None
 
        .EXAMPLE
        New-NetworkDrive -Name @("drive1", "drive2") -Path "D:\data" -GroupDriveOrganizationalUnit (Get-ADOrganizationalUnit -Identity "OU=Drives,OU=Groups,OU=test,DC=test,DC=local") -GroupAccessOrganizationalUnit (Get-ADOrganizationalUnit -Identity "OU=Access,OU=Groups,OU=test,DC=test,DC=local")
 
        .LINK
        https://github.com/gisp497/psgisp
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(
            Mandatory = $true,
            HelpMessage = "Name of the Folders/Networkdrives"
        )]
        [Array]$Name,

        [Parameter(
            Mandatory = $true,
            HelpMessage = "Path in which the folders should be created."
        )]
        [String]$Path,

        [Parameter(
            Mandatory = $true,
            HelpMessage = "AD Organizationl Unit to safe new AD group. Those groups can be used to give users the folder as network drive (GPO)."
        )]
        $GroupDriveOrganizationalUnit,

        [Parameter(
            Mandatory = $true,
            HelpMessage = "AD Organizationl Unit to safe new AD group. Those groups can be used to give users access to the folder."
        )]
        $GroupAccessOrganizationalUnit
    )
    Begin {
    }
    Process {
        $Name | ForEach-Object {
            #create folder
            $folder = New-Item -Path $Path -Name $_ -ItemType Directory

            #create groups
            $drivegroup = New-ADGroup -Name ('d_' + $_) -GroupScope Global -GroupCategory Security -Path $GroupDriveOrganizationalUnit -PassThru
            $accessreadgroup = New-ADGroup -Name ('a_' + $_ + '_r') -GroupScope Global -GroupCategory Security -Path $GroupAccessOrganizationalUnit -PassThru
            $accesswritegroup = New-ADGroup -Name ('a_' + $_ + '_rw') -GroupScope Global -GroupCategory Security -Path $GroupAccessOrganizationalUnit -PassThru

            #add access groups to drive group
            Add-ADGroupMember -Identity $drivegroup -Members $accessreadgroup,$accesswritegroup

            #format group to correct syntax
            $netbios = (Get-ADDomain).NetBIOSName
            $accessreadgroup = $netbios + '\' + $accessreadgroup.Name
            $accesswritegroup = $netbios + '\' + $accesswritegroup.Name

            #get current access rights
            $permission = Get-Acl $folder.FullName

            #disable inheritance but let permissions the same
            $permission.SetAccessRuleProtection($true,$true)
            Set-Acl -AclObject $permission -Path $folder.FullName
            $permission = Get-Acl $folder.FullName

            #create new permissions for both groups
            $accessread = New-Object System.Security.AccessControl.FileSystemAccessRule($accessreadgroup,"ReadAndExecute, Synchronize","ContainerInherit,ObjectInherit", "None","Allow")
            $accesswrite = New-Object System.Security.AccessControl.FileSystemAccessRule($accesswritegroup,"DeleteSubdirectoriesAndFiles, Modify, Synchronize","ContainerInherit,ObjectInherit", "None","Allow")
            $permission.SetAccessRule($accessread)
            $permission.SetAccessRule($accesswrite)

            #remove permission for builtin users
            $builtinusers = New-Object System.Security.Principal.Ntaccount ("BUILTIN\Users")
            $permission.PurgeAccessRules($builtinusers)

            #set permissions to folder
            Set-Acl -AclObject $permission -Path $folder.FullName
        }
    }
    End {
    }
}
Function Set-ADUserPassword {
    <#
        .SYNOPSIS
        Set password for AD user
 
        .DESCRIPTION
        This script creates random password and sets them to specific ad users.
 
        .PARAMETER User
        Ad user to get a new password.
 
        .PARAMETER PasswordLength
        The length of the new password.
 
        .INPUTS
        System.String[]
        System.Object[]
        System.Int[]
 
        .OUTPUTS
        System.Object[]
 
        .EXAMPLE
        Set-ADUserPassword -User gisp -PasswordLength 12
 
        .LINK
        https://github.com/gisp497/psgisp
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(
            Mandatory=$true,
            HelpMessage="Ad user to get a new password"
        )]
        $User,
        [Parameter(
            Mandatory=$false,
            HelpMessage="Length of the password"
        )]
        [int]$PasswordLength = 24,
        [Parameter(
            Mandatory = $false,
            HelpMessage = "number of special characters in password")]
        [int]$SpecialCharacter = 8
    )
    Begin{
        Write-Verbose "Install ActiveDirectory module"
        if(Get-Module -ListAvailable ActiveDirectory){
            Import-Module ActiveDirectory
        }else{
            Try{
                Install-WindowsFeature -Name "RSAT-AD-PowerShell"
            }catch{
                Throw "Cant import module ActiveDirectory. Error: $_"
            }
        }

        #create Output Array
        $OutputObject = @()
    }
    Process{
        #Get ADuser
        $user | Get-ADUser | Foreach-Object{
            Write-Verbose "Set the new user password"
            $password = Get-RandomString -Length $PasswordLength -SpecialCharacter $SpecialCharacter -SecureString
            Set-ADAccountPassword -Identity $_ -NewPassword $password -Reset

            #Create psobject
            $credential = New-Object -TypeName System.Management.Automation.PSCredential ($_.SamAccountName, $password)
            
            #Add Object to Array
            $OutputObject += $credential
        }
    }
    End{
        #Return output
        return $OutputObject
    }
}

#---------------------------------------------
#--------- Export Public Functions -----------
#---------------------------------------------
$PublicFunctions = @(
    'Import-BulkGPO',
    'Import-PrintGPO',
    'New-NetworkDrive',
    'Set-ADUserPassword'
)
Export-ModuleMember -Function $PublicFunctions