ArcGIS.Microsoft365.psm1

# Register-PnPManagementShellAccess
# Connect-PnPOnline -Url https://m4o365.sharepoint.com
# https://pnp.github.io/powershell/cmdlets/Add-PnPAlert.html
using namespace System.Net

Function New-ArcGISTermSet {

    [CmdletBinding(
        SupportsShouldProcess = $True,
        HelpURI = "https://doc.arcgis.com/en/sharepoint/latest/use-maps/understand-geotags-arcgis-maps.htm"
    )]
    Param()

    If ($PSCmdlet.ShouldProcess("Create the 'Esri' Term Group and the 'ArcGIS' Term Set in SharePoint Term Store", $null, $null)) {

        $EsriTermGroupName = "Esri"

        $EsriTermSetName = "ArcGIS"

        $OldEsriTermSetName = "M4SP"

        $TermGroups = Get-PnPTermGroup

        $HasEsriTermGroup = $false

        $HasEsriTermSet = $false

        Foreach ($TermGroup in $TermGroups) {
            if ( $TermGroup.Name -eq $EsriTermGroupName ) {
                $HasEsriTermGroup = $true
            }
        }

        # Create "Esri" term group if it is missing
        if (-not $HasEsriTermGroup) {
            New-PnPTermGroup -GroupName $EsriTermGroupName
            Start-Sleep -s 3
        }

        $TermSets = Get-PnPTermSet -TermGroup $EsriTermGroupName

        Foreach ($TermSet in $TermSets) {
            if ($TermSet.Name -eq $EsriTermSetName) {
                $HasEsriTermSet = $true
            }

            # Rename existing "M4SP" term set(if any) to "ArcGIS"
            if ($TermSet.Name -eq $OldEsriTermSetName) {
                $HasEsriTermSet = $true
                Set-PnPTermSet -Identity $OldEsriTermSetName -TermGroup $EsriTermGroupName -Name $EsriTermSetName
            }
        }

        # Create "ArcGIS" term set if it is missing
        if (-not $HasEsriTermSet) {
            New-PnPTermSet -Name $EsriTermSetName -TermGroup $EsriTermGroupName
        }
    }
}

$ArcGISOpenExtensionId = "com.esri.a4m365.arcgisSettings"

Function New-ArcGISConnection {

    [CmdletBinding(SupportsShouldProcess = $True)]
    Param
    (
        [Parameter(Mandatory = $true, HelpMessage = "Specify ArcGIS Online organization (or ArcGIS Enterprise) URL")]
        [ValidateScript({ Test-ArcGISConnectionURL $_ })]
        [String] $ArcGISConnectionUrl,
        [Parameter(Mandatory = $true, HelpMessage = "Specify a title for this ArcGIS connection")]
        [String] $ArcGISConnectionTitle,
        [Parameter(Mandatory = $false)]
        [Switch] $Default
    )

    If ($PSCmdlet.ShouldProcess("Create a new ArcGIS connection configuration with the URL '${ArcGISConnectionUrl}' and Title 'ArcGISConnectionTitle'", $null, $null)) {

        # (Connect-MgGraph -Scopes "Organization.ReadWrite.All") 1> $null
        if (Get-IsAuthenticated) {
            $OrganizationId = (Get-MgOrganization).Id

            $ConfigurationProperty = @{
                "url"       = $ArcGISConnectionUrl;
                "title"     = $ArcGISConnectionTitle;
                "isDefault" = $Default.IsPresent;
            }

            $ExtensionIds = (Get-MgOrganizationExtension -OrganizationId $OrganizationId).Id

            if (($null -ne $ExtensionIds) -and $ExtensionIds.Contains($ArcGISOpenExtensionId)) {

                [Array]$ExtentionConfigurationArray = Get-ArcGISConnectionInfos

                if ($null -ne ($ExtentionConfigurationArray | Where-Object { $_.url.ToLower() -eq $ArcGISConnectionUrl.ToLower() })) {
                    Write-Warning "The ArcGIS connection with the URL '$ArcGISConnectionUrl' is already existing"
                    return
                }

                if ($Default.IsPresent) {
                    foreach ($ExtentionConfigurationItem in $ExtentionConfigurationArray) {
                        $ExtentionConfigurationItem.isDefault = $false
                    }
                }

                $ExtentionConfigurationArray += $ConfigurationProperty

                $ExtensionProperties = @{
                    "connections" = ConvertTo-Json $ExtentionConfigurationArray -Compress
                }
                Update-MgOrganizationExtension -OrganizationId $OrganizationId -ExtensionId $ArcGISOpenExtensionId -AdditionalProperties $ExtensionProperties
            }
            else {
                # create the extension and add the first arcgis connection
                $ExtensionProperties = @{
                    "@odata.type"   = "#microsoft.graph.openTypeExtension";
                    "extensionName" = $ArcGISOpenExtensionId;
                    "connections"   = ConvertTo-Json @($ConfigurationProperty) -Compress
                }
                New-MgOrganizationExtension -OrganizationId $OrganizationId -AdditionalProperties $ExtensionProperties 1> $null
            }
        }
    }
}

Function Update-ArcGISConnection {

    [CmdletBinding(SupportsShouldProcess = $True)]
    Param
    (
        [Parameter(Mandatory = $true, HelpMessage = "Specify ArcGIS Online organization (or ArcGIS Enterprise) URL")]
        [ValidateScript({ Test-ArcGISConnectionURL $_ })]
        [String] $ArcGISConnectionUrl,
        [Parameter(Mandatory = $true, HelpMessage = "Specify a title for this ArcGIS connection")]
        [String] $ArcGISConnectionTitle,
        [Parameter(Mandatory = $false)]
        [Switch] $Default
    )

    If ($PSCmdlet.ShouldProcess("Update the existing ArcGIS connection configuration with the URL '${ArcGISConnectionUrl}' and Title 'ArcGISConnectionTitle'", $null, $null)) {

        # (Connect-MgGraph -Scopes "Organization.ReadWrite.All") 1> $null
        if (Get-IsAuthenticated) {
            [Array]$ExtentionConfigurationArray = Get-ArcGISConnectionInfos

            if ($ExtentionConfigurationArray.length -gt 0) {
                [Array]$ExtentionConfigurationNewArray = $ExtentionConfigurationArray | Where-Object { $_.url.ToLower() -ne $ArcGISConnectionUrl.ToLower() }
                if ($ExtentionConfigurationNewArray.length -eq $ExtentionConfigurationArray.length) {
                    Write-Warning "The ArcGIS connection with the URL '$ArcGISConnectionUrl' is not found"
                }
                else {
                    if ($Default.IsPresent) {
                        foreach ($ExtentionConfigurationItem in $ExtentionConfigurationNewArray) {
                            $ExtentionConfigurationItem.isDefault = $false
                        }
                    }
                    $ConfigurationProperty = @{
                        "url"       = $ArcGISConnectionUrl;
                        "title"     = $ArcGISConnectionTitle;
                        "isDefault" = $Default.IsPresent;
                    }
                    $ExtentionConfigurationNewArray += $ConfigurationProperty

                    $ExtensionProperties = @{
                        "connections" = ConvertTo-Json $ExtentionConfigurationNewArray -Compress
                    }

                    $OrganizationId = (Get-MgOrganization).Id
                    Update-MgOrganizationExtension -OrganizationId $OrganizationId -ExtensionId $ArcGISOpenExtensionId -AdditionalProperties $ExtensionProperties
                }
            }
            else {
                Write-Warning "The ArcGIS connection with the URL '$ArcGISConnectionUrl' is not found."
            }
        }
    }
}
Function Remove-ArcGISConnection {

    [CmdletBinding(SupportsShouldProcess = $True)]
    Param
    (
        [Parameter(Mandatory = $true, HelpMessage = "Specify title of the ArcGIS connection configuration that you want to remove")]
        [String] $ArcGISConnectionTitle
    )

    If ($PSCmdlet.ShouldProcess("Remove ArcGIS connection configuration with the tille '${ArcGISConnectionTitle}'", $null, $null)) {
        # (Connect-MgGraph -Scopes "Organization.ReadWrite.All") 1> $null
        if (Get-IsAuthenticated) {
            $OrganizationId = (Get-MgOrganization).Id

            [Array]$ExtentionConfigurationArray = Get-ArcGISConnectionInfos

            if ($ExtentionConfigurationArray.length -gt 0) {

                [Array]$ExtentionConfigurationNewArray = $ExtentionConfigurationArray | Where-Object { $_.title.ToLower() -ne $ArcGISConnectionTitle.ToLower() }

                if ($ExtentionConfigurationNewArray.length -eq $ExtentionConfigurationArray.length) {
                    Write-Warning "The ArcGIS connection configuration with title '$ArcGISConnectionTitle' is not found."
                }
                else {
                    $ExtensionProperties = @{
                        "connections" = ConvertTo-Json $ExtentionConfigurationNewArray -Compress
                    }
                    Update-MgOrganizationExtension -OrganizationId $OrganizationId -ExtensionId $ArcGISOpenExtensionId -AdditionalProperties $ExtensionProperties
                    Write-Host "The ArcGIS connections configuration with the title '$ArcGISConnectionTitle' has been removed." -ForegroundColor "Green"
                }
            }
        }
    }
}

Function Clear-ArcGISConnections {

    [CmdletBinding(SupportsShouldProcess = $True)]
    Param()

    If ($PSCmdlet.ShouldProcess("Remove all ArcGIS connection configurations", $null, $null)) {
        # (Connect-MgGraph -Scopes "Organization.ReadWrite.All") 1> $null
        if (Get-IsAuthenticated) {
            $OrganizationId = (Get-MgOrganization).Id

            $ExtensionIds = (Get-MgOrganizationExtension -OrganizationId $OrganizationId).Id

            if (($null -ne $ExtensionIds) -and $ExtensionIds.Contains($ArcGISOpenExtensionId)) {
                Remove-MgOrganizationExtension -OrganizationId $OrganizationId -ExtensionId $ArcGISOpenExtensionId
            }

            Write-Host "All ArcGIS connection configurations have been removed." -ForegroundColor "Green"
        }
    }
}

Function Show-ArcGISConnections {

    [CmdletBinding(SupportsShouldProcess = $True)]
    Param()

    If ($PSCmdlet.ShouldProcess("List all ArcGIS connection configurations", $null, $null)) {
        # (Connect-MgGraph -Scopes "Organization.Read.All") 1> $null
        if (Get-IsAuthenticated) {
            [Array]$ExtentionConfigurationArray = Get-ArcGISConnectionInfos

            $index = 1;
            foreach ($ExtentionConfigurationItem in $ExtentionConfigurationArray) {
                Write-Host "`n#$index" -ForegroundColor  "Green"
                if ($ExtentionConfigurationItem.isDefault -eq $true) {
                    Write-Host "[Default]" -ForegroundColor "Green"
                }
                Write-Host("Title: {0}" -f $ExtentionConfigurationItem.title) -ForegroundColor "Green"
                Write-Host("URL: {0}" -f $ExtentionConfigurationItem.url) -ForegroundColor "Green"
                $index = $index + 1
            }

            Write-Host("`n{0} ArcGISConnection(s) in total.`n" -f $ExtentionConfigurationArray.length) -ForegroundColor  "Green"
        }
    }

}

Function Export-ArcGISConnections {

    [CmdletBinding(SupportsShouldProcess = $True)]
    Param()

    If ($PSCmdlet.ShouldProcess("Export all ArcGIS connection configurations to a file in the same directory.", $null, $null)) {
        # (Connect-MgGraph -Scopes "Organization.Read.All") 1> $null
        if (Get-IsAuthenticated) {
            [Array]$ExtentionConfigurationArray = Get-ArcGISConnectionInfos
            ConvertTo-Json $ExtentionConfigurationArray | Out-File "connections.txt"
        }
    }
}

# private Function
Function Get-ArcGISConnectionInfos {

    $OrganizationId = (Get-MgOrganization).Id

    $ExtensionIds = (Get-MgOrganizationExtension -OrganizationId $OrganizationId).Id

    if (($null -ne $ExtensionIds) -and $ExtensionIds.Contains($ArcGISOpenExtensionId)) {
        $Extention = Get-MgOrganizationExtension -OrganizationId $OrganizationId | Where-Object { $_.Id -eq $ArcGISOpenExtensionId }
        $ExtentionConfiguration = $Extention.AdditionalProperties["connections"]
        try {
            $ExtentionConfigurationArray = ConvertFrom-Json $ExtentionConfiguration
            if ($ExtentionConfigurationArray -is [Array]) {
                return $ExtentionConfigurationArray
            }
            else {
                return , @()
            }
        }
        catch {
            return , @()
        }
    }
    else {
        return , @()
    }
}

# private Function
Function Test-ArcGISConnectionURL {
    Param
    (
        [Parameter(Mandatory = $true)]
        [String] $ArcGISConnectionUrl
    )

    if (-not ($ArcGISConnectionUrl -cmatch '(?i)(https:\/\/)([^\s,]+)')) {
        Throw "The specified ArcGISConnectionUrl parameter: '$ArcGISConnectionUrl' is not a valid URL string."
    }
    try {
        $Response = Invoke-WebRequest -URI "$ArcGISConnectionUrl/sharing/rest/portals/self?f=json" -TimeoutSec 5
        if ($Response.StatusCode -eq [HttpStatusCode]::OK) {
            return $true
        }
        else {
            Throw "The specified ArcGISConnectionUrl parameter: '$ArcGISConnectionUrl' is not a ArcGIS Online organization or ArcGIS Enterprise URL."
        }
    }
    catch {
        $WebException = $_.Exception;
        if ($WebException.Response.StatusCode -eq [HttpStatusCode]::Forbidden -or $WebException.Response.StatusCode -eq [HttpStatusCode]::Unauthorized) {
            return $true
        }
        else {
            Throw "The specified ArcGISConnectionUrl parameter: '$ArcGISConnectionUrl' is not a ArcGIS Online organization or ArcGIS Enterprise URL."
        }
    }
}

#private function
Function Get-IsAuthenticated {
    $OrganizationId = (Get-MgOrganization).Id 2> $null
    if ($null -eq $OrganizationId) {
        Write-Host "Authentication needed, please call Connect-MgGraph -Scopes ""Organization.Read.All""." -ForegroundColor "Red"
        return $false
    }
    else {
        return $true
    }
}

Export-ModuleMember -Function New-ArcGISTermSet

Export-ModuleMember -Function New-ArcGISConnection
Export-ModuleMember -Function Update-ArcGISConnection
Export-ModuleMember -Function Remove-ArcGISConnection
Export-ModuleMember -Function Show-ArcGISConnections
Export-ModuleMember -Function Clear-ArcGISConnections
Export-ModuleMember -Function Export-ArcGISConnections