DSCResources/MSFT_EXODynamicDistributionGroup/MSFT_EXODynamicDistributionGroup.psm1

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [Parameter()]
        [ValidateLength(1,64)]
        [System.String]
        $Name,

        [Parameter()]
        [System.String[]]
        $AcceptMessagesOnlyFrom,

        [Parameter()]
        [System.String[]]
        $AcceptMessagesOnlyFromDLMembers,

        [Parameter()]
        [ValidateLength(1,64)]
        [System.String]
        $Alias,

        [Parameter()]
        [System.String[]]
        $BypassModerationFromSendersOrMembers,

        [Parameter()]
        [System.String[]]
        $ConditionalCompany,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute1,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute10,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute11,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute12,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute13,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute14,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute15,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute2,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute3,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute4,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute5,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute6,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute7,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute8,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute9,

        [Parameter()]
        [System.String[]]
        $ConditionalDepartment,

        [Parameter()]
        [System.String[]]
        $ConditionalStateOrProvince,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute1,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute10,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute11,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute12,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute13,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute14,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute15,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute2,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute3,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute4,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute5,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute6,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute7,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute8,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute9,

        [Parameter()]
        [ValidateLength(0, 256)]
        [System.String]
        $DisplayName,

        [Parameter()]
        [System.String[]]
        $EmailAddresses,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute1,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute2,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute3,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute4,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute5,

        [Parameter()]
        [System.String[]]
        $GrantSendOnBehalfTo,

        [Parameter()]
        [System.Boolean]
        $HiddenFromAddressListsEnabled,

        [Parameter()]
        [ValidateSet('AllRecipients','MailboxUsers','MailboxContacts','MailGroups','MailUsers','Resources')]
        [System.String[]]
        $IncludedRecipients,

        [Parameter()]
        [System.String]
        $MailTip,

        [Parameter()]
        [System.String[]]
        $MailTipTranslations,

        [Parameter()]
        [System.String]
        $ManagedBy,

        [Parameter()]
        [System.String[]]
        $ModeratedBy,

        [Parameter()]
        [System.Boolean]
        $ModerationEnabled,

        [Parameter()]
        [System.String]
        $Notes,

        [Parameter()]
        [ValidateLength(0,256)]
        [System.String]
        $PhoneticDisplayName,

        [Parameter()]
        [System.String]
        $PrimarySmtpAddress,

        [Parameter()]
        [System.String]
        $RecipientContainer,

        [Parameter()]
        [System.String]
        $RecipientFilter,

        [Parameter()]
        [System.String[]]
        $RejectMessagesFrom,

        [Parameter()]
        [System.String[]]
        $RejectMessagesFromDLMembers,

        [Parameter()]
        [System.String[]]
        $RejectMessagesFromSendersOrMembers,

        [Parameter()]
        [System.Boolean]
        $ReportToManagerEnabled,

        [Parameter()]
        [System.Boolean]
        $ReportToOriginatorEnabled,

        [Parameter()]
        [System.Boolean]
        $RequireSenderAuthenticationEnabled,

        [Parameter()]
        [ValidateSet('Always','Internal','Never')]
        [System.String]
        $SendModerationNotifications,

        [Parameter()]
        [System.Boolean]
        $SendOofMessageToOriginatorEnabled,

        [Parameter()]
        [System.String]
        $SimpleDisplayName,

        [Parameter()]
        [System.String]
        $WindowsEmailAddress,

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present',

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter()]
        [System.String]
        $ApplicationId,

        [Parameter()]
        [System.String]
        $TenantId,

        [Parameter()]
        [System.String]
        $CertificateThumbprint,

        [Parameter()]
        [System.String]
        $CertificatePath,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $CertificatePassword,

        [Parameter()]
        [Switch]
        $ManagedIdentity,

        [Parameter()]
        [System.String[]]
        $AccessTokens
    )

    Write-Verbose -Message "Getting configuration of the EXO Dynamic Distribution Group with Identity {$Identity}"

    try
    {
        if (-not $Script:exportedInstance -or $Script:exportedInstance.Name -ne $Name)
        {
            $null = New-M365DSCConnection -Workload 'ExchangeOnline' `
                -InboundParameters $PSBoundParameters

            #Ensure the proper dependencies are installed in the current environment.
            Confirm-M365DSCDependencies

            #region Telemetry
            $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '')
            $CommandName = $MyInvocation.MyCommand
            $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName `
                -CommandName $CommandName `
                -Parameters $PSBoundParameters
            Add-M365DSCTelemetryEvent -Data $data
            #endregion

            $nullResult = $PSBoundParameters
            $nullResult.Ensure = 'Absent'

            if (-not [System.String]::IsNullOrEmpty($PrimarySmtpAddress))
            {
                $instance = Get-DynamicDistributionGroup -Identity $PrimarySmtpAddress -ErrorAction SilentlyContinue
            }
            else
            {
                $instance = Get-DynamicDistributionGroup -Identity $Identity -ErrorAction SilentlyContinue
            }

            if ($null -eq $instance)
            {
                Write-Verbose -Message "Could not find an EXO Dynamic Distribution Group with Identity $($Identity)."
                return $nullResult
            }
        }
        else
        {
            $instance = $Script:exportedInstance
        }

        Write-Verbose -Message "An EXO Dynamic Distribution Group with Identity {$Identity} was found"

        $acceptMessagesOnlyFromValue = @()
        if ($null -ne $instance.AcceptMessagesOnlyFrom)
        {
            Write-Verbose -Message "Getting Dynamic Distribution Group AcceptMessagesOnlyFrom for $Identity"
            $acceptMessagesOnlyFromValue += Get-ElementFromRecipientsCacheAsPrimarySmtpAddress -RecipientName $instance.AcceptMessagesOnlyFrom
        }

        $acceptMessagesOnlyFromDLMembersValue = @()
        if ($null -ne $instance.AcceptMessagesOnlyFromDLMembers)
        {
            Write-Verbose -Message "Getting Dynamic Distribution Group AcceptMessagesOnlyFromDLMembers for $Identity"
            $acceptMessagesOnlyFromDLMembersValue += Get-ElementFromRecipientsCacheAsPrimarySmtpAddress -RecipientName $instance.AcceptMessagesOnlyFromDLMembers
        }

        $bypassModerationFromSendersOrMembersValue = @()
        if ($null -ne $instance.BypassModerationFromSendersOrMembers)
        {
            Write-Verbose -Message "Getting Dynamic Distribution Group BypassModerationFromSendersOrMembers for $Identity"
            $bypassModerationFromSendersOrMembersValue += Get-ElementFromRecipientsCacheAsPrimarySmtpAddress -RecipientName $instance.BypassModerationFromSendersOrMembers
        }

        $grantSendOnBehalfToValue = @()
        if ($null -ne $instance.GrantSendOnBehalfTo)
        {
            Write-Verbose -Message "Getting Dynamic Distribution Group GrantSendOnBehalfTo for $Identity"
            $grantSendOnBehalfToValue += Get-ElementFromRecipientsCacheAsPrimarySmtpAddress -RecipientName $instance.GrantSendOnBehalfTo
        }

        $managedByValue = $null
        if ($null -ne $instance.ManagedBy)
        {
            Write-Verbose -Message "Getting Dynamic Distribution Group manager for $Identity"
            $managedByValue = Get-ElementFromRecipientsCacheAsPrimarySmtpAddress -RecipientName $instance.ManagedBy
        }

        $moderatedByValue = @()
        if ($null -ne $instance.ModeratedBy)
        {
            Write-Verbose -Message "Getting Dynamic Distribution Group moderators for $Identity"
            $moderatedByValue += Get-ElementFromRecipientsCacheAsPrimarySmtpAddress -RecipientName $instance.ModeratedBy
        }

        $rejectMessagesFromValue = @()
        if ($null -ne $instance.RejectMessagesFrom)
        {
            Write-Verbose -Message "Getting Dynamic Distribution Group RejectMessagesFrom for $Identity"
            $rejectMessagesFromValue += Get-ElementFromRecipientsCacheAsPrimarySmtpAddress -RecipientName $instance.RejectMessagesFrom
        }

        $rejectMessagesFromDLMembersValue = @()
        if ($null -ne $instance.RejectMessagesFromDLMembers)
        {
            Write-Verbose -Message "Getting Dynamic Distribution Group RejectMessagesFromDLMembers for $Identity"
            $rejectMessagesFromDLMembersValue += Get-ElementFromRecipientsCacheAsPrimarySmtpAddress -RecipientName $instance.RejectMessagesFromDLMembers
        }

        $results = @{
            AcceptMessagesOnlyFrom                 = $acceptMessagesOnlyFromValue
            AcceptMessagesOnlyFromDLMembers        = $acceptMessagesOnlyFromDLMembersValue
            Alias                                  = $instance.Alias
            BypassModerationFromSendersOrMembers   = $bypassModerationFromSendersOrMembersValue
            ConditionalCompany                     = $instance.ConditionalCompany
            ConditionalCustomAttribute1            = $instance.ConditionalCustomAttribute1
            ConditionalCustomAttribute10           = $instance.ConditionalCustomAttribute10
            ConditionalCustomAttribute11           = $instance.ConditionalCustomAttribute11
            ConditionalCustomAttribute12           = $instance.ConditionalCustomAttribute12
            ConditionalCustomAttribute13           = $instance.ConditionalCustomAttribute13
            ConditionalCustomAttribute14           = $instance.ConditionalCustomAttribute14
            ConditionalCustomAttribute15           = $instance.ConditionalCustomAttribute15
            ConditionalCustomAttribute2            = $instance.ConditionalCustomAttribute2
            ConditionalCustomAttribute3            = $instance.ConditionalCustomAttribute3
            ConditionalCustomAttribute4            = $instance.ConditionalCustomAttribute4
            ConditionalCustomAttribute5            = $instance.ConditionalCustomAttribute5
            ConditionalCustomAttribute6            = $instance.ConditionalCustomAttribute6
            ConditionalCustomAttribute7            = $instance.ConditionalCustomAttribute7
            ConditionalCustomAttribute8            = $instance.ConditionalCustomAttribute8
            ConditionalCustomAttribute9            = $instance.ConditionalCustomAttribute9
            ConditionalDepartment                  = $instance.ConditionalDepartment
            ConditionalStateOrProvince             = $instance.ConditionalStateOrProvince
            CustomAttribute1                       = $instance.CustomAttribute1
            CustomAttribute10                      = $instance.CustomAttribute10
            CustomAttribute11                      = $instance.CustomAttribute11
            CustomAttribute12                      = $instance.CustomAttribute12
            CustomAttribute13                      = $instance.CustomAttribute13
            CustomAttribute14                      = $instance.CustomAttribute14
            CustomAttribute15                      = $instance.CustomAttribute15
            CustomAttribute2                       = $instance.CustomAttribute2
            CustomAttribute3                       = $instance.CustomAttribute3
            CustomAttribute4                       = $instance.CustomAttribute4
            CustomAttribute5                       = $instance.CustomAttribute5
            CustomAttribute6                       = $instance.CustomAttribute6
            CustomAttribute7                       = $instance.CustomAttribute7
            CustomAttribute8                       = $instance.CustomAttribute8
            CustomAttribute9                       = $instance.CustomAttribute9
            DisplayName                            = $instance.DisplayName
            EmailAddresses                         = $instance.EmailAddresses
            ExtensionCustomAttribute1              = $instance.ExtensionCustomAttribute1
            ExtensionCustomAttribute2              = $instance.ExtensionCustomAttribute2
            ExtensionCustomAttribute3              = $instance.ExtensionCustomAttribute3
            ExtensionCustomAttribute4              = $instance.ExtensionCustomAttribute4
            ExtensionCustomAttribute5              = $instance.ExtensionCustomAttribute5
            ForceMembershipRefresh                 = $instance.ForceMembershipRefresh
            GrantSendOnBehalfTo                    = $grantSendOnBehalfToValue
            HiddenFromAddressListsEnabled          = $instance.HiddenFromAddressListsEnabled
            Identity                               = $instance.Identity
            IncludedRecipients                     = $instance.IncludedRecipients
            MailTip                                = $instance.MailTip
            MailTipTranslations                    = $instance.MailTipTranslations
            ManagedBy                              = $managedByValue
            ModeratedBy                            = $moderatedByValue
            ModerationEnabled                      = $instance.ModerationEnabled
            Name                                   = $instance.Name
            Notes                                  = $instance.Notes
            PhoneticDisplayName                    = $instance.PhoneticDisplayName
            PrimarySmtpAddress                     = $instance.PrimarySmtpAddress
            RecipientContainer                     = $instance.RecipientContainer
            RecipientFilter                        = Restore-OriginalRecipientFilter -ExpandedFilter $instance.RecipientFilter
            RejectMessagesFrom                     = $rejectMessagesFromValue
            RejectMessagesFromDLMembers            = $rejectMessagesFromDLMembersValue
            ReportToManagerEnabled                 = $instance.ReportToManagerEnabled
            ReportToOriginatorEnabled              = $instance.ReportToOriginatorEnabled
            RequireSenderAuthenticationEnabled     = $instance.RequireSenderAuthenticationEnabled
            SendModerationNotifications            = $instance.SendModerationNotifications
            SendOofMessageToOriginatorEnabled      = $instance.SendOofMessageToOriginatorEnabled
            SimpleDisplayName                      = $instance.SimpleDisplayName
            UpdateMemberCount                      = $instance.UpdateMemberCount
            WindowsEmailAddress                    = $instance.WindowsEmailAddress
            Ensure                                 = 'Present'
            Credential                             = $Credential
            ApplicationId                          = $ApplicationId
            TenantId                               = $TenantId
            CertificateThumbprint                  = $CertificateThumbprint
            ApplicationSecret                      = $ApplicationSecret
        }
        return $results
    }
    catch
    {
        New-M365DSCLogEntry -Message 'Error retrieving data:' `
            -Exception $_ `
            -Source $($MyInvocation.MyCommand.Source) `
            -TenantId $TenantId `
            -Credential $Credential

        return $nullResult
    }
}

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [Parameter()]
        [ValidateLength(1,64)]
        [System.String]
        $Name,

        [Parameter()]
        [System.String[]]
        $AcceptMessagesOnlyFrom,

        [Parameter()]
        [System.String[]]
        $AcceptMessagesOnlyFromDLMembers,

        [Parameter()]
        [ValidateLength(1,64)]
        [System.String]
        $Alias,

        [Parameter()]
        [System.String[]]
        $BypassModerationFromSendersOrMembers,

        [Parameter()]
        [System.String[]]
        $ConditionalCompany,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute1,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute10,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute11,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute12,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute13,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute14,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute15,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute2,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute3,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute4,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute5,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute6,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute7,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute8,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute9,

        [Parameter()]
        [System.String[]]
        $ConditionalDepartment,

        [Parameter()]
        [System.String[]]
        $ConditionalStateOrProvince,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute1,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute10,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute11,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute12,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute13,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute14,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute15,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute2,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute3,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute4,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute5,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute6,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute7,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute8,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute9,

        [Parameter()]
        [ValidateLength(0, 256)]
        [System.String]
        $DisplayName,

        [Parameter()]
        [System.String[]]
        $EmailAddresses,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute1,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute2,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute3,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute4,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute5,

        [Parameter()]
        [System.String[]]
        $GrantSendOnBehalfTo,

        [Parameter()]
        [System.Boolean]
        $HiddenFromAddressListsEnabled,

        [Parameter()]
        [ValidateSet('AllRecipients','MailboxUsers','MailboxContacts','MailGroups','MailUsers','Resources')]
        [System.String[]]
        $IncludedRecipients,

        [Parameter()]
        [System.String]
        $MailTip,

        [Parameter()]
        [System.String[]]
        $MailTipTranslations,

        [Parameter()]
        [System.String]
        $ManagedBy,

        [Parameter()]
        [System.String[]]
        $ModeratedBy,

        [Parameter()]
        [System.Boolean]
        $ModerationEnabled,

        [Parameter()]
        [System.String]
        $Notes,

        [Parameter()]
        [ValidateLength(0,256)]
        [System.String]
        $PhoneticDisplayName,

        [Parameter()]
        [System.String]
        $PrimarySmtpAddress,

        [Parameter()]
        [System.String]
        $RecipientContainer,

        [Parameter()]
        [System.String]
        $RecipientFilter,

        [Parameter()]
        [System.String[]]
        $RejectMessagesFrom,

        [Parameter()]
        [System.String[]]
        $RejectMessagesFromDLMembers,

        [Parameter()]
        [System.String[]]
        $RejectMessagesFromSendersOrMembers,

        [Parameter()]
        [System.Boolean]
        $ReportToManagerEnabled,

        [Parameter()]
        [System.Boolean]
        $ReportToOriginatorEnabled,

        [Parameter()]
        [System.Boolean]
        $RequireSenderAuthenticationEnabled,

        [Parameter()]
        [ValidateSet('Always','Internal','Never')]
        [System.String]
        $SendModerationNotifications,

        [Parameter()]
        [System.Boolean]
        $SendOofMessageToOriginatorEnabled,

        [Parameter()]
        [System.String]
        $SimpleDisplayName,

        [Parameter()]
        [System.String]
        $WindowsEmailAddress,

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present',

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter()]
        [System.String]
        $ApplicationId,

        [Parameter()]
        [System.String]
        $TenantId,

        [Parameter()]
        [System.String]
        $CertificateThumbprint,

        [Parameter()]
        [System.String]
        $CertificatePath,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $CertificatePassword,

        [Parameter()]
        [Switch]
        $ManagedIdentity,

        [Parameter()]
        [System.String[]]
        $AccessTokens
    )

    $null = New-M365DSCConnection -Workload 'ExchangeOnline' `
        -InboundParameters $PSBoundParameters

    #Ensure the proper dependencies are installed in the current environment.
    Confirm-M365DSCDependencies

    #region Telemetry
    $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '')
    $CommandName = $MyInvocation.MyCommand
    $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName `
        -CommandName $CommandName `
        -Parameters $PSBoundParameters
    Add-M365DSCTelemetryEvent -Data $data
    #endregion

    $currentInstance = Get-TargetResource @PSBoundParameters
    $boundParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters
    if ($boundParameters.ContainsKey('RecipientFilter') -and -not [System.String]::IsNullOrEmpty($boundParameters['RecipientFilter']))
    {
        Write-Verbose -Message 'Removing Conditional* parameters because RecipientFilter is set'
        $boundParameters.Keys.Clone() | ForEach-Object {
            if ($_ -like 'Conditional*')
            {
                $boundParameters.Remove($_) | Out-Null
            }
        }
    }

    if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent')
    {
        $createParameters = ([Hashtable]$boundParameters).Clone()
        $onlyUpateParameters = @(
            'Identity',
            'AcceptMessagesOnlyFrom',
            'AcceptMessagesOnlyFromDLMembers',
            'BypassModerationFromSendersOrMembers',
            'CustomAttribute1',
            'CustomAttribute10',
            'CustomAttribute11',
            'CustomAttribute12',
            'CustomAttribute13',
            'CustomAttribute14',
            'CustomAttribute15',
            'CustomAttribute2',
            'CustomAttribute3',
            'CustomAttribute4',
            'CustomAttribute5',
            'CustomAttribute6',
            'CustomAttribute7',
            'CustomAttribute8',
            'CustomAttribute9',
            'EmailAddresses',
            'ExtensionCustomAttribute1',
            'ExtensionCustomAttribute2',
            'ExtensionCustomAttribute3',
            'ExtensionCustomAttribute4',
            'ExtensionCustomAttribute5',
            'GrantSendOnBehalfTo',
            'HiddenFromAddressListsEnabled',
            'MailTip',
            'MailTipTranslations',
            'ManagedBy',
            'Notes',
            'PhoneticDisplayName',
            'RejectMessagesFrom',
            'RejectMessagesFromDLMembers',
            'ReportToManagerEnabled',
            'ReportToOriginatorEnabled',
            'RequireSenderAuthenticationEnabled',
            'SendOofMessageToOriginatorEnabled',
            'SimpleDisplayName',
            'WindowsEmailAddress'
        )

        $updateParameters = @{}
        foreach ($param in $onlyUpateParameters)
        {
            if ($createParameters.ContainsKey($param) -and $param -ne 'Identity')
            {
                $updateParameters[$param] = $createParameters[$param]
            }
            $createParameters.Remove($param) | Out-Null
        }

        Write-Verbose -Message "Creating an EXO Dynamic Distribution Group with Identity {$Identity}"
        $group = New-DynamicDistributionGroup @createParameters

        if ($updateParameters.Count -gt 1)
        {
            Write-Verbose -Message "Updating the EXO Dynamic Distribution Group with Identity {$Identity} after creation"
            Set-DynamicDistributionGroup -Identity $group.Identity @updateParameters | Out-Null
        }
    }
    elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present')
    {
        Write-Verbose -Message "Updating the EXO Dynamic Distribution Group with Identity {$Identity}"

        $updateParameters = ([Hashtable]$boundParameters).Clone()
        if ($EmailAddresses.Length -gt 0)
        {
            $updateParameters.Remove('PrimarySmtpAddress') | Out-Null
        }

        if ($AcceptMessagesOnlyFrom.Length -gt 0)
        {
            $updateParameters.Remove('AcceptMessagesOnlyFromDLMembers') | Out-Null
            $updateParameters.Remove('AcceptMessagesOnlyFromSendersOrMembers') | Out-Null
        }

        Set-DynamicDistributionGroup @updateParameters | Out-Null
    }
    elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present')
    {
        Write-Verbose -Message "Removing the EXO Dynamic Distribution Group with Identity {$Identity}"
        Remove-DynamicDistributionGroup -Identity $currentInstance.Identity
    }
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [Parameter()]
        [ValidateLength(1,64)]
        [System.String]
        $Name,

        [Parameter()]
        [System.String[]]
        $AcceptMessagesOnlyFrom,

        [Parameter()]
        [System.String[]]
        $AcceptMessagesOnlyFromDLMembers,

        [Parameter()]
        [ValidateLength(1,64)]
        [System.String]
        $Alias,

        [Parameter()]
        [System.String[]]
        $BypassModerationFromSendersOrMembers,

        [Parameter()]
        [System.String[]]
        $ConditionalCompany,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute1,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute10,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute11,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute12,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute13,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute14,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute15,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute2,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute3,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute4,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute5,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute6,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute7,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute8,

        [Parameter()]
        [System.String[]]
        $ConditionalCustomAttribute9,

        [Parameter()]
        [System.String[]]
        $ConditionalDepartment,

        [Parameter()]
        [System.String[]]
        $ConditionalStateOrProvince,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute1,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute10,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute11,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute12,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute13,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute14,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute15,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute2,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute3,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute4,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute5,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute6,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute7,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute8,

        [Parameter()]
        [ValidateLength(0, 1024)]
        [System.String]
        $CustomAttribute9,

        [Parameter()]
        [ValidateLength(0, 256)]
        [System.String]
        $DisplayName,

        [Parameter()]
        [System.String[]]
        $EmailAddresses,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute1,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute2,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute3,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute4,

        [Parameter()]
        [System.String[]]
        $ExtensionCustomAttribute5,

        [Parameter()]
        [System.String[]]
        $GrantSendOnBehalfTo,

        [Parameter()]
        [System.Boolean]
        $HiddenFromAddressListsEnabled,

        [Parameter()]
        [ValidateSet('AllRecipients','MailboxUsers','MailboxContacts','MailGroups','MailUsers','Resources')]
        [System.String[]]
        $IncludedRecipients,

        [Parameter()]
        [System.String]
        $MailTip,

        [Parameter()]
        [System.String[]]
        $MailTipTranslations,

        [Parameter()]
        [System.String]
        $ManagedBy,

        [Parameter()]
        [System.String[]]
        $ModeratedBy,

        [Parameter()]
        [System.Boolean]
        $ModerationEnabled,

        [Parameter()]
        [System.String]
        $Notes,

        [Parameter()]
        [ValidateLength(0,256)]
        [System.String]
        $PhoneticDisplayName,

        [Parameter()]
        [System.String]
        $PrimarySmtpAddress,

        [Parameter()]
        [System.String]
        $RecipientContainer,

        [Parameter()]
        [System.String]
        $RecipientFilter,

        [Parameter()]
        [System.String[]]
        $RejectMessagesFrom,

        [Parameter()]
        [System.String[]]
        $RejectMessagesFromDLMembers,

        [Parameter()]
        [System.String[]]
        $RejectMessagesFromSendersOrMembers,

        [Parameter()]
        [System.Boolean]
        $ReportToManagerEnabled,

        [Parameter()]
        [System.Boolean]
        $ReportToOriginatorEnabled,

        [Parameter()]
        [System.Boolean]
        $RequireSenderAuthenticationEnabled,

        [Parameter()]
        [ValidateSet('Always','Internal','Never')]
        [System.String]
        $SendModerationNotifications,

        [Parameter()]
        [System.Boolean]
        $SendOofMessageToOriginatorEnabled,

        [Parameter()]
        [System.String]
        $SimpleDisplayName,

        [Parameter()]
        [System.String]
        $WindowsEmailAddress,

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present',

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter()]
        [System.String]
        $ApplicationId,

        [Parameter()]
        [System.String]
        $TenantId,

        [Parameter()]
        [System.String]
        $CertificateThumbprint,

        [Parameter()]
        [System.String]
        $CertificatePath,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $CertificatePassword,

        [Parameter()]
        [Switch]
        $ManagedIdentity,

        [Parameter()]
        [System.String[]]
        $AccessTokens
    )

    #region Telemetry
    $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '')
    $CommandName = $MyInvocation.MyCommand
    $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName `
        -CommandName $CommandName `
        -Parameters $PSBoundParameters
    Add-M365DSCTelemetryEvent -Data $data
    #endregion

    $compareParameters = Get-CompareParameters
    $result = Test-M365DSCTargetResource -DesiredValues $PSBoundParameters `
        -ResourceName $($MyInvocation.MyCommand.Source).Replace('MSFT_', '') `
        @compareParameters
    return $result
}

function Export-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter()]
        [System.String]
        $Filter = "*",

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter()]
        [System.String]
        $ApplicationId,

        [Parameter()]
        [System.String]
        $TenantId,

        [Parameter()]
        [System.String]
        $CertificateThumbprint,

        [Parameter()]
        [System.String]
        $CertificatePath,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $CertificatePassword,

        [Parameter()]
        [Switch]
        $ManagedIdentity,

        [Parameter()]
        [System.String[]]
        $AccessTokens
    )

   $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' `
        -InboundParameters $PSBoundParameters

    #Ensure the proper dependencies are installed in the current environment.
    Confirm-M365DSCDependencies

    #region Telemetry
    $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '')
    $CommandName = $MyInvocation.MyCommand
    $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName `
        -CommandName $CommandName `
        -Parameters $PSBoundParameters
    Add-M365DSCTelemetryEvent -Data $data
    #endregion

    try
    {
        [array]$getValue = Get-DynamicDistributionGroup -Filter $Filter -ErrorAction Stop

        $i = 1
        $dscContent = ''
        if ($getValue.Length -eq 0)
        {
            Write-M365DSCHost -Message $Global:M365DSCEmojiGreenCheckMark
        }
        else
        {
            Write-M365DSCHost -Message "`r`n" -DeferWrite
        }
        foreach ($config in $getValue)
        {
            if ($null -ne $Global:M365DSCExportResourceInstancesCount)
            {
                $Global:M365DSCExportResourceInstancesCount++
            }

            $displayedKey = $config.Identity
            if (-not [String]::IsNullOrEmpty($config.displayName))
            {
                $displayedKey = $config.displayName
            }
            Write-M365DSCHost -Message " |---[$i/$($getValue.Count)] $displayedKey" -DeferWrite
            $params = @{
                Identity              = $config.Identity
                Ensure                = 'Present'
                Credential            = $Credential
                ApplicationId         = $ApplicationId
                TenantId              = $TenantId
                CertificateThumbprint = $CertificateThumbprint
                CertificatePassword   = $CertificatePassword
                ManagedIdentity       = $ManagedIdentity.IsPresent
                CertificatePath       = $CertificatePath
                AccessTokens          = $AccessTokens
            }

            $Results = Get-TargetResource @Params
            $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName `
                -ConnectionMode $ConnectionMode `
                -ModulePath $PSScriptRoot `
                -Results $Results `
                -Credential $Credential
            $dscContent += $currentDSCBlock
            Save-M365DSCPartialExport -Content $currentDSCBlock `
                -FileName $Global:PartialExportFileName
            $i++
            Write-M365DSCHost -Message $Global:M365DSCEmojiGreenCheckMark -CommitWrite
        }
        return $dscContent
    }
    catch
    {
        Write-M365DSCHost -Message $Global:M365DSCEmojiRedX -CommitWrite

        New-M365DSCLogEntry -Message 'Error during Export:' `
            -Exception $_ `
            -Source $($MyInvocation.MyCommand.Source) `
            -TenantId $TenantId `
            -Credential $Credential

        return ''
    }
}

function Get-ElementFromRecipientsCacheAsPrimarySmtpAddress
{
    param
    (
        [Parameter(Mandatory = $true)]
        [AllowEmptyCollection()]
        [System.String[]]
        $RecipientName
    )

    if ($null -eq $Script:RecipientsCache)
    {
        if ($null -eq $Script:RecipientsCache)
        {
            $Script:RecipientsCache = [System.Collections.Generic.Dictionary[System.String, System.Object]]::new()

        }
    }

    foreach ($name in $RecipientName)
    {
        if (-not $Script:RecipientsCache.ContainsKey($name))
        {
            Get-Recipient -Identity $name -ErrorAction SilentlyContinue | ForEach-Object {
                $Script:RecipientsCache[$_.Name] = @{
                    PrimarySmtpAddress = $_.PrimarySmtpAddress
                    WindowsLiveID      = $_.WindowsLiveID
                }
            }
        }
        $Script:RecipientsCache[$name].PrimarySmtpAddress
    }
}

<#
.SYNOPSIS
    Normalize RecipientFilter like Exchange Online's wrapping behavior.
 
.DESCRIPTION
    - Expects atomic conditions to be parenthesized, e.g. "(Company -eq 'Contoso')".
    - Respects operator precedence: -and higher than -or.
    - Wraps -and nodes with two extra parentheses (so the -and group becomes triple-wrapped).
    - Adds one additional outer parentheses per operator in the whole expression (to mimic Exchange).
#>

function Convert-ToExchangeFilterSyntax
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Expression
    )

    function ConvertFrom-ExchangeExpression {
        param ([System.String] $expr)

        # Trim outer spaces and redundant parentheses
        $expr = $expr.Trim()
        while ($expr.StartsWith('(') -and $expr.EndsWith(')'))
        {
            $inner = $expr.Substring(1, $expr.Length - 2).Trim()
            $balance = 0
            $valid = $true
            foreach ($ch in $inner.ToCharArray())
            {
                if ($ch -eq '(')
                {
                    $balance++
                }
                elseif ($ch -eq ')')
                {
                    $balance--
                }

                if ($balance -lt 0)
                {
                    $valid = $false
                    break
                }
            }

            if (-not $valid -or $balance -ne 0)
            {
                break
            }
            $expr = $inner
        }

        # Parse into tokens considering nested parentheses
        $tokens = [System.Collections.Generic.List[string]]::new()
        $current = ''
        $depth = 0
        for ($i = 0; $i -lt $expr.Length; $i++)
        {
            $ch = $expr[$i]
            if ($ch -eq '(')
            {
                $depth++
                $current += $ch
            }
            elseif ($ch -eq ')')
            {
                $depth--
                $current += $ch
            }
            elseif ($depth -eq 0 -and $expr.Substring($i) -match '^-or\s|^-and\s')
            {
                # Split at top-level logical operator
                $match = $matches[0].Trim()
                $tokens.Add(($current.Trim()))
                $tokens.Add($match)
                $current = ''
                $i += $match.Length - 1
            }
            else
            {
                $current += $ch
            }
        }
        if ($current.Trim())
        {
            $tokens.Add(($current.Trim()))
        }

        # Base condition — no logical operators
        if ($tokens.Count -eq 1)
        {
            return $tokens[0]
        }

        # Handle operator precedence: -and before -or
        # First handle all -and operations
        for ($i = 0; $i -lt $tokens.Count; $i++)
        {
            if ($tokens[$i] -eq '-and')
            {
                $left = ConvertFrom-ExchangeExpression $tokens[$i - 1]
                $right = ConvertFrom-ExchangeExpression $tokens[$i + 1]
                $combined = "(($left) -and ($right))"
                $tokens[$i - 1] = $combined
                $tokens.RemoveAt($i)    # remove operator
                $tokens.RemoveAt($i)    # remove right operand
                $i--
            }
        }

        # Then handle -or operations
        for ($i = 0; $i -lt $tokens.Count; $i++)
        {
            if ($tokens[$i] -eq '-or')
            {
                $left = ConvertFrom-ExchangeExpression $tokens[$i - 1]
                $right = ConvertFrom-ExchangeExpression $tokens[$i + 1]
                $combined = "(($left) -or ($right))"
                $tokens[$i - 1] = $combined
                $tokens.RemoveAt($i)
                $tokens.RemoveAt($i)
                $i--
            }
        }

        return $tokens[0]
    }

    $normalized = ConvertFrom-ExchangeExpression $Expression
    return "($normalized)"  # Always add one final outer wrapper
}


<#
.SYNOPSIS
    Restores the original RecipientFilter text as it was before Exchange Online expanded it.
 
.DESCRIPTION
    Exchange Online automatically expands RecipientFilters with extra clauses for system mailboxes,
    arbitration mailboxes, etc. This function removes those known injected query patterns and
    returns the user-defined part of the filter.
 
.PARAMETER ExpandedFilter
    The RecipientFilter string as returned by Get-DynamicDistributionGroup.
 
.EXAMPLE
    $group = Get-DynamicDistributionGroup -Identity "Test"
    Restore-OriginalRecipientFilter -ExpandedFilter $group.RecipientFilter
#>

function Restore-OriginalRecipientFilter
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ExpandedFilter
    )

    # region --- define the two known Exchange Online auto-append patterns ---
    $patterns = @()

    # 1. System/Arbitration mailbox exclusion block
    $patterns += [regex]::Escape("-and (-not(Name -like 'SystemMailbox{*')) -and (-not(Name -like 'CAS_{*')) -and (-not(RecipientTypeDetailsValue -eq 'MailboxPlan')) -and (-not(RecipientTypeDetailsValue -eq 'DiscoveryMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'PublicFolderMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'ArbitrationMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'AuditLogMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'AuxAuditLogMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'SupervisoryReviewPolicyMailbox'))")

    # 2. Extended RecipientType filtering block
    $patterns += [regex]::Escape("-and (((RecipientType -eq 'UserMailbox') -or (RecipientType -eq 'MailContact') -or (RecipientType -eq 'MailUser') -or (((RecipientType -eq 'MailUniversalDistributionGroup') -or (RecipientType -eq 'MailUniversalSecurityGroup') -or (RecipientType -eq 'MailNonUniversalGroup') -or (RecipientType -eq 'DynamicDistributionGroup'))) -or (((RecipientType -eq 'UserMailbox') -and (ResourceMetaData -like 'ResourceType:*') -and (ResourceSearchProperties -ne ))))))) -and (-not(RecipientTypeDetailsValue -eq 'GuestMailUser')))")

    # endregion

    $cleaned = $ExpandedFilter

    foreach ($pattern in $patterns)
    {
        $cleaned = [regex]::Replace($cleaned, $pattern, '', 'IgnoreCase')
    }

    # Normalize whitespace
    $cleaned = $cleaned -replace '\s{2,}', ' '

    # Trim outer parentheses if they wrap the whole expression
    $trimmed = $cleaned.Trim()
    if ($trimmed.StartsWith('(') -and $trimmed.EndsWith(')'))
    {
        # Check parentheses balance before trimming
        $open = 0
        $balanced = $true
        for ($i = 0; $i -lt $trimmed.Length; $i++)
        {
            switch ($trimmed[$i])
            {
                '(' { $open++ }
                ')' { $open-- }
            }

            if ($open -lt 0)
            {
                $balanced = $false
                break
            }
        }
        if ($balanced -and $open -eq 0)
        {
            # Remove only one outer layer of parentheses
            $trimmed = $trimmed.Substring(1, $trimmed.Length - 2).Trim()
        }
    }

    return $trimmed
}

function Get-CompareParameters
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param()

    return @{
        PostProcessing = {
            param($DesiredValues, $CurrentValues, $ValuesToCheck, $ignore)
            if ($DesiredValues.ContainsKey('RecipientFilter') -and -not [System.String]::IsNullOrEmpty($DesiredValues.RecipientFilter))
            {
                $DesiredValues.RecipientFilter = Convert-ToExchangeFilterSyntax -Expression $DesiredValues.RecipientFilter

                # If RecipientFilter is specified, ignore the conditional properties
                $ValuesToCheck.Keys.Clone() | Where-Object { $_ -like "Conditional*" } | Foreach-Object {
                    $ValuesToCheck.Remove($_) | Out-Null
                }
            }
            return [System.Tuple[Hashtable, Hashtable, Hashtable]]::new($DesiredValues, $CurrentValues, $ValuesToCheck)
        }
    }
}

Export-ModuleMember -Function @('*-TargetResource', 'Get-CompareParameters')