Private/Set-GPOConfigSection.ps1

function Set-GPOConfigSection {

    <#
        .SYNOPSIS
            Configures a specific section and key in a GPT template (GptTmpl.inf) file with specified members.

        .DESCRIPTION
            This function creates a new key or updates an existing key within a specified section of a GPT template
            (GptTmpl.inf represented by [IniFileHandler.IniFile] class) file. It processes and merges existing values
            (if the key exists) with new members, ensuring correct resolution of SIDs and avoiding duplicates.
            A single Null-string is considered a valid value.

            1.- Check if the provided section (Parameter CurrentSection) exist on the [IniFileHandler.IniFile]$GptTmpl variable (Parameter GptTmpl)
            2.- Section exists (GptTmpl does contains the section)
            3.- Check if Key exist. If key does not exist, just create it and continue with step 4
                A.- If key exist, get the values contained.
                    Value can be a single $null string
                    comma delimited string being each item a member represented by its SID and * prefix

                    (for example,
                        Administrators would be *S-1-5-32-544,
                        Event Log Readers would be *S-1-5-32-573,
                        Server Operators would be *S-1-5-32-549...

                    full string value would be *S-1-5-32-544,*S-1-5-32-573,*S-1-5-32-549 ).

                E.- Get value as array and strip prefix '*', just having pure SID.
                F.- Iterate through all members, except if just 1 value and this is null.
                G.- Each member or iteration has to be resolved (first remove * prefix, otherwise will throw an error), either a "normal" SID or a Well-Known SID. Having SID translated to an account to ensure that it continues to exist on ActiveDirectory. If the account is successfully translated, meaning it does exist in AD, and it can be added to the OK list with an * prefix. Skip duplicated.
            4.- Key did not exist, so no values exist either. Key was created earlier.
                A.- Get new members from Parameter Members (Tis parameter can accept $null and should be treated as a single null string)
                B.- Each member or iteration has to be resolved, either a "normal" SID or a Well-Known SID. Having SID translated to an account to ensure that it continues to exist on ActiveDirectory. If the account is successfully translated, meaning it does exist in AD, it can be added to the OK list with an * prefix. Skip duplicated.
            5.- Convert the arrayList to a comma-delimited string (except if nullString single instance). trim end comma, period or space.
            6. Add key and value the $GptTmpl
            7.- Return updated $GptTmpl



        .PARAMETER CurrentSection
             The section in the GPT template file to be configured (e.g., "Privilege Rights" or "Registry Values").
             This section is assumed to exist.

        .PARAMETER CurrentKey
             The key within the given section (e.g., "SeAuditPrivilege" or "SeBatchLogonRight").

        .PARAMETER Members
            An array of members to be added to the key. Can be null, which will be treated as a single null string.

        .PARAMETER GptTmpl
            The GPT template object representing the GptTmpl.inf file of type [IniFileHandler.IniFile].

        .OUTPUTS
            [IniFileHandler.IniFile]

        .EXAMPLE
            Set-GPOConfigSection -CurrentSection "User Rights Assignment" -CurrentKey "SeDenyNetworkLogonRight" -Members @("User1", "Group1") -GptTmpl $GptTmpl

    #>


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

    param (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'The section in the GPT template file to be configured (ex. [Privilege Rights] or [Registry Values]).',
            Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]
        $CurrentSection,

        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'TheKEY within given section (ex. SeAuditPrivilege or SeBatchLogonRight).',
            Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]
        $CurrentKey,

        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Member of given KEY. This value can be Empty or Null',
            Position = 2)]
        [AllowNull()]
        [AllowEmptyString()]
        $Members,

        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = '.',
            Position = 3)]
        [ValidateNotNullOrEmpty()]
        [IniFileHandler.IniFile]
        $GptTmpl
    )

    Begin {
        $txt = ($Variables.HeaderDelegation -f
            (Get-Date).ToShortDateString(),
            $MyInvocation.Mycommand,
            (Get-FunctionDisplay -HashTable $PsBoundParameters -Verbose:$False)
        )
        Write-Verbose -Message $txt

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

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

        $resolvedMembers = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)

    } #end Begin

    Process {
        try {

            # Check if the key exists
            $currentValue = $GptTmpl.GetKeyValue($CurrentSection, $CurrentKey)

            if ([string]::IsNullOrEmpty($currentValue)) {

                Write-Verbose -Message ('
                    Key {0} not found in
                    section {1}.
                    Creating new key.'
 -f
                    $CurrentKey, $CurrentSection
                )

            } else {
                Write-Verbose -Message ('
                    Key {0} found in
                    section {1}.
                    Processing existing members.'
 -f
                    $CurrentKey, $CurrentSection
                )

                # Process existing members

                # get value, separate it by comma and remove empty
                $existingMembers = $currentValue.TrimEnd(',').Split(',', [StringSplitOptions]::RemoveEmptyEntries)

                if (
                    ($existingMembers.Count -eq 1) -and
                    [string]::IsNullOrEmpty($existingMembers[0])
                ) {

                    Write-Verbose -Message 'Existing value is a single null string.'

                } else {

                    # Iterate all existing members
                    foreach ($member in $existingMembers) {

                        $resolvedAccount = $null

                        #remove * prefix from member
                        $sid = $member.TrimStart('*')

                        try {
                            # Call function to resolve SID
                            $resolvedAccount = ConvertTo-AccountName -SID $sid
                        } catch {
                            $txt = [System.Text.StringBuilder]::new()
                            [void]$txt.AppendLine($Constants.NL)
                            [void]$txt.AppendLine('Failed to resolve new member with SID: {0}' -f $sid)
                            [void]$txt.AppendLine('Item might not be added to the corresponding section. Please verify it!')
                            [void]$txt.AppendLine($Constants.NL)
                            Write-Warning -Message $txt
                            ##Get-ErrorDetail -ErrorRecord $_
                        } #end Try-Catch

                        if ($resolvedAccount) {
                            [void]$resolvedMembers.Add('*{0}' -f $sid)
                            Write-Verbose ('
                                Resolved existing member: {0}
                                                     SID: {1}'
 -f
                                $resolvedAccount[0], $sid
                            )
                        } #end If
                    } #end Foreach
                } #end If-Else
            } #end If-Else

            # Process new members
            if (
                ($null -eq $Members) -or
                (($Members.Count -eq 1) -and [string]::IsNullOrEmpty($Members[0]))
            ) {
                Write-Verbose -Message 'New members parameter is null or a single null string.'

                $resolvedMembers.Clear()
                [void]$resolvedMembers.Add( [string]::Empty )

            } else {

                #iterate all new members
                foreach ($member in $Members) {

                    if (-not [string]::IsNullOrWhiteSpace($member)) {

                        # Resolve SID to AD Identity
                        #$sid = Resolve-MemberIdentity -Member $member
                        $ReturnedMember = Get-AdObjectType -Identity $member

                        If ($ReturnedMember) {
                            if ($ReturnedMember.PSobject.Properties.name -match 'SID') {
                                $Sid = $ReturnedMember.SID.Value
                            } else {
                                $Sid = $ReturnedMember
                            }
                        } else {
                            $Sid = Test-NameIsWellKnownSid -Name $member
                        } #end If-Else

                        if ($sid) {

                            [void]$resolvedMembers.Add('*{0}' -f $sid)
                            Write-Verbose ('
                                Resolved new member: {0}
                                                SID: {1}'
 -f
                                $member, $sid
                            )

                        } else {
                            $txt = [System.Text.StringBuilder]::new()
                            [void]$txt.AppendLine($Constants.NL)
                            [void]$txt.AppendLine('Failed to resolve new member: {0}' -f $member)
                            [void]$txt.AppendLine('Item might not be added to the corresponding section. Please verify it!')
                            [void]$txt.AppendLine($Constants.NL)
                            Write-Warning -Message $txt
                            ##Get-ErrorDetail -ErrorRecord $_
                        } #end If-Else

                    } #end If

                } #end Foreach

            } #end If-Else

            # Convert resolved members to string
            $updatedValue = if (
                ($resolvedMembers.Count -eq 1) -and
                ($null -eq $resolvedMembers[0])
            ) {

                # add empty string
                [string]::Empty

            } else {

                # Join all members to a comma limited string
                ($resolvedMembers | Sort-Object) -join ','

            } #end If-Else

            # remove unwanted characters from the end.
            $updatedValue = $updatedValue.TrimEnd(',. ')

            if ($PSCmdlet.ShouldProcess("$CurrentKey in section $CurrentSection", 'Updating key value')) {

                # Update the GPT template
                $GptTmpl.SetKeyValue($CurrentSection, $CurrentKey, $updatedValue)

                Write-Verbose -Message ('
                    Updated key {0}
                    in section {1}
                    with value: {2}'
 -f
                    $CurrentKey, $CurrentSection, $updatedValue
                )

            } else {
                Write-Verbose -Message 'Skipping update due to WhatIf condition'
            }

        } catch {
            Write-Error -Message ('
                Failed to update key {0} in section {1}.
                {2}'
 -f
                $CurrentKey, $CurrentSection, $_
            )
            ##Get-ErrorDetail -ErrorRecord $_
        } #end Try-Catch
    } #end Process

    End {
        $txt = ($Variables.FooterDelegation -f $MyInvocation.InvocationName,
            'configuration of GptTmpl object section (Private Function).'
        )
        Write-Verbose -Message $txt

        return $GptTmpl
    }
} #end Set-GPOConfigSection