Public/Set-CGMMStagingGroup.ps1

Function Set-CGMMStagingGroup {
    <#
    .SYNOPSIS
    Sets properties on a staged Exchange Online distribution group.
 
    .DESCRIPTION
    Sets properties on a staged Exchange Online distribution group. A prefix is applied to properties that require unique values. Groups are hidden from address lists if HiddenFromAddressListsEnabled is not explicitly set to $False. Parameters include nearly every parameter used with Set-DistributionGroup in Exchange Online. Some parameters may require you to call Set-DistributionGroup from outside this module to properly apply.
 
    Most parameters utilize ValueFromPipelineByPropertyName. The most value comes from using Get-CGMMTargetGroup to pipe its custom properties when configuring a staged group.
 
    .EXAMPLE
    Set-CGMMStagingGroup -Identity $Identity
 
    .EXAMPLE
    Get-CGMMTargetGroup $OnPremiseDLIdentity | Set-CGMMStagingGroup -Identity $StagingGroupIdentity
 
    Pipe the on premise distribution group's settings into the new staging group.
    .NOTES
 
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        # Mandatory Parameters
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            Try {$_ -match "^$($StagingGroupPrefix)"}
            Catch {
                Throw "The target group must begin with $($StagingGroupPrefix)."
            }
        })]
        [string]$Identity,

        # Optional Parameters
        [Parameter()]
        [array]$AcceptMessagesOnlyFrom,

        [Parameter()]
        [array]$AcceptMessagesOnlyFromDLMembers,

        [Parameter()]
        [array]$AcceptMessagesOnlyFromSendersOrMembers,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Alias,

        [Parameter()]
        [array]$BypassModerationFromSendersOrMembers,
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Boolean]$BypassNestedModerationEnabled, 
        
        [Parameter()]
        [System.Management.Automation.SwitchParameter]$BypassSecurityGroupManagerCheck, 
        
        [Parameter()]
        [Boolean]$CreateDTMFMap, 
        
        [Parameter()]
        [string]$DisplayName,
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string[]]$EmailAddresses, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$ExtensionCustomAttribute1, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$ExtensionCustomAttribute2, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$ExtensionCustomAttribute3, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$ExtensionCustomAttribute4, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$ExtensionCustomAttribute5, 
        
        [Parameter()]
        [string[]]$GrantSendOnBehalfTo, 
        
        [Parameter()]
        [Boolean]$HiddenFromAddressListsEnabled=$True, 
        
        [Parameter()]
        [System.Management.Automation.SwitchParameter]$IgnoreNamingPolicy, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$MailTip, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string[]]$MailTipTranslations, 
        
        [Parameter()]
        [string[]]$ManagedBy, 
        
        [Parameter()]
        [ValidateSet('Open','Closed')]
        [string]$MemberDepartRestriction, 
        
        [Parameter()]
        [ValidateSet('Open','Closed','ApprovalRequired')]
        [string]$MemberJoinRestriction, 
        
        [Parameter()]
        [string[]]$ModeratedBy, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Boolean]$ModerationEnabled, 

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$PrimarySmtpAddress,
        
        [Parameter()]
        [string[]]$RejectMessagesFrom, 
        
        [Parameter()]
        [string[]]$RejectMessagesFromDLMembers, 
        
        [Parameter()]
        [string[]]$RejectMessagesFromSendersOrMembers, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Boolean]$ReportToManagerEnabled, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Boolean]$ReportToOriginatorEnabled, 
        
        [Parameter()]
        [Boolean]$RequireSenderAuthenticationEnabled, 
        
        [Parameter()]
        [switch]$RoomList, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [ValidateSet('Always','Internal','Never')]
        [string]$SendModerationNotifications, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Boolean]$SendOofMessageToOriginatorEnabled, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$SimpleDisplayName, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string[]]$UMDtmfMap, 
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$WindowsEmailAddress,

        # CGMM Custom Attributes
        
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string[]]$AcceptMessagesOnlyFromSmtpAddresses,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string[]]$GrantSendOnBehalfToSmtpAddresses,

        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$LegacyExchangeDNCloud,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string[]]$ManagedBySmtpAddresses,

        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string[]]$ManagersSmtpAddresses,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string[]]$ModeratedBySmtpAddresses,

        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string[]]$RejectSendersSmtpAddresses,

        # Default property from Get-DistributionGroup that isn't default for Set-DistributionGroup
        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [string]$LegacyExchangeDN
    )

    begin {}
    process    {
        # Check for Exchange cmdlet availability in On Prem & Exchange Online
        Try {Test-CGMMCmdletAccess -Environment Cloud -ErrorAction Stop}
        Catch {
            $PsCmdlet.ThrowTerminatingError($PSItem)
        }

        # Get the current primary smtp address to see if a former primary needs added as a secondary
        $EAPSaved = $Global:ErrorActionPreference
        $Global:ErrorActionPreference = 'Stop'
        Write-Verbose "Searching for cloud distribution group $Identity"
        Try {
            $DistGroupCurrentState = Get-CloudCGMMDistributionGroup $Identity | Select-Object PrimarySMTPAddress,EmailAddresses
        }
        Catch {
            $Global:ErrorActionPreference = $EAPSaved
            $PsCmdlet.ThrowTerminatingError($PSItem)
        }
        $Global:ErrorActionPreference = $EAPSaved

        # Check for and apply prefix to optional fields
        Write-Verbose "Applying the prefix '$StagingGroupPrefix' to properties that must be unique."
        $OptionalParameters = @('Name','Alias','DisplayName','PrimarySMTPAddress','SimpleDisplayName','WindowsEmailAddress')
        ForEach ($Parameter in $OptionalParameters) {
            If ($PSBoundParameters.$Parameter) {
                $PSBoundParameters.$Parameter = $StagingGroupPrefix + $PSBoundParameters.$Parameter
            }
        }

        # Update the EmailAddresses to include the prefix
        If ($PSBoundParameters.EmailAddresses) {
            Write-Verbose "Processing email addresses and applying the prefix '$StagingGroupPrefix'"
            # Normalize the case sensitivity of X500. Office 365 defaults to uppercase and this script
            # sets the case to uppercase when adding legacy Exchange DNs. Other versions of Exchange, at
            # least Exchange 2010, returns the value in lowercase. Required when doing
            # `$NewAddressCollection | Select-Object -Unique` below.
            [array]$PSBoundParameters.EmailAddresses = $PSBoundParameters.EmailAddresses -replace "^x500:","X500:"
            # Create an array containing each address with the prefix added
            [array]$PrefixedEmailAddresses = ForEach ($Address in $PSBoundParameters.EmailAddresses) {
                # Prefix the addresses with a type (smtp:,X500:) that aren't already prefixed
                If ($Address -match ':' -and $Address -notmatch ":$($StagingGroupPrefix)") {
                    $Address -replace ":",":$($StagingGroupPrefix)"
                }
                # Prefix addresses without a type that aren't already prefixed
                ElseIf ($Address -notmatch "^$($StagingGroupPrefix)"-and $Address -notmatch ":" ) {
                    $Address.Insert(0,$StagingGroupPrefix)
                }
                # Capture already prefixed addresses
                Else {$Address}
            }
            
            If ($PrefixedEmailAddresses) {
                # Force EmailAddresses to only add secondaries
                [array]$PrefixedEmailAddresses = $PrefixedEmailAddresses -replace "^SMTP:","smtp:"
            }
            $Address = $Null
        }

        # Update the EmailAddresses to include both legacy Exchange DNs with the prefix
        $AddressingParameters = @('LegacyExchangeDNCloud','LegacyExchangeDN')
        [array]$PrefixedLegacyDNs = ForEach ($Parameter in $AddressingParameters) {
            If ($PSBoundParameters.$Parameter) {
                If ($($PSBoundParameters.$Parameter) -notmatch "^$($StagingGroupPrefix)") {
                    "X500:$($StagingGroupPrefix)$($PSBoundParameters.$Parameter)"
                }
                [void]$PSBoundParameters.Remove($Parameter)
            }
        }

        # Create an array list out of the new addresses.
        $NewAddressCollection = New-Object System.Collections.ArrayList
        If ($PrefixedEmailAddresses) {
            [void]$NewAddressCollection.AddRange($PrefixedEmailAddresses)
        }
        If ($PrefixedLegacyDNs) {
            [void]$NewAddressCollection.AddRange($PrefixedLegacyDNs)
        }

        # If new addresses exist only add addresses that don't already exist
        If ($NewAddressCollection.count -gt 0) {
            # Ensure $NewAddressCollection does not contain duplicates
            $NewAddressCollection = $NewAddressCollection | Select-Object -Unique
            # Get the current status minus address type. This is used to ensure we don't try to add
            # an SMTP and smtp version of an address.
            $CurrentStateTypeLess = $DistGroupCurrentState.EmailAddresses -replace "^.*:"
            [array]$SecondariesToAdd = ForEach ($NewAddress in $NewAddressCollection) {
                If ($CurrentStateTypeLess -notcontains $NewAddress.Split(':')[1]){
                    $NewAddress
                }
            }
            
            # Combine the addresses that don't exist to the current list and apply them back to
            # $PSBoundParameters.EmailAddresses so they may be splatted.
            If ($SecondariesToAdd) {
                [void]$DistGroupCurrentState.EmailAddresses.AddRange($SecondariesToAdd)
            }
            $PSBoundParameters.EmailAddresses = $DistGroupCurrentState.EmailAddresses
        }

        # Reassign parameters. On premise properties that use distinguished name won't work in the cloud, but
        # the property names are the same. The code segment below takes a custom properties populated by
        # Get-CGMMTargetGroup and reassigns their values to the property expected by the New-DistributionGroup
        # parameters.
        Write-Verbose "Reassigning custom properties to Exchange Online properties."
        $ReassignedParameters = @(
            @{'AcceptMessagesOnlyFromSendersOrMembers'    = 'AcceptMessagesOnlyFromSmtpAddresses'}
            @{'BypassModerationFromSendersOrMembers'    = 'BypassModerationOnlyFromSmtpAddresses'}
            @{'GrantSendOnBehalfTo'                     = 'GrantSendOnBehalfToSmtpAddresses'}
            @{'ManagedBy'                                = 'ManagedBySmtpAddresses'},
            @{'ModeratedBy'                                = 'ModeratedBySmtpAddresses'}
            @{'RejectMessagesFromSendersOrMembers'        = 'RejectSendersSmtpAddresses'}
        )
        ForEach ($Parameter in $ReassignedParameters) {
            $AssignTo = $($Parameter.Keys)
            $AssignFrom = $($Parameter.Values)

            # If the custom 'AssignFrom' property has a value rewrite the 'AssignTo' property Exchange cmdlet expects
            If ($PSBoundParameters.$AssignFrom) {
                Write-Verbose "Adding $AssignFrom to $AssignTo"
                $PSBoundParameters.Add($AssignTo,$($PSBoundParameters.$AssignFrom))
            }
            # Remove the custom property - it won't be expected by the Exchange cmdlet
            [void]$PSBoundparameters.Remove($AssignFrom)
        }

        # HiddenFromAddressListsEnabled
        If (-not $PSBoundParameters.HiddenFromAddressListsEnabled) {
            $PSBoundParameters.HiddenFromAddressListsEnabled = $HiddenFromAddressListsEnabled
        }

        # Prefixed call of New-DistributionGroup (Exchange Online version) with -WhatIf support
        If ($PSCmdlet.ShouldProcess($Identity,$MyInvocation.MyCommand)) {
            $EAPSaved = $Global:ErrorActionPreference
            $Global:ErrorActionPreference = 'Stop'
            Try {
                Set-CloudCGMMDistributionGroup @PSBoundParameters
            }
            Catch {
                $PsCmdlet.ThrowTerminatingError($PSItem)
            }
            Finally {
                $Global:ErrorActionPreference = $EAPSaved
            }
        }
    }
    end {}
}