From 50ad4ea0638f906f343465444ffc798aabf093f1 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 12:32:56 +0100 Subject: [PATCH 01/44] preventing incorrect logs from breaking job --- Scheduler_Alert/run.ps1 | 9 ++++++++- Scheduler_CIPPNotifications/run.ps1 | 14 +++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Scheduler_Alert/run.ps1 b/Scheduler_Alert/run.ps1 index 5eef6f80d845..cfacac0e5ffd 100644 --- a/Scheduler_Alert/run.ps1 +++ b/Scheduler_Alert/run.ps1 @@ -84,7 +84,14 @@ $ShippedAlerts = switch ($Alerts) { } } } -$currentlog = Get-Content "Logs\$((Get-Date).ToString('ddMMyyyy')).log" | ConvertFrom-Csv -Header "DateTime", "Tenant", "API", "Message", "User", "Severity" -Delimiter "|" | Where-Object -Property Tenant -EQ $tenant.tenant +$currentlog = Get-Content "Logs\$((Get-Date).ToString('ddMMyyyy')).log" | ForEach-Object { + try { + $_ | ConvertFrom-Csv -Header "DateTime", "Tenant", "API", "Message", "User", "Severity" -Delimiter "|" | Where-Object -Property Tenant -EQ $tenant.tenant + } + catch { + + } +} Write-Host $ShippedAlerts $ShippedAlerts | ForEach-Object { if ($_ -in $currentlog.message) { diff --git a/Scheduler_CIPPNotifications/run.ps1 b/Scheduler_CIPPNotifications/run.ps1 index f0a7f41438fb..ba3ba5212d86 100644 --- a/Scheduler_CIPPNotifications/run.ps1 +++ b/Scheduler_CIPPNotifications/run.ps1 @@ -13,12 +13,16 @@ else { $Settings = if ($Config.psobject.properties.name) { @($Config.psobject.properties.name, "Alerts") } else { @("Alerts") } $logdate = (Get-Date).ToString('ddMMyyyy') -try { - $Currentlog = Get-Content "Logs\$($logdate).log" | ConvertFrom-Csv -Header 'DateTime', 'Tenant', 'API', 'Message', 'User', 'Severity' -Delimiter '|' | Where-Object { [datetime]$_.Datetime -gt (Get-Date).AddMinutes(-10) -and $_.api -in $Settings -and $_.Severity -ne 'debug' } -} -catch { - $Currentlog = @{ date = $logdate; message = "The log might be corrupted. We could not retrieve the information: $($_.Exception.message)" } +$Currentlog = Get-Content "Logs\$($logdate).log" | ForEach-Object { + try { + $Line = $_ + $Line | ConvertFrom-Csv -Header 'DateTime', 'Tenant', 'API', 'Message', 'User', 'Severity' -Delimiter '|' | Where-Object { [datetime]$_.Datetime -gt (Get-Date).AddMinutes(-10) -and $_.api -in $Settings -and $_.Severity -ne 'debug' } + } + catch { + + } } + if ($Config.email -ne '' -and $null -ne $CurrentLog) { $HTMLLog = ($CurrentLog | ConvertTo-Html -frag) -replace '', '
' | Out-String $JSONBody = @" From 55636e76b0ca47abdddff6547353d360f90616ae Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 12:33:46 +0100 Subject: [PATCH 02/44] added adding admin agent groups --- ExecSAMSetup/run.ps1 | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index cc9ac803b39b..d04a5b807dfd 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -60,13 +60,7 @@ try { $TenantId = Get-Content '.\Cache_SAMSetup\cache.tenantid' $AppID = Get-Content '.\Cache_SAMSetup\cache.appid' $PartnerSetup = Get-Content '.\Cache_SAMSetup\PartnerSetup.json' -ErrorAction SilentlyContinue - if ($PartnerSetup) { - $FirstLogonRefreshtoken = New-DeviceLogin -clientid $AppID -Scope 'https://api.partnercenter.microsoft.com/user_impersonation' -FirstLogon -TenantId $TenantId - } - else { - $FirstLogonRefreshtoken = New-DeviceLogin -clientid $AppID -Scope 'https://graph.microsoft.com/.default' -FirstLogon -TenantId $TenantId - - } + $FirstLogonRefreshtoken = New-DeviceLogin -clientid $AppID -Scope 'https://graph.microsoft.com/.default' -FirstLogon -TenantId $TenantId New-Item '.\Cache_SAMSetup\SamSetup.json' -Value ($FirstLogonRefreshtoken | ConvertTo-Json) -Force $step = 3 $Results = @{ message = $FirstLogonRefreshtoken.message ; step = $step } @@ -77,14 +71,14 @@ try { $TenantId = Get-Content '.\Cache_SAMSetup\cache.tenantid' $AppID = Get-Content '.\Cache_SAMSetup\cache.appid' $PartnerSetup = Get-Content '.\Cache_SAMSetup\PartnerSetup.json' -ErrorAction SilentlyContinue - if ($PartnerSetup) { - $RefreshToken = (New-DeviceLogin -clientid $AppID -Scope 'https://api.partnercenter.microsoft.com/user_impersonation' -device_code $SAMSetup.device_code) - } - else { - $RefreshToken = (New-DeviceLogin -clientid $AppID -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) + $RefreshToken = (New-DeviceLogin -clientid $AppID -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) - } if ($RefreshToken.Refresh_Token) { + if ($PartnerSetup) { + $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id + $SPN = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appId eq '$($Appid.appid)'" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id + $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN)`"}" -ContentType 'application/json') + } Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $RefreshToken.Refresh_Token -AsPlainText -Force) $step = 4 $Results = @{"message" = "Retrieved refresh token and saving to Keyvault."; step = $step } From d3b0c10af54c654a5a8506679e29e1eaaeedcbcb Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 15:10:00 +0100 Subject: [PATCH 03/44] added adding of adminagentsgroup with retry --- ExecSAMSetup/run.ps1 | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index d04a5b807dfd..527b3dbbfb12 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -74,12 +74,28 @@ try { $RefreshToken = (New-DeviceLogin -clientid $AppID -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) if ($RefreshToken.Refresh_Token) { + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $RefreshToken.Refresh_Token -AsPlainText -Force) if ($PartnerSetup) { - $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id - $SPN = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appId eq '$($Appid.appid)'" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id - $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN)`"}" -ContentType 'application/json') + $attempt = 0 + do { + try { + Start-Sleep 3 + $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id + Write-Host "Id is $GroupID" + $SPN = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appId eq '$($Appid)'" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id + Write-Host "SPN is $SPN" + $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN)`"}" -ContentType 'application/json') + Write-Host "Added to adminagents" + $attempt ++ + } + catch { + $attempt ++ + } + } until ($attempt -gt 5) + + + } - Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $RefreshToken.Refresh_Token -AsPlainText -Force) $step = 4 $Results = @{"message" = "Retrieved refresh token and saving to Keyvault."; step = $step } } From 36a90e1d9fca9835dc5ab570a20c30f3dd692033 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 16:08:28 +0100 Subject: [PATCH 04/44] changes to api url for refresh token --- ExecSAMSetup/run.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index 527b3dbbfb12..0953ac2c6f8f 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -60,7 +60,7 @@ try { $TenantId = Get-Content '.\Cache_SAMSetup\cache.tenantid' $AppID = Get-Content '.\Cache_SAMSetup\cache.appid' $PartnerSetup = Get-Content '.\Cache_SAMSetup\PartnerSetup.json' -ErrorAction SilentlyContinue - $FirstLogonRefreshtoken = New-DeviceLogin -clientid $AppID -Scope 'https://graph.microsoft.com/.default' -FirstLogon -TenantId $TenantId + $FirstLogonRefreshtoken = New-DeviceLogin -clientid $AppID -Scope 'https://api.partnercenter.microsoft.com/user_impersonation' -FirstLogon -TenantId $TenantId New-Item '.\Cache_SAMSetup\SamSetup.json' -Value ($FirstLogonRefreshtoken | ConvertTo-Json) -Force $step = 3 $Results = @{ message = $FirstLogonRefreshtoken.message ; step = $step } From f555b25eb161a90cb5b981b23d44aa9b29b42da4 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 18:23:31 +0100 Subject: [PATCH 05/44] modified version to allow interactivity with tokens --- Cache_SAMSetup/SAMManifest.json | 155 +++++++++++++++++++++++++++++++- ExecSAMSetup/run.ps1 | 95 ++++++++++---------- 2 files changed, 202 insertions(+), 48 deletions(-) diff --git a/Cache_SAMSetup/SAMManifest.json b/Cache_SAMSetup/SAMManifest.json index dc996e8dd99a..6eb861811a8a 100644 --- a/Cache_SAMSetup/SAMManifest.json +++ b/Cache_SAMSetup/SAMManifest.json @@ -1 +1,154 @@ -{"isFallbackPublicClient":true,"signInAudience":"AzureADMyOrg","displayName":"CIPP-SAM","web":{"redirectUris":["https://login.microsoftonline.com/common/oauth2/nativeclient","https://localhost","http://localhost","http://localhost:8400"]},"requiredResourceAccess":[{"resourceAppId":"fa3d9a0c-3fb0-42cc-9193-47c7ecd2edbd","resourceAccess":[{"id":"1cebfa2a-fb4d-419e-b5f9-839b4383e05a","type":"Scope"}]},{"resourceAppId":"00000003-0000-0000-c000-000000000000","resourceAccess":[{"id":"0f4595f7-64b1-4e13-81bc-11a249df07a9","type":"Scope"},{"id":"73e75199-7c3e-41bb-9357-167164dbb415","type":"Scope"},{"id":"7ab1d787-bae7-4d5d-8db6-37ea32df9186","type":"Scope"},{"id":"d01b97e9-cbc0-49fe-810a-750afd5527a3","type":"Scope"},{"id":"46ca0847-7e6b-426e-9775-ea810a948356","type":"Scope"},{"id":"dc38509c-b87d-4da0-bd92-6bec988bac4a","type":"Scope"},{"id":"7427e0e9-2fba-42fe-b0c0-848c9e6a8182","type":"Scope"},{"id":"ad902697-1014-4ef5-81ef-2b4301988e8c","type":"Scope"},{"id":"572fea84-0151-49b2-9301-11cb16974376","type":"Scope"},{"id":"e4c9e354-4dc5-45b8-9e7c-e1393b0b1a20","type":"Scope"},{"id":"0883f392-0a7a-443d-8c76-16a6d39c7b63","type":"Scope"},{"id":"7b3f05d5-f68c-4b8d-8c59-a2ecd12f24af","type":"Scope"},{"id":"0c5e8a55-87a6-4556-93ab-adc52c4d862d","type":"Scope"},{"id":"44642bfe-8385-4adc-8fc6-fe3cb2c375c3","type":"Scope"},{"id":"662ed50a-ac44-4eef-ad86-62eed9be2a29","type":"Scope"},{"id":"8696daa5-bce5-4b2e-83f9-51b6defc4e1e","type":"Scope"},{"id":"6aedf524-7e1c-45a7-bd76-ded8cab8d0fc","type":"Scope"},{"id":"bac3b9c2-b516-4ef4-bd3b-c2ef73d8d804","type":"Scope"},{"id":"11d4cd79-5ba5-460f-803f-e22c8ab85ccd","type":"Scope"},{"id":"02e97553-ed7b-43d0-ab3c-f8bace0d040c","type":"Scope"},{"id":"89fe6a52-be36-487e-b7d8-d061c450a026","type":"Scope"},{"id":"a367ab51-6b49-43bf-a716-a1fb06d2a174","type":"Scope"},{"id":"204e0828-b5ca-4ad8-b9f3-f32a958e7cc4","type":"Scope"},{"id":"4e46008b-f24c-477d-8fff-7bb4ec7aafe0","type":"Scope"},{"id":"0e263e50-5827-48a4-b97c-d940288653c7","type":"Scope"},{"id":"e383f46e-2787-4529-855e-0e479a3ffac0","type":"Scope"},{"id":"37f7f235-527c-4136-accd-4a02d197296e","type":"Scope"},{"id":"14dad69e-099b-42c9-810b-d002981feec1","type":"Scope"},{"id":"f6a3db3e-f7e8-4ed2-a414-557c8c9830be","type":"Scope"},{"id":"0e755559-83fb-4b44-91d0-4cc721b9323e","type":"Scope"},{"id":"a84a9652-ffd3-496e-a991-22ba5529156a","type":"Scope"},{"id":"1d89d70c-dcac-4248-b214-903c457af83a","type":"Scope"},{"id":"32ea53ac-4a89-4cde-bac4-727c6fb9ac29","type":"Scope"},{"id":"2b61aa8a-6d36-4b2f-ac7b-f29867937c53","type":"Scope"},{"id":"ebf0f66e-9fb1-49e4-a278-222f76911cf4","type":"Scope"},{"id":"daef10fc-047a-48b0-b1a5-da4b5e72fabc","type":"Scope"},{"id":"2a5addc2-4d9e-4d7d-8527-5215aec410f3","type":"Scope"},{"id":"c79f8feb-a9db-4090-85f9-90d820caa0eb","type":"Scope"},{"id":"bdfbf15f-ee85-4955-8675-146e8e5296b5","type":"Scope"},{"id":"f81125ac-d3b7-4573-a3b2-7099cc39df9e","type":"Scope"},{"id":"cac97e40-6730-457d-ad8d-4852fddab7ad","type":"Scope"},{"id":"b7887744-6746-4312-813d-72daeaee7e2d","type":"Scope"},{"id":"48971fc1-70d7-4245-af77-0beb29b53ee2","type":"Scope"},{"id":"aec28ec7-4d02-4e8c-b864-50163aea77eb","type":"Scope"},{"id":"d3f0af02-b22d-4778-a433-14f7e3f2e1e2","type":"Scope"},{"id":"9127ba42-f79f-43b1-be80-f23ecd42377e","type":"Scope"},{"id":"a9ff19c2-f369-4a95-9a25-ba9d460efc8e","type":"Scope"},{"id":"59dacb05-e88d-4c13-a684-59f1afc8cc98","type":"Scope"},{"id":"b98bfd41-87c6-45cc-b104-e2de4f0dafb9","type":"Scope"},{"id":"2f9ee017-59c1-4f1d-9472-bd5529a7b311","type":"Scope"},{"id":"951183d1-1a61-466f-a6d1-1fde911bfd95","type":"Scope"},{"id":"637d7bec-b31e-4deb-acc9-24275642a2c9","type":"Scope"},{"id":"101147cf-4178-4455-9d58-02b5c164e759","type":"Scope"},{"id":"cc83893a-e232-4723-b5af-bd0b01bcfe65","type":"Scope"},{"id":"233e0cf1-dd62-48bc-b65b-b38fe87fcf8e","type":"Scope"},{"id":"d649fb7c-72b4-4eec-b2b4-b15acf79e378","type":"Scope"},{"id":"485be79e-c497-4b35-9400-0e3fa7f2a5d4","type":"Scope"},{"id":"9d8982ae-4365-4f57-95e9-d6032a4c0b87","type":"Scope"},{"id":"48638b3c-ad68-4383-8ac4-e6880ee6ca57","type":"Scope"},{"id":"39d65650-9d3e-4223-80db-a335590d027e","type":"Scope"},{"id":"4a06efd2-f825-4e34-813e-82a57b03d1ee","type":"Scope"},{"id":"f3bfad56-966e-4590-a536-82ecf548ac1e","type":"Scope"},{"id":"4d135e65-66b8-41a8-9f8b-081452c91774","type":"Scope"},{"id":"2eadaff8-0bce-4198-a6b9-2cfc35a30075","type":"Scope"},{"id":"0c3e411a-ce45-4cd1-8f30-f99a3efa7b11","type":"Scope"},{"id":"edb72de9-4252-4d03-a925-451deef99db7","type":"Scope"},{"id":"767156cb-16ae-4d10-8f8b-41b657c8c8c8","type":"Scope"},{"id":"7e823077-d88e-468f-a337-e18f1f0e6c7c","type":"Scope"},{"id":"edd3c878-b384-41fd-95ad-e7407dd775be","type":"Scope"},{"id":"40b534c3-9552-4550-901b-23879c90bcf9","type":"Scope"},{"id":"bf3fbf03-f35f-4e93-963e-47e4d874c37a","type":"Scope"},{"id":"5248dcb1-f83b-4ec3-9f4d-a4428a961a72","type":"Scope"},{"id":"c395395c-ff9a-4dba-bc1f-8372ba9dca84","type":"Scope"},{"id":"2e25a044-2580-450d-8859-42eeb6e996c0","type":"Scope"},{"id":"0ce33576-30e8-43b7-99e5-62f8569a4002","type":"Scope"},{"id":"207e0cb1-3ce7-4922-b991-5a760c346ebc","type":"Scope"},{"id":"093f8818-d05f-49b8-95bc-9d2a73e9a43c","type":"Scope"},{"id":"7825d5d6-6049-4ce7-bdf6-3b8d53f4bcd0","type":"Scope"},{"id":"2104a4db-3a2f-4ea0-9dba-143d457dc666","type":"Scope"},{"id":"eda39fa6-f8cf-4c3c-a909-432c683e4c9b","type":"Scope"},{"id":"55896846-df78-47a7-aa94-8d3d4442ca7f","type":"Scope"},{"id":"aa85bf13-d771-4d5d-a9e6-bca04ce44edf","type":"Scope"},{"id":"ee928332-e9c2-4747-b4a0-f8c164b68de6","type":"Scope"},{"id":"c975dd04-a06e-4fbb-9704-62daad77bb49","type":"Scope"},{"id":"c37c9b61-7762-4bff-a156-afc0005847a0","type":"Scope"},{"id":"999f8c63-0a38-4f1b-91fd-ed1947bdd1a9","type":"Role"},{"id":"292d869f-3427-49a8-9dab-8c70152b74e9","type":"Role"},{"id":"2f51be20-0bb4-4fed-bf7b-db946066c75e","type":"Role"},{"id":"58ca0d9a-1575-47e1-a3cb-007ef2e4583b","type":"Role"},{"id":"06a5fe6d-c49d-46a7-b082-56b1b14103c7","type":"Role"},{"id":"246dd0d5-5bd0-4def-940b-0421030a5b68","type":"Role"},{"id":"bf394140-e372-4bf9-a898-299cfc7564e5","type":"Role"},{"id":"741f803b-c850-494e-b5df-cde7c675a1ca","type":"Role"},{"id":"230c1aed-a721-4c5d-9cb4-a90514e508ef","type":"Role"},{"id":"b633e1c5-b582-4048-a93e-9f11b44c7e96","type":"Role"},{"id":"5b567255-7703-4780-807c-7be8301ae99b","type":"Role"},{"id":"62a82d76-70ea-41e2-9197-370581804d09","type":"Role"},{"id":"7ab1d382-f21e-4acd-a863-ba3e13f7da61","type":"Role"},{"id":"1138cb37-bd11-4084-a2b7-9f71582aeddb","type":"Role"},{"id":"78145de6-330d-4800-a6ce-494ff2d33d07","type":"Role"},{"id":"9241abd9-d0e6-425a-bd4f-47ba86e767a4","type":"Role"},{"id":"5b07b0dd-2377-4e44-a38d-703f09a0dc3c","type":"Role"},{"id":"243333ab-4d21-40cb-a475-36241daa0842","type":"Role"},{"id":"e330c4f0-4170-414e-a55a-2f022ec2b57b","type":"Role"},{"id":"5ac13192-7ace-4fcf-b828-1a26f28068ee","type":"Role"},{"id":"2f6817f8-7b12-4f0f-bc18-eeaf60705a9e","type":"Role"},{"id":"dbaae8cf-10b5-4b86-a4a1-f871c94c6695","type":"Role"},{"id":"bf7b1a76-6e77-406b-b258-bf5c7720e98f","type":"Role"},{"id":"01c0a623-fc9b-48e9-b794-0756f8e8f067","type":"Role"},{"id":"50483e42-d915-4231-9639-7fdb7fd190e5","type":"Role"},{"id":"dbb9058a-0e50-45d7-ae91-66909b5d4664","type":"Role"},{"id":"a82116e5-55eb-4c41-a434-62fe8a61c773","type":"Role"},{"id":"f3a65bd4-b703-46df-8f7e-0174fea562aa","type":"Role"},{"id":"59a6b24b-4225-4393-8165-ebaec5f55d7a","type":"Role"},{"id":"0121dc95-1b9f-4aed-8bac-58c5ac466691","type":"Role"},{"id":"3b55498e-47ec-484f-8136-9013221c06a9","type":"Role"},{"id":"35930dcf-aceb-4bd1-b99a-8ffed403c974","type":"Role"},{"id":"25f85f3c-f66c-4205-8cd5-de92dd7f0cec","type":"Role"},{"id":"29c18626-4985-4dcd-85c0-193eef327366","type":"Role"},{"id":"4437522e-9a86-4a41-a7da-e380edd4a97d","type":"Role"}]},{"resourceAppId":"00000002-0000-0000-c000-000000000000","resourceAccess":[{"id":"5778995a-e1bf-45b8-affa-663a9f3f4d04","type":"Role"},{"id":"a42657d6-7f20-40e3-b6f0-cee03008a62a","type":"Scope"},{"id":"311a71cc-e848-46a1-bdf8-97ff7156d8e6","type":"Scope"}]}]} +{ + "isFallbackPublicClient": true, + "signInAudience": "AzureADMyOrg", + "displayName": "CIPP-SAM", + "web": { + "redirectUris": [ + "https://login.microsoftonline.com/common/oauth2/nativeclient", + "https://localhost", + "http://localhost", + "http://localhost:8400" + ] + }, + "requiredResourceAccess": [ + { + "resourceAppId": "fa3d9a0c-3fb0-42cc-9193-47c7ecd2edbd", + "resourceAccess": [ + { "id": "1cebfa2a-fb4d-419e-b5f9-839b4383e05a", "type": "Scope" } + ] + }, + { + "resourceAppId": "00000003-0000-0000-c000-000000000000", + "resourceAccess": [ + { "id": "0f4595f7-64b1-4e13-81bc-11a249df07a9", "type": "Scope" }, + { "id": "73e75199-7c3e-41bb-9357-167164dbb415", "type": "Scope" }, + { "id": "7ab1d787-bae7-4d5d-8db6-37ea32df9186", "type": "Scope" }, + { "id": "d01b97e9-cbc0-49fe-810a-750afd5527a3", "type": "Scope" }, + { "id": "46ca0847-7e6b-426e-9775-ea810a948356", "type": "Scope" }, + { "id": "dc38509c-b87d-4da0-bd92-6bec988bac4a", "type": "Scope" }, + { "id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182", "type": "Scope" }, + { "id": "ad902697-1014-4ef5-81ef-2b4301988e8c", "type": "Scope" }, + { "id": "572fea84-0151-49b2-9301-11cb16974376", "type": "Scope" }, + { "id": "e4c9e354-4dc5-45b8-9e7c-e1393b0b1a20", "type": "Scope" }, + { "id": "0883f392-0a7a-443d-8c76-16a6d39c7b63", "type": "Scope" }, + { "id": "7b3f05d5-f68c-4b8d-8c59-a2ecd12f24af", "type": "Scope" }, + { "id": "0c5e8a55-87a6-4556-93ab-adc52c4d862d", "type": "Scope" }, + { "id": "44642bfe-8385-4adc-8fc6-fe3cb2c375c3", "type": "Scope" }, + { "id": "662ed50a-ac44-4eef-ad86-62eed9be2a29", "type": "Scope" }, + { "id": "8696daa5-bce5-4b2e-83f9-51b6defc4e1e", "type": "Scope" }, + { "id": "6aedf524-7e1c-45a7-bd76-ded8cab8d0fc", "type": "Scope" }, + { "id": "bac3b9c2-b516-4ef4-bd3b-c2ef73d8d804", "type": "Scope" }, + { "id": "11d4cd79-5ba5-460f-803f-e22c8ab85ccd", "type": "Scope" }, + { "id": "02e97553-ed7b-43d0-ab3c-f8bace0d040c", "type": "Scope" }, + { "id": "89fe6a52-be36-487e-b7d8-d061c450a026", "type": "Scope" }, + { "id": "a367ab51-6b49-43bf-a716-a1fb06d2a174", "type": "Scope" }, + { "id": "204e0828-b5ca-4ad8-b9f3-f32a958e7cc4", "type": "Scope" }, + { "id": "4e46008b-f24c-477d-8fff-7bb4ec7aafe0", "type": "Scope" }, + { "id": "0e263e50-5827-48a4-b97c-d940288653c7", "type": "Scope" }, + { "id": "e383f46e-2787-4529-855e-0e479a3ffac0", "type": "Scope" }, + { "id": "37f7f235-527c-4136-accd-4a02d197296e", "type": "Scope" }, + { "id": "14dad69e-099b-42c9-810b-d002981feec1", "type": "Scope" }, + { "id": "f6a3db3e-f7e8-4ed2-a414-557c8c9830be", "type": "Scope" }, + { "id": "0e755559-83fb-4b44-91d0-4cc721b9323e", "type": "Scope" }, + { "id": "a84a9652-ffd3-496e-a991-22ba5529156a", "type": "Scope" }, + { "id": "1d89d70c-dcac-4248-b214-903c457af83a", "type": "Scope" }, + { "id": "32ea53ac-4a89-4cde-bac4-727c6fb9ac29", "type": "Scope" }, + { "id": "2b61aa8a-6d36-4b2f-ac7b-f29867937c53", "type": "Scope" }, + { "id": "ebf0f66e-9fb1-49e4-a278-222f76911cf4", "type": "Scope" }, + { "id": "daef10fc-047a-48b0-b1a5-da4b5e72fabc", "type": "Scope" }, + { "id": "2a5addc2-4d9e-4d7d-8527-5215aec410f3", "type": "Scope" }, + { "id": "c79f8feb-a9db-4090-85f9-90d820caa0eb", "type": "Scope" }, + { "id": "bdfbf15f-ee85-4955-8675-146e8e5296b5", "type": "Scope" }, + { "id": "f81125ac-d3b7-4573-a3b2-7099cc39df9e", "type": "Scope" }, + { "id": "cac97e40-6730-457d-ad8d-4852fddab7ad", "type": "Scope" }, + { "id": "b7887744-6746-4312-813d-72daeaee7e2d", "type": "Scope" }, + { "id": "48971fc1-70d7-4245-af77-0beb29b53ee2", "type": "Scope" }, + { "id": "aec28ec7-4d02-4e8c-b864-50163aea77eb", "type": "Scope" }, + { "id": "d3f0af02-b22d-4778-a433-14f7e3f2e1e2", "type": "Scope" }, + { "id": "9127ba42-f79f-43b1-be80-f23ecd42377e", "type": "Scope" }, + { "id": "a9ff19c2-f369-4a95-9a25-ba9d460efc8e", "type": "Scope" }, + { "id": "59dacb05-e88d-4c13-a684-59f1afc8cc98", "type": "Scope" }, + { "id": "b98bfd41-87c6-45cc-b104-e2de4f0dafb9", "type": "Scope" }, + { "id": "2f9ee017-59c1-4f1d-9472-bd5529a7b311", "type": "Scope" }, + { "id": "951183d1-1a61-466f-a6d1-1fde911bfd95", "type": "Scope" }, + { "id": "637d7bec-b31e-4deb-acc9-24275642a2c9", "type": "Scope" }, + { "id": "101147cf-4178-4455-9d58-02b5c164e759", "type": "Scope" }, + { "id": "cc83893a-e232-4723-b5af-bd0b01bcfe65", "type": "Scope" }, + { "id": "233e0cf1-dd62-48bc-b65b-b38fe87fcf8e", "type": "Scope" }, + { "id": "d649fb7c-72b4-4eec-b2b4-b15acf79e378", "type": "Scope" }, + { "id": "485be79e-c497-4b35-9400-0e3fa7f2a5d4", "type": "Scope" }, + { "id": "9d8982ae-4365-4f57-95e9-d6032a4c0b87", "type": "Scope" }, + { "id": "48638b3c-ad68-4383-8ac4-e6880ee6ca57", "type": "Scope" }, + { "id": "39d65650-9d3e-4223-80db-a335590d027e", "type": "Scope" }, + { "id": "4a06efd2-f825-4e34-813e-82a57b03d1ee", "type": "Scope" }, + { "id": "f3bfad56-966e-4590-a536-82ecf548ac1e", "type": "Scope" }, + { "id": "4d135e65-66b8-41a8-9f8b-081452c91774", "type": "Scope" }, + { "id": "2eadaff8-0bce-4198-a6b9-2cfc35a30075", "type": "Scope" }, + { "id": "0c3e411a-ce45-4cd1-8f30-f99a3efa7b11", "type": "Scope" }, + { "id": "edb72de9-4252-4d03-a925-451deef99db7", "type": "Scope" }, + { "id": "767156cb-16ae-4d10-8f8b-41b657c8c8c8", "type": "Scope" }, + { "id": "7e823077-d88e-468f-a337-e18f1f0e6c7c", "type": "Scope" }, + { "id": "edd3c878-b384-41fd-95ad-e7407dd775be", "type": "Scope" }, + { "id": "40b534c3-9552-4550-901b-23879c90bcf9", "type": "Scope" }, + { "id": "bf3fbf03-f35f-4e93-963e-47e4d874c37a", "type": "Scope" }, + { "id": "5248dcb1-f83b-4ec3-9f4d-a4428a961a72", "type": "Scope" }, + { "id": "c395395c-ff9a-4dba-bc1f-8372ba9dca84", "type": "Scope" }, + { "id": "2e25a044-2580-450d-8859-42eeb6e996c0", "type": "Scope" }, + { "id": "0ce33576-30e8-43b7-99e5-62f8569a4002", "type": "Scope" }, + { "id": "207e0cb1-3ce7-4922-b991-5a760c346ebc", "type": "Scope" }, + { "id": "093f8818-d05f-49b8-95bc-9d2a73e9a43c", "type": "Scope" }, + { "id": "7825d5d6-6049-4ce7-bdf6-3b8d53f4bcd0", "type": "Scope" }, + { "id": "2104a4db-3a2f-4ea0-9dba-143d457dc666", "type": "Scope" }, + { "id": "eda39fa6-f8cf-4c3c-a909-432c683e4c9b", "type": "Scope" }, + { "id": "55896846-df78-47a7-aa94-8d3d4442ca7f", "type": "Scope" }, + { "id": "aa85bf13-d771-4d5d-a9e6-bca04ce44edf", "type": "Scope" }, + { "id": "ee928332-e9c2-4747-b4a0-f8c164b68de6", "type": "Scope" }, + { "id": "c975dd04-a06e-4fbb-9704-62daad77bb49", "type": "Scope" }, + { "id": "c37c9b61-7762-4bff-a156-afc0005847a0", "type": "Scope" }, + { "id": "999f8c63-0a38-4f1b-91fd-ed1947bdd1a9", "type": "Role" }, + { "id": "292d869f-3427-49a8-9dab-8c70152b74e9", "type": "Role" }, + { "id": "2f51be20-0bb4-4fed-bf7b-db946066c75e", "type": "Role" }, + { "id": "58ca0d9a-1575-47e1-a3cb-007ef2e4583b", "type": "Role" }, + { "id": "06a5fe6d-c49d-46a7-b082-56b1b14103c7", "type": "Role" }, + { "id": "246dd0d5-5bd0-4def-940b-0421030a5b68", "type": "Role" }, + { "id": "bf394140-e372-4bf9-a898-299cfc7564e5", "type": "Role" }, + { "id": "741f803b-c850-494e-b5df-cde7c675a1ca", "type": "Role" }, + { "id": "230c1aed-a721-4c5d-9cb4-a90514e508ef", "type": "Role" }, + { "id": "b633e1c5-b582-4048-a93e-9f11b44c7e96", "type": "Role" }, + { "id": "5b567255-7703-4780-807c-7be8301ae99b", "type": "Role" }, + { "id": "62a82d76-70ea-41e2-9197-370581804d09", "type": "Role" }, + { "id": "7ab1d382-f21e-4acd-a863-ba3e13f7da61", "type": "Role" }, + { "id": "1138cb37-bd11-4084-a2b7-9f71582aeddb", "type": "Role" }, + { "id": "78145de6-330d-4800-a6ce-494ff2d33d07", "type": "Role" }, + { "id": "9241abd9-d0e6-425a-bd4f-47ba86e767a4", "type": "Role" }, + { "id": "5b07b0dd-2377-4e44-a38d-703f09a0dc3c", "type": "Role" }, + { "id": "243333ab-4d21-40cb-a475-36241daa0842", "type": "Role" }, + { "id": "e330c4f0-4170-414e-a55a-2f022ec2b57b", "type": "Role" }, + { "id": "5ac13192-7ace-4fcf-b828-1a26f28068ee", "type": "Role" }, + { "id": "2f6817f8-7b12-4f0f-bc18-eeaf60705a9e", "type": "Role" }, + { "id": "dbaae8cf-10b5-4b86-a4a1-f871c94c6695", "type": "Role" }, + { "id": "bf7b1a76-6e77-406b-b258-bf5c7720e98f", "type": "Role" }, + { "id": "01c0a623-fc9b-48e9-b794-0756f8e8f067", "type": "Role" }, + { "id": "50483e42-d915-4231-9639-7fdb7fd190e5", "type": "Role" }, + { "id": "dbb9058a-0e50-45d7-ae91-66909b5d4664", "type": "Role" }, + { "id": "a82116e5-55eb-4c41-a434-62fe8a61c773", "type": "Role" }, + { "id": "f3a65bd4-b703-46df-8f7e-0174fea562aa", "type": "Role" }, + { "id": "59a6b24b-4225-4393-8165-ebaec5f55d7a", "type": "Role" }, + { "id": "0121dc95-1b9f-4aed-8bac-58c5ac466691", "type": "Role" }, + { "id": "3b55498e-47ec-484f-8136-9013221c06a9", "type": "Role" }, + { "id": "35930dcf-aceb-4bd1-b99a-8ffed403c974", "type": "Role" }, + { "id": "25f85f3c-f66c-4205-8cd5-de92dd7f0cec", "type": "Role" }, + { "id": "29c18626-4985-4dcd-85c0-193eef327366", "type": "Role" }, + { "id": "4437522e-9a86-4a41-a7da-e380edd4a97d", "type": "Role" } + ] + }, + { + "resourceAppId": "00000002-0000-0000-c000-000000000000", + "resourceAccess": [ + { "id": "5778995a-e1bf-45b8-affa-663a9f3f4d04", "type": "Role" }, + { "id": "a42657d6-7f20-40e3-b6f0-cee03008a62a", "type": "Scope" }, + { "id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6", "type": "Scope" } + ] + } + ] +} diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index 0953ac2c6f8f..a987dae7b1b4 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -2,7 +2,6 @@ using namespace System.Net # Input bindings are passed in via param block. param($Request, $TriggerMetadata) - $APIName = $TriggerMetadata.FunctionName Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $ResourceGroup = $ENV:Website_Resource_Group @@ -14,8 +13,22 @@ if ($env:MSI_SECRET) { $KV = Get-AzKeyVault -SubscriptionID $Subscription -ResourceGroupName $ResourceGroup try { + if ($request.query.code) { + try { + #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Request.query.code -AsPlainText -Force) + $Results = "Authentication is now complete. You may now close this window." + New-Item ".\Cache_SAMSetup\Validated.json" -Value "true" + } + catch { + $Results = "Authentication failed. $($_.Exception.message)" + } + } if ($request.query.CreateSAM) { + Remove-Item ".\Cache_SAMSetup\SamSetup.json" -Force -ErrorAction SilentlyContinue + Remove-Item ".\Cache_SAMSetup\Cache.*" -Force -ErrorAction SilentlyContinue + Remove-Item ".\Cache_SAMSetup\SamSetup.json" -Force -ErrorAction SilentlyContinue Remove-Item ".\Cache_SAMSetup\PartnerSetup.json" -Force -ErrorAction SilentlyContinue + Remove-Item ".\Cache_SAMSetup\Validated.json" -Force -ErrorAction SilentlyContinue if ($Request.query.partnersetup) { New-Item -Path '.\Cache_SAMSetup\PartnerSetup.json' -Value 'True' } $step = 1 $DeviceLogon = New-DeviceLogin -clientid "1b730954-1685-4b74-9bfd-dac224a7b894" -Scope 'https://graph.microsoft.com/.default' -FirstLogon @@ -27,25 +40,27 @@ try { $Token = (New-DeviceLogin -clientid "1b730954-1685-4b74-9bfd-dac224a7b894" -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) if ($token.Access_Token) { $step = 2 - + $URL = ($Request.url).split('?') | Select-Object -First 1 $PartnerSetup = Get-Content '.\Cache_SAMSetup\PartnerSetup.json' -ErrorAction SilentlyContinue $TenantId = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/organization" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method GET -ContentType 'application/json').value.id $TenantId | Out-File '.\Cache_SAMSetup\cache.tenantid' if ($PartnerSetup) { - $app = Get-Content '.\Cache_SAMSetup\SAMManifest.json' + $app = Get-Content '.\Cache_SAMSetup\SAMManifest.json' | ConvertFrom-Json + $App.web.redirectUris = @($App.web.redirectUris + $URL) + $app = $app | ConvertTo-Json -Depth 15 $AppId = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') $AppId.appId | Out-File '.\Cache_SAMSetup\cache.appid' + } else { $app = Get-Content '.\Cache_SAMSetup\SAMManifestNoPartner.json' $AppId = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') $AppId.appId | Out-File '.\Cache_SAMSetup\cache.appid' } - Write-Host $AppId $AppPassword = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications/$($AppID.id)/addPassword" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body '{"passwordCredential":{"displayName":"CIPPInstall"}}' -ContentType 'application/json').secretText - Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $TenantId -AsPlainText -Force) - Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appid -AsPlainText -Force) - Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $AppPassword -AsPlainText -Force) + #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $TenantId -AsPlainText -Force) + #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appid -AsPlainText -Force) + #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $AppPassword -AsPlainText -Force) $Results = @{"message" = "Created application. Waiting 30 seconds for Azure propagation"; step = $step } } else { @@ -60,49 +75,34 @@ try { $TenantId = Get-Content '.\Cache_SAMSetup\cache.tenantid' $AppID = Get-Content '.\Cache_SAMSetup\cache.appid' $PartnerSetup = Get-Content '.\Cache_SAMSetup\PartnerSetup.json' -ErrorAction SilentlyContinue - $FirstLogonRefreshtoken = New-DeviceLogin -clientid $AppID -Scope 'https://api.partnercenter.microsoft.com/user_impersonation' -FirstLogon -TenantId $TenantId New-Item '.\Cache_SAMSetup\SamSetup.json' -Value ($FirstLogonRefreshtoken | ConvertTo-Json) -Force - $step = 3 - $Results = @{ message = $FirstLogonRefreshtoken.message ; step = $step } + $URL = ($Request.url).split('?') | Select-Object -First 1 + $Validated = Get-Content ".\Cache_SAMSetup\Validated.json" -ErrorAction SilentlyContinue + if ($Validated) { $step = 3 } + $Results = @{ message = "Please visit https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)&client-request-id=eef306a9-fa85-4c1f-bb9a-a340d11de748&x-client-SKU=CIPP-AUTH&x-client-Ver=4.8.1.0&prompt=select_account" ; step = $step } } 3 { - $step = 3 - $SAMSetup = Get-Content '.\Cache_SAMSetup\SAMSetup.json' | ConvertFrom-Json - $TenantId = Get-Content '.\Cache_SAMSetup\cache.tenantid' - $AppID = Get-Content '.\Cache_SAMSetup\cache.appid' - $PartnerSetup = Get-Content '.\Cache_SAMSetup\PartnerSetup.json' -ErrorAction SilentlyContinue - $RefreshToken = (New-DeviceLogin -clientid $AppID -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) - - if ($RefreshToken.Refresh_Token) { - Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $RefreshToken.Refresh_Token -AsPlainText -Force) - if ($PartnerSetup) { - $attempt = 0 - do { - try { - Start-Sleep 3 - $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id - Write-Host "Id is $GroupID" - $SPN = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appId eq '$($Appid)'" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id - Write-Host "SPN is $SPN" - $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN)`"}" -ContentType 'application/json') - Write-Host "Added to adminagents" - $attempt ++ - } - catch { - $attempt ++ - } - } until ($attempt -gt 5) - - - + $RefreshToken = Get-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'refreshtoken' -AsPlainText + $attempt = 0 + do { + try { + Start-Sleep 3 + $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id + Write-Host "Id is $GroupID" + $SPN = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appId eq '$($Appid)'" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id + Write-Host "SPN is $SPN" + $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN)`"}" -ContentType 'application/json') + Write-Host "Added to adminagents" + $attempt ++ } - $step = 4 - $Results = @{"message" = "Retrieved refresh token and saving to Keyvault."; step = $step } - } - else { - $step = 3 - $Results = @{"message" = $SAMSetup.message ; step = $step } - } + catch { + $attempt ++ + } + } until ($attempt -gt 5) + $step = 4 + $Results = @{"message" = "Received token, Add application to AdminAgents if required"; step = $step } + + } 4 { $step = 4 @@ -118,7 +118,7 @@ try { $SAMSetup = Get-Content '.\Cache_SAMSetup\SamSetup.json' | ConvertFrom-Json $ExchangeRefreshToken = (New-DeviceLogin -clientid 'a0c73c16-a7e3-4564-9a95-2bdf47383716' -Scope 'https://outlook.office365.com/.default' -device_code $SAMSetup.device_code) if ($ExchangeRefreshToken.Refresh_Token) { - Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'exchangerefreshtoken' -SecretValue (ConvertTo-SecureString -String $ExchangeRefreshToken.Refresh_Token -AsPlainText -Force) + #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'exchangerefreshtoken' -SecretValue (ConvertTo-SecureString -String $ExchangeRefreshToken.Refresh_Token -AsPlainText -Force) $step = 6 $Results = @{"message" = "Retrieved refresh token and saving to Keyvault."; step = $step } } @@ -127,6 +127,7 @@ try { } } 6 { + Remove-Item ".\Cache_SAMSetup\Validated.json" -Force -ErrorAction SilentlyContinue Remove-Item ".\Cache_SAMSetup\SamSetup.json" -Force -ErrorAction SilentlyContinue Remove-Item ".\Cache_SAMSetup\Cache.*" -Force -ErrorAction SilentlyContinue Remove-Item ".\Cache_SAMSetup\SamSetup.json" -Force -ErrorAction SilentlyContinue From 483f33c319a8d6c52738b2837346b17444b384ba Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 19:14:38 +0100 Subject: [PATCH 06/44] added failsafes and prettier urls --- ExecSAMSetup/run.ps1 | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index a987dae7b1b4..a7d33aa2f479 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -15,7 +15,7 @@ $KV = Get-AzKeyVault -SubscriptionID $Subscription -ResourceGroupName $ResourceG try { if ($request.query.code) { try { - #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Request.query.code -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Request.query.code -AsPlainText -Force) $Results = "Authentication is now complete. You may now close this window." New-Item ".\Cache_SAMSetup\Validated.json" -Value "true" } @@ -33,7 +33,7 @@ try { $step = 1 $DeviceLogon = New-DeviceLogin -clientid "1b730954-1685-4b74-9bfd-dac224a7b894" -Scope 'https://graph.microsoft.com/.default' -FirstLogon New-Item '.\Cache_SAMSetup\SamSetup.json' -Value ($DeviceLogon | ConvertTo-Json) -Force - $Results = @{ message = $DeviceLogon.message ; step = $step } + $Results = @{ message = "Your code is $($DeviceLogon.user_code). Enter the code" ; step = $step; url = $DeviceLogon.verification_uri } } if ($Request.query.CheckSetupProcess -and $request.query.step -eq 1) { $SAMSetup = Get-Content '.\Cache_SAMSetup\SamSetup.json' | ConvertFrom-Json @@ -50,7 +50,23 @@ try { $app = $app | ConvertTo-Json -Depth 15 $AppId = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') $AppId.appId | Out-File '.\Cache_SAMSetup\cache.appid' - + $attempt = 0 + do { + try { + Write-Host "{ `"appId`": `"$($AppId.appId)`" }" + $SPN = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"$($AppId.appId)`" }" -ContentType 'application/json') + Write-Host "SPN" + Start-Sleep 3 + $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method Get -ContentType 'application/json').value.id + Write-Host "Id is $GroupID" + $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN.id)`"}" -ContentType 'application/json') + Write-Host "Added to adminagents" + $attempt ++ + } + catch { + $attempt ++ + } + } until ($attempt -gt 5) } else { $app = Get-Content '.\Cache_SAMSetup\SAMManifestNoPartner.json' @@ -58,14 +74,14 @@ try { $AppId.appId | Out-File '.\Cache_SAMSetup\cache.appid' } $AppPassword = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications/$($AppID.id)/addPassword" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body '{"passwordCredential":{"displayName":"CIPPInstall"}}' -ContentType 'application/json').secretText - #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $TenantId -AsPlainText -Force) - #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appid -AsPlainText -Force) - #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $AppPassword -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $TenantId -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appid -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $AppPassword -AsPlainText -Force) $Results = @{"message" = "Created application. Waiting 30 seconds for Azure propagation"; step = $step } } else { $step = 1 - $Results = @{"message" = $SAMSetup.message ; step = $step } + $Results = @{ message = "Your code is $($SAMSetup.user_code). Enter the code " ; step = $step; url = $SAMSetup.verification_uri } } } @@ -79,28 +95,12 @@ try { $URL = ($Request.url).split('?') | Select-Object -First 1 $Validated = Get-Content ".\Cache_SAMSetup\Validated.json" -ErrorAction SilentlyContinue if ($Validated) { $step = 3 } - $Results = @{ message = "Please visit https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)&client-request-id=eef306a9-fa85-4c1f-bb9a-a340d11de748&x-client-SKU=CIPP-AUTH&x-client-Ver=4.8.1.0&prompt=select_account" ; step = $step } + $Results = @{ message = "Give the next approval by clicking " ; step = $step; url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)&client-request-id=eef306a9-fa85-4c1f-bb9a-a340d11de748&x-client-SKU=CIPP-AUTH&x-client-Ver=4.8.1.0&prompt=select_account" } } 3 { - $RefreshToken = Get-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'refreshtoken' -AsPlainText - $attempt = 0 - do { - try { - Start-Sleep 3 - $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id - Write-Host "Id is $GroupID" - $SPN = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appId eq '$($Appid)'" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method Get -ContentType 'application/json').value.id - Write-Host "SPN is $SPN" - $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($RefreshToken.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN)`"}" -ContentType 'application/json') - Write-Host "Added to adminagents" - $attempt ++ - } - catch { - $attempt ++ - } - } until ($attempt -gt 5) + $step = 4 - $Results = @{"message" = "Received token, Add application to AdminAgents if required"; step = $step } + $Results = @{"message" = "Received token."; step = $step } } @@ -110,7 +110,7 @@ try { $FirstExchangeLogonRefreshtoken = New-DeviceLogin -clientid 'a0c73c16-a7e3-4564-9a95-2bdf47383716' -Scope 'https://outlook.office365.com/.default' -FirstLogon -TenantId $TenantId New-Item '.\Cache_SAMSetup\SamSetup.json' -Value ($FirstExchangeLogonRefreshtoken | ConvertTo-Json) -Force $step = 5 - $Results = @{ message = $FirstExchangeLogonRefreshtoken.message ; step = $step } + $Results = @{ message = "Your code is $($FirstExchangeLogonRefreshtoken.user_code). Enter the code " ; step = $step; url = $FirstExchangeLogonRefreshtoken.verification_uri } } 5 { $step = 5 @@ -123,7 +123,7 @@ try { $Results = @{"message" = "Retrieved refresh token and saving to Keyvault."; step = $step } } else { - $Results = @{"message" = $SAMSetup.message ; step = $step } + $Results = @{ message = "Your code is $($SAMSetup.user_code). Enter the code " ; step = $step; url = $SAMSetup.verification_uri } } } 6 { From 8273b8835556ee4e22c636a8044e71c75baec224 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 19:24:22 +0100 Subject: [PATCH 07/44] used wrong url for swa --- ExecSAMSetup/run.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index a7d33aa2f479..921b7bd6ce37 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -2,6 +2,7 @@ using namespace System.Net # Input bindings are passed in via param block. param($Request, $TriggerMetadata) +Write-Host ($request | ConvertTo-Json) $APIName = $TriggerMetadata.FunctionName Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $ResourceGroup = $ENV:Website_Resource_Group @@ -40,7 +41,7 @@ try { $Token = (New-DeviceLogin -clientid "1b730954-1685-4b74-9bfd-dac224a7b894" -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) if ($token.Access_Token) { $step = 2 - $URL = ($Request.url).split('?') | Select-Object -First 1 + $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 $PartnerSetup = Get-Content '.\Cache_SAMSetup\PartnerSetup.json' -ErrorAction SilentlyContinue $TenantId = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/organization" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method GET -ContentType 'application/json').value.id $TenantId | Out-File '.\Cache_SAMSetup\cache.tenantid' @@ -92,7 +93,7 @@ try { $AppID = Get-Content '.\Cache_SAMSetup\cache.appid' $PartnerSetup = Get-Content '.\Cache_SAMSetup\PartnerSetup.json' -ErrorAction SilentlyContinue New-Item '.\Cache_SAMSetup\SamSetup.json' -Value ($FirstLogonRefreshtoken | ConvertTo-Json) -Force - $URL = ($Request.url).split('?') | Select-Object -First 1 + $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 $Validated = Get-Content ".\Cache_SAMSetup\Validated.json" -ErrorAction SilentlyContinue if ($Validated) { $step = 3 } $Results = @{ message = "Give the next approval by clicking " ; step = $step; url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)&client-request-id=eef306a9-fa85-4c1f-bb9a-a340d11de748&x-client-SKU=CIPP-AUTH&x-client-Ver=4.8.1.0&prompt=select_account" } From fbeb768d2302a2138bb4a032551a66393432373b Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 19:32:21 +0100 Subject: [PATCH 08/44] removed info from url --- ExecSAMSetup/run.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index 921b7bd6ce37..c3ac748c6f6b 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -96,7 +96,7 @@ try { $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 $Validated = Get-Content ".\Cache_SAMSetup\Validated.json" -ErrorAction SilentlyContinue if ($Validated) { $step = 3 } - $Results = @{ message = "Give the next approval by clicking " ; step = $step; url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)&client-request-id=eef306a9-fa85-4c1f-bb9a-a340d11de748&x-client-SKU=CIPP-AUTH&x-client-Ver=4.8.1.0&prompt=select_account" } + $Results = @{ message = "Give the next approval by clicking " ; step = $step; url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)" } } 3 { From 691399e348c93fda247cc0e45d3da346e34c770b Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 20:24:15 +0100 Subject: [PATCH 09/44] fix issue with token generation. --- ExecSAMSetup/run.ps1 | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index c3ac748c6f6b..8e7441077795 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -2,7 +2,6 @@ using namespace System.Net # Input bindings are passed in via param block. param($Request, $TriggerMetadata) -Write-Host ($request | ConvertTo-Json) $APIName = $TriggerMetadata.FunctionName Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $ResourceGroup = $ENV:Website_Resource_Group @@ -16,9 +15,15 @@ $KV = Get-AzKeyVault -SubscriptionID $Subscription -ResourceGroupName $ResourceG try { if ($request.query.code) { try { + $TenantId = Get-Content '.\Cache_SAMSetup\cache.tenantid' + $AppID = Get-Content '.\Cache_SAMSetup\cache.appid' + $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 + $clientsecret = Get-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationsecret' -AsPlainText + $RefreshToken = Invoke-RestMethod -Method POST -Body "client_id=$appid&scope=https://graph.microsoft.com/.default+offline_access+openid+profile&code=$($request.query.code)&grant_type=authorization_code&redirect_uri=$($url)&client_secret=$clientsecret" -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" + Write-Host ($RefreshToken | ConvertTo-Json) Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Request.query.code -AsPlainText -Force) $Results = "Authentication is now complete. You may now close this window." - New-Item ".\Cache_SAMSetup\Validated.json" -Value "true" + New-Item ".\Cache_SAMSetup\Validated.json" -Value "true" -Force } catch { $Results = "Authentication failed. $($_.Exception.message)" @@ -119,7 +124,7 @@ try { $SAMSetup = Get-Content '.\Cache_SAMSetup\SamSetup.json' | ConvertFrom-Json $ExchangeRefreshToken = (New-DeviceLogin -clientid 'a0c73c16-a7e3-4564-9a95-2bdf47383716' -Scope 'https://outlook.office365.com/.default' -device_code $SAMSetup.device_code) if ($ExchangeRefreshToken.Refresh_Token) { - #Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'exchangerefreshtoken' -SecretValue (ConvertTo-SecureString -String $ExchangeRefreshToken.Refresh_Token -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'exchangerefreshtoken' -SecretValue (ConvertTo-SecureString -String $ExchangeRefreshToken.Refresh_Token -AsPlainText -Force) $step = 6 $Results = @{"message" = "Retrieved refresh token and saving to Keyvault."; step = $step } } From d48c544c8cfae183f5ddf4d59a2a26d739c697e2 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 20:25:19 +0100 Subject: [PATCH 10/44] correct item in keyvault --- ExecSAMSetup/run.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index 8e7441077795..a82fd9d6808c 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -20,8 +20,7 @@ try { $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 $clientsecret = Get-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationsecret' -AsPlainText $RefreshToken = Invoke-RestMethod -Method POST -Body "client_id=$appid&scope=https://graph.microsoft.com/.default+offline_access+openid+profile&code=$($request.query.code)&grant_type=authorization_code&redirect_uri=$($url)&client_secret=$clientsecret" -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" - Write-Host ($RefreshToken | ConvertTo-Json) - Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Request.query.code -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $RefreshToken.refresh_token -AsPlainText -Force) $Results = "Authentication is now complete. You may now close this window." New-Item ".\Cache_SAMSetup\Validated.json" -Value "true" -Force } From db0b56cf8b671e26da339078120cd34c2944ec00 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 20:36:34 +0100 Subject: [PATCH 11/44] some fixes for prettier errorhandling --- ExecSAMSetup/run.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index a82fd9d6808c..7bb0eca5ca32 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -13,6 +13,7 @@ if ($env:MSI_SECRET) { $KV = Get-AzKeyVault -SubscriptionID $Subscription -ResourceGroupName $ResourceGroup try { + if ($Request.query.count -lt 1 ) { $Results = "No authentication code found. Please retry cllicking the URL" } if ($request.query.code) { try { $TenantId = Get-Content '.\Cache_SAMSetup\cache.tenantid' From f5c0ef8beb170a87ea6daf186f8f4152632920c4 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 20:40:04 +0100 Subject: [PATCH 12/44] added verbosity to error logs --- ExecSAMSetup/run.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index 7bb0eca5ca32..1f7b0538860b 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -13,7 +13,8 @@ if ($env:MSI_SECRET) { $KV = Get-AzKeyVault -SubscriptionID $Subscription -ResourceGroupName $ResourceGroup try { - if ($Request.query.count -lt 1 ) { $Results = "No authentication code found. Please retry cllicking the URL" } + if ($Request.query.count -lt 1 ) { $Results = "No authentication code found. Please retry." } + if ($Request.query.error -eq 'invalid_client') { $Results = "Client ID was not found in Azure. Try waiting 10 seconds to try again, if you have gotten this error after 5 minutes, please restart the process." } if ($request.query.code) { try { $TenantId = Get-Content '.\Cache_SAMSetup\cache.tenantid' From b8df791489dd9b0ab17753b9e341a9ad81ddf94e Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 21 Mar 2022 22:22:27 +0100 Subject: [PATCH 13/44] allow setkeys by post --- ExecSAMSetup/run.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index 1f7b0538860b..89ae7e9ed4a5 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -13,6 +13,14 @@ if ($env:MSI_SECRET) { $KV = Get-AzKeyVault -SubscriptionID $Subscription -ResourceGroupName $ResourceGroup try { + if ($request.body.setkeys) { + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $request.body.tenantid -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $request.body.RefreshToken -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'ExchangeRefreshToken' -SecretValue (ConvertTo-SecureString -String $request.body.exchangeRefreshToken -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $request.body.applicationid -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv.vaultname -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $request.body.applicationsecret -AsPlainText -Force) + $Results = @{ Results = "Replaced keys" } + } if ($Request.query.count -lt 1 ) { $Results = "No authentication code found. Please retry." } if ($Request.query.error -eq 'invalid_client') { $Results = "Client ID was not found in Azure. Try waiting 10 seconds to try again, if you have gotten this error after 5 minutes, please restart the process." } if ($request.query.code) { From d85fe83a2283c591883f2c70922194b42a32ee4c Mon Sep 17 00:00:00 2001 From: Gavsto Date: Wed, 23 Mar 2022 01:46:55 +0000 Subject: [PATCH 14/44] NEW API FEATURE: Added All Tenant Service Health Still need to make the front end :-) --- ListServiceHealth/function.json | 19 ++++++++++++ ListServiceHealth/run.ps1 | 53 +++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 ListServiceHealth/function.json create mode 100644 ListServiceHealth/run.ps1 diff --git a/ListServiceHealth/function.json b/ListServiceHealth/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/ListServiceHealth/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/ListServiceHealth/run.ps1 b/ListServiceHealth/run.ps1 new file mode 100644 index 000000000000..8f1661070389 --- /dev/null +++ b/ListServiceHealth/run.ps1 @@ -0,0 +1,53 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + +# Write to the Azure Functions log stream. +Write-Host "PowerShell HTTP trigger function processed a request." + +# Get me some of that classic token! +$token = Get-ClassicAPIToken -Resource "https://admin.microsoft.com" -tenantID $Env:TenantID + +# This allows access to a tenant key that is needed in the POST for service health +$ResultClients = Invoke-RestMethod -ContentType "application/json;charset=UTF-8" -Uri 'https://admin.microsoft.com/admin/api/partners/GetAOBOClients/true' -Method Get -Headers @{ + Authorization = "Bearer $($token.access_token)"; + "x-ms-client-request-id" = [guid]::NewGuid().ToString(); + "x-ms-client-session-id" = [guid]::NewGuid().ToString() + 'x-ms-correlation-id' = [guid]::NewGuid() + 'X-Requested-With' = 'XMLHttpRequest' +} + +# Build the body +$Body = $ResultClients.TenantKey | ConvertTo-Json + +# Get the service health info +$ResultHealthSummary = Invoke-RestMethod -ContentType "application/json;charset=UTF-8" -Uri 'https://admin.microsoft.com/admin/api/tenant/listservicehealthsummary' -Method POST -Body $body -Headers @{ + Authorization = "Bearer $($token.access_token)"; + "x-ms-client-request-id" = [guid]::NewGuid().ToString(); + "x-ms-client-session-id" = [guid]::NewGuid().ToString() + 'x-ms-correlation-id' = [guid]::NewGuid() + 'X-Requested-With' = 'XMLHttpRequest' +} + +# Build up a better object and some stats with it. Note we are removing anything that has an end date as we only care about ongoing health alerts + +$ReturnObject = foreach ($h in $ResultHealthSummary) { + $SH = [PSCustomObject]@{ + TenantName = $($ResultClients | Where-Object { $_.TenantID -eq $h.TenantID } | Select-Object -ExpandProperty Name) + TenantID = $h.TenantID + AdvisoryCount = $($h.HealthIssueDetails | Where-Object { ($null -eq $_.EndDateTime) -and ($_.Classification -eq 1) } | Measure-Object | Select-Object -ExpandProperty Count) + IncidentCount = $($h.HealthIssueDetails | Where-Object { ($null -eq $_.EndDateTime) -and ($_.Classification -eq 2) } | Measure-Object | Select-Object -ExpandProperty Count) + HealthIssueDetails = $($h.HealthIssueDetails | Where-Object { $null -eq $_.EndDateTime }) + } + $SH +} + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($ReturnObject) + }) From 28eaa37d65d8eb566dde4dbf31b92c766683a42e Mon Sep 17 00:00:00 2001 From: Gavsto Date: Wed, 23 Mar 2022 01:49:06 +0000 Subject: [PATCH 15/44] NEW API FEATURE: All Tenant Service Health Not made the front-end yet. --- ListServiceHealth/function.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ListServiceHealth/function.json b/ListServiceHealth/function.json index 306b0c51e560..4ee273331c44 100644 --- a/ListServiceHealth/function.json +++ b/ListServiceHealth/function.json @@ -5,10 +5,7 @@ "type": "httpTrigger", "direction": "in", "name": "Request", - "methods": [ - "get", - "post" - ] + "methods": ["get", "post"] }, { "type": "http", @@ -16,4 +13,4 @@ "name": "Response" } ] -} \ No newline at end of file +} From 2f73d6576494e3e70ac3f9c9f23cdb2a0425503c Mon Sep 17 00:00:00 2001 From: Gavsto Date: Wed, 23 Mar 2022 02:18:40 +0000 Subject: [PATCH 16/44] FIX: Service Health API --- ListServiceHealth/run.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/ListServiceHealth/run.ps1 b/ListServiceHealth/run.ps1 index 8f1661070389..51f72b546568 100644 --- a/ListServiceHealth/run.ps1 +++ b/ListServiceHealth/run.ps1 @@ -45,6 +45,7 @@ $ReturnObject = foreach ($h in $ResultHealthSummary) { } $SH } +$StatusCode = [HttpStatusCode]::OK # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ From dc842f57920ae5bba6d705a80aaedb24f3ae5e6f Mon Sep 17 00:00:00 2001 From: Gavsto Date: Wed, 23 Mar 2022 18:21:01 +0000 Subject: [PATCH 17/44] FIX Service Health: Ensure Excluded Tenants List Applies Fixed to account for excluded tenants --- ListServiceHealth/run.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ListServiceHealth/run.ps1 b/ListServiceHealth/run.ps1 index 51f72b546568..c4bc6bfe398f 100644 --- a/ListServiceHealth/run.ps1 +++ b/ListServiceHealth/run.ps1 @@ -12,6 +12,9 @@ Write-Host "PowerShell HTTP trigger function processed a request." # Get me some of that classic token! $token = Get-ClassicAPIToken -Resource "https://admin.microsoft.com" -tenantID $Env:TenantID +# Get the list of authorised tenants so we can take into account exclusions +$Tenants = Get-Tenants + # This allows access to a tenant key that is needed in the POST for service health $ResultClients = Invoke-RestMethod -ContentType "application/json;charset=UTF-8" -Uri 'https://admin.microsoft.com/admin/api/partners/GetAOBOClients/true' -Method Get -Headers @{ Authorization = "Bearer $($token.access_token)"; @@ -21,9 +24,13 @@ $ResultClients = Invoke-RestMethod -ContentType "application/json;charset=UTF-8" 'X-Requested-With' = 'XMLHttpRequest' } +# Filter out the tenants that shouldn't be there +$ResultClients = $ResultClients | Where-Object { $Tenants.customerId -contains $_.TenantId } + # Build the body $Body = $ResultClients.TenantKey | ConvertTo-Json + # Get the service health info $ResultHealthSummary = Invoke-RestMethod -ContentType "application/json;charset=UTF-8" -Uri 'https://admin.microsoft.com/admin/api/tenant/listservicehealthsummary' -Method POST -Body $body -Headers @{ Authorization = "Bearer $($token.access_token)"; From 873a6755d6b78e7e904e5a308b603519747f815f Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Fri, 25 Mar 2022 08:24:06 +0100 Subject: [PATCH 18/44] added method to ListLogs to prevent broken logs from ruining interface --- ListLogs/run.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ListLogs/run.ps1 b/ListLogs/run.ps1 index 349dd95bd23c..d0cd5681bc57 100644 --- a/ListLogs/run.ps1 +++ b/ListLogs/run.ps1 @@ -12,7 +12,7 @@ if ($request.Query.filter -eq "True") { $username = $Request.Query.User } else { - $LogLevel = "Info", "Warn", "Error", "Critical" + $LogLevel = "Info", "Warn", "Error", "Critical", "Alert" $date = (Get-Date).ToString('ddMMyyyy') $username = '*' } @@ -24,7 +24,7 @@ $ReturnedLog = if ($Request.Query.ListLogs) { } } } else { - Get-Content "Logs\$($date).log" | ConvertFrom-Csv -Header "DateTime", "Tenant", "API", "Message", "User", "Severity" -Delimiter "|" | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username } + Get-Content "Logs\$($date).log" | ForEach-Object { $_ | ConvertFrom-Csv -Header "DateTime", "Tenant", "API", "Message", "User", "Severity" -Delimiter "|" | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username } } } Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK From 8c61615a74775fd1f896c0836b151ce221d8f458 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Fri, 25 Mar 2022 08:44:51 +0100 Subject: [PATCH 19/44] added linenumbers alert when log is corrupt --- ListLogs/run.ps1 | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ListLogs/run.ps1 b/ListLogs/run.ps1 index d0cd5681bc57..28669f0f015d 100644 --- a/ListLogs/run.ps1 +++ b/ListLogs/run.ps1 @@ -17,6 +17,9 @@ else { $username = '*' } + +$IllegalLines = New-Object -TypeName "System.Collections.ArrayList" + $ReturnedLog = if ($Request.Query.ListLogs) { Get-ChildItem "Logs" | Select-Object Name, BaseName | ForEach-Object { @{ value = $_.BaseName @@ -24,8 +27,20 @@ $ReturnedLog = if ($Request.Query.ListLogs) { } } } else { - Get-Content "Logs\$($date).log" | ForEach-Object { $_ | ConvertFrom-Csv -Header "DateTime", "Tenant", "API", "Message", "User", "Severity" -Delimiter "|" | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username } } + $content = Get-Content "Logs\$($date).log" + foreach ($line in $content) { + try { + $line | ConvertFrom-Csv -Header "DateTime", "Tenant", "API", "Message", "User", "Severity" -Delimiter "|" -ErrorAction Stop | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username + } + } + catch { + Write-Host $content.IndexOf($line) + $IllegalLines.Add($content.IndexOf($line)) + } + } } +if ($IllegalLines.count -ge 1) { Log-Request "The following line numbers in the log are invalid: $IllegalLines" -API $APINAME -Sev Warn } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @($ReturnedLog) From bcb19b634b9040e4958b3bba4b8755473e135f4d Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Fri, 25 Mar 2022 08:44:57 +0100 Subject: [PATCH 20/44] bugfix dashboard --- GetDashboard/run.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GetDashboard/run.ps1 b/GetDashboard/run.ps1 index f9d3682c5080..6c592eb3e07a 100644 --- a/GetDashboard/run.ps1 +++ b/GetDashboard/run.ps1 @@ -192,7 +192,7 @@ $dash = [PSCustomObject]@{ NextBPARun = (Get-CronNextExecutionTime -Expression '0 3 * * *').tostring('s') queuedApps = [int64](Get-ChildItem '.\ChocoApps.Cache' -ErrorAction SilentlyContinue).count queuedStandards = [int64](Get-ChildItem '.\Cache_Standards' -ErrorAction SilentlyContinue).count - tenantCount = [int64](Get-ChildItem '.\tenants.cache.json' -ErrorAction SilentlyContinue).count + tenantCount = [int64](Get-Content '.\tenants.cache.json' | ConvertFrom-Json -ErrorAction SilentlyContinue).count RefreshTokenDate = (Get-CronNextExecutionTime -Expression '0 0 * * 0').AddDays('-7').tostring('s') -split "T" | Select-Object -First 1 ExchangeTokenDate = (Get-CronNextExecutionTime -Expression '0 0 * * 0').AddDays('-7').tostring('s') -split "T" | Select-Object -First 1 LastLog = @(Get-Content "Logs\$((Get-Date).ToString('ddMMyyyy')).log" | ConvertFrom-Csv -Header "DateTime", "Tenant", "API", "Message", "User", "Severity" -Delimiter "|" | Select-Object -Last 10) From c24295a1d2ba585be37e230508204f0b6c38d2fc Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Mon, 28 Mar 2022 13:22:08 +0200 Subject: [PATCH 21/44] list transport rule api --- ListTransportRules/function.json | 19 +++++++++++++++++++ ListTransportRules/run.ps1 | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 ListTransportRules/function.json create mode 100644 ListTransportRules/run.ps1 diff --git a/ListTransportRules/function.json b/ListTransportRules/function.json new file mode 100644 index 000000000000..bec6849b58ab --- /dev/null +++ b/ListTransportRules/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/ListTransportRules/run.ps1 b/ListTransportRules/run.ps1 new file mode 100644 index 000000000000..c3950b98ebc5 --- /dev/null +++ b/ListTransportRules/run.ps1 @@ -0,0 +1,24 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" +$Tenantfilter = $request.Query.tenantfilter + +try { + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-TransportRule" + $StatusCode = [HttpStatusCode]::OK +} +catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage +} + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) From 4f7e467eabab6b7c4f3f72395d5e9f3316dd9e0f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 28 Mar 2022 20:40:38 -0400 Subject: [PATCH 22/44] Domain Analyser - Bugfix Correct scoring system --- DomainAnalyser_All/run.ps1 | 57 ++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/DomainAnalyser_All/run.ps1 b/DomainAnalyser_All/run.ps1 index 670e4a2bc35d..a5785d45eed9 100644 --- a/DomainAnalyser_All/run.ps1 +++ b/DomainAnalyser_All/run.ps1 @@ -19,10 +19,7 @@ $Result = [PSCustomObject]@{ SupportedServices = $Tenant.supportedServices ExpectedSPFRecord = '' ActualSPFRecord = '' - SPFPassTest = '' SPFPassAll = '' - ExpectedMXRecord = '' - ActualMXRecord = '' MXPassTest = '' DMARCPresent = '' DMARCFullPolicy = '' @@ -33,16 +30,15 @@ $Result = [PSCustomObject]@{ MailProvider = '' DKIMEnabled = '' Score = '' - MaximumScore = 180 + MaximumScore = 160 ScorePercentage = '' ScoreExplanation = '' } $Scores = [PSCustomObject]@{ SPFPresent = 10 - SPFMSRecommended = 10 - SPFCorrectAll = 10 - MXMSRecommended = 10 + SPFCorrectAll = 20 + MXRecommended = 10 DMARCPresent = 10 DMARCSetQuarantine = 20 DMARCSetReject = 30 @@ -54,10 +50,25 @@ $Scores = [PSCustomObject]@{ $ScoreDomain = 0 # Setup Score Explanation -[System.Collections.ArrayList]$ScoreExplanation = @() +$ScoreExplanation = [System.Collections.Generic.List[string]]::new() +# Check MX Record $MXRecord = Read-MXRecord -Domain $Domain + $Result.ExpectedSPFRecord = $MXRecord.ExpectedInclude +$Result.MXPassTest = $false + +# Check fail counts to ensure all tests pass +#$MXWarnCount = $MXRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count +$MXFailCount = $MXRecord.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count + +if ($MXFailCount -eq 0) { + $Result.MXPassTest = $true + $ScoreDomain += $Scores.MXRecommended +} +else { + $ScoreExplanation.Add('MX record did not pass validation') | Out-Null +} if ([string]::IsNullOrEmpty($MXRecord.MailProvider)) { $Result.MailProvider = 'Unknown' @@ -74,6 +85,9 @@ try { if ($SPFRecord.RecordCount -eq 1) { $ScoreDomain += $Scores.SPFPresent } + else { + $ScoreExplanation.Add('Multiple SPF records detected') | Out-Null + } } else { $Result.ActualSPFRecord = 'No SPF Record' @@ -86,15 +100,6 @@ catch { # Check SPF Record $Result.SPFPassAll = $false -$Result.SPFPassTest = $false - -foreach ($Validation in $SPFRecord.ValidationPasses) { - if ($Validation -match 'Expected SPF') { - $ScoreDomain += $Scores.SPFMSRecommended - $Result.SPFPassTest = $true - break - } -} # Check warning + fail counts to ensure all tests pass #$SPFWarnCount = $SPFRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count @@ -104,17 +109,8 @@ if ($SPFFailCount -eq 0) { $ScoreDomain += $Scores.SPFCorrectAll $Result.SPFPassAll = $true } - -# Check MX Record - -$Result.MXPassTest = $false -# Check warning + fail counts to ensure all tests pass -$MXWarnCount = $MXRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count -$MXFailCount = $MXRecord.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count - -if (($MXWarnCount + $MXFailCount) -eq 0) { - $Result.MXPassTest = $true - $ScoreDomain += $Scores.MXMSRecommended +else { + $ScoreExplanation.Add('SPF record did not pass validation') | Out-Null } # Get DMARC Record @@ -143,6 +139,7 @@ try { $ScoreDomain += $Scores.DMARCSetQuarantine $ScoreExplanation.Add('DMARC Partially Enforced with quarantine') | Out-Null } + $ReportEmailCount = $DMARCPolicy.ReportingEmails | Measure-Object | Select-Object -ExpandProperty Count if ($ReportEmailCount -gt 0) { $Result.DMARCReportingActive = $true @@ -191,8 +188,8 @@ try { $DkimRecordCount = $DkimRecord.Records | Measure-Object | Select-Object -ExpandProperty Count $DkimFailCount = $DkimRecord.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count - $DkimWarnCount = $DkimRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count - if ($DkimRecordCount -gt 0 -and ($DkimFailCount + $DkimWarnCount) -eq 0) { + #$DkimWarnCount = $DkimRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count + if ($DkimRecordCount -gt 0 -and $DkimFailCount -eq 0) { $Result.DKIMEnabled = $true $ScoreDomain += $Scores.DKIMActiveAndWorking } From 15c5bbcf7de5fe48888d45f30a6bd0cd5882d74e Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Tue, 29 Mar 2022 10:05:26 +0200 Subject: [PATCH 23/44] added transportrule disable/enable --- EditTransportRule/function.json | 19 +++++++++++++++++++ EditTransportRule/run.ps1 | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 EditTransportRule/function.json create mode 100644 EditTransportRule/run.ps1 diff --git a/EditTransportRule/function.json b/EditTransportRule/function.json new file mode 100644 index 000000000000..bec6849b58ab --- /dev/null +++ b/EditTransportRule/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/EditTransportRule/run.ps1 b/EditTransportRule/run.ps1 new file mode 100644 index 000000000000..a450be3cf0db --- /dev/null +++ b/EditTransportRule/run.ps1 @@ -0,0 +1,29 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" +$Tenantfilter = $request.Query.tenantfilter + + +$Params = @{ + Identity = $request.query.guid +} + +try { + $cmdlet = if ($request.query.state -eq "enable") { "Enable-TransportRule" } else { "Disable-TransportRule" } + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params + $Result = "Set transport rule $($Request.query.guid) to $($request.query.State)" + Log-request -API "TransportRules" -tenant $tenantfilter -message "Set transport rule $($Request.query.guid) to $($request.query.State)" -sev Debug +} +catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception + $Result = $ErrorMessage +} +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = $Result } + }) From 750f44e7d19e2a8175cea79470ff0e6673e3f733 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Tue, 29 Mar 2022 11:06:43 +0200 Subject: [PATCH 24/44] added transport rule template engine --- AddTransportTemplate/function.json | 19 ++++++++++++++ AddTransportTemplate/run.ps1 | 40 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 AddTransportTemplate/function.json create mode 100644 AddTransportTemplate/run.ps1 diff --git a/AddTransportTemplate/function.json b/AddTransportTemplate/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/AddTransportTemplate/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/AddTransportTemplate/run.ps1 b/AddTransportTemplate/run.ps1 new file mode 100644 index 000000000000..e675011390d7 --- /dev/null +++ b/AddTransportTemplate/run.ps1 @@ -0,0 +1,40 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + + + +try { + if (!$Request.body.displayname) { throw "You must enter a displayname" } + if (!$Request.body.rawjson) { throw "You must fill in the RAW json" } + if ($null -eq ($Request.body.Rawjson | ConvertFrom-Json)) { throw "the JSON is invalid" } + $GUID = New-Guid + + $object = [PSCustomObject]@{ + Displayname = $request.body.displayname + Description = $request.body.description + RAWJson = $request.body.RawJSON + Type = $request.body.TemplateType + GUID = $GUID + } | ConvertTo-Json + New-Item Config -ItemType Directory -ErrorAction SilentlyContinue + Set-Content "Config\$($GUID).IntuneTemplate.json" -Value $Object -Force + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created intune policy template named $($Request.body.displayname) with GUID $GUID" -Sev "Debug" + + $body = [pscustomobject]@{"Results" = "Successfully added template" } +} +catch { + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Intune Template Deployment failed: $($_.Exception.Message)" -Sev "Error" + $body = [pscustomobject]@{"Results" = "Intune Template Deployment failed: $($_.Exception.Message)" } +} + + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) From 94bcb11d18185a06ddeb82ddd66e4795232c0047 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Tue, 29 Mar 2022 12:43:04 +0200 Subject: [PATCH 25/44] added list transport rule templates --- AddTransportTemplate/run.ps1 | 23 +++++++++-------------- ListTransportRulesTemplates/function.json | 19 +++++++++++++++++++ ListTransportRulesTemplates/run.ps1 | 21 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 ListTransportRulesTemplates/function.json create mode 100644 ListTransportRulesTemplates/run.ps1 diff --git a/AddTransportTemplate/run.ps1 b/AddTransportTemplate/run.ps1 index e675011390d7..222b02ff1b65 100644 --- a/AddTransportTemplate/run.ps1 +++ b/AddTransportTemplate/run.ps1 @@ -7,28 +7,23 @@ $APIName = $TriggerMetadata.FunctionName Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - try { - if (!$Request.body.displayname) { throw "You must enter a displayname" } - if (!$Request.body.rawjson) { throw "You must fill in the RAW json" } - if ($null -eq ($Request.body.Rawjson | ConvertFrom-Json)) { throw "the JSON is invalid" } $GUID = New-Guid - $object = [PSCustomObject]@{ - Displayname = $request.body.displayname - Description = $request.body.description - RAWJson = $request.body.RawJSON - Type = $request.body.TemplateType - GUID = $GUID - } | ConvertTo-Json + $object = [PSCustomObject]@{} + $request.body.PowerShellCommand | ConvertFrom-Csv -Delimiter " " -Header "name", "value" | ForEach-Object { + $object | Add-Member -NotePropertyName ($_.name -replace '-', '') -NotePropertyValue $_.value + } + + New-Item Config -ItemType Directory -ErrorAction SilentlyContinue - Set-Content "Config\$($GUID).IntuneTemplate.json" -Value $Object -Force - Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created intune policy template named $($Request.body.displayname) with GUID $GUID" -Sev "Debug" + Set-Content "Config\$($GUID).TransportRuleTemplate.json" -Value ($Object | ConvertTo-Json -Depth 10) -Force + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created Transport Rule Template $($Request.body.displayname) with GUID $GUID" -Sev "Debug" $body = [pscustomobject]@{"Results" = "Successfully added template" } } catch { - Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Intune Template Deployment failed: $($_.Exception.Message)" -Sev "Error" + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create Transport Rule Template: $($_.Exception.Message)" -Sev "Error" $body = [pscustomobject]@{"Results" = "Intune Template Deployment failed: $($_.Exception.Message)" } } diff --git a/ListTransportRulesTemplates/function.json b/ListTransportRulesTemplates/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/ListTransportRulesTemplates/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/ListTransportRulesTemplates/run.ps1 b/ListTransportRulesTemplates/run.ps1 new file mode 100644 index 000000000000..474185c67d15 --- /dev/null +++ b/ListTransportRulesTemplates/run.ps1 @@ -0,0 +1,21 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + + +# Write to the Azure Functions log stream. +Write-Host "PowerShell HTTP trigger function processed a request." +Write-Host $Request.query.id +$Templates = Get-ChildItem "Config\*.TransportRuleTemplate.json" | ForEach-Object { Get-Content $_ | ConvertFrom-Json } +if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property guid -EQ $Request.query.id } + + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Templates) + }) From bafb2da63f766a634e0442bec811be88a6327206 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Tue, 29 Mar 2022 12:51:23 +0200 Subject: [PATCH 26/44] added removal of transport rule templates --- ListTransportRulesTemplates/run.ps1 | 6 +++++- RemoveTransportRuleTemplate/function.json | 19 +++++++++++++++++ RemoveTransportRuleTemplate/run.ps1 | 26 +++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 RemoveTransportRuleTemplate/function.json create mode 100644 RemoveTransportRuleTemplate/run.ps1 diff --git a/ListTransportRulesTemplates/run.ps1 b/ListTransportRulesTemplates/run.ps1 index 474185c67d15..922e91fecb54 100644 --- a/ListTransportRulesTemplates/run.ps1 +++ b/ListTransportRulesTemplates/run.ps1 @@ -10,7 +10,11 @@ Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -messa # Write to the Azure Functions log stream. Write-Host "PowerShell HTTP trigger function processed a request." Write-Host $Request.query.id -$Templates = Get-ChildItem "Config\*.TransportRuleTemplate.json" | ForEach-Object { Get-Content $_ | ConvertFrom-Json } +$Templates = Get-ChildItem "Config\*.TransportRuleTemplate.json" | ForEach-Object { + $data = Get-Content $_ | ConvertFrom-Json + $data | Add-Member -NotePropertyName "GUID" -NotePropertyValue (($_.name).split('.') | Select-Object -First 1) + $data +} if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property guid -EQ $Request.query.id } diff --git a/RemoveTransportRuleTemplate/function.json b/RemoveTransportRuleTemplate/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/RemoveTransportRuleTemplate/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/RemoveTransportRuleTemplate/run.ps1 b/RemoveTransportRuleTemplate/run.ps1 new file mode 100644 index 000000000000..57eeb7ac1d29 --- /dev/null +++ b/RemoveTransportRuleTemplate/run.ps1 @@ -0,0 +1,26 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + +$ID = $request.query.id +try { + Remove-Item "Config\$($ID).TransportRuleTemplate.json" -Force + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Transport Rule Template with ID $ID." -Sev "Info" + $body = [pscustomobject]@{"Results" = "Successfully removed Transport Rule Template" } +} +catch { + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Transport Rule template $ID. $($_.Exception.Message)" -Sev "Error" + $body = [pscustomobject]@{"Results" = "Failed to remove template" } +} + + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + From 66b69f42a2e262886009b5170cbcbe0294c40d99 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Tue, 29 Mar 2022 16:00:09 +0200 Subject: [PATCH 27/44] added functionality to add transport rule --- AddTransportRule/function.json | 19 +++++++++++++++++++ AddTransportRule/run.ps1 | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 AddTransportRule/function.json create mode 100644 AddTransportRule/run.ps1 diff --git a/AddTransportRule/function.json b/AddTransportRule/function.json new file mode 100644 index 000000000000..bec6849b58ab --- /dev/null +++ b/AddTransportRule/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/AddTransportRule/run.ps1 b/AddTransportRule/run.ps1 new file mode 100644 index 000000000000..a99e4a04df35 --- /dev/null +++ b/AddTransportRule/run.ps1 @@ -0,0 +1,29 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + +$RequestParams = $Request.Body.PowerShellCommand | ConvertFrom-Json | Select-Object -Property * -ExcludeProperty GUID +Write-Host $RequestParams + +$Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value +$Result = foreach ($Tenantfilter in $tenants) { + try { + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "New-TransportRule" -cmdParams $RequestParams + "Succesfully created transport rule for $tenantfilter." + Log-request -API $APINAME -tenant $tenantfilter -message "Created transport rule for $($tenantfilter)" -sev Debug + } + catch { + "Could not create created transport rule for $($tenantfilter): $($_.Exception.message)" + } +} + + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = @($Result) } + }) From 4c02d2a3357df864605e7d478a8fb03cf1bd13ea Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Tue, 29 Mar 2022 16:05:36 +0200 Subject: [PATCH 28/44] added exchange error handling --- GraphHelper.psm1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GraphHelper.psm1 b/GraphHelper.psm1 index c18ffef0bd36..c167884c92aa 100644 --- a/GraphHelper.psm1 +++ b/GraphHelper.psm1 @@ -334,7 +334,8 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams) { $ReturnedData = Invoke-RestMethod "https://outlook.office365.com/adminapi/beta/$($tenant)/InvokeCommand" -Method POST -Body $ExoBody -Headers $Headers -ContentType 'application/json; charset=utf-8' } catch { - $Message = ($_.ErrorDetails | ConvertFrom-Json -ErrorAction SilentlyContinue).error.details.message + $ReportedError = ($_.ErrorDetails | ConvertFrom-Json -ErrorAction SilentlyContinue) + $Message = if ($ReportedError.error.details.message) { $ReportedError.error.details.message } else { $ReportedError.error.innererror.internalException.message } if ($Message -eq $null) { $Message = $($_.Exception.Message) } throw $Message } From c7e8efe976dbb32820d87fc63fd70696e33a41b3 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Wed, 30 Mar 2022 10:54:51 +0200 Subject: [PATCH 29/44] playing with templates --- AddTransportTemplate/run.ps1 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/AddTransportTemplate/run.ps1 b/AddTransportTemplate/run.ps1 index 222b02ff1b65..26f5a53c7b01 100644 --- a/AddTransportTemplate/run.ps1 +++ b/AddTransportTemplate/run.ps1 @@ -9,18 +9,18 @@ Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -messa try { $GUID = New-Guid - - $object = [PSCustomObject]@{} - $request.body.PowerShellCommand | ConvertFrom-Csv -Delimiter " " -Header "name", "value" | ForEach-Object { - $object | Add-Member -NotePropertyName ($_.name -replace '-', '') -NotePropertyValue $_.value - } - - New-Item Config -ItemType Directory -ErrorAction SilentlyContinue - Set-Content "Config\$($GUID).TransportRuleTemplate.json" -Value ($Object | ConvertTo-Json -Depth 10) -Force - Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created Transport Rule Template $($Request.body.displayname) with GUID $GUID" -Sev "Debug" + $Object = if ($request.body.PowerShellCommand) { + $request.body.PowerShellCommand | Select-Object -Property * -ExcludeProperty GUID + } + else { + $request.body | Select-Object -Property * -ExcludeProperty GUID + } + Set-Content "Config\$($GUID).TransportRuleTemplate.json" -Value ($Object | ConvertTo-Json -Depth 10).ToLower() -Force + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created Transport Rule Template $($Request.body.name) with GUID $GUID" -Sev "Debug" $body = [pscustomobject]@{"Results" = "Successfully added template" } + } catch { Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create Transport Rule Template: $($_.Exception.Message)" -Sev "Error" From e933229588b4c9388f4639b8107a62e8ad80ca58 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Wed, 30 Mar 2022 13:10:45 +0200 Subject: [PATCH 30/44] add deletion of transport rules --- RemoveTransportRule/function.json | 19 +++++++++++++++++++ RemoveTransportRule/run.ps1 | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 RemoveTransportRule/function.json create mode 100644 RemoveTransportRule/run.ps1 diff --git a/RemoveTransportRule/function.json b/RemoveTransportRule/function.json new file mode 100644 index 000000000000..bec6849b58ab --- /dev/null +++ b/RemoveTransportRule/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/RemoveTransportRule/run.ps1 b/RemoveTransportRule/run.ps1 new file mode 100644 index 000000000000..261bbc733006 --- /dev/null +++ b/RemoveTransportRule/run.ps1 @@ -0,0 +1,29 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" +$Tenantfilter = $request.Query.tenantfilter + + +$Params = @{ + Identity = $request.query.guid +} + +try { + $cmdlet = "Remove-TransportRule" + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params + $Result = "Deleted $($Request.query.guid)" + Log-request -API "TransportRules" -tenant $tenantfilter -message "Deleted transport rule $($Request.query.guid)" -sev Debug +} +catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception + $Result = $ErrorMessage +} +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = $Result } + }) From 73f8b12ac248d6026b46660c2cf8f9fee18ba453 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Wed, 30 Mar 2022 13:10:59 +0200 Subject: [PATCH 31/44] added transport template improvements --- AddTransportRule/run.ps1 | 1 - AddTransportTemplate/run.ps1 | 16 ++++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/AddTransportRule/run.ps1 b/AddTransportRule/run.ps1 index a99e4a04df35..576063c3cf1f 100644 --- a/AddTransportRule/run.ps1 +++ b/AddTransportRule/run.ps1 @@ -7,7 +7,6 @@ $APIName = $TriggerMetadata.FunctionName Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $RequestParams = $Request.Body.PowerShellCommand | ConvertFrom-Json | Select-Object -Property * -ExcludeProperty GUID -Write-Host $RequestParams $Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value $Result = foreach ($Tenantfilter in $tenants) { diff --git a/AddTransportTemplate/run.ps1 b/AddTransportTemplate/run.ps1 index 26f5a53c7b01..3d556250215c 100644 --- a/AddTransportTemplate/run.ps1 +++ b/AddTransportTemplate/run.ps1 @@ -5,19 +5,23 @@ param($Request, $TriggerMetadata) $APIName = $TriggerMetadata.FunctionName Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - +Write-Host ($request | ConvertTo-Json -Compress) try { $GUID = New-Guid New-Item Config -ItemType Directory -ErrorAction SilentlyContinue - $Object = if ($request.body.PowerShellCommand) { - $request.body.PowerShellCommand | Select-Object -Property * -ExcludeProperty GUID + $JSON = if ($request.body.PowerShellCommand) { + Write-Host "PowerShellCommand" + $request.body.PowerShellCommand } else { - $request.body | Select-Object -Property * -ExcludeProperty GUID - + ([pscustomobject]$Request.body | Select-Object Name, ActivationDate, ADComparisonAttribute, ADComparisonOperator, AddManagerAsRecipientType, AddToRecipients, AnyOfCcHeader, AnyOfCcHeaderMemberOf, AnyOfRecipientAddressContainsWords, AnyOfRecipientAddressMatchesPatterns, AnyOfToCcHeader, AnyOfToCcHeaderMemberOf, AnyOfToHeader, AnyOfToHeaderMemberOf, ApplyClassification, ApplyHtmlDisclaimerFallbackAction, ApplyHtmlDisclaimerLocation, ApplyHtmlDisclaimerText, ApplyOME, ApplyRightsProtectionCustomizationTemplate, ApplyRightsProtectionTemplate, AttachmentContainsWords, AttachmentExtensionMatchesWords, AttachmentHasExecutableContent, AttachmentIsPasswordProtected, AttachmentIsUnsupported, AttachmentMatchesPatterns, AttachmentNameMatchesPatterns, AttachmentProcessingLimitExceeded, AttachmentPropertyContainsWords, AttachmentSizeOver, BetweenMemberOf1, BetweenMemberOf2, BlindCopyTo, Comments, Confirm, ContentCharacterSetContainsWords, CopyTo, DeleteMessage, DlpPolicy, DomainController, Enabled, ExceptIfADComparisonAttribute, ExceptIfADComparisonOperator, ExceptIfAnyOfCcHeader, ExceptIfAnyOfCcHeaderMemberOf, ExceptIfAnyOfRecipientAddressContainsWords, ExceptIfAnyOfRecipientAddressMatchesPatterns, ExceptIfAnyOfToCcHeader, ExceptIfAnyOfToCcHeaderMemberOf, ExceptIfAnyOfToHeader, ExceptIfAnyOfToHeaderMemberOf, ExceptIfAttachmentContainsWords, ExceptIfAttachmentExtensionMatchesWords, ExceptIfAttachmentHasExecutableContent, ExceptIfAttachmentIsPasswordProtected, ExceptIfAttachmentIsUnsupported, ExceptIfAttachmentMatchesPatterns, ExceptIfAttachmentNameMatchesPatterns, ExceptIfAttachmentProcessingLimitExceeded, ExceptIfAttachmentPropertyContainsWords, ExceptIfAttachmentSizeOver, ExceptIfBetweenMemberOf1, ExceptIfBetweenMemberOf2, ExceptIfContentCharacterSetContainsWords, ExceptIfFrom, ExceptIfFromAddressContainsWords, ExceptIfFromAddressMatchesPatterns, ExceptIfFromMemberOf, ExceptIfFromScope, ExceptIfHasClassification, ExceptIfHasNoClassification, ExceptIfHasSenderOverride, ExceptIfHeaderContainsMessageHeader, ExceptIfHeaderContainsWords, ExceptIfHeaderMatchesMessageHeader, ExceptIfHeaderMatchesPatterns, ExceptIfManagerAddresses, ExceptIfManagerForEvaluatedUser, ExceptIfMessageContainsDataClassifications, ExceptIfMessageSizeOver, ExceptIfMessageTypeMatches, ExceptIfRecipientADAttributeContainsWords, ExceptIfRecipientADAttributeMatchesPatterns, ExceptIfRecipientAddressContainsWords, ExceptIfRecipientAddressMatchesPatterns, ExceptIfRecipientDomainIs, ExceptIfRecipientInSenderList, ExceptIfSCLOver, ExceptIfSenderADAttributeContainsWords, ExceptIfSenderADAttributeMatchesPatterns, ExceptIfSenderDomainIs, ExceptIfSenderInRecipientList, ExceptIfSenderIpRanges, ExceptIfSenderManagementRelationship, ExceptIfSentTo, ExceptIfSentToMemberOf, ExceptIfSentToScope, ExceptIfSubjectContainsWords, ExceptIfSubjectMatchesPatterns, ExceptIfSubjectOrBodyContainsWords, ExceptIfSubjectOrBodyMatchesPatterns, ExceptIfWithImportance, ExpiryDate, From, FromAddressContainsWords, FromAddressMatchesPatterns, FromMemberOf, FromScope, GenerateIncidentReport, GenerateNotification, HasClassification, HasNoClassification, HasSenderOverride, HeaderContainsMessageHeader, HeaderContainsWords, HeaderMatchesMessageHeader, HeaderMatchesPatterns, IncidentReportContent, IncidentReportOriginalMail, LogEventText, ManagerAddresses, ManagerForEvaluatedUser, MessageContainsDataClassifications, MessageSizeOver, MessageTypeMatches, Mode, ModerateMessageByManager, ModerateMessageByUser, NotifySender, PrependSubject, Quarantine, RecipientADAttributeContainsWords, RecipientADAttributeMatchesPatterns, RecipientAddressContainsWords, RecipientAddressMatchesPatterns, RecipientAddressType, RecipientDomainIs, RecipientInSenderList, RedirectMessageTo, RejectMessageEnhancedStatusCode, RejectMessageReasonText, RemoveHeader, RemoveOME, RemoveOMEv2, RemoveRMSAttachmentEncryption, RouteMessageOutboundConnector, RouteMessageOutboundRequireTls, RuleErrorAction, RuleSubType, SCLOver, SenderADAttributeContainsWords, SenderADAttributeMatchesPatterns, SenderAddressLocation, SenderDomainIs, SenderInRecipientList, SenderIpRanges, SenderManagementRelationship, SentTo, SentToMemberOf, SentToScope, SetAuditSeverity, SetHeaderName, SetHeaderValue, SetSCL, SmtpRejectMessageRejectStatusCode, SmtpRejectMessageRejectText, StopRuleProcessing, SubjectContainsWords, SubjectMatchesPatterns, SubjectOrBodyContainsWords, SubjectOrBodyMatchesPatterns, UseLegacyRegex, WithImportance ) | ForEach-Object { + $NonEmptyProperties = $_.psobject.Properties | Where-Object { $null -ne $_.Value } | Select-Object -ExpandProperty Name + $_ | Select-Object -Property $NonEmptyProperties + } } - Set-Content "Config\$($GUID).TransportRuleTemplate.json" -Value ($Object | ConvertTo-Json -Depth 10).ToLower() -Force + $JSON = ($JSON | ConvertTo-Json -Depth 10).tolower() + Set-Content "Config\$($GUID).TransportRuleTemplate.json" -Value ($JSON) -Force Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created Transport Rule Template $($Request.body.name) with GUID $GUID" -Sev "Debug" $body = [pscustomobject]@{"Results" = "Successfully added template" } From 27c9f43c02a426859630bc75c59bc97e6670233f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 30 Mar 2022 13:23:22 -0400 Subject: [PATCH 32/44] DNS Helper bugfix Return DNSSEC validation failure --- DNSHelper.psm1 | 75 +++++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/DNSHelper.psm1 b/DNSHelper.psm1 index 65a411f29cf4..dd593039e306 100644 --- a/DNSHelper.psm1 +++ b/DNSHelper.psm1 @@ -78,9 +78,11 @@ function Resolve-DnsHttpsQuery { if ($Results.Answer) { $DataReturned = $true } + elseif ($Results.Status -ne 0) { + $DataReturned = $true + } else { if ($Retry -gt 3) { - $Results = $null $DataReturned = $true } $Retry++ @@ -133,31 +135,30 @@ function Test-DNSSEC { } $Result = Resolve-DnsHttpsQuery @DnsQuery - - $RecordCount = ($Result.Answer.data | Measure-Object).Count - if ($null -eq $Result) { - $ValidationFails.Add('DNSSEC is not set up for this domain.') | Out-Null + if ($Result.Status -eq 2 -and $Result.AD -eq $false) { + $ValidationFails.Add('DNSSEC Validation failed.') | Out-Null } else { - if ($Result.Status -eq 2) { - if ($Result.AD -eq $false) { - $ValidationFails.Add("$($Result.Comment)") | Out-Null - } - } - elseif ($Result.Status -eq 3) { + $RecordCount = ($Result.Answer.data | Measure-Object).Count + if ($null -eq $Result) { $ValidationFails.Add('DNSSEC is not set up for this domain.') | Out-Null } - elseif ($RecordCount -gt 0) { - if ($Result.AD -eq $false) { - $ValidationFails.Add('DNSSEC is enabled, but the DNS query response was not validated. Ensure DNSSEC has been enabled on your domain provider.') | Out-Null + else { + if ($Result.Status -eq 3) { + $ValidationFails.Add('DNSSEC is not set up for this domain.') | Out-Null + } + elseif ($RecordCount -gt 0) { + if ($Result.AD -eq $false) { + $ValidationFails.Add('DNSSEC is enabled, but the DNS query response was not validated. Ensure DNSSEC has been enabled on your domain provider.') | Out-Null + } + else { + $ValidationPasses.Add('DNSSEC is enabled and validated for this domain.') | Out-Null + } + $DSResults.Keys = $Result.answer.data } else { - $ValidationPasses.Add('DNSSEC is enabled and validated for this domain.') | Out-Null + $ValidationFails.Add('DNSSEC is not set up for this domain.') | Out-Null } - $DSResults.Keys = $Result.answer.data - } - else { - $ValidationFails.Add('DNSSEC is not set up for this domain.') | Out-Null } } @@ -208,7 +209,10 @@ function Read-NSRecord { $Result = Resolve-DnsHttpsQuery @DnsQuery } catch { $Result = $null } - if ($Result.Status -ne 0 -or -not ($Result.Answer)) { + if ($Result.Status -eq 2 -and $Result.AD -eq $false) { + $ValidationFails.Add('DNSSEC Validation failed.') | Out-Null + } + elseif ($Result.Status -ne 0 -or -not ($Result.Answer)) { $ValidationFails.Add('No nameservers found for this domain.') | Out-Null $NSRecords = $null } @@ -276,7 +280,10 @@ function Read-MXRecord { $Result = Resolve-DnsHttpsQuery @DnsQuery } catch { $Result = $null } - if ($Result.Status -ne 0 -or -not ($Result.Answer)) { + if ($Result.Status -eq 2 -and $Result.AD -eq $false) { + $ValidationFails.Add('DNSSEC validation failed.') | Out-Null + } + elseif ($Result.Status -ne 0 -or -not ($Result.Answer)) { if ($Result.Status -eq 3) { $ValidationFails.Add($NoMxValidation) | Out-Null $MXResults.MailProvider = Get-Content 'MailProviders\Null.json' | ConvertFrom-Json @@ -453,7 +460,10 @@ function Read-SpfRecord { } else { $Query = Resolve-DnsHttpsQuery @DnsQuery - if ($Query.Status -ne 0) { + if ($Query.Status -eq 2 -and $Query.AD -eq $false) { + $ValidationFails.Add('DNSSEC validation failed.') | Out-Null + } + elseif ($Query.Status -ne 0) { if ($Query.Status -eq 3) { $ValidationFails.Add($NoSpfValidation) | Out-Null $Status = 'permerror' @@ -932,8 +942,10 @@ function Read-DmarcPolicy { $DmarcAnalysis.Record = $DmarcRecord $RecordCount++ } - - if ($Query.Status -ne 0 -or $RecordCount -eq 0) { + if ($Query.Status -eq 2 -and $Query.AD -eq $false) { + $ValidationFails.Add('DNSSEC validation failed.') | Out-Null + } + elseif ($Query.Status -ne 0 -or $RecordCount -eq 0) { $ValidationFails.Add('This domain does not have a DMARC record.') | Out-Null } elseif (($Query.Answer | Measure-Object).Count -eq 1 -and $RecordCount -eq 0) { @@ -1212,7 +1224,10 @@ function Read-DkimRecord { $QueryResults = Resolve-DnsHttpsQuery @DnsQuery if ([string]::IsNullOrEmpty($Selector)) { continue } - + + if ($QueryResults.Status -eq 2 -and $QueryResults.AD -eq $false) { + $ValidationFails.Add('DNSSEC validation failed.') | Out-Null + } if ($QueryResults -eq '' -or $QueryResults.Status -ne 0) { if ($QueryResults.Status -eq 3) { if ($MinimumSelectorPass -eq 0) { @@ -2035,8 +2050,10 @@ function Read-MtaStsRecord { $StsAnalysis.Record = $StsRecord $RecordCount++ } - - if ($Query.Status -ne 0 -or $RecordCount -eq 0) { + if ($Query.Status -eq 2 -and $Query.AD -eq $false) { + $ValidationFails.Add('DNSSEC validation failed.') | Out-Null + } + elseif ($Query.Status -ne 0 -or $RecordCount -eq 0) { if ($Query.Status -eq 3) { $ValidationFails.Add('Record does not exist (NXDOMAIN)') | Out-Null } @@ -2295,7 +2312,9 @@ function Read-TlsRptRecord { $TlsRptAnalysis.Record = $TlsRtpRecord $RecordCount++ } - + if ($Query.Status -eq 2 -and $Query.AD -eq $false) { + $ValidationFails.Add('DNSSEC validation failed.') | Out-Null + } if ($Query.Status -ne 0 -or $RecordCount -eq 0) { if ($Query.Status -eq 3) { $ValidationFails.Add('Record does not exist (NXDOMAIN)') | Out-Null From b6d34c24ef712d0807830eebd09a09531bb0ec97 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 13:32:20 +0200 Subject: [PATCH 33/44] added list ca templates --- ListCAtemplates/function.json | 19 +++++++++++++++++++ ListCAtemplates/run.ps1 | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 ListCAtemplates/function.json create mode 100644 ListCAtemplates/run.ps1 diff --git a/ListCAtemplates/function.json b/ListCAtemplates/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/ListCAtemplates/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/ListCAtemplates/run.ps1 b/ListCAtemplates/run.ps1 new file mode 100644 index 000000000000..54abd2a3f5e4 --- /dev/null +++ b/ListCAtemplates/run.ps1 @@ -0,0 +1,21 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + + +# Write to the Azure Functions log stream. +Write-Host "PowerShell HTTP trigger function processed a request." +Write-Host $Request.query.id +$Templates = Get-ChildItem "Config\*.IntuneTemplate.json" | ForEach-Object { Get-Content $_ | ConvertFrom-Json } +if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property guid -EQ $Request.query.id } + + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Templates) + }) From c12406c33a67d8f2acbc6ab7fce9bedd3f635c9f Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 13:32:31 +0200 Subject: [PATCH 34/44] added options for CA templates --- ListConditionalAccessPolicies/run.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/ListConditionalAccessPolicies/run.ps1 b/ListConditionalAccessPolicies/run.ps1 index ebc518207772..7e126a9affad 100644 --- a/ListConditionalAccessPolicies/run.ps1 +++ b/ListConditionalAccessPolicies/run.ps1 @@ -432,6 +432,7 @@ try { builtInControls = ($cap.grantControls.builtInControls) -join "," customAuthenticationFactors = ($cap.grantControls.customAuthenticationFactors) -join "," termsOfUse = ($cap.grantControls.termsOfUse) -join "," + rawjson = ($cap | ConvertTo-Json -Depth 10) } $temp } From a9506e8730f28f7e947ceddeb08975313cc61c22 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 13:33:27 +0200 Subject: [PATCH 35/44] changes from intune to ca template --- ListCAtemplates/run.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ListCAtemplates/run.ps1 b/ListCAtemplates/run.ps1 index 54abd2a3f5e4..1601bba39736 100644 --- a/ListCAtemplates/run.ps1 +++ b/ListCAtemplates/run.ps1 @@ -10,7 +10,7 @@ Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -messa # Write to the Azure Functions log stream. Write-Host "PowerShell HTTP trigger function processed a request." Write-Host $Request.query.id -$Templates = Get-ChildItem "Config\*.IntuneTemplate.json" | ForEach-Object { Get-Content $_ | ConvertFrom-Json } +$Templates = Get-ChildItem "Config\*.CATemplate.json" | ForEach-Object { Get-Content $_ | ConvertFrom-Json } if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property guid -EQ $Request.query.id } From 1e03c12d82457440fdf84bbf13ff24afd921e49d Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 13:59:53 +0200 Subject: [PATCH 36/44] remove template added --- RemoveCATemplate/function.json | 19 +++++++++++++++++++ RemoveCATemplate/run.ps1 | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 RemoveCATemplate/function.json create mode 100644 RemoveCATemplate/run.ps1 diff --git a/RemoveCATemplate/function.json b/RemoveCATemplate/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/RemoveCATemplate/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/RemoveCATemplate/run.ps1 b/RemoveCATemplate/run.ps1 new file mode 100644 index 000000000000..230d1f026650 --- /dev/null +++ b/RemoveCATemplate/run.ps1 @@ -0,0 +1,26 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + +$ID = $request.query.id +try { + Remove-Item "Config\$($ID).TransportRuleTemplate.json" -Force + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Conditional Access Template with ID $ID." -Sev "Info" + $body = [pscustomobject]@{"Results" = "Successfully removed Conditional Access Template" } +} +catch { + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Conditional Access template $ID. $($_.Exception.Message)" -Sev "Error" + $body = [pscustomobject]@{"Results" = "Failed to remove template" } +} + + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + From f1650286046f07b06590168248f0cf1ebc5b282c Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 14:00:06 +0200 Subject: [PATCH 37/44] added raw json to cap --- ListConditionalAccessPolicies/run.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ListConditionalAccessPolicies/run.ps1 b/ListConditionalAccessPolicies/run.ps1 index 7e126a9affad..86df23d3ee37 100644 --- a/ListConditionalAccessPolicies/run.ps1 +++ b/ListConditionalAccessPolicies/run.ps1 @@ -432,7 +432,7 @@ try { builtInControls = ($cap.grantControls.builtInControls) -join "," customAuthenticationFactors = ($cap.grantControls.customAuthenticationFactors) -join "," termsOfUse = ($cap.grantControls.termsOfUse) -join "," - rawjson = ($cap | ConvertTo-Json -Depth 10) + rawjson = ($cap | ConvertTo-Json -Depth 100) } $temp } From d6273debb4a4f8663d409751214914dc0174ae01 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 14:13:54 +0200 Subject: [PATCH 38/44] added add ca template --- AddCATemplate/function.json | 19 ++++++++++++++++++ AddCATemplate/run.ps1 | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 AddCATemplate/function.json create mode 100644 AddCATemplate/run.ps1 diff --git a/AddCATemplate/function.json b/AddCATemplate/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/AddCATemplate/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/AddCATemplate/run.ps1 b/AddCATemplate/run.ps1 new file mode 100644 index 000000000000..0655fb273f09 --- /dev/null +++ b/AddCATemplate/run.ps1 @@ -0,0 +1,39 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" +Write-Host ($request | ConvertTo-Json -Compress) + +try { + $GUID = New-Guid + New-Item Config -ItemType Directory -ErrorAction SilentlyContinue + $JSON = if ($request.body.rawjson) { + Write-Host "PowerShellCommand" + $request.body.rawjson + } + else { + ([pscustomobject]$Request.body) | ForEach-Object { + $NonEmptyProperties = $_.psobject.Properties | Where-Object { $null -ne $_.Value } | Select-Object -ExpandProperty Name + $_ | Select-Object -Property $NonEmptyProperties + } + } + $JSON = ($JSON | ConvertTo-Json -Depth 10).tolower() + Set-Content "Config\$($GUID).CATemplate.json" -Value ($JSON) -Force + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created Transport Rule Template $($Request.body.name) with GUID $GUID" -Sev "Debug" + $body = [pscustomobject]@{"Results" = "Successfully added template" } + +} +catch { + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create Transport Rule Template: $($_.Exception.Message)" -Sev "Error" + $body = [pscustomobject]@{"Results" = "Intune Template Deployment failed: $($_.Exception.Message)" } +} + + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) From 5f19dd87730625f2bf88c2806a80658117f28485 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 14:39:10 +0200 Subject: [PATCH 39/44] added CA stuff --- AddCATemplate/run.ps1 | 2 +- ListCAtemplates/run.ps1 | 5 ++++- RemoveCATemplate/run.ps1 | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/AddCATemplate/run.ps1 b/AddCATemplate/run.ps1 index 0655fb273f09..7f5d0102e268 100644 --- a/AddCATemplate/run.ps1 +++ b/AddCATemplate/run.ps1 @@ -15,7 +15,7 @@ try { $request.body.rawjson } else { - ([pscustomobject]$Request.body) | ForEach-Object { + ([pscustomobject]$Request.body | Select-Object -Property * -ExcludeProperty id, GUID) | ForEach-Object { $NonEmptyProperties = $_.psobject.Properties | Where-Object { $null -ne $_.Value } | Select-Object -ExpandProperty Name $_ | Select-Object -Property $NonEmptyProperties } diff --git a/ListCAtemplates/run.ps1 b/ListCAtemplates/run.ps1 index 1601bba39736..b785b8d8d6db 100644 --- a/ListCAtemplates/run.ps1 +++ b/ListCAtemplates/run.ps1 @@ -10,7 +10,10 @@ Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -messa # Write to the Azure Functions log stream. Write-Host "PowerShell HTTP trigger function processed a request." Write-Host $Request.query.id -$Templates = Get-ChildItem "Config\*.CATemplate.json" | ForEach-Object { Get-Content $_ | ConvertFrom-Json } +$Templates = Get-ChildItem "Config\*.CATemplate.json" | ForEach-Object { + $data = Get-Content $_ | ConvertFrom-Json + $data | Add-Member -NotePropertyName "GUID" -NotePropertyValue (($_.name).split('.') | Select-Object -First 1) + $data } if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property guid -EQ $Request.query.id } diff --git a/RemoveCATemplate/run.ps1 b/RemoveCATemplate/run.ps1 index 230d1f026650..f7a47300cbc9 100644 --- a/RemoveCATemplate/run.ps1 +++ b/RemoveCATemplate/run.ps1 @@ -8,7 +8,7 @@ Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -messa $ID = $request.query.id try { - Remove-Item "Config\$($ID).TransportRuleTemplate.json" -Force + Remove-Item "Config\$($ID).CATemplate.json" -Force Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Conditional Access Template with ID $ID." -Sev "Info" $body = [pscustomobject]@{"Results" = "Successfully removed Conditional Access Template" } } From ee1e87213ed58f3fc8dc3f5c8db4ab0fc9b173d7 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 15:46:29 +0200 Subject: [PATCH 40/44] finished deployment of CA policies --- AddCAPolicy/function.json | 19 +++++++++++ AddCAPolicy/run.ps1 | 67 +++++++++++++++++++++++++++++++++++++++ AddCATemplate/run.ps1 | 4 +-- 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 AddCAPolicy/function.json create mode 100644 AddCAPolicy/run.ps1 diff --git a/AddCAPolicy/function.json b/AddCAPolicy/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/AddCAPolicy/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/AddCAPolicy/run.ps1 b/AddCAPolicy/run.ps1 new file mode 100644 index 000000000000..a4781a83c6bc --- /dev/null +++ b/AddCAPolicy/run.ps1 @@ -0,0 +1,67 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + +$Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value +if ("AllTenants" -in $Tenants) { $Tenants = (Get-Tenants).DefaultDomainName } +$displayname = ($request.body.RawJSON | ConvertFrom-Json).Displayname +function Remove-EmptyArrays ($Object) { + if ($Object -is [Array]) { + foreach ($Item in $Object) { Remove-EmptyArrays $Item } + } + elseif ($Object -is [HashTable]) { + foreach ($Key in @($Object.get_Keys())) { + if ($Object[$Key] -is [Array] -and $Object[$Key].get_Count() -eq 0) { + $Object.Remove($Key) + } + else { Remove-EmptyArrays $Object[$Key] } + } + } + elseif ($Object -is [PSCustomObject]) { + foreach ($Name in @($Object.psobject.properties.Name)) { + if ($Object.$Name -is [Array] -and $Object.$Name.get_Count() -eq 0) { + $Object.PSObject.Properties.Remove($Name) + } + elseif ($object.$name -eq $null) { + $Object.PSObject.Properties.Remove($Name) + } + else { Remove-EmptyArrays $Object.$Name } + } + } +} + +$JSONObj = $request.body.RawJSON | ConvertFrom-Json | Select-Object * -ExcludeProperty ID, GUID, *time* +Remove-EmptyArrays $JSONObj +$RawJSON = $JSONObj | ConvertTo-Json -Depth 10 + +$results = foreach ($Tenant in $tenants) { + try { + $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" -tenantid $tenant + $PolicyName = ($RawJSON | ConvertFrom-Json).displayName + if ($PolicyName -in $CheckExististing.displayName) { + Throw "Conditional Access Policy with Display Name $($Displayname) Already exists" + } + + $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" -tenantid $tenant -type POST -body $RawJSON + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Added Conditional Access Policy $($Displayname)" -Sev "Error" + "Succesfully added Conditional Access Policy for $($Tenant)" + } + catch { + "Failed to add policy for $($Tenant): $($_.Exception.Message)" + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Failed adding Conditional Access Policy $($Displayname). Error: $($_.Exception.Message)" -Sev "Error" + continue + } + +} + +$body = [pscustomobject]@{"Results" = @($results) } + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) diff --git a/AddCATemplate/run.ps1 b/AddCATemplate/run.ps1 index 7f5d0102e268..2f677f35eac6 100644 --- a/AddCATemplate/run.ps1 +++ b/AddCATemplate/run.ps1 @@ -15,12 +15,12 @@ try { $request.body.rawjson } else { - ([pscustomobject]$Request.body | Select-Object -Property * -ExcludeProperty id, GUID) | ForEach-Object { + ([pscustomobject]$Request.body) | ForEach-Object { $NonEmptyProperties = $_.psobject.Properties | Where-Object { $null -ne $_.Value } | Select-Object -ExpandProperty Name $_ | Select-Object -Property $NonEmptyProperties } } - $JSON = ($JSON | ConvertTo-Json -Depth 10).tolower() + $JSON = ($JSON | ConvertTo-Json -Depth 10) Set-Content "Config\$($GUID).CATemplate.json" -Value ($JSON) -Force Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created Transport Rule Template $($Request.body.name) with GUID $GUID" -Sev "Debug" $body = [pscustomobject]@{"Results" = "Successfully added template" } From 3e1bcce58be6e384021617fb5af6f341ccc62bc3 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 15:58:23 +0200 Subject: [PATCH 41/44] added edit CA policy --- EditCAPolicy/function.json | 19 +++++++++++++++++++ EditCAPolicy/run.ps1 | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 EditCAPolicy/function.json create mode 100644 EditCAPolicy/run.ps1 diff --git a/EditCAPolicy/function.json b/EditCAPolicy/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/EditCAPolicy/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/EditCAPolicy/run.ps1 b/EditCAPolicy/run.ps1 new file mode 100644 index 000000000000..04541ae02e24 --- /dev/null +++ b/EditCAPolicy/run.ps1 @@ -0,0 +1,29 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + +$Tenant = $request.query.tenantFilter +$ID = $request.query.guid +$results = try { + $EditBody = "{`"state`": `"$($request.query.state)`"}" + $Request = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta//identity/conditionalAccess/policies/$($id)" -tenantid $tenant -type PATCH -body $EditBody + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Edited CA policy $($ID)" -Sev "Error" + "Succesfully edited CA policy" +} +catch { + "Failed to add CA policy: $($_.Exception.Message)" + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Failed editing CA policy $($ID). Error: $($_.Exception.Message)" -Sev "Error" + continue +} + +$body = [pscustomobject]@{"Results" = $results } + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) From 3610d47fca364be7616f1e33be381483020f1641 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 16:03:54 +0200 Subject: [PATCH 42/44] added remove policy --- RemoveCAPolicy/function.json | 19 +++++++++++++++++++ RemoveCAPolicy/run.ps1 | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 RemoveCAPolicy/function.json create mode 100644 RemoveCAPolicy/run.ps1 diff --git a/RemoveCAPolicy/function.json b/RemoveCAPolicy/function.json new file mode 100644 index 000000000000..306b0c51e560 --- /dev/null +++ b/RemoveCAPolicy/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + } + ] +} \ No newline at end of file diff --git a/RemoveCAPolicy/run.ps1 b/RemoveCAPolicy/run.ps1 new file mode 100644 index 000000000000..77f46cb17646 --- /dev/null +++ b/RemoveCAPolicy/run.ps1 @@ -0,0 +1,29 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + +# Interact with query parameters or the body of the request. +$TenantFilter = $Request.Query.TenantFilter +$policyId = $Request.Query.GUID +if (!$policyId) { exit } +try { + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$($policyId)" -type DELETE -tenant $TenantFilter + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted CA Policy $policyId" -Sev "Info" -tenant $TenantFilter + $body = [pscustomobject]@{"Results" = "Succesfully deleted the policy" } + +} +catch { + Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete CA policy $policyId. $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter + $body = [pscustomobject]@{"Results" = "Could not delete policy: $($_.Exception.Message)" } + +} + +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + From 540f6410e85521913cb071126bed5dd76e171451 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Thu, 31 Mar 2022 16:28:00 +0200 Subject: [PATCH 43/44] fix conversion issue --- AddCATemplate/run.ps1 | 3 +-- AddTransportTemplate/run.ps1 | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/AddCATemplate/run.ps1 b/AddCATemplate/run.ps1 index 2f677f35eac6..5013002fd575 100644 --- a/AddCATemplate/run.ps1 +++ b/AddCATemplate/run.ps1 @@ -11,8 +11,7 @@ try { $GUID = New-Guid New-Item Config -ItemType Directory -ErrorAction SilentlyContinue $JSON = if ($request.body.rawjson) { - Write-Host "PowerShellCommand" - $request.body.rawjson + ([pscustomobject]$request.body.rawjson) | ConvertFrom-Json } else { ([pscustomobject]$Request.body) | ForEach-Object { diff --git a/AddTransportTemplate/run.ps1 b/AddTransportTemplate/run.ps1 index 3d556250215c..7cc9d7613835 100644 --- a/AddTransportTemplate/run.ps1 +++ b/AddTransportTemplate/run.ps1 @@ -12,7 +12,7 @@ try { New-Item Config -ItemType Directory -ErrorAction SilentlyContinue $JSON = if ($request.body.PowerShellCommand) { Write-Host "PowerShellCommand" - $request.body.PowerShellCommand + $request.body.PowerShellCommand | ConvertFrom-Json } else { ([pscustomobject]$Request.body | Select-Object Name, ActivationDate, ADComparisonAttribute, ADComparisonOperator, AddManagerAsRecipientType, AddToRecipients, AnyOfCcHeader, AnyOfCcHeaderMemberOf, AnyOfRecipientAddressContainsWords, AnyOfRecipientAddressMatchesPatterns, AnyOfToCcHeader, AnyOfToCcHeaderMemberOf, AnyOfToHeader, AnyOfToHeaderMemberOf, ApplyClassification, ApplyHtmlDisclaimerFallbackAction, ApplyHtmlDisclaimerLocation, ApplyHtmlDisclaimerText, ApplyOME, ApplyRightsProtectionCustomizationTemplate, ApplyRightsProtectionTemplate, AttachmentContainsWords, AttachmentExtensionMatchesWords, AttachmentHasExecutableContent, AttachmentIsPasswordProtected, AttachmentIsUnsupported, AttachmentMatchesPatterns, AttachmentNameMatchesPatterns, AttachmentProcessingLimitExceeded, AttachmentPropertyContainsWords, AttachmentSizeOver, BetweenMemberOf1, BetweenMemberOf2, BlindCopyTo, Comments, Confirm, ContentCharacterSetContainsWords, CopyTo, DeleteMessage, DlpPolicy, DomainController, Enabled, ExceptIfADComparisonAttribute, ExceptIfADComparisonOperator, ExceptIfAnyOfCcHeader, ExceptIfAnyOfCcHeaderMemberOf, ExceptIfAnyOfRecipientAddressContainsWords, ExceptIfAnyOfRecipientAddressMatchesPatterns, ExceptIfAnyOfToCcHeader, ExceptIfAnyOfToCcHeaderMemberOf, ExceptIfAnyOfToHeader, ExceptIfAnyOfToHeaderMemberOf, ExceptIfAttachmentContainsWords, ExceptIfAttachmentExtensionMatchesWords, ExceptIfAttachmentHasExecutableContent, ExceptIfAttachmentIsPasswordProtected, ExceptIfAttachmentIsUnsupported, ExceptIfAttachmentMatchesPatterns, ExceptIfAttachmentNameMatchesPatterns, ExceptIfAttachmentProcessingLimitExceeded, ExceptIfAttachmentPropertyContainsWords, ExceptIfAttachmentSizeOver, ExceptIfBetweenMemberOf1, ExceptIfBetweenMemberOf2, ExceptIfContentCharacterSetContainsWords, ExceptIfFrom, ExceptIfFromAddressContainsWords, ExceptIfFromAddressMatchesPatterns, ExceptIfFromMemberOf, ExceptIfFromScope, ExceptIfHasClassification, ExceptIfHasNoClassification, ExceptIfHasSenderOverride, ExceptIfHeaderContainsMessageHeader, ExceptIfHeaderContainsWords, ExceptIfHeaderMatchesMessageHeader, ExceptIfHeaderMatchesPatterns, ExceptIfManagerAddresses, ExceptIfManagerForEvaluatedUser, ExceptIfMessageContainsDataClassifications, ExceptIfMessageSizeOver, ExceptIfMessageTypeMatches, ExceptIfRecipientADAttributeContainsWords, ExceptIfRecipientADAttributeMatchesPatterns, ExceptIfRecipientAddressContainsWords, ExceptIfRecipientAddressMatchesPatterns, ExceptIfRecipientDomainIs, ExceptIfRecipientInSenderList, ExceptIfSCLOver, ExceptIfSenderADAttributeContainsWords, ExceptIfSenderADAttributeMatchesPatterns, ExceptIfSenderDomainIs, ExceptIfSenderInRecipientList, ExceptIfSenderIpRanges, ExceptIfSenderManagementRelationship, ExceptIfSentTo, ExceptIfSentToMemberOf, ExceptIfSentToScope, ExceptIfSubjectContainsWords, ExceptIfSubjectMatchesPatterns, ExceptIfSubjectOrBodyContainsWords, ExceptIfSubjectOrBodyMatchesPatterns, ExceptIfWithImportance, ExpiryDate, From, FromAddressContainsWords, FromAddressMatchesPatterns, FromMemberOf, FromScope, GenerateIncidentReport, GenerateNotification, HasClassification, HasNoClassification, HasSenderOverride, HeaderContainsMessageHeader, HeaderContainsWords, HeaderMatchesMessageHeader, HeaderMatchesPatterns, IncidentReportContent, IncidentReportOriginalMail, LogEventText, ManagerAddresses, ManagerForEvaluatedUser, MessageContainsDataClassifications, MessageSizeOver, MessageTypeMatches, Mode, ModerateMessageByManager, ModerateMessageByUser, NotifySender, PrependSubject, Quarantine, RecipientADAttributeContainsWords, RecipientADAttributeMatchesPatterns, RecipientAddressContainsWords, RecipientAddressMatchesPatterns, RecipientAddressType, RecipientDomainIs, RecipientInSenderList, RedirectMessageTo, RejectMessageEnhancedStatusCode, RejectMessageReasonText, RemoveHeader, RemoveOME, RemoveOMEv2, RemoveRMSAttachmentEncryption, RouteMessageOutboundConnector, RouteMessageOutboundRequireTls, RuleErrorAction, RuleSubType, SCLOver, SenderADAttributeContainsWords, SenderADAttributeMatchesPatterns, SenderAddressLocation, SenderDomainIs, SenderInRecipientList, SenderIpRanges, SenderManagementRelationship, SentTo, SentToMemberOf, SentToScope, SetAuditSeverity, SetHeaderName, SetHeaderValue, SetSCL, SmtpRejectMessageRejectStatusCode, SmtpRejectMessageRejectText, StopRuleProcessing, SubjectContainsWords, SubjectMatchesPatterns, SubjectOrBodyContainsWords, SubjectOrBodyMatchesPatterns, UseLegacyRegex, WithImportance ) | ForEach-Object { From 978cfc7018e29a71a445ba615b0a9991a1a9d4f6 Mon Sep 17 00:00:00 2001 From: Kelvin Tegelaar Date: Fri, 1 Apr 2022 11:03:16 +0200 Subject: [PATCH 44/44] upped version --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index 0bfbd57387f6..abb16582324b 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -1.8.2 \ No newline at end of file +1.9.0 \ No newline at end of file