Public/New-CentralItOU.ps1

function New-CentralItOu {
    <#
        .SYNOPSIS
            Creates and configures a complete Active Directory Tiered Administration model.
 
        .DESCRIPTION
            Creates and configures the complete Active Directory tiered administration model including:
            - Organizational Units structure following Microsoft's tier model
            - Security groups for delegated administration
            - Group Policy Objects (GPOs) with security baselines
            - Fine-grained password policies
            - Kerberos authentication policies and silos
            - Group Managed Service Accounts (gMSAs)
            - Rights delegation model across all tiers
            - Optional enterprise components (Exchange, DFS, PKI, AGPM, LAPS, DHCP)
 
            This function implements Microsoft's recommended three-tier administration model:
            - Tier 0: Domain Controllers and critical infrastructure
            - Tier 1: Servers and server administrators
            - Tier 2: User workstations and standard users
 
            The implementation provides:
            - Least-privilege security model
            - Isolated administration boundaries between tiers
            - Clear segregation of duties
            - Enhanced security for privileged accounts
            - Comprehensive auditing and monitoring
 
        .PARAMETER ConfigXMLFile
            Full path to the XML configuration file containing all naming conventions,
            OU structure, and security settings.
            The XML file must contain required elements: Admin, Servers, Sites, and NC sections.
 
        .PARAMETER CreateExchange
            If present, creates all Exchange-related objects, containers and delegations.
            Requires valid Exchange configuration in the XML file.
 
        .PARAMETER CreateDfs
            If present, creates all DFS-related objects, containers and delegations.
            Requires valid DFS configuration in the XML file.
 
        .PARAMETER CreateCa
            If present, creates Certificate Authority (PKI) objects and delegations.
            Requires valid PKI configuration in the XML file.
 
        .PARAMETER CreateAGPM
            If present, creates Advanced Group Policy Management objects and delegations.
            Requires valid AGPM configuration in the XML file.
 
        .PARAMETER CreateLAPS
            If present, creates Local Administrator Password Solution objects and delegations.
            Requires valid LAPS configuration in the XML file.
 
        .PARAMETER CreateDHCP
            If present, creates DHCP-related objects, containers and delegations.
            Requires valid DHCP configuration in the XML file.
 
        .PARAMETER DMScripts
            Path to all supporting scripts and files needed by this function.
            Must contain a SecTmpl subfolder with required templates.
            Default is C:\PsScripts\
 
        .EXAMPLE
            New-CentralItOu -ConfigXMLFile 'C:\PsScripts\Configuration.xml'
 
            Creates the basic tier model structure using the specified configuration file.
 
        .EXAMPLE
            New-CentralItOu -ConfigXMLFile 'C:\PsScripts\Configuration.xml' -CreateLAPS -CreateDHCP
 
            Creates the tier model structure including LAPS and DHCP components.
 
        .EXAMPLE
            # Create parameter hashtable
            $Params = @{
                ConfigXMLFile = 'C:\PsScripts\Config.xml'
                CreateExchange = $true
                CreateDfs = $true
                CreateCa = $true
                DMScripts = 'D:\AdminScripts\'
                Verbose = $true
            }
 
            # Create the complete AD structure
            New-CentralItOu @Params
 
            Creates a comprehensive tier model with Exchange, DFS and PKI components using
            a custom scripts directory and verbose output.
 
        .INPUTS
            [System.IO.FileInfo]
            You can pipe the path to the XML configuration file to this function.
 
        .OUTPUTS
            [String]
            Returns completion status message.
 
        .NOTES
            Used Functions:
                Name ║ Module/Namespace
            ═══════════════════════════════════════╬════════════════════════
            Import-MyModule ║ EguibarIT
            New-Tier0CreateOU ║ EguibarIT
            New-Tier0MoveObject ║ EguibarIT
            New-Tier0AdminAccount ║ EguibarIT
            New-Tier0AdminGroup ║ EguibarIT
            New-Tier0gMSA ║ EguibarIT
            New-Tier0FineGrainPasswordPolicy ║ EguibarIT
            New-Tier0NestingGroup ║ EguibarIT
            New-Tier0Redirection ║ EguibarIT
            New-Tier0Delegation ║ EguibarIT
            New-Tier0Gpo ║ EguibarIT
            New-Tier0AuthPolicyAndSilo ║ EguibarIT
            New-Tier0GpoRestriction ║ EguibarIT
            New-Tier1 ║ EguibarIT
            New-Tier2 ║ EguibarIT
            New-ExchangeObject ║ EguibarIT
            New-DfsObject ║ EguibarIT
            New-CaObject ║ EguibarIT
            New-AGPMObject ║ EguibarIT
            New-LAPSobject ║ EguibarIT
            New-DHCPobject ║ EguibarIT
            Set-AdAclMngPrivilegedAccount ║ EguibarIT
            Set-AdAclMngPrivilegedGroup ║ EguibarIT
            Get-FunctionDisplay ║ EguibarIT
            Get-SafeVariable ║ EguibarIT
            Get-ADUser ║ ActiveDirectory
            Get-ADGroup ║ ActiveDirectory
 
        .NOTES
            Version: 1.5
            DateModified: 07/May/2025
            LastModifiedBy: Vicente Rodriguez Eguibar
                            vicente@eguibar.com
                            Eguibar IT
                            http://www.eguibarit.com
 
        .LINK
            https://github.com/vreguibar/EguibarIT
 
        .LINK
            https://docs.microsoft.com/en-us/windows-server/identity/securing-privileged-access/securing-privileged-access-reference-material
 
        .LINK
            https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/implementing-least-privilege-administrative-models
 
        .COMPONENT
            Active Directory
 
        .ROLE
            System Administrator
 
        .FUNCTIONALITY
            Active Directory, Security, Tier Model
    #>


    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'High',
        DefaultParameterSetName = 'Default'
    )]
    [OutputType([String])]

    Param (
        # PARAM1 full path to the configuration.xml file
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'Full path to the configuration.xml file',
            Position = 0)]
        [ValidateScript({
                if (-Not ($_ | Test-Path -PathType Leaf) ) {
                    throw ('File not found: {0}' -f $_)
                }
                if ($_.Extension -ne '.xml') {
                    throw ('File must be XML: {0}' -f $_)
                }
                try {
                    [xml]$xml = Get-Content -Path $_ -ErrorAction Stop
                    # Verify required XML elements are present
                    if ($null -eq $xml.n.Admin -or
                        $null -eq $xml.n.Servers -or
                        $null -eq $xml.n.Sites -or
                        $null -eq $xml.n.NC) {
                        throw 'XML file is missing required elements (Admin, Servers, Sites or NC section)'
                    }
                    return $true
                } catch {
                    throw ('Invalid XML file: {0}' -f $_.Exception.Message)
                }
            })]
        [PSDefaultValue(Help = 'Default Value is "C:\PsScripts\Config.xml"',
            Value = 'C:\PsScripts\Config.xml'
        )]
        [Alias('Config', 'XML', 'ConfigXml')]
        [System.IO.FileInfo]
        $ConfigXMLFile,

        # Param2 If present It will create all needed Exchange objects, containers and delegations
        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'If present It will create all needed Exchange objects, containers and delegations.',
            Position = 1)]
        [Alias('Exchange')]
        [switch]
        $CreateExchange,

        # Param3 Create DFS Objects
        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'If present It will create all needed DFS objects, containers and delegations.',
            Position = 2)]
        [Alias('DFS', 'DistributedFileSystem')]
        [switch]
        $CreateDfs,

        # Param4 Create CA (PKI) Objects
        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'If present It will create all needed Certificate Authority (PKI) objects, containers and delegations.',
            Position = 3)]
        [Alias('PKI', 'CA', 'CertificateAuthority')]
        [switch]
        $CreateCa,

        # Param5 Create AGPM Objects
        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'If present It will create all needed AGPM objects, containers and delegations.',
            Position = 4)]
        [Alias('GPM')]
        [switch]
        $CreateAGPM,

        # Param6 Create LAPS Objects
        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'If present It will create all needed LAPS objects, containers and delegations.',
            Position = 5)]
        [switch]
        $CreateLAPS,

        # Param7 Create DHCP Objects
        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'If present It will create all needed DHCP objects, containers and delegations.',
            Position = 6)]
        [switch]
        $CreateDHCP,

        # Param8 Location of all scripts & files
        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'Path to all the scripts and files needed by this function',
            Position = 7)]
        [ValidateScript({
                if (-not (Test-Path -Path $_ -PathType Container)) {
                    throw ('Directory not found: {0}' -f $_)
                }
                if (-not (Test-Path -Path (Join-Path -Path $_ -ChildPath 'SecTmpl'))) {
                    throw ('SecTmpl subfolder not found in: {0}' -f $_)
                }
                return $true
            })]
        [PSDefaultValue(
            Help = 'Default Value is "C:\PsScripts\"',
            Value = 'C:\PsScripts\'
        )]
        [Alias('ScriptPath')]
        [string]
        $DMScripts = 'C:\PsScripts\'
    )

    Begin {
        Set-StrictMode -Version Latest

        # Display function header if variables exist
        if ($null -ne $Variables -and
            $null -ne $Variables.Header) {

            $txt = ($Variables.Header -f
                (Get-Date).ToString('dd/MMM/yyyy'),
                $MyInvocation.Mycommand,
                (Get-FunctionDisplay -HashTable $PsBoundParameters -Verbose:$False)
            )
            Write-Verbose -Message $txt
        } #end If

        ##############################
        # Module imports

        Import-MyModule -Name 'ActiveDirectory' -Verbose:$false
        Import-MyModule -Name 'EguibarIT' -Verbose:$false
        Import-MyModule -Name 'EguibarIT.DelegationPS' -Verbose:$false


        ##############################
        # Variables Definition

        # parameters variable for splatting CMDlets
        [hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase)

        If (-not $PSBoundParameters.ContainsKey('ConfigXMLFile')) {
            $PSBoundParameters['ConfigXMLFile'] = 'C:\PsScripts\Config.xml'
        } #end If

        If (-not $PSBoundParameters.ContainsKey('DMScripts')) {
            $PSBoundParameters['DMScripts'] = 'C:\PsScripts\'
        } #end If

        # Load the XML configuration file
        try {
            [xml]$ConfXML = [xml](Get-Content $PSBoundParameters['ConfigXMLFile'])
            Write-Verbose -Message ('Successfully loaded configuration file: {0}' -f $PSBoundParameters['ConfigXMLFile'])
        } catch {
            Write-Error -Message ('Error reading XML file: {0}' -f $_.Exception.Message)
            throw
        } #end Try-Catch

        # Load naming conventions from XML
        [hashtable]$NC = @{
            'sl'    = $confXML.n.NC.LocalDomainGroupPreffix
            'sg'    = $confXML.n.NC.GlobalGroupPreffix
            'su'    = $confXML.n.NC.UniversalGroupPreffix
            'Delim' = $confXML.n.NC.Delimiter
            'T0'    = $confXML.n.NC.AdminAccSufix0
            'T1'    = $confXML.n.NC.AdminAccSufix1
            'T2'    = $confXML.n.NC.AdminAccSufix2
        }


    } #end Begin

    Process {

        # Create splat hashtable ensuring case matches exactly with parameter name
        $Splat = @{
            ConfigXMLFile = $PSBoundParameters['ConfigXMLFile']
            DMScripts     = $PSBoundParameters['DMScripts']
            Confirm       = $false
        }

        if ($PSCmdlet.ShouldProcess('Active Directory', 'Create full tier model structure')) {

            ###############################################################################
            # Create IT Admin and Sub OUs
            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Create Admin Area and related structure...')

            # Create the IT Admin OU and sub OUs
            New-Tier0CreateOU @Splat -EnableTranscript



            ###############################################################################
            # Move Built-In Admin user & Groups (Builtin OU groups can't be moved)

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Moving objects to Admin (Tier 0)...')

            New-Tier0MoveObject @Splat -EnableTranscript



            ###############################################################################
            # Creating Secured Admin accounts

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating and securing Admin accounts...')

            New-Tier0AdminAccount @Splat -EnableTranscript


            ###############################################################################
            # Create Admin groups

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating Admin groups...')

            New-Tier0AdminGroup @Splat -EnableTranscript



            ###############################################################################
            # Create Group Managed Service Account

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Create Group Managed Service Account')

            New-Tier0gMSA @Splat -EnableTranscript



            ###############################################################################
            # Create a New Fine Grained Password Policies

            Write-Verbose -Message ($Variables.NewRegionMessage -f
                'Create a New Fine Grained Password Policy for Admins Accounts...')

            New-Tier0FineGrainPasswordPolicy @Splat -EnableTranscript



            ###############################################################################
            # Nesting Groups


            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Nesting groups...')

            New-Tier0NestingGroup @Splat -EnableTranscript



            ###############################################################################
            # Enabling Management Accounts to Modify the Membership of Protected Groups

            Write-Verbose -Message ($Variables.NewRegionMessage -f
                'Enabling Management Accounts to Modify the Membership of Protected Groups...'
            )

            # ToDo: the GetSafeVariable is finding the variable, but variable has old DN. Interim fix filling the variable again
            $SL_PGM = Get-AdObjectType -Identity ('{0}{1}{2}' -f $NC['sl'], $NC['Delim'], $confXML.n.Admin.LG.PGM.Name)

            # ToDo: the GetSafeVariable is finding the variable, but variable has old DN. Interim fix filling the variable again
            $SL_PUM = Get-AdObjectType -Identity ('{0}{1}{2}' -f $NC['sl'], $NC['Delim'], $confXML.n.Admin.LG.PUM.Name)

            # Enable PUM to manage Privileged Accounts (Reset PWD, enable/disable Administrator built-in account)
            Set-AdAclMngPrivilegedAccount -Group $SL_PUM

            # Enable PGM to manage Privileged Groups (Administrators, Domain Admins...)
            Set-AdAclMngPrivilegedGroup -Group $SL_PGM





            ###############################################################################
            # redirect Users & Computers containers

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'redirect Users & Computers containers...')

            New-Tier0Redirection @Splat -EnableTranscript



            ###############################################################################
            # Delegation to ADMIN area (Tier 0)

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Delegate Admin Area (Tier 0)...')

            New-Tier0Delegation @Splat -EnableTranscript



            ###############################################################################
            # Create Baseline GPO

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating Baseline GPOs and configure them accordingly...')

            New-Tier0Gpo @Splat -EnableTranscript

            # Configure Kerberos Claims and Authentication Policies/Silos

            New-Tier0AuthPolicyAndSilo @Splat -EnableTranscript




            ###############################################################################
            # Configure GPO Restrictions based on Tier Model

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Configure GPO Restrictions based on Tier Model...')

            New-Tier0GpoRestriction @Splat -EnableTranscript



            ###############################################################################
            # SERVERS OU (area)

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating Servers Area (Tier 1)...')

            New-Tier1 @Splat -EnableTranscript



            ###############################################################################
            # Create Sites OUs (Area)

            Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating Sites Area (Tier 2)...')

            New-Tier2 @Splat -EnableTranscript




            ###############################################################################
            # Check if Exchange objects have to be created. Process if TRUE
            if ($PSBoundParameters['CreateExchange']) {

                Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating Exchange On-Prem objects and delegations')

                # Get the Config.xml file
                $param = @{
                    ConfigXMLFile = $PSBoundParameters['ConfigXMLFile']
                    verbose       = $true
                }

                New-ExchangeObject @param
            }

            ###############################################################################
            # Check if DFS objects have to be created. Process if TRUE
            if ($PSBoundParameters['CreateDfs']) {

                Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating DFS objects and delegations')
                # Get the Config.xml file
                $param = @{
                    ConfigXMLFile = $PSBoundParameters['ConfigXMLFile']
                    verbose       = $true
                }
                New-DfsObject @param
            }

            ###############################################################################
            # Check if Certificate Authority (PKI) objects have to be created. Process if TRUE
            if ($PSBoundParameters['CreateCa']) {

                Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating CA Services, objects and delegations')

                New-CaObject -ConfigXMLFile $PSBoundParameters['ConfigXMLFile']
            }

            ###############################################################################
            # Check if Advanced Group Policy Management (AGPM) objects have to be created. Process if TRUE
            if ($PSBoundParameters['CreateAGPM']) {

                try {
                    Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating AGPM objects and delegations')

                    # Create parameter hashtable for AGPM
                    [hashtable]$AgpmParams = @{
                        ConfigXMLFile = $PSBoundParameters['ConfigXMLFile']
                        Verbose       = $VerbosePreference -eq 'Continue'
                    }

                    # Execute AGPM configuration
                    New-AGPMObject $AgpmParams

                } catch {

                    Write-Error -Message ('Failed to create AGPM objects: {0}' -f $_.Exception.Message)

                } #end Try-Catch

            } #end If

            ###############################################################################
            # Check if MS Local Administrator Password Service (LAPS) is to be used. Process if TRUE
            if ($PSBoundParameters['CreateLAPS']) {
                try {

                    Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating LAPS objects and delegations')

                    # Create parameter hashtable for LAPS
                    [hashtable]$LapsParams = @{
                        ConfigXMLFile = $PSBoundParameters['ConfigXMLFile']
                        Verbose       = $VerbosePreference -eq 'Continue'
                    }

                    # Execute LAPS configuration
                    New-LAPSobject @LapsParams

                } catch {

                    Write-Error -Message ('Failed to create LAPS objects: {0}' -f $_.Exception.Message)

                } #end Try-Catch

            } #end If

            ###############################################################################
            # Check if DHCP is to be used. Process if TRUE
            if ($PSBoundParameters['CreateDHCP']) {

                try {
                    Write-Verbose -Message ($Variables.NewRegionMessage -f 'Creating DHCP objects and delegations')

                    # Create parameter hashtable for DHCP
                    [hashtable]$DhcpParams = @{
                        ConfigXMLFile = $PSBoundParameters['ConfigXMLFile']
                        Verbose       = $VerbosePreference -eq 'Continue'
                    }

                    # Execute DHCP configuration
                    New-DHCPobject @DhcpParams

                } catch {

                    Write-Error -Message ('Failed to create DHCP objects: {0}' -f $_.Exception.Message)

                } #end Try-Catch

            } #end If

        } #end If ShouldProcess

    } #end Process

    End {

        # Display function footer if variables exist
        if ($null -ne $Variables -and
            $null -ne $Variables.Footer) {

            $txt = ($Variables.Footer -f $MyInvocation.InvocationName,
                'creating Tier0 central IT OU structure and delegations.'
            )
            Write-Verbose -Message $txt
        } #end If

    } #end End

} #end Function New-CentralItOu