Functions/Public/MSAzure.ps1

# Microsoft Azure and Graph API functions

Function Get-MSAzureUser {
    <#
        .SYNOPSIS
        Return a list of users from Azure AD.
         
        .DESCRIPTION
        Return a list of users from Azure AD.
         
        .PARAMETER Properties
        A comma-delimited list of properties to return in the results
        Format as per https://docs.microsoft.com/en-us/graph/query-parameters?view=graph-rest-1.0#select-parameter
        Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0#properties
         
        .PARAMETER Filter
        Add filter parameters as per https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0&view=graph-rest-1.0#filter-parameter
        Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0#properties
         
        .PARAMETER UPN
        Return user details for the given UserPrincipalName
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
 
        .PARAMETER HideProgressBar
        Don't show the progress bar. Cleans up logs when running on Docker.
         
        .PARAMETER AuthToken
        The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken
         
        .PARAMETER TotalCount
        Only return a total count of objects
         
        .PARAMETER ResultSize
        The number of results to return. Defaults to 1000.
         
        .EXAMPLE
        Get-MSAzureUser
        Returns all MS Azure user accounts
         
        .EXAMPLE
        Get-MSAzureUser -Properties 'id,userPrincipalName' -AuthToken $AuthToken
        Returns a list of Azure users' ID and userPrincipalNames using a previously-obtained authtoken
         
        .EXAMPLE
        Get-MSAzureUser -Filter "startswith(displayName,'Meeting Room')"
        Returns a list of Azure users whose display names start with 'Meeting Room'
 
        .NOTES
        Version 1.3
    #>


    
    Param (
        [Parameter(Mandatory=$False)]
        [string]$Properties,
        [Parameter(Mandatory=$False)]
        [string]$Filter,
        [Parameter(Mandatory=$False)]
        [string]$UPN,        
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(Mandatory=$False)]
        [switch]$TotalCount,
        [Parameter(Mandatory=$False)]
        [switch]$HideProgressBar,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$AuthToken,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,500000)]
        [int]$ResultSize = 500000,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,99999)]
        [int]$ProgressUpdateFreq = 1    
    )

    Process {
        $Body = @{'$count' = 'true'}
        
        Try {
            # Use globally set tenant name, if one was set and not explicitly included in the command
            If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }    
            
            If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsConfig -TenantName $TenantName | Get-MSGraphAccessToken }
        }
        Catch {
            Write-Error "Could not obtain authorization token. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }        
        }
        
        If ($AuthToken) {
            Try {
                $Headers = @{
                    Authorization         = "Bearer $AuthToken"
                    ConsistencyLevel     = 'eventual'
                }

                If ($Properties) { $Body.Add('$select',$Properties)    }
                If ($Filter) { $Body.Add('$filter',$Filter) }

                # If UPN is entered, just look for that user and exit.
                If ($UPN) {
                    $URI = "https://graph.microsoft.com/v1.0/users/$UPN"
                    Write-Verbose $URI
    
                    $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Headers

                    Return $JSON
                }
                
                If ($TotalCount) {
                    $URI = 'https://graph.microsoft.com/v1.0/users/$count'
                    $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Headers -Body $Body
                    
                    $UserCount = New-Object PsObject
                    $UserCount | Add-Member -NotePropertyName 'UserCount' -NotePropertyValue $JSON
                
                    If ($TenantName) { $UserCount | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName }
                    Clear-Variable -Name AuthToken
                    Return $UserCount
                }
                Else {
                    $URI = "https://graph.microsoft.com/v1.0/users"
                    $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Headers -Body $Body
                    $TotalUsers = $JSON.'@odata.count'
                    $JSON.value
                    $PageSize = $JSON.value.count
                    $Message = "Getting $TotalUsers Azure AD users for tenant $TenantName." 
                    
                    If ($HideProgressBar) { Write-Host $Message }
                    
                    $UserCount = 0
                    $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
                    
                    While (($JSON.'@odata.nextLink') -And ($PageSize -lt $ResultSize)) {
                        $NextURI = $JSON.'@odata.nextLink'
                        $JSON = Invoke-RestMethod -Method GET -URI $NextURI -Headers $Headers
                        $JSON.value
                        $PageSize += $JSON.value.count
                        
                        If ($Stopwatch.Elapsed.TotalSeconds -ge $ProgressUpdateFreq) {
                            $Percentage = ($PageSize / $TotalUsers) * 100
                                                    
                            If ($HideProgressBar) {
                                $Percentage = [math]::Round($Percentage,1)
                                Write-Host "Retrieving $Percentage`% of $($TotalUsers) users on tenant $TenantName..."
                            }
                            Else { 
                                Write-Progress -Activity $Message -PercentComplete $Percentage -Status 'Retrieving...' 
                            }
                        
                            $Stopwatch.Reset()
                            $Stopwatch.Start()
                        }
                    }
                }
                Clear-Variable -Name AuthToken
            }
            Catch {
                Write-Error "Could not get user data. $($_.Exception.Message)"
                Clear-Variable -Name AuthToken
            }
        }
    }
}


Function Get-MSAzureUserGroupMembership {
    <#
        .SYNOPSIS
        Return the groups that a user is a member of from Azure AD.
         
        .DESCRIPTION
        Return the groups that a user is a member of from Azure AD.
         
        .PARAMETER ID
        The Azure AD GUID of the user
         
        .PARAMETER Transitive
        Show groups where the user is a member of a group that is a member of another group
         
        .PARAMETER Properties
        A comma-delimited list of properties to return in the results
        Format as per https://docs.microsoft.com/en-us/graph/query-parameters?view=graph-rest-1.0#select-parameter
        Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties
         
        .PARAMETER Filter
        Add filter parameters as per https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0&view=graph-rest-1.0#filter-parameter
        Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties
         
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
 
        .PARAMETER AuthToken
        The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken
         
        .PARAMETER TotalCount
        Only return a total count of objects
         
        .PARAMETER ResultSize
        The number of results to return. Defaults to 10000.
         
        .EXAMPLE
        Get-MSAzureUserGroupMembership -ID abcdefab-1234-1234-1234-abcdabcdabcd
        Returns the groups that the selected user is a member of
         
        .EXAMPLE
        Get-MSAzureUser -Properties 'id,userPrincipalName' -AuthToken $AuthToken
        Returns a list of Azure users' ID and userPrincipalNames using a previously-obtained authtoken
 
        .NOTES
        Version 1.0
    #>


    
    Param (
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)]
        [string]$ID,    
        [Parameter(Mandatory=$False)]
        [switch]$Transitive,
        [string]$Properties,
        [Parameter(Mandatory=$False)]
        [string]$Filter,        
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(Mandatory=$False)]
        [switch]$TotalCount,
        [Parameter(Mandatory=$False)]
        [switch]$HideProgressBar,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$AuthToken,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,500000)]
        [int]$ResultSize = 10000,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,99999)]
        [int]$ProgressUpdateFreq = 1    
    )
    
    Begin {
        If ($Transitive) {
            $MemberOfScope = 'transitiveMemberOf'
        }
        Else {
            $MemberOfScope = 'MemberOf'
        }
    }
    Process {
        $Body = @{}
        $Params = @{}
        
        Try {
            # Use globally set tenant name, if one was set and not explicitly included in the command
            If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }    
            
            If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsConfig -TenantName $TenantName | Get-MSGraphAccessToken }
        }
        Catch {
            Write-Error "Could not obtain authorization token. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }        
        }
        
        If ($AuthToken) {
            Try {
                $Headers = @{
                    Authorization = "Bearer $AuthToken"
                }

                If ($Properties) { 
                    $Body.Add('$select',$Properties)
                    $Params.Add('Properties',$Properties)
                }

                If ($Filter) { 
                    $Body.Add('$filter',$Filter)
                    $Params.Add('Filter',$Filter)
                }

                If ($TotalCount) {
                    $Body.Add('ConsistencyLevel','eventual')
                    
                    $URI = "https://graph.microsoft.com/v1.0/users/$ID/$MemberOfScope/`$count"
                    Write-Verbose $URI
                    $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Headers -Body $Body
                    
                    $GroupCount = New-Object PsObject
                    $GroupCount | Add-Member -NotePropertyName 'UserCount' -NotePropertyValue $JSON
                
                    If ($TenantName) { $GroupCount | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName }
                    Clear-Variable -Name AuthToken
                    Return $GroupCount
                }
                Else {
                    $URI = "https://graph.microsoft.com/v1.0/users/$ID/$MemberOfScope"                    
                    Write-Verbose $URI
                    
                    $Params = @{}
                    If ($AuthToken) { $Params.Add('AuthToken',$AuthToken) }
                    If ($TenantName) { $Params.Add('Tenant',$TenantName) }
                    
                    $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Headers -Body $Body
                    $JSON.value
                    
                    $PageSize = $JSON.value.count
                    
                    While (($JSON.'@odata.nextLink') -And ($PageSize -lt $ResultSize)) {
                        $NextURI = $JSON.'@odata.nextLink'
                        $JSON = Invoke-RestMethod -Method GET -URI $NextURI -Headers $Headers
                        $JSON.value
                        $PageSize += $JSON.value.count
                    }
                }
                Clear-Variable -Name AuthToken
            }
            Catch {
                Write-Error "Could not get group membership data. $($_.Exception.Message)"
                Clear-Variable -Name AuthToken
            }
        }
    }
}


Function Get-MSAzureGroup {
    <#
        .SYNOPSIS
        Return a list of groups from Azure AD.
         
        .DESCRIPTION
        Return a list of groups from Azure AD.
         
        .PARAMETER Properties
        A comma-delimited list of properties to return in the results
        Format as per https://docs.microsoft.com/en-us/graph/query-parameters?view=graph-rest-1.0#select-parameter
        Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties
         
        .PARAMETER Filter
        Add filter parameters as per https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0&view=graph-rest-1.0#filter-parameter
        Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties
         
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
 
        .PARAMETER HideProgressBar
        Don't show the progress bar. Cleans up logs when running on Docker.
         
        .PARAMETER AuthToken
        The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken
         
        .PARAMETER TotalCount
        Only return a total count of objects
         
        .PARAMETER ResultSize
        The number of results to return. Defaults to 1000.
         
        .EXAMPLE
        Get-MSAzureGroup
        Returns all MS Azure groups
         
        .EXAMPLE
        Get-MSAzureGroup -Filter "startsWith(displayName,'Global-')"
        Returns all MS Azure groups whose display names start with Global-
         
        .EXAMPLE
        Get-MSAzureGroup -Properties 'id,DisplayName' -AuthToken $AuthToken
        Returns a list of Azure group ID and display names using a previously-obtained authtoken
 
        .NOTES
        Version 1.0
    #>


    
    Param (
        [Parameter(Mandatory=$False)]
        [string]$Properties,
        [Parameter(Mandatory=$False)]
        [string]$Filter,        
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(Mandatory=$False)]
        [switch]$TotalCount,
        [Parameter(Mandatory=$False)]
        [switch]$HideProgressBar,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$AuthToken,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,500000)]
        [int]$ResultSize,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,99999)]
        [int]$ProgressUpdateFreq = 1    
    )

    Process {
        $Body = @{}
        $Params = @{}
        
        Try {
            # Use globally set tenant name, if one was set and not explicitly included in the command
            If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }    
            
            If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsConfig -TenantName $TenantName | Get-MSGraphAccessToken }
        }
        Catch {
            Write-Error "Could not obtain authorization token. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }        
        }
        
        If ($AuthToken) {
            Try {
                $Headers = @{
                    Authorization = "Bearer $AuthToken"
                }

                If ($Properties) { 
                    $Body.Add('$select',$Properties)
                    $Params.Add('Properties',$Properties)
                }

                If ($Filter) { 
                    $Body.Add('$filter',$Filter)
                    $Params.Add('Filter',$Filter)
                }

                If ($TotalCount) {
                    $Body.Add('ConsistencyLevel','eventual')
                    
                    $URI = 'https://graph.microsoft.com/v1.0/groups/$count'
                    $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Headers -Body $Body
                    
                    $GroupCount = New-Object PsObject
                    $GroupCount | Add-Member -NotePropertyName 'GroupCount' -NotePropertyValue $JSON
                
                    If ($TenantName) { $GroupCount | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName }
                    Clear-Variable -Name AuthToken
                    Return $GroupCount
                }
                Else {
                    $URI = "https://graph.microsoft.com/v1.0/groups"
                    
                    Write-Verbose $URI
                    
                    $Params = @{}
                    If ($AuthToken) { $Params.Add('AuthToken',$AuthToken) }
                    If ($TenantName) { $Params.Add('Tenant',$TenantName) }
                    
                    $TotalGroups = Get-MSAzureGroup @Params -TotalCount
                    
                    $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Headers -Body $Body
                    $JSON.value
                    
                    $PageSize = $JSON.value.count
                    
                    $Message = "Getting Azure AD groups for tenant $TenantName." 
                    
                    If ($HideProgressBar) { Write-Host $Message }
                    
                    $GroupCount = 0
                    $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
                    
                    While (($JSON.'@odata.nextLink') -And ($PageSize -lt $ResultSize)) {
                        $NextURI = $JSON.'@odata.nextLink'
                        $JSON = Invoke-RestMethod -Method GET -URI $NextURI -Headers $Headers
                        $JSON.value
                        $PageSize += $JSON.value.count
                        
                        If ($Stopwatch.Elapsed.TotalSeconds -ge $ProgressUpdateFreq) {
                            $Percentage = ($PageSize / $TotalGroups.GroupCount) * 100
                                                    
                            If ($HideProgressBar) {
                                $Percentage = [math]::Round($Percentage,1)
                                Write-Host "Retrieving $Percentage`% of $($TotalGroups.GroupCount) users on tenant $TenantName..."
                            }
                            Else { 
                                Write-Progress -Activity $Message -PercentComplete $Percentage -Status 'Retrieving...' 
                            }
                        
                            $Stopwatch.Reset()
                            $Stopwatch.Start()
                        }
                    }
                }
                Clear-Variable -Name AuthToken
            }
            Catch {
                Write-Error "Could not get group data. $($_.Exception.Message)"
                Clear-Variable -Name AuthToken
            }
        }
    }
}


Function Get-MSAzureGroupMembers {
    <#
        .SYNOPSIS
        Return a list of group members from Azure AD.
         
        .DESCRIPTION
        Return a list of group members from Azure AD.
         
        .PARAMETER ID
        The GUID of the group to return membership info
     
        .PARAMETER Transitive
        Show members where the members are members of a group that is a member of another group
         
        .PARAMETER Properties
        A comma-delimited list of properties to return in the results
        Format as per https://docs.microsoft.com/en-us/graph/query-parameters?view=graph-rest-1.0#select-parameter
        Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties
         
        .PARAMETER Filter
        Add filter parameters as per https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0&view=graph-rest-1.0#filter-parameter
        Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties
         
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
 
        .PARAMETER AuthToken
        The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken
         
        .PARAMETER ResultSize
        The number of results to return. Defaults to 10000.
         
        .EXAMPLE
        Get-MSAzureGroup -Filter "startsWith(displayName,'Global-Sales')" | Get-MSAzureGroupMembers -TotalOnly
        Returns all MS Azure groups whose display names start with Global-Sales
         
        .NOTES
        Version 1.0
    #>


    
    Param (
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$ID,
        [Parameter(Mandatory=$False)]
        [switch]$Transitive,
        [Parameter(Mandatory=$False)]
        [string]$Properties,
        [Parameter(Mandatory=$False)]
        [string]$Filter,    
        [Parameter(Mandatory=$False)]
        [switch]$TotalCount,        
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$AuthToken,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,500000)]
        [int]$ResultSize = 10000
    )

    Begin {
        If ($Transitive) {
            $MemberScope = 'transitiveMembers'
        }
        Else {
            $MemberScope = 'members'
        }
        
        Try {
            # Use globally set tenant name, if one was set and not explicitly included in the command
            If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }    
            
            If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsConfig -TenantName $TenantName | Get-MSGraphAccessToken }
        }
        Catch {
            Write-Error "Could not obtain authorization token. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }        
        }
    }
    Process {
        $Body = @{}
        $Params = @{}
        
        If ($AuthToken) {
            Try {
                $Headers = @{
                    Authorization = "Bearer $AuthToken"
                }

                If ($Properties) { 
                    $Body.Add('$select',$Properties)
                    $Params.Add('Properties',$Properties)
                }

                If ($Filter) { 
                    $Body.Add('$filter',$Filter)
                    $Params.Add('Filter',$Filter)
                }

                If ($TotalCount) {
                    $Body.Add('ConsistencyLevel','eventual')
                    
                    $URI = "https://graph.microsoft.com/v1.0/groups/$ID/$MemberScope/`$count"
                    $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Headers -Body $Body
                    
                    $MemberCount = New-Object PsObject
                    $MemberCount | Add-Member -NotePropertyName 'MemberCount' -NotePropertyValue $JSON
                
                    If ($TenantName) { $MemberCount | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName }
                    Clear-Variable -Name AuthToken
                    Return $MemberCount
                }
                Else {
                    $URI = "https://graph.microsoft.com/v1.0/groups/$ID/$MemberScope"
                    
                    $Params = @{}
                    If ($AuthToken) { $Params.Add('AuthToken',$AuthToken) }
                    If ($TenantName) { $Params.Add('Tenant',$TenantName) }
                    
                    $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Headers -Body $Body
                    $JSON.value
                    
                    $PageSize = $JSON.value.count
                    
                    While (($JSON.'@odata.nextLink') -And ($PageSize -lt $ResultSize)) {
                        $NextURI = $JSON.'@odata.nextLink'
                        $JSON = Invoke-RestMethod -Method GET -URI $NextURI -Headers $Headers
                        $JSON.value
                        $PageSize += $JSON.value.count
                    }
                }
                Clear-Variable -Name AuthToken
            }
            Catch {
                Write-Error "Could not get group member data. $($_.Exception.Message)"
                Clear-Variable -Name AuthToken
            }
        }
    }
}

# SIG # Begin signature block
# MIIlcwYJKoZIhvcNAQcCoIIlZDCCJWACAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAuhKQ7wx6R3STR
# 5mf/3i6uJ8J35tNDI37O3cZWN39MHKCCEvMwggXdMIIDxaADAgECAgh7LJvTFoAy
# mTANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx
# EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8G
# A1UEAwwoU1NMLmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTAe
# Fw0xNjAyMTIxNzM5MzlaFw00MTAyMTIxNzM5MzlaMHwxCzAJBgNVBAYTAlVTMQ4w
# DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENv
# cnBvcmF0aW9uMTEwLwYDVQQDDChTU0wuY29tIFJvb3QgQ2VydGlmaWNhdGlvbiBB
# dXRob3JpdHkgUlNBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA+Q/d
# oyt9y9Aq/uxnhabnLhu6d+Hj9a+k7PpKXZHEV0drGHdrdvL9k+Q9D8IWngtmw1aU
# nheDhc5W7/IW/QBi9SIJVOhlF05BueBPRpeqG8i4bmJeabFf2yoCfvxsyvNB2O3Q
# 6Pw/YUjtsAMUHRAOSxngu07shmX/NvNeZwILnYZVYf16OO3+4hkAt2+hUGJ1dDyg
# +sglkrRueiLH+B6h47LdkTGrKx0E/6VKBDfphaQzK/3i1lU0fBmkSmjHsqjTt8qh
# k4jrwZe8jPkd2SKEJHTHBD1qqSmTzOu4W+H+XyWqNFjIwSNUnRuYEcM4nH49hmyl
# D0CGfAL0XAJPKMuucZ8POsgz/hElNer8usVgPdl8GNWyqdN1eANyIso6wx/vLOUu
# qfqeLLZRRv2vA9bqYGjqhRY2a4XpHsCz3cQk3IAqgUFtlD7I4MmBQQCeXr9/xQiY
# ohgsQkCz+W84J0tOgPQ9gUfgiHzqHM61dVxRLhwrfxpyKOcAtdF0xtfkn60Hk7ZT
# NTX8N+TD9l0WviFz3pIK+KBjaryWkmo++LxlVZve9Q2JJgT8JRqmJWnLwm3KfOJZ
# X5es6+8uyLzXG1k8K8zyGciTaydjGc/86Sb4ynGbf5P+NGeETpnr/LN4CTNwumam
# du0bc+sapQ3EIhMglFYKTixsTrH9z5wJuqIz7YcCAwEAAaNjMGEwHQYDVR0OBBYE
# FN0ECQei9Xp9UlMSkpXuOIAlDaZZMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgw
# FoAU3QQJB6L1en1SUxKSle44gCUNplkwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3
# DQEBCwUAA4ICAQAgGBGUKfsmnRweHnBh8ZVyk3EkrWiTWI4yrxuzcAP8JSt0hZA9
# eGr0uYullzu1GJG7Hqf5QFuR+VWZrx4R0Fwdp2bjsZQHDDI5puobsHnYHZxwROOK
# 3cT5lR+KOEM/AYWlR6c9RrK85SJo93uc2Cw+CiHILTOsv8WBmTF0wXVxxb6x8CNF
# 9J1r/BljnaO8BMYYCyW7U4kPs4BQ3kXuRH+rlHhkmNP2KN2H2HBldPsOuRPrpw9h
# qTKWzN677WNMGLupQPegVG4giHF1GOp6tDRy4CMnd1y2kOqGJUCr7zMPy5+CvqIg
# +/a1LRrmwoWxdA/7yGUCpFIBR91JIsG/2OtrrH7e7GMzFbcjCI/GD41BWt2OxbmP
# 5UU/eNu60htAsf5xTT/ggaK6XrTsFeCT3QgffuFVmQsh3pOeCvvmo0m9NjD+53ey
# oHWXtS2BiBdlIPfakACfyVLMMso1fPU9D9gr1/UmbMkGNJYW6nBZGjJ5eQu2iH8P
# Ukg9v2zYokQu0U63cljTiROV/kSr+NeLG26cvCygW9VqAK9fN+HV+hALmJyG5yaP
# zvDsbopXC4DjTrLAoGNhkLpVaDd0araS25+hhiK2ZScO7LafQmDkZ8K12kELxNOL
# YRu8+h+RK9dEB166KazZxenvU0ha64DxKFghzbAGVfsnP1OQcKkEHlcnuTCCBnIw
# ggRaoAMCAQICCGQzUdPHOJ8IMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVT
# MQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NM
# IENvcnBvcmF0aW9uMTEwLwYDVQQDDChTU0wuY29tIFJvb3QgQ2VydGlmaWNhdGlv
# biBBdXRob3JpdHkgUlNBMB4XDTE2MDYyNDIwNDQzMFoXDTMxMDYyNDIwNDQzMFow
# eDELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9u
# MREwDwYDVQQKDAhTU0wgQ29ycDE0MDIGA1UEAwwrU1NMLmNvbSBDb2RlIFNpZ25p
# bmcgSW50ZXJtZWRpYXRlIENBIFJTQSBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
# ADCCAgoCggIBAJ+DE3OqsMZtIcvbi3qHdNBx3I6Xcprku4g0tN2AA8YvRaR0mr8e
# D1Dqnm1485/6USapPZ3RspRXPvs5iRuRK1bvZ8vmC+MOOYzGNfSMPd0l6QGsF0J9
# WBZA3PnVKEQdlWQwYTpk8pfXc0x9eyMCbfN161U9b6otxK++dKxd/mq2/OpceekP
# Q5y1UgUP7z6xsY/QSa2m40IZVD/zLw6hy3z+E/kjOdolHLg+AEo6bzIwN2Qex651
# B9hV0hjJDoq8o1zwfAqnhYHCDq+PmVzTYCW8g1ppHCUTzXL165yAm9wsZ8TdyQmY
# 1XPrxCGj5TKOPi9SmMZgN2SMsm9KVHIYzCeH+s11omMhTLU9ZP0rpptVryZMYLS5
# XP6rQ72t0BNmUB8L0omm/9eABvHDEQIzM2EX91Yfji87aOcV8XdWSimeA9rCKyZh
# MlugVuVJKY02p/XHUqJWAyAvOHiAvfYGrkE0y5RFvZvHiRgfC7r/qa5qQJkT3e9Q
# 3wG68gTW0DHfNDheV1vIOB5W1KxIpu3/+bjBO+3CJL5EYKd3zdU9mFm0Q+qqYH3N
# wuUv8ev11CDVlzRuXQRrBRHS05KMCSdE7U81MUZ+dBkFYuyJ4+ojcJjk0S/UihMY
# RpNl5Vhz00w9J3oiP8P4o1W3+eaHguxFHsVuOnyxTrmraPebY9WRQbypAgMBAAGj
# gfswgfgwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTdBAkHovV6fVJTEpKV
# 7jiAJQ2mWTAwBggrBgEFBQcBAQQkMCIwIAYIKwYBBQUHMAGGFGh0dHA6Ly9vY3Nw
# cy5zc2wuY29tMBEGA1UdIAQKMAgwBgYEVR0gADATBgNVHSUEDDAKBggrBgEFBQcD
# AzA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8vY3Jscy5zc2wuY29tL3NzbC5jb20t
# cnNhLVJvb3RDQS5jcmwwHQYDVR0OBBYEFFTC/hCVAJPNavXnwNfZsku4jwzjMA4G
# A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEA9Q8mh3CvmaLK9dbJ8I1m
# PTmC04gj2IK/j1SEJ7bTgwfXnieJTYSOVNEg7mBD21dCPMewlfa+zOqjPY5PBsYr
# WYZ/63MbyuVAJuA9b8z2vXHGzX0OIEA51gXSr5QIv3/CUbcrtXuDIfBj2uWc4Wku
# dR1Oy2Ee9aUz3wKdFdntaZNXukZFLoC8Zb7nEj7eR/+QnBCt9laypNT61vwuvJch
# s3aD0pH6BlDRsYAogP7brQ9n7fh93NlwW3q6aLWzSmYXj+fw51fdaf68XuHVjJ8T
# u5WaFft5K4XVbT5nR24bB1z7VEUPFhEuEcOwvLVuHDNXlB7+QjRGjjFQTtszV5X6
# OOTmEturWC5Ft9kiyvRaR0ksKOhPjEI8ZGjp5kOsGZGpxxOCX/xxCje3nVB7PF33
# olKCNeS159MKb2v+jfmk19UdS+d9Ygj42desmUnbtYRBFC72LmCXU0ua/vGIenS6
# nnXp4NqnycwsO3tMCnjPlPc2YLaDPIpUy04NaCqUEXUmFOogN8zreRd2VXhxbeJJ
# ODM32+RsWccjYua8zi5US/1eAyrI3R5LcUTQdT4xYmWLKabtJOF6HYQ0f6QXfLSs
# fT81WMvDvxrdn1RWbUXlU/OIiisxo8o+UNEANOwnCMNnxlzoaL/PLhZluDxm/zuy
# lauajZ3MlPDteFB/7GRHo50wggaYMIIEgKADAgECAhBHaHS/7B9x7N3XWwMIISW7
# MA0GCSqGSIb3DQEBCwUAMHgxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQ
# MA4GA1UEBwwHSG91c3RvbjERMA8GA1UECgwIU1NMIENvcnAxNDAyBgNVBAMMK1NT
# TC5jb20gQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBDQSBSU0EgUjEwHhcNMjYw
# NzA5MTQzMTE4WhcNMjcxMDEwMTQzMTE4WjB/MQswCQYDVQQGEwJVUzERMA8GA1UE
# CAwITmV3IFlvcmsxEDAOBgNVBAcMB0plcmljaG8xHjAcBgNVBAoMFU5lY3RhciBT
# ZXJ2aWNlcyBDb3JwLjELMAkGA1UECwwCSVQxHjAcBgNVBAMMFU5lY3RhciBTZXJ2
# aWNlcyBDb3JwLjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAJmOMPRo
# al1bEwe/HPHB533mkxLFf2LGg8R3yZQfDMkVWS0ME52aHJT33z61eMLqFamuvqY7
# qeb6jcOjqenYUhNmFcnl1Oq2hFWWE7McdgYAc0boWHemqxHWrUXN53sCp1DFVTh3
# NFwTo2PaY/KLdIjsXXvmA9qQBytzxEmeaF5HyCmmTlQ/N4eDeqpMOAbQn1n7lLi7
# xiHq1UAll3uvB+y7Vq/3V/dCO33/aiQif1t6h2R+N2JhoW0vtvd+yMyY58klGfN6
# O9WHto+MAiGoyP8rs9XWAzhIRZ2JjwFjfTqQOxVv0lUH+ZolhJ9uQHvybL3wHwLN
# Qu8szoII3tqksiosdPc4Xk1nSyzrBfETafVVpey19HD6RhSIHrnPPHwWOzrdsT9j
# O9szScE3i+cfJcKiL98lJkhFq/ZGOIwAPgxPvZZQZqbzEcsR0iddA+KNLxKqzkv9
# 1GtETHdFoU07xY7+2NsY1xhs9jFFNhoeCVh+dk3lIBvpWkuMwM8mZKziUQIDAQAB
# o4IBlTCCAZEwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRUwv4QlQCTzWr158DX
# 2bJLuI8M4zB6BggrBgEFBQcBAQRuMGwwSAYIKwYBBQUHMAKGPGh0dHA6Ly9jZXJ0
# LnNzbC5jb20vU1NMY29tLVN1YkNBLUNvZGVTaWduaW5nLVJTQS00MDk2LVIxLmNl
# cjAgBggrBgEFBQcwAYYUaHR0cDovL29jc3BzLnNzbC5jb20wUQYDVR0gBEowSDAI
# BgZngQwBBAEwPAYMKwYBBAGCqTABAwMBMCwwKgYIKwYBBQUHAgEWHmh0dHBzOi8v
# d3d3LnNzbC5jb20vcmVwb3NpdG9yeTATBgNVHSUEDDAKBggrBgEFBQcDAzBNBgNV
# HR8ERjBEMEKgQKA+hjxodHRwOi8vY3Jscy5zc2wuY29tL1NTTGNvbS1TdWJDQS1D
# b2RlU2lnbmluZy1SU0EtNDA5Ni1SMS5jcmwwHQYDVR0OBBYEFB7iZgW8t2SQ3I8n
# dKd+fgqBvDYxMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAhCFC
# ydfNbwEfqJhrp612HM4Qh7JUphMm5ZLNomSEsonhjz1ltmMpB/0nGVH9Ond23hac
# 12+xzfnDAB2HsNhA9vVWYbgbxcH1duQbdRatjrtWKd1+YOfsvAS+O652yRQf26OL
# +2efdAnzaqsTYFoEJwFcgl1M5Uo+uSht961Jm7McHouRm/zYOnC+NjvGOgIL40KN
# P1pLQTxK5zc54+yFQANjiCPNI07AnkYCSgHDJCoKZbJTcUk26kQNqNE9AvjsIlpY
# uY8sTXHSIkN3WNuSPYr66GHaYj9m0AGUKGt/ZZePKmOsLu+nON5hwqXx+QX75yc2
# AIlKw/ItfZvBLeRuejoKnbOh6QiyDxn0lfAH8f/upDPCUfvBVADF56itElnCo+fC
# 36rzFnfBaVPbLVzqlAGSs7e6Yn669snsTHkKQm1fALEiZyt3dqOvppr8AO1f+oCc
# r/0jVezz0VyNUemChoAMBosREctn/fOYfL5NClq/mnI5m+tAHOajVk8LI6h7mlNv
# tIFKwfHCIH2EJM4ze+ZrSXWt8bfY+6pQwd4xCB9UmCw2DBBMQ11rvu1rLD+u3kIL
# NBAXHQGEnCoP+sY6h17wc/2Jb8BPg+yAvPXevqXmAdAKJrhvMmgsbttRxOQuVjrq
# 42h1me0i3Jy0obhODmpXsrVPm02ZBIYgiRcK/q8xghHWMIIR0gIBATCBjDB4MQsw
# CQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xETAP
# BgNVBAoMCFNTTCBDb3JwMTQwMgYDVQQDDCtTU0wuY29tIENvZGUgU2lnbmluZyBJ
# bnRlcm1lZGlhdGUgQ0EgUlNBIFIxAhBHaHS/7B9x7N3XWwMIISW7MA0GCWCGSAFl
# AwQCAQUAoHwwEAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQB
# gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkE
# MSIEIAxAvK0PCvyRFxwKSASRow5DsMI92OszxTf4ZcmlClTaMA0GCSqGSIb3DQEB
# CwUABIIBgITOqFOpdWTlZWeHQle96r+qfaxaX8t/GUc2VJHS77UExnIQ4cwW+XXQ
# De5LbA12wBOUqDrtWk1PSHqz1vH8c5LtKMUlAr6DwQ6GXMqZAvBmzToKY19UGK6V
# Ku3X1hlHR0nhHSpcUY+HHG4hFQxhopVj1xSgBG/bQw5m42ZhcuxYybonXRXjvupt
# Msk0apAz4doGN5SLiMB9rwljXevpP3DwBO+fT3vWZZXp8Nzp3ZtEYKkE5euKgEo6
# PQsIsMD4PuSt1Sr2vFj0lKyUEsdpgj8WcxbJngiIQpbrAOoNd2N4NBNNGXfLgm5i
# soFB5MzZrhzz3Tzuin/oiXqBhhb5itiG14t4e/bfhRL6FT79P/kdV4y7o50BhXeO
# rRv19FlwrgkKRViAt2WVqB69ivdusP/NS1ICZlcGxE8BTVw9oPFyL9oWjDrE8fzo
# EN361J6+hsZXbtKggmGcEW3Nr3b5V1oEssHAuKb7obanFhEH8cKciR7xUQhQ3uh1
# utSJwI64fqGCDxwwgg8YBgorBgEEAYI3AwMBMYIPCDCCDwQGCSqGSIb3DQEHAqCC
# DvUwgg7xAgEDMQ0wCwYJYIZIAWUDBAIBMH0GCyqGSIb3DQEJEAEEoG4EbDBqAgEB
# BgwrBgEEAYKpMAEDBgEwLzALBglghkgBZQMEAgEEIHeoTyYip6t/jB0osj1Wd+0w
# z9GCxF/Lg4U7q1B3TvkRAggjALO3iBOOXhgPMjAyNjA5MTUxNzI5NDdaMAMCAQEC
# BgGgph4hC6CCDAAwggT8MIIC5KADAgECAhAfaxZi0i4bbF3xwMGgYA44MA0GCSqG
# SIb3DQEBCwUAMHMxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UE
# BwwHSG91c3RvbjERMA8GA1UECgwIU1NMIENvcnAxLzAtBgNVBAMMJlNTTC5jb20g
# VGltZXN0YW1waW5nIElzc3VpbmcgUlNBIENBIFIxMB4XDTI1MDIxODE2MzIwMloX
# DTM0MTExMjE4NTAwNVowbjELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRAw
# DgYDVQQHDAdIb3VzdG9uMREwDwYDVQQKDAhTU0wgQ29ycDEqMCgGA1UEAwwhU1NM
# LmNvbSBUaW1lc3RhbXBpbmcgVW5pdCAyMDI1IEUxMFkwEwYHKoZIzj0CAQYIKoZI
# zj0DAQcDQgAEG/tRUcdv5lWW7E9eV8Tczq2DReerx2Jz47e884JGlqVQzW870D4Z
# HNJVWPLKAeFisHDrZcsWHWS/t77JF39pNqOCAVowggFWMB8GA1UdIwQYMBaAFAyd
# ECWOmqcbmYdDzwh+4b2BkPTPMFEGCCsGAQUFBwEBBEUwQzBBBggrBgEFBQcwAoY1
# aHR0cDovL2NlcnQuc3NsLmNvbS9TU0wuY29tLXRpbWVTdGFtcGluZy1JLVJTQS1S
# MS5jZXIwUQYDVR0gBEowSDA8BgwrBgEEAYKpMAEDBgEwLDAqBggrBgEFBQcCARYe
# aHR0cHM6Ly93d3cuc3NsLmNvbS9yZXBvc2l0b3J5MAgGBmeBDAEEAjAWBgNVHSUB
# Af8EDDAKBggrBgEFBQcDCDBGBgNVHR8EPzA9MDugOaA3hjVodHRwOi8vY3Jscy5z
# c2wuY29tL1NTTC5jb20tdGltZVN0YW1waW5nLUktUlNBLVIxLmNybDAdBgNVHQ4E
# FgQUznzZwASAxSQQagnqHKslPRH9qNIwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3
# DQEBCwUAA4ICAQCAc3Ukhb1mU2KnTsV9j2vUsnAspOXWH/L2vUGMOAcwTPtTsnuY
# DLfYnEDUovKMIImo2S5F+EMcYUR9m2NM6u7sBAwNIOJQO8IJzeNrPmnL2Ma/Ah7m
# emQttepeED5KoLMbvX1RKKDCEeRivu/w2JehpjRe7TenQGJlmt5mWmeCYYH37zo3
# 3gWogXHYjlnmK67t3iPtoA5kE3F9T2MUMggYO1Z9Z4KkXRDyssT/cMcOXMkqzkiX
# eL9Wg6XutNT3fyhKvEzDDDoYMGUpfysYfG+SOAhv0xeRWCUlIMew0BkN4JL+KdrE
# ocD4KG4Hwrg7EjFrqTV754cHKlqQBjfC43vDs+U+aE3qTkh2pmfcdkezZWOhHzjV
# n3CZU8V0YN2QFntc6Zvk5lRoq5+y+0RHRVtjYOTNqoBoi23WRz4j4VTPs+JXPY9T
# Ol6CR+1FHG+s/IgvTxuUlOdsxDReuoM3SsR+5Mu/heGGcrIlpeHEJR2M79xG6YzN
# nflBNQwi0FbLXEanSLKgVWcDJrak+xUy4Aj6zLXPGU5L2XmJLG8onyCmek6COphN
# ru7V7Jmj7gmVwaiKJHXsu2ExOsXWrra07nE6kjy3FRnqC0oa2QlXrB2P69ktzApn
# Yz3capWk6jpQGUaPHWwqxVnsAhTMlmWLg0nQzYphyt82eV5uRgqkOdKpbDCCBvww
# ggTkoAMCAQICEG1SGHCH6CNNhWAA0ICPk1YwDQYJKoZIhvcNAQELBQAwfDELMAkG
# A1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYD
# VQQKDA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0
# aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTkxMTEzMTg1MDA1WhcNMzQxMTEy
# MTg1MDA1WjBzMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcM
# B0hvdXN0b24xETAPBgNVBAoMCFNTTCBDb3JwMS8wLQYDVQQDDCZTU0wuY29tIFRp
# bWVzdGFtcGluZyBJc3N1aW5nIFJTQSBDQSBSMTCCAiIwDQYJKoZIhvcNAQEBBQAD
# ggIPADCCAgoCggIBAK5REBPS+TwgoCCF3slQHGTJ4f3F6TT/Cn8xSOhyWsVeqGH9
# 8Yf3UVz7t+bQwcITsD7CY6KoGP04OskBgareubfeMKcdKwIE1YBBjKhq4urwiOqx
# LUmVcvb2oM0wx3BnxQ3NBLu9ZkwMnjQlIY2mEwZMgDaqfZuiEa2BFzinXf3kRLKl
# Q5oa8ne3QU0vcG4qZvphy0xxBQXayqigzN3z2HQTq6N28EOjpnA2dajGPtiZ9aNJ
# eDfcDka5j3KbhBkzk4RWCjx5vP8H6DKHIIs02GHgxv/jG8JMIxWY1isG+IaB09li
# vKbxlvzhNAKZK5fQmUstrpYrVo7qqXAhJtv1tUaHzrp6QpuUL9dE/bSAC7UKO9xh
# yJSA1OsYWDx/wAmBA84JzX8IJ1olJjCEmlJ2F4o6dCARKA2Zhk+EU4LogpowBReT
# lTW2NNwUKAW+8Cte0rhrMBZQ47Vjd92V0gEvouOTMtQJgk2QVeqGwFVw8y4HSdQN
# a8sl8+Kay2MnyUXhLoQLFaeVaLs4SVXBOe3Ua1Gp5j3J2+8Yue1T4V5wrsNuocNR
# 3frpSt4yRIG3N68Bz1qqhk+eNUyO8WpXWlg6POZOJUdm0BzzRsB8V7kst8nM8joO
# e03KqhunBN69Ckeo8M32qo07zeveRrDwD2P4dmJLDYBflwZ1A/SQbS+HN+AHAgMB
# AAGjggGBMIIBfTASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFN0ECQei
# 9Xp9UlMSkpXuOIAlDaZZMIGDBggrBgEFBQcBAQR3MHUwUQYIKwYBBQUHMAKGRWh0
# dHA6Ly93d3cuc3NsLmNvbS9yZXBvc2l0b3J5L1NTTGNvbVJvb3RDZXJ0aWZpY2F0
# aW9uQXV0aG9yaXR5UlNBLmNydDAgBggrBgEFBQcwAYYUaHR0cDovL29jc3BzLnNz
# bC5jb20wPwYDVR0gBDgwNjA0BgRVHSAAMCwwKgYIKwYBBQUHAgEWHmh0dHBzOi8v
# d3d3LnNzbC5jb20vcmVwb3NpdG9yeTATBgNVHSUEDDAKBggrBgEFBQcDCDA7BgNV
# HR8ENDAyMDCgLqAshipodHRwOi8vY3Jscy5zc2wuY29tL3NzbC5jb20tcnNhLVJv
# b3RDQS5jcmwwHQYDVR0OBBYEFAydECWOmqcbmYdDzwh+4b2BkPTPMA4GA1UdDwEB
# /wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAkhl1DaZaQs8ZB9ny/JT6wJvwFelE
# llovcTPdUOUTe5mTdw/E+3JtV8u6ppyLRbpIHbYlMy20KJAychU6xdaci4BsP9oV
# NxSRMsEjfHKz7ARqPNdpclhYAINLjsFGMO1iUNbXiAsnF/xboNCgfeMcMYbLyQYk
# U6UMobv9isrtQZ8e0EAQNV7qXJn4W0KyuTt0P8iIv/5DdDpIUBIktDZcjz2KEW6B
# 1gvvsKIM1esjYwWylAazBcQAake5pANMdSn8t1HdPKsiwuWfOguyRQazAX8oXz6S
# lZSIok0Lis9a02vGVtdhEaB0R3HxIyNRMMKWV1yuSeUXFuoexWav3GRPZC0WYb50
# SrW/l+wgrS8doetaMwyZon2L7ioYlIPSy1h9Dq/Q911PsSkbEZ3zrsB1roVnIfBu
# 5BJp0xvQrQ/Q4LavuvCoFR7QFoypNrotbNYi2AGMZw5td4zGZtCqUTPZi0BwSuRm
# +HRYAEMMThTwbJX/fYV1oC8mBN970yIvadIGKhh7+DmYdRJYBrL8inVFCZAK+YX2
# w1+qWEnCSPL/VTWJtSRMhQFfceDKbJC+pBNksvKzqkva0J1ZyMj1i4vDfSuBmbz4
# rfzsvvJxS+quZDdkmW6MeXevWGBXvqzdbAw+AqTVsAQUyP6tFeKZIL4S/fSFdl2r
# Ix2X+KXkqx3S+EYxggJYMIICVAIBATCBhzBzMQswCQYDVQQGEwJVUzEOMAwGA1UE
# CAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xETAPBgNVBAoMCFNTTCBDb3JwMS8w
# LQYDVQQDDCZTU0wuY29tIFRpbWVzdGFtcGluZyBJc3N1aW5nIFJTQSBDQSBSMQIQ
# H2sWYtIuG2xd8cDBoGAOODALBglghkgBZQMEAgGgggFhMBoGCSqGSIb3DQEJAzEN
# BgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYwOTE1MTcyOTQ3WjAoBgkq
# hkiG9w0BCTQxGzAZMAsGCWCGSAFlAwQCAaEKBggqhkjOPQQDAjAvBgkqhkiG9w0B
# CQQxIgQgzhxqXNvR/Kf8tctvQ4e+Z8G/2AbSY7bm4QGiZxhd9s0wgckGCyqGSIb3
# DQEJEAIvMYG5MIG2MIGzMIGwBCBUKvmhao1yLmYRSXiK6ZTBipqu5aZcs0SiVJr5
# bHnHizCBizB3pHUwczELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYD
# VQQHDAdIb3VzdG9uMREwDwYDVQQKDAhTU0wgQ29ycDEvMC0GA1UEAwwmU1NMLmNv
# bSBUaW1lc3RhbXBpbmcgSXNzdWluZyBSU0EgQ0EgUjECEB9rFmLSLhtsXfHAwaBg
# DjgwCgYIKoZIzj0EAwIERzBFAiBKrGR3svLHlos+n3oZpSK5LJFou8TfogDgeRO2
# KQEBtgIhAJFNLPGCgGrQ295/q93RLZnhahbSdGI9oP7AJ51n1nHx
# SIG # End signature block