|
| 1 | +function Enable-CIPPMDEConnector { |
| 2 | + <# |
| 3 | + .SYNOPSIS |
| 4 | + Provisions the Microsoft Defender for Endpoint Intune connector for a tenant. |
| 5 | + .DESCRIPTION |
| 6 | + Checks whether the MDE mobile threat defense connector (partnerState) is already 'available' or 'enabled'. |
| 7 | + If not, iterates through regional MDE API portal endpoints until one succeeds, then verifies |
| 8 | + the connector state afterwards. Endpoints are ordered so that the tenant's likely region |
| 9 | + (based on org countryLetterCode) is tried first. |
| 10 | + .PARAMETER TenantFilter |
| 11 | + The tenant domain or ID to provision the connector for. |
| 12 | + .FUNCTIONALITY |
| 13 | + Internal |
| 14 | + #> |
| 15 | + [CmdletBinding()] |
| 16 | + param( |
| 17 | + [Parameter(Mandatory = $true)] |
| 18 | + [string]$TenantFilter |
| 19 | + ) |
| 20 | + |
| 21 | + # MDE connector ID is fixed across all tenants |
| 22 | + $ConnectorId = 'fc780465-2017-40d4-a0c5-307022471b92' |
| 23 | + $ConnectorUri = "https://graph.microsoft.com/beta/deviceManagement/mobileThreatDefenseConnectors/$ConnectorId" |
| 24 | + |
| 25 | + # All known regional provisioning endpoints |
| 26 | + $AllEndpoints = @( |
| 27 | + 'mde-rsp-apiportal-prd-eus.securitycenter.windows.com' |
| 28 | + 'mde-rsp-apiportal-prd-eus3.securitycenter.windows.com' |
| 29 | + 'mde-rsp-apiportal-prd-cus.securitycenter.windows.com' |
| 30 | + 'mde-rsp-apiportal-prd-cus3.securitycenter.windows.com' |
| 31 | + 'mde-rsp-apiportal-prd-weu.securitycenter.windows.com' |
| 32 | + 'mde-rsp-apiportal-prd-weu3.securitycenter.windows.com' |
| 33 | + 'mde-rsp-apiportal-prd-neu.securitycenter.windows.com' |
| 34 | + 'mde-rsp-apiportal-prd-neu3.securitycenter.windows.com' |
| 35 | + 'mde-rsp-apiportal-prd-uks.securitycenter.windows.com' |
| 36 | + 'mde-rsp-apiportal-prd-ukw.securitycenter.windows.com' |
| 37 | + 'mde-rsp-apiportal-prd-aue.securitycenter.windows.com' |
| 38 | + 'mde-rsp-apiportal-prd-aus.securitycenter.windows.com' |
| 39 | + 'mde-rsp-apiportal-prd-aec0a.securitycenter.windows.com' |
| 40 | + 'mde-rsp-apiportal-prd-aen0a.securitycenter.windows.com' |
| 41 | + 'mde-rsp-apiportal-prd-ins0a.securitycenter.windows.com' |
| 42 | + 'mde-rsp-apiportal-prd-inc0a.securitycenter.windows.com' |
| 43 | + 'mde-rsp-apiportal-prd-sww0a.securitycenter.windows.com' |
| 44 | + 'mde-rsp-apiportal-prd-swn0a.securitycenter.windows.com' |
| 45 | + ) |
| 46 | + |
| 47 | + # Country code -> likely regional endpoint prefixes (used to prioritize, not restrict) |
| 48 | + $RegionPriority = @{ |
| 49 | + 'US' = @('eus', 'eus3', 'cus', 'cus3') |
| 50 | + 'CA' = @('eus', 'eus3', 'cus', 'cus3') |
| 51 | + 'GB' = @('uks', 'ukw') |
| 52 | + 'AU' = @('aue', 'aus', 'aec0a', 'aen0a') |
| 53 | + 'IN' = @('ins0a', 'inc0a') |
| 54 | + 'SE' = @('sww0a', 'swn0a') |
| 55 | + 'DE' = @('weu', 'weu3') |
| 56 | + 'FR' = @('weu', 'weu3') |
| 57 | + 'NL' = @('weu', 'weu3') |
| 58 | + 'BE' = @('weu', 'weu3') |
| 59 | + 'AT' = @('weu', 'weu3') |
| 60 | + 'CH' = @('weu', 'weu3') |
| 61 | + 'IE' = @('neu', 'neu3') |
| 62 | + 'FI' = @('neu', 'neu3') |
| 63 | + 'NO' = @('neu', 'neu3') |
| 64 | + 'DK' = @('neu', 'neu3') |
| 65 | + } |
| 66 | + |
| 67 | + # Check current connector state |
| 68 | + try { |
| 69 | + $ConnectorState = New-GraphGetRequest -uri $ConnectorUri -tenantid $TenantFilter |
| 70 | + } catch { |
| 71 | + $ErrorMessage = Get-CippException -Exception $_ |
| 72 | + Write-LogMessage -API 'MDEConnector' -tenant $TenantFilter -message "Failed to retrieve MDE connector state. Error: $($ErrorMessage.NormalizedError)" -Sev Error -LogData $ErrorMessage |
| 73 | + throw "Failed to retrieve MDE connector state for $TenantFilter. Error: $($ErrorMessage.NormalizedError)" |
| 74 | + } |
| 75 | + |
| 76 | + if ($ConnectorState.partnerState -in @('available', 'enabled')) { |
| 77 | + Write-LogMessage -API 'MDEConnector' -tenant $TenantFilter -message 'MDE Intune connector is already in available state.' -Sev Info |
| 78 | + return [PSCustomObject]@{ |
| 79 | + Success = $true |
| 80 | + AlreadyDone = $true |
| 81 | + PartnerState = $ConnectorState.partnerState |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + # Build a prioritized endpoint list based on tenant country |
| 86 | + $PrioritizedEndpoints = [System.Collections.Generic.List[string]]::new() |
| 87 | + try { |
| 88 | + $OrgInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/organization' -tenantid $TenantFilter |
| 89 | + $CountryCode = $OrgInfo.countryLetterCode |
| 90 | + if ($CountryCode -and $RegionPriority.ContainsKey($CountryCode)) { |
| 91 | + $PrefixHints = $RegionPriority[$CountryCode] |
| 92 | + foreach ($endpoint in $AllEndpoints) { |
| 93 | + foreach ($prefix in $PrefixHints) { |
| 94 | + if ($endpoint -like "*-prd-$prefix.*") { |
| 95 | + $PrioritizedEndpoints.Add($endpoint) |
| 96 | + break |
| 97 | + } |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | + Write-Information "MDE connector provisioning for $TenantFilter (country: $CountryCode): prioritized $($PrioritizedEndpoints.Count) regional endpoint(s)" |
| 102 | + } catch { |
| 103 | + Write-Information "Could not retrieve org country for $TenantFilter - will try all endpoints" |
| 104 | + } |
| 105 | + |
| 106 | + # Append remaining endpoints that weren't already prioritized |
| 107 | + foreach ($endpoint in $AllEndpoints) { |
| 108 | + if ($endpoint -notin $PrioritizedEndpoints) { |
| 109 | + $PrioritizedEndpoints.Add($endpoint) |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + # Try each endpoint until one succeeds |
| 114 | + $ProvisionBody = '{"timeout":60000}' |
| 115 | + $ProvisionScope = 'https://api.securitycenter.windows.com/.default' |
| 116 | + $SuccessfulEndpoint = $null |
| 117 | + |
| 118 | + foreach ($endpoint in $PrioritizedEndpoints) { |
| 119 | + $ProvisionUri = "https://$endpoint/api/cloud/portal/onboarding/intune/provision" |
| 120 | + try { |
| 121 | + Write-Information "Attempting MDE provisioning for $TenantFilter via $endpoint" |
| 122 | + $null = New-GraphPOSTRequest -uri $ProvisionUri -tenantid $TenantFilter -body $ProvisionBody -scope $ProvisionScope |
| 123 | + $SuccessfulEndpoint = $endpoint |
| 124 | + Write-LogMessage -API 'MDEConnector' -tenant $TenantFilter -message "MDE Intune connector provisioned successfully via $endpoint" -Sev Info |
| 125 | + break |
| 126 | + } catch { |
| 127 | + $ErrorMessage = Get-CippException -Exception $_ |
| 128 | + Write-Information "Endpoint $endpoint failed for $TenantFilter`: $($ErrorMessage.NormalizedError)" |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + if (-not $SuccessfulEndpoint) { |
| 133 | + $Msg = "Failed to provision MDE Intune connector for $TenantFilter - all regional endpoints were unsuccessful." |
| 134 | + Write-LogMessage -API 'MDEConnector' -tenant $TenantFilter -message $Msg -Sev Error |
| 135 | + throw $Msg |
| 136 | + } |
| 137 | + |
| 138 | + # Verify the connector state after provisioning |
| 139 | + try { |
| 140 | + $UpdatedState = New-GraphGetRequest -uri $ConnectorUri -tenantid $TenantFilter |
| 141 | + } catch { |
| 142 | + $UpdatedState = $null |
| 143 | + } |
| 144 | + |
| 145 | + return [PSCustomObject]@{ |
| 146 | + Success = $UpdatedState.partnerState -in @('available', 'enabled') |
| 147 | + AlreadyDone = $false |
| 148 | + Endpoint = $SuccessfulEndpoint |
| 149 | + PartnerState = $UpdatedState.partnerState |
| 150 | + } |
| 151 | +} |
0 commit comments