Private/Get-GptTemplate.ps1

function Get-GptTemplate {
    <#
        .SYNOPSIS
            Retrieves or creates the GPT template (GptTmpl.inf) for a specified Group Policy Object.

        .DESCRIPTION
            The Get-GptTemplate function retrieves or creates the GPT template file (GptTmpl.inf) for a specified Group Policy Object (GPO). It locates or creates the necessary directory structure in SYSVOL, creates the GptTmpl.inf file if it doesn't exist, and returns an IniFileHandler.IniFile object representing the template. This function is essential for Group Policy security settings management as it provides access to the underlying infrastructure file that stores security configurations. Handles errors gracefully and supports pipeline input for batch processing.

        .PARAMETER GpoName
            The name of the Group Policy Object (GPO) for which the GPT template is to be retrieved. Accepts direct input and pipeline input, including objects from Get-GPO.

        .PARAMETER DomainName
            The Fully Qualified Domain Name (FQDN) of the domain containing the GPO. If not specified, the current user's domain (from environment variable USERDNSDOMAIN) is used.

        .PARAMETER Server
            The Domain Controller to connect to for Group Policy operations. If not specified, the nearest Domain Controller is automatically selected.

        .EXAMPLE
            Get-GptTemplate -GpoName "Default Domain Policy"
            Retrieves the GPT template for the "Default Domain Policy" GPO in the current domain. Creates the template file if it doesn't exist.

        .EXAMPLE
            Get-GptTemplate -GpoName "Default Domain Policy" -DomainName "EguibarIT.local" -Server "DC01.EguibarIT.local"
            Retrieves the GPT template for the "Default Domain Policy" GPO in the EguibarIT.local domain, connecting to the specified domain controller DC01.EguibarIT.local.

        .INPUTS
            [String], [Microsoft.GroupPolicy.GPO]
            You can pipe a GPO name or GPO object to this function.

        .OUTPUTS
            [IniFileHandler.IniFile]
            Returns an IniFileHandler.IniFile object representing the GPT template.

        .NOTES
            Used Functions:
                Name ║ Module/Namespace
                ═════════════════════════════════╬══════════════════════════════
                Set-StrictMode ║ Microsoft.PowerShell.Core
                Get-Date ║ Microsoft.PowerShell.Utility
                Get-FunctionDisplay ║ EguibarIT.DelegationPS
                Import-MyModule ║ EguibarIT.DelegationPS
                Get-ADDomain ║ ActiveDirectory
                Get-GPO ║ GroupPolicy
                Write-Verbose ║ Microsoft.PowerShell.Utility
                Write-Debug ║ Microsoft.PowerShell.Utility
                Write-Error ║ Microsoft.PowerShell.Utility
                Test-Path ║ Microsoft.PowerShell.Management
                New-Item ║ Microsoft.PowerShell.Management
                [IniFileHandler.IniFile] ║ EguibarIT.DelegationPS.Classes

        .NOTES
            Version: 2.1
            DateModified: 06/Jun/2025
            LastModifiedBy: Vicente Rodriguez Eguibar
                            vicente@eguibarit.com
                            Eguibar IT
                            http://www.eguibarit.com

        .LINK
            https://github.com/vreguibar/EguibarIT.DelegationPS/blob/main/Private/Get-GptTemplate.ps1

        .COMPONENT
            Group Policy

        .ROLE
            Security Configuration

        .FUNCTIONALITY
            Group Policy Template Management, Security Settings
    #>


    [CmdletBinding(SupportsShouldProcess = $true,
        ConfirmImpact = 'Low')]
    [OutputType([IniFileHandler.IniFile])]

    param (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Specify the name of the GPO.',
            Position = 0)]
        [ValidateNotNullOrEmpty()]
        [Alias('Name', 'PolicyName')]
        [string]
        $GpoName,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Specify the domain name containing the GPO. If not specified, the current user domain is used.',
            Position = 1)]
        [ValidateNotNullOrEmpty()]
        [Alias('Domain')]
        [string]
        $DomainName,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Specify the domain controller to connect to. If not specified, the nearest DC is used.',
            Position = 2)]
        [ValidateNotNullOrEmpty()]
        [Alias('DC', 'DomainController')]
        [string]
        $Server
    )

    Begin {
        # Set strict mode to catch syntax errors
        Set-StrictMode -Version Latest

        # Display function header if variables exist
        if ($null -ne $Variables -and $null -ne $Variables.HeaderDelegation) {
            $txt = ($Variables.HeaderDelegation -f
                (Get-Date).ToString('dd/MMM/yyyy'),
                $MyInvocation.MyCommand,
                (Get-FunctionDisplay -HashTable $PsBoundParameters -Verbose:$false)
            )
            Write-Verbose -Message $txt
        } #end if

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

        # Import required modules
        Import-MyModule -Name 'GroupPolicy' -SkipEditionCheck -Verbose:$false

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

        # Initialize the splat hashtable for Get-GPO parameters
        [hashtable]$SplatGpo = [hashtable]::New([StringComparer]::OrdinalIgnoreCase)

        # Create and return the IniFileHandler.IniFile object
        $GptTmpl = [IniFileHandler.IniFile]::new()

        # Add the GPO name to the splat if provided
        if ($PSBoundParameters.ContainsKey('GpoName')) {
            $SplatGpo['Name'] = $PSBoundParameters['GpoName']
        }

        # Add the domain name to the splat if provided
        if ($PSBoundParameters.ContainsKey('DomainName')) {
            $SplatGpo['Domain'] = $PSBoundParameters['DomainName']
        }

        # Add the server to the splat if provided
        if ($PSBoundParameters.ContainsKey('Server')) {
            $SplatGpo['Server'] = $PSBoundParameters['Server']
        }

        # Add ErrorAction to the splat
        $SplatGpo['ErrorAction'] = 'Stop'


        # Get the domain FQDN - use the provided domain or the current user's domain
        [string]$DomainFQDN = if ($PSBoundParameters.ContainsKey('DomainName')) {
            $PSBoundParameters['DomainName']
        } else {
            $env:USERDNSDOMAIN
        } #end if-else

        $PDCEmulator = (Get-ADDomain | Select-Object -Property PDCEmulator).PDCEmulator

    } #end Begin

    Process {
        try {

            # Get the GPO object
            $Gpo = Get-GPO @SplatGpo

            Write-Debug -Message ('
                GPO object retrieved successfully: {0}
                GPO ID: {0}'
 -f $Gpo.DisplayName, $Gpo.Id
            )


            # Construct the GPT template path
            #[string]$DomainPath = '\\{0}\SYSVOL\{0}' -f $PDCEmulator
            [string]$DomainPath = '\\{0}\SYSVOL\{0}' -f $DomainFQDN
            [string]$GpoPath = '{0}\Policies\{1}\Machine\Microsoft\Windows NT\SecEdit\' -f $DomainPath, ('{' + $($Gpo.Id) + '}')
            [string]$GpoPathFile = '{0}GptTmpl.inf' -f $GpoPath

            Write-Debug -Message ('Constructed GPT template path: {0}' -f $GpoPath)

            # Check if the directory exists and create it if necessary
            if (-not (Test-Path -Path $GpoPath -PathType Container)) {

                Write-Debug -Message ('GPT template path does not exist. Creating new folder path: {0}' -f $GpoPath)

                # Use ShouldProcess to confirm the directory creation
                if ($PSCmdlet.ShouldProcess($GpoPath, 'Create Directory')) {

                    try {

                        # Create the directory
                        New-Item -ItemType Directory -Path $GpoPath -Force -ErrorAction Stop | Out-Null
                        Write-Debug -Message ('Directory created successfully: {0}' -f $GpoPath)

                    } catch {

                        Write-Error -Message ('
                            Error while trying to create the folder for {0}.
                            Error: {1}'
 -f $Gpo.DisplayName, $_.Exception.Message
                        )
                        return $null

                    } #end Try-Catch

                } else {

                    # User chose not to create the directory
                    Write-Debug -Message ('Directory creation skipped due to ShouldProcess: {0}' -f $GpoPath)
                    return $null

                } #end if-else

            } #end if

            # Check if the file exists and create it if necessary
            if (-not (Test-Path -Path $GpoPathFile -PathType Leaf)) {

                Write-Debug -Message ('GPT template file does not exist. Creating new file: {0}' -f $GpoPathFile)

                # Use ShouldProcess to confirm the file creation
                if ($PSCmdlet.ShouldProcess($GpoPathFile, 'Create File')) {

                    try {

                        # Create the file with Unicode encoding
                        [System.IO.File]::WriteAllText($GpoPathFile, '', [System.Text.Encoding]::Unicode)
                        Write-Debug -Message ('File created successfully: {0}' -f $GpoPathFile)

                    } catch {

                        Write-Error -Message ('
                            Error while trying to create GptTmpl.inf file within folder for {0}.
                            Error: {1}'
 -f $Gpo.DisplayName, $_.Exception.Message
                        )
                        return $null

                    } #end Try-Catch

                } else {

                    # User chose not to create the file
                    Write-Debug -Message ('File creation skipped due to ShouldProcess: {0}' -f $GpoPathFile)
                    return $null

                } #end if-else

            } #end if


            Write-Debug -Message ('Creating IniFileHandler.IniFile object for: {0}' -f $GpoPathFile)

            # Read the GptTmpl.inf file into an IniFileHandler.IniFile object
            $GptTmpl.ReadFile($GpoPathFile)

            Write-Debug -Message 'GPT template object created successfully.'

            # Ensure we're returning an IniFileHandler.IniFile object
            if ($GptTmpl -is [IniFileHandler.IniFile]) {

                return $GptTmpl

            } else {

                throw 'Failed to create an IniFileHandler.IniFile object'

            } #end If-Else

        } catch {

            # Check the error type and provide appropriate messages
            if ($_.Exception.ToString() -like '*GPNotFoundException*') {

                Write-Error -Message ('GPO not found: {0}. Error: {1}' -f $PSBoundParameters['GpoName'], $_.Exception.Message)

            } elseif ($_.Exception.ToString() -like '*DirectoryNotFoundException*') {

                Write-Error -Message ('Directory not found: {0}. Error: {1}' -f $GpoPath, $_.Exception.Message)

            } elseif ($_.Exception.ToString() -like '*FileNotFoundException*') {

                Write-Error -Message ('File not found: {0}. Error: {1}' -f $GpoPathFile, $_.Exception.Message)

            } elseif ($_.Exception.ToString() -like '*UnauthorizedAccessException*') {

                Write-Error -Message (
                    'Access denied while attempting to access {0}. Error: {1}' -f
                    $GpoPathFile, $_.Exception.Message
                )

            } else {

                Write-Error -Message (
                    'An unexpected error occurred while handling the GPT template path. Error: {0}' -f
                    $_.Exception.Message
                )

            } #end If-ElseIf-Else

            return $null
        } #end Try-Catch

    } #end Process

    End {
        # Display function footer if variables exist
        if ($null -ne $Variables -and $null -ne $Variables.FooterDelegation) {
            $txt = ($Variables.FooterDelegation -f $MyInvocation.InvocationName,
                'Returning GptTmpl object (Private Function).'
            )
            Write-Verbose -Message $txt
        } #end if
    } #end End
} #end function Get-GptTemplate