From 7f1339631e3f02699367579ddb9774719006e70a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= <31723128+kris6673@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:17:42 +0200 Subject: [PATCH 01/26] Merge pull request #58 from KelvinTegelaar/dev [pull] dev from KelvinTegelaar:dev From 5b98c82fbd2bca16924057b04ada750ef3a56532 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:40:37 +0200 Subject: [PATCH 02/26] Merge pull request #861 from Ren-Roros-Digital/editorconfig Create .editorconfig --- .editorconfig | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000000..b0c2c5085172 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +insert_final_newline = true + +[*.{ps1, psd1, psm1}] +indent_size = 4 +end_of_line = crlf +trim_trailing_whitespace = true + +[*.json] +indent_size = 2 +end_of_line = crlf +trim_trailing_whitespace = true + +[*.{md, txt}] +end_of_line = crlf +max_line_length = off +trim_trailing_whitespace = false From 38d45dc2d999af1916fa0511ce9f14001cb5051b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= <31723128+kris6673@users.noreply.github.com> Date: Mon, 10 Jun 2024 17:38:51 +0200 Subject: [PATCH 03/26] Merge branch 'KelvinTegelaar:dev' into dev From a253d76c46b748b4af969751f6daef102eef4959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 10 Jun 2024 17:44:30 +0200 Subject: [PATCH 04/26] Apparently there is 2 of these --- Cache_SAMSetup/SAMManifest.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cache_SAMSetup/SAMManifest.json b/Cache_SAMSetup/SAMManifest.json index 82bab306ef89..b6b291da57b4 100644 --- a/Cache_SAMSetup/SAMManifest.json +++ b/Cache_SAMSetup/SAMManifest.json @@ -157,7 +157,9 @@ { "id": "885f682f-a990-4bad-a642-36736a74b0c7", "type": "Scope" }, { "id": "913b9306-0ce1-42b8-9137-6a7df690a760", "type": "Role" }, { "id": "cb8f45a0-5c2e-4ea1-b803-84b870a7d7ec", "type": "Scope" }, - { "id": "4c06a06a-098a-4063-868e-5dfee3827264", "type": "Scope" } + { "id": "4c06a06a-098a-4063-868e-5dfee3827264", "type": "Scope" }, + { "id": "1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9", "type": "Role" }, + { "id": "e67e6727-c080-415e-b521-e3f35d5248e9", "type": "Scope" } ] }, { From e8f7a55cd26ee4870db0c1b870f7639caa6056a9 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:17:01 +0200 Subject: [PATCH 05/26] Merge pull request #885 from kris6673/dev Add roles to second SAMManifest file From 64f4965b5bc6c116d8761f4f39ac2571e87d4ecc Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 10 Jun 2024 19:02:16 -0400 Subject: [PATCH 06/26] Custom Roles: Fix blocked tenants --- Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 | 4 +--- .../HTTP Functions/CIPP/Settings/Invoke-ExecCustomRole.ps1 | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 b/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 index 1336247272bb..c10a38349d7b 100644 --- a/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 +++ b/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 @@ -47,7 +47,6 @@ function Test-CIPPAccess { $Permission.AllowedTenants | Where-Object { $Permission.BlockedTenants -notcontains $_ } } } - Write-Information ($LimitedTenantList | ConvertTo-Json) return $LimitedTenantList } @@ -77,11 +76,10 @@ function Test-CIPPAccess { } else { $Tenant = ($Tenants | Where-Object { $Request.Query.TenantFilter -eq $_.customerId -or $Request.Body.TenantFilter -eq $_.customerId -or $Request.Query.TenantFilter -eq $_.defaultDomainName -or $Request.Body.TenantFilter -eq $_.defaultDomainName }).customerId if ($Role.AllowedTenants -contains 'AllTenants') { - $AllowedTenants = $Tenants + $AllowedTenants = $Tenants.customerId } else { $AllowedTenants = $Role.AllowedTenants } - if ($Tenant) { $TenantAllowed = $AllowedTenants -contains $Tenant -and $Role.BlockedTenants -notcontains $Tenant if (!$TenantAllowed) { continue } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCustomRole.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCustomRole.ps1 index dafe35d226f8..a1e92d2c3f89 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCustomRole.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCustomRole.ps1 @@ -43,12 +43,12 @@ function Invoke-ExecCustomRole { if ($Role.AllowedTenants) { $Role.AllowedTenants = @($Role.AllowedTenants | ConvertFrom-Json) } else { - $Role | Add-Member -NotePropertyName AllowedTenants -NotePropertyValue @() + $Role | Add-Member -NotePropertyName AllowedTenants -NotePropertyValue @() -Force } if ($Role.BlockedTenants) { $Role.BlockedTenants = @($Role.BlockedTenants | ConvertFrom-Json) } else { - $Role | Add-Member -NotePropertyName BlockedTenants -NotePropertyValue @() + $Role | Add-Member -NotePropertyName BlockedTenants -NotePropertyValue @() -Force } $Role } From 5056fcf2a533a8123f11c8d103ab0b0c36368f08 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 12 Jun 2024 20:57:30 +0200 Subject: [PATCH 07/26] per user MFA --- .../CIPPCore/Public/Set-CIPPPerUserMFA.ps1 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 diff --git a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 new file mode 100644 index 000000000000..74babb8fa860 --- /dev/null +++ b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 @@ -0,0 +1,19 @@ +function Set-CIPPPerUserMFA { + [CmdletBinding()] + param( + $TenantFilter, + $userId, + [ValidateSet('enabled', 'disabled', 'enforced')] + $State = 'users', + $executingUser + ) + try { + $state = @{ 'perUserMfaState' = "$state" } | ConvertTo-Json + New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/users/$userId/authentication/requirements" -tenantid $tenantfilter -type PUT -body $state -ContentType $ContentType + "Successfully set Per user MFA State for $id" + Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Successfully set Per user MFA State for $id" -Sev 'Info' -tenant $TenantFilter + } catch { + "Failed to set MFA State for $id : $_" + Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Failed to set MFA State for $id : $_" -Sev 'Error' -tenant $TenantFilter + } +} \ No newline at end of file From 8d3b66b3518e6679e18ff8171bafb99b4c1cfd0f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 12 Jun 2024 20:59:30 +0200 Subject: [PATCH 08/26] fix per user patch --- Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 index 74babb8fa860..e044fe6797a3 100644 --- a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 @@ -9,7 +9,7 @@ function Set-CIPPPerUserMFA { ) try { $state = @{ 'perUserMfaState' = "$state" } | ConvertTo-Json - New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/users/$userId/authentication/requirements" -tenantid $tenantfilter -type PUT -body $state -ContentType $ContentType + New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/users/$userId/authentication/requirements" -tenantid $tenantfilter -type PATCH -body $state "Successfully set Per user MFA State for $id" Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Successfully set Per user MFA State for $id" -Sev 'Info' -tenant $TenantFilter } catch { From 62ee4fb81d2ee62c2e3d720012ee84156034f906 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 12 Jun 2024 15:43:02 -0400 Subject: [PATCH 09/26] Schema tweaks --- .../Public/Get-CIPPSchemaExtensions.ps1 | 11 ++++++++--- .../GraphHelper/New-GraphGetRequest.ps1 | 4 +++- .../Public/Set-CIPPUserSchemaProperties.ps1 | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 Modules/CIPPCore/Public/Set-CIPPUserSchemaProperties.ps1 diff --git a/Modules/CIPPCore/Public/Get-CIPPSchemaExtensions.ps1 b/Modules/CIPPCore/Public/Get-CIPPSchemaExtensions.ps1 index 26794daffea7..b85edb06af86 100644 --- a/Modules/CIPPCore/Public/Get-CIPPSchemaExtensions.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPSchemaExtensions.ps1 @@ -30,6 +30,10 @@ function Get-CIPPSchemaExtensions { name = 'autoExpandingArchiveEnabled' type = 'Boolean' } + @{ + name = 'perUserMfaState' + type = 'String' + } ) } ) @@ -40,8 +44,8 @@ function Get-CIPPSchemaExtensions { $SchemaFound = $true $Schema = $Schemas | Where-Object { $_.id -match $SchemaDefinition.id } $Patch = @{} - if (Compare-Object -ReferenceObject ($SchemaDefinition.properties | Select-Object name, type) -DifferenceObject $Schema.properties) { - $Patch.properties = $Properties + if (Compare-Object -ReferenceObject ($SchemaDefinition.properties | Select-Object name, type) -DifferenceObject ($Schema.properties | Select-Object name, type)) { + $Patch.properties = $SchemaDefinitions.Properties } if ($Schema.status -ne 'Available') { $Patch.status = 'Available' @@ -49,9 +53,10 @@ function Get-CIPPSchemaExtensions { if ($Schema.targetTypes -ne $SchemaDefinition.targetTypes) { $Patch.targetTypes = $SchemaDefinition.targetTypes } - if ($Patch.Keys.Count -gt 0) { + if ($Patch -and $Patch.Keys.Count -gt 0) { Write-Information "Updating $($Schema.id)" $Json = ConvertTo-Json -Depth 5 -InputObject $Patch + Write-Information $Json New-GraphPOSTRequest -type PATCH -Uri "https://graph.microsoft.com/v1.0/schemaExtensions/$($Schema.id)" -Body $Json -AsApp $true -NoAuthCheck $true } else { $Schema diff --git a/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 index 28e88e204d79..65a5edca23f2 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 @@ -52,7 +52,9 @@ function New-GraphGetRequest { if ($noPagination) { $nextURL = $null } else { $nextURL = $data.'@odata.nextLink' } } } catch { - $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message + try { + $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message + } catch { $Message = $null } if ($Message -eq $null) { $Message = $($_.Exception.Message) } if ($Message -ne 'Request not applicable to target tenant.' -and $Tenant) { $Tenant.LastGraphError = $Message diff --git a/Modules/CIPPCore/Public/Set-CIPPUserSchemaProperties.ps1 b/Modules/CIPPCore/Public/Set-CIPPUserSchemaProperties.ps1 new file mode 100644 index 000000000000..0c6f1960ad0d --- /dev/null +++ b/Modules/CIPPCore/Public/Set-CIPPUserSchemaProperties.ps1 @@ -0,0 +1,19 @@ +function Set-CIPPUserSchemaProperties { + [CmdletBinding(SupportsShouldProcess = $true)] + Param( + [string]$TenantFilter, + [string]$UserId, + [hashtable]$Properties + ) + + $Schema = Get-CIPPSchemaExtensions | Where-Object { $_.id -match '_cippUser' } + + $Body = [PSCustomObject]@{ + "$($Schema.id)" = $Properties + } + + $Json = ConvertTo-Json -Depth 5 -InputObject $Body + if ($PSCmdlet.ShouldProcess("User: $UserId", "Set Schema Properties to $($Properties|ConvertTo-Json -Compress)")) { + New-GraphPOSTRequest -type PATCH -Uri "https://graph.microsoft.com/beta/users/$UserId" -Body $Json -tenantid $TenantFilter + } +} \ No newline at end of file From cd8b2929fcc7e9047d15d42c49ca298cb609c01a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 12 Jun 2024 15:43:16 -0400 Subject: [PATCH 10/26] Fix casing on sam wizard --- .../CIPP/Setup/Invoke-ExecSAMSetup.ps1 | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Setup/Invoke-ExecSAMSetup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Setup/Invoke-ExecSAMSetup.ps1 index 3818794ddbb0..9f38b50965a0 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Setup/Invoke-ExecSAMSetup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Setup/Invoke-ExecSAMSetup.ps1 @@ -11,7 +11,7 @@ Function Invoke-ExecSAMSetup { param($Request, $TriggerMetadata) $UserCreds = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($request.headers.'x-ms-client-principal')) | ConvertFrom-Json) - if ($Request.query.error) { + if ($Request.Query.error) { Add-Type -AssemblyName System.Web Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ ContentType = 'text/html' @@ -61,25 +61,25 @@ Function Invoke-ExecSAMSetup { $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) try { - if ($Request.query.count -lt 1 ) { $Results = 'No authentication code found. Please go back to the wizard.' } + if ($Request.Query.count -lt 1 ) { $Results = 'No authentication code found. Please go back to the wizard.' } - if ($request.body.setkeys) { + if ($Request.Body.setkeys) { if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { - if ($request.body.TenantId) { $Secret.TenantId = $Request.body.tenantid } - if ($request.body.RefreshToken) { $Secret.RefreshToken = $Request.body.RefreshToken } - if ($request.body.applicationid) { $Secret.ApplicationId = $Request.body.ApplicationId } - if ($request.body.ApplicationSecret) { $Secret.ApplicationSecret = $Request.body.ApplicationSecret } + if ($Request.Body.TenantId) { $Secret.TenantId = $Request.Body.tenantid } + if ($Request.Body.RefreshToken) { $Secret.RefreshToken = $Request.Body.RefreshToken } + if ($Request.Body.applicationid) { $Secret.ApplicationId = $Request.Body.ApplicationId } + if ($Request.Body.ApplicationSecret) { $Secret.ApplicationSecret = $Request.Body.ApplicationSecret } Add-CIPPAzDataTableEntity @DevSecretsTable -Entity $Secret -Force } else { - if ($request.body.tenantid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $request.body.tenantid -AsPlainText -Force) } - if ($request.body.RefreshToken) { Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $request.body.RefreshToken -AsPlainText -Force) } - if ($request.body.applicationid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $request.body.applicationid -AsPlainText -Force) } - if ($request.body.applicationsecret) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $request.body.applicationsecret -AsPlainText -Force) } + if ($Request.Body.tenantid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $Request.Body.tenantid -AsPlainText -Force) } + if ($Request.Body.RefreshToken) { Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Request.Body.RefreshToken -AsPlainText -Force) } + if ($Request.Body.applicationid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Request.Body.applicationid -AsPlainText -Force) } + if ($Request.Body.applicationsecret) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $Request.Body.applicationsecret -AsPlainText -Force) } } $Results = @{ Results = 'The keys have been replaced. Please perform a permissions check.' } } - 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) { + 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 = $Rows.tenantid if (!$TenantId) { $TenantId = $ENV:TenantId } @@ -89,11 +89,11 @@ Function Invoke-ExecSAMSetup { if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { $clientsecret = $Secret.ApplicationSecret } else { - $clientsecret = Get-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -AsPlainText + $clientsecret = Get-AzKeyVaultSecret -VaultName $kv -Name 'ApplicationSecret' -AsPlainText } if (!$clientsecret) { $clientsecret = $ENV:ApplicationSecret } - Write-Host "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" - $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 "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" + $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" -ContentType 'application/x-www-form-urlencoded' if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { $Secret.RefreshToken = $RefreshToken.refresh_token @@ -113,7 +113,7 @@ Function Invoke-ExecSAMSetup { $Results = "Authentication failed. $($_.Exception.message)" } } - if ($request.query.CreateSAM) { + if ($Request.Query.CreateSAM) { $Rows = @{ RowKey = 'setup' PartitionKey = 'setup' @@ -126,7 +126,7 @@ Function Invoke-ExecSAMSetup { Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) - if ($Request.query.partnersetup) { + if ($Request.Query.partnersetup) { $SetupPhase = $Rows.partnersetup = $true Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null } @@ -136,46 +136,46 @@ Function Invoke-ExecSAMSetup { Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null $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) { + if ($Request.Query.CheckSetupProcess -and $Request.Query.step -eq 1) { $SAMSetup = $Rows.SamSetup | ConvertFrom-Json -ErrorAction SilentlyContinue $Token = (New-DeviceLogin -clientid '1b730954-1685-4b74-9bfd-dac224a7b894' -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) - if ($token.Access_Token) { + if ($Token.access_token) { $step = 2 $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 $PartnerSetup = $Rows.partnersetup - $TenantId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/organization' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method GET -ContentType 'application/json').value.id + $TenantId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/organization' -Headers @{ authorization = "Bearer $($Token.access_token)" } -Method GET -ContentType 'application/json').value.id $SetupPhase = $rows.tenantid = [string]($TenantId) Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null if ($PartnerSetup) { $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 = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/applications' -Headers @{ authorization = "Bearer $($Token.access_token)" } -Method POST -Body $app -ContentType 'application/json') $rows.appid = [string]($AppId.appId) Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null $attempt = 0 do { try { try { - $SPNDefender = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"fc780465-2017-40d4-a0c5-307022471b92`" }" -ContentType 'application/json') + $SPNDefender = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.access_token)" } -Method POST -Body "{ `"appId`": `"fc780465-2017-40d4-a0c5-307022471b92`" }" -ContentType 'application/json') } catch { Write-Host "didn't deploy spn for defender, probably already there." } try { - $SPNTeams = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"48ac35b8-9aa8-4d74-927d-1f4a14a0b239`" }" -ContentType 'application/json') + $SPNTeams = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.access_token)" } -Method POST -Body "{ `"appId`": `"48ac35b8-9aa8-4d74-927d-1f4a14a0b239`" }" -ContentType 'application/json') } catch { Write-Host "didn't deploy spn for Teams, probably already there." } try { - $SPNPartnerCenter = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"fa3d9a0c-3fb0-42cc-9193-47c7ecd2edbd`" }" -ContentType 'application/json') + $SPNPartnerCenter = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.access_token)" } -Method POST -Body "{ `"appId`": `"fa3d9a0c-3fb0-42cc-9193-47c7ecd2edbd`" }" -ContentType 'application/json') } catch { Write-Host "didn't deploy spn for PartnerCenter, probably already there." } - $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') + $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') 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 + $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') + $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 { @@ -184,21 +184,22 @@ Function Invoke-ExecSAMSetup { } until ($attempt -gt 5) } 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') - $rows.appid = [string]($AppId.appId) + $AppId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/applications' -Headers @{ authorization = "Bearer $($Token.access_token)" } -Method POST -Body $app -ContentType 'application/json') + $Rows.appid = [string]($AppId.appId) Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null } - $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 + $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 if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { - $Secret.TenantId = $Request.body.tenantid - $Secret.ApplicationId = $Request.body.ApplicationId - $Secret.ApplicationSecret = $Request.body.ApplicationSecret + $Secret.TenantId = $TenantId + $Secret.ApplicationId = $AppId.appId + $Secret.ApplicationSecret = $AppPassword Add-CIPPAzDataTableEntity @DevSecretsTable -Entity $Secret -Force + Write-Information ($Secret | ConvertTo-Json -Depth 5) } else { Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $TenantId -AsPlainText -Force) - Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appid -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appId -AsPlainText -Force) Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $AppPassword -AsPlainText -Force) } $Results = @{'message' = 'Created application. Waiting 30 seconds for Azure propagation'; step = $step } @@ -208,7 +209,7 @@ Function Invoke-ExecSAMSetup { } } - switch ($request.query.step) { + switch ($Request.Query.step) { 2 { $step = 2 $TenantId = $Rows.tenantid From 7d61557ced6385d885b646be5fb2227fb2323766 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 13 Jun 2024 00:04:13 +0200 Subject: [PATCH 11/26] Get per user MFA --- Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 diff --git a/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 new file mode 100644 index 000000000000..86acfc501082 --- /dev/null +++ b/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 @@ -0,0 +1,17 @@ +function Get-CIPPPerUserMFA { + [CmdletBinding()] + param( + $TenantFilter, + $userId, + $executingUser + ) + try { + $MFAState = New-graphGetRequest -Uri "https://graph.microsoft.com/beta/users/$($userId)/authentication/requirements" -tenantid $tenantfilter + return [PSCustomObject]@{ + user = $userId + PerUserMFA = $MFAState.perUserMfaState + } + } catch { + "Failed to get MFA State for $id : $_" + } +} \ No newline at end of file From cda510e736803575fe2f7ba26fc4a6185d303298 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 13 Jun 2024 01:36:30 +0200 Subject: [PATCH 12/26] bulk request support --- .../GraphHelper/New-GraphBulkRequest.ps1 | 3 +- .../CIPPCore/Public/Set-CIPPPerUserMFA.ps1 | 20 ++++++++--- .../Invoke-CIPPStandardPerUserMFA.ps1 | 33 +++++++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 diff --git a/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 index ebe6af9675b9..f37be378e39d 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 @@ -33,7 +33,8 @@ function New-GraphBulkRequest { $req = @{} # Use select to create hashtables of id, method and url for each call $req['requests'] = ($Requests[$i..($i + 19)]) - Invoke-RestMethod -Uri $URL -Method POST -Headers $headers -ContentType 'application/json; charset=utf-8' -Body ($req | ConvertTo-Json -Depth 10) + $ReqBody = ($req | ConvertTo-Json -Depth 10) + Invoke-RestMethod -Uri $URL -Method POST -Headers $headers -ContentType 'application/json; charset=utf-8' -Body $ReqBody } foreach ($MoreData in $ReturnedData.Responses | Where-Object { $_.body.'@odata.nextLink' }) { diff --git a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 index e044fe6797a3..74c3ecbb5d66 100644 --- a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 @@ -4,13 +4,25 @@ function Set-CIPPPerUserMFA { $TenantFilter, $userId, [ValidateSet('enabled', 'disabled', 'enforced')] - $State = 'users', + $State = 'enabled', $executingUser ) try { - $state = @{ 'perUserMfaState' = "$state" } | ConvertTo-Json - New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/users/$userId/authentication/requirements" -tenantid $tenantfilter -type PATCH -body $state - "Successfully set Per user MFA State for $id" + $int = 0 + $Requests = foreach ($id in $userId) { + @{ + id = $int++ + method = 'PATCH' + url = "users/$id/authentication/requirements" + body = @{ 'perUserMfaState' = "$state" } + 'headers' = @{ + 'Content-Type' = 'application/json' + } + } + } + #Split the requests by batches of 20, we can have anywhere from 1 to 1000 batches and execute New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests $batch + $Requests = New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests $Requests -asapp $true + "Successfully set Per user MFA State for $userId" Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Successfully set Per user MFA State for $id" -Sev 'Info' -tenant $TenantFilter } catch { "Failed to set MFA State for $id : $_" diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 new file mode 100644 index 000000000000..c651e9171a28 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 @@ -0,0 +1,33 @@ +function Invoke-CIPPStandardPerUserMFA { + <# + .FUNCTIONALITY + Internal + #> + param($Tenant, $Settings) + + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=UserPrincipalName,accountEnabled" -scope 'https://graph.microsoft.com/.default' -tenantid $Tenant | Where-Object { $_.AccountEnabled -EQ $true } + + If ($Settings.remediate -eq $true) { + if ($GraphRequest) { + try { + Set-CIPPPeruserMFA -TenantFilter $Tenant -UserId $GraphRequest.UserPrincipalName -State 'Enforced' + Write-LogMessage -API 'Standards' -tenant $tenant -message '' -sev Info + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable guest $($guest.UserPrincipalName) ($($guest.id)): $ErrorMessage" -sev Error + } + } + } + if ($Settings.alert -eq $true) { + + if ($GraphRequest) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Guests accounts with a login longer than 90 days ago: $($GraphRequest.count)" -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'No guests accounts with a login longer than 90 days ago.' -sev Info + } + } + if ($Settings.report -eq $true) { + $filtered = $GraphRequest | Select-Object -Property UserPrincipalName, id, signInActivity, mail, userType, accountEnabled + Add-CIPPBPAField -FieldName 'DisableGuests' -FieldValue $filtered -StoreAs json -Tenant $tenant + } +} From 2f97db6ed4530f18f090d5994edf131141dbfb75 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 13 Jun 2024 01:38:16 +0200 Subject: [PATCH 13/26] batching single item fix --- Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 index 74c3ecbb5d66..9675b104bc82 100644 --- a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 @@ -21,7 +21,7 @@ function Set-CIPPPerUserMFA { } } #Split the requests by batches of 20, we can have anywhere from 1 to 1000 batches and execute New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests $batch - $Requests = New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests $Requests -asapp $true + $Requests = New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests @($Requests) -asapp $true "Successfully set Per user MFA State for $userId" Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Successfully set Per user MFA State for $id" -Sev 'Info' -tenant $TenantFilter } catch { From 0914e2f161531f023c59831d17f660f337ff858b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 13 Jun 2024 02:20:40 +0200 Subject: [PATCH 14/26] add more bulk features --- Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 | 1 - .../Invoke-CIPPStandardPerUserMFA.ps1 | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 index 9675b104bc82..eedab329061c 100644 --- a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 @@ -20,7 +20,6 @@ function Set-CIPPPerUserMFA { } } } - #Split the requests by batches of 20, we can have anywhere from 1 to 1000 batches and execute New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests $batch $Requests = New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests @($Requests) -asapp $true "Successfully set Per user MFA State for $userId" Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Successfully set Per user MFA State for $id" -Sev 'Info' -tenant $TenantFilter diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 index c651e9171a28..16b0c037f223 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 @@ -6,15 +6,23 @@ function Invoke-CIPPStandardPerUserMFA { param($Tenant, $Settings) $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=UserPrincipalName,accountEnabled" -scope 'https://graph.microsoft.com/.default' -tenantid $Tenant | Where-Object { $_.AccountEnabled -EQ $true } - + $int = 0 + $Requests = foreach ($id in $GraphRequest.userPrincipalName) { + @{ + id = $int++ + method = 'GET' + url = "/users/$id/authentication/requirements" + } + } + $UsersWithoutMFA = (New-GraphBulkRequest -tenantid $tenant -scope 'https://graph.microsoft.com/.default' -Requests @($Requests) -asapp $true).body | Where-Object { $_.perUserMfaState -ne 'enforced' } | Select-Object peruserMFAState, @{Name = 'UserPrincipalName'; Expression = { [System.Web.HttpUtility]::UrlDecode($_.'@odata.context'.split("'")[1]) } } If ($Settings.remediate -eq $true) { - if ($GraphRequest) { + if ($UsersWithoutMFA) { try { - Set-CIPPPeruserMFA -TenantFilter $Tenant -UserId $GraphRequest.UserPrincipalName -State 'Enforced' - Write-LogMessage -API 'Standards' -tenant $tenant -message '' -sev Info + $MFAMessage = Set-CIPPPeruserMFA -TenantFilter $Tenant -UserId $GraphRequest.UserPrincipalName -State 'Enforced' + Write-LogMessage -API 'Standards' -tenant $tenant -message $MFAMessage -sev Info } catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable guest $($guest.UserPrincipalName) ($($guest.id)): $ErrorMessage" -sev Error + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enforce MFA for all users: $ErrorMessage" -sev Error } } } From 82a0f19ec177707949c2ae650d7ef9a28f5457b3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 12 Jun 2024 23:53:01 -0400 Subject: [PATCH 15/26] Per user mfa tweaks --- .../Users/Invoke-ExecPerUserMFA.ps1 | 28 +++++++++++ .../CIPPCore/Public/Set-CIPPPerUserMFA.ps1 | 50 ++++++++++++++++--- .../Public/Set-CIPPUserSchemaProperties.ps1 | 43 +++++++++++++--- 3 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 new file mode 100644 index 000000000000..b52a1595f513 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 @@ -0,0 +1,28 @@ +function Invoke-ExecPerUserMFA { + <# + .FUNCTIONALITY + Entrypoint + + .ROLE + Identity.User.ReadWrite + #> + Param( + $Request, + $TriggerMetadata + ) + + $Request = @{ + userId = $Request.Body.userId + TenantFilter = $Request.Body.TenantFilter + State = $Request.Body.State + executingUser = $Request.Headers.'x-ms-client-principal' + } + $Result = Set-CIPPPerUserMFA @Request + $Body = @{ + Results = @($Result) + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 index eedab329061c..5bb406658e37 100644 --- a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 @@ -1,30 +1,68 @@ function Set-CIPPPerUserMFA { + <# + .SYNOPSIS + Change Per-User MFA State for a User + + .DESCRIPTION + Change the Per-User MFA State for a user via the /users/{id}/authentication/requirements endpoint + + .PARAMETER TenantFilter + Tenant where the user resides + + .PARAMETER userId + One or more User IDs to set the MFA state for (GUID or UserPrincipalName) + + .PARAMETER State + State to set the user to (enabled, disabled, enforced) + + .PARAMETER executingUser + User executing the command + + .EXAMPLE + Set-CIPPPerUserMFA -TenantFilter 'contoso.onmicrosoft.com' -userId user@contoso.onmicrosoft.com -State 'disabled' -executingUser 'mspuser@partner.com' + #> [CmdletBinding()] param( - $TenantFilter, - $userId, + [Parameter(Mandatory = $true)] + [string]$TenantFilter, + [Parameter(Mandatory = $true)] + [string[]]$userId, [ValidateSet('enabled', 'disabled', 'enforced')] $State = 'enabled', - $executingUser + [string]$executingUser = 'CIPP' ) try { $int = 0 + $Body = @{ + PerUserState = $State + } $Requests = foreach ($id in $userId) { @{ id = $int++ method = 'PATCH' url = "users/$id/authentication/requirements" - body = @{ 'perUserMfaState' = "$state" } + body = $Body 'headers' = @{ 'Content-Type' = 'application/json' } } } + $Requests = New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests @($Requests) -asapp $true "Successfully set Per user MFA State for $userId" - Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Successfully set Per user MFA State for $id" -Sev 'Info' -tenant $TenantFilter + + $Users = foreach ($id in $userId) { + @{ + userId = $id + Properties = @{ + perUserMfaState = $State + } + } + } + Set-CIPPUserSchemaProperties -TenantFilter $TenantFilter -Users $Users + Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Successfully set Per user MFA State to $State for $id" -Sev 'Info' -tenant $TenantFilter } catch { "Failed to set MFA State for $id : $_" - Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Failed to set MFA State for $id : $_" -Sev 'Error' -tenant $TenantFilter + Write-LogMessage -user $executingUser -API 'Set-CIPPPerUserMFA' -message "Failed to set MFA State to $State for $id : $_" -Sev 'Error' -tenant $TenantFilter } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Set-CIPPUserSchemaProperties.ps1 b/Modules/CIPPCore/Public/Set-CIPPUserSchemaProperties.ps1 index 0c6f1960ad0d..b006a27069ef 100644 --- a/Modules/CIPPCore/Public/Set-CIPPUserSchemaProperties.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPUserSchemaProperties.ps1 @@ -1,19 +1,46 @@ function Set-CIPPUserSchemaProperties { + <# + .SYNOPSIS + Set Schema Properties for a user + + .DESCRIPTION + Uses scheam extensions to set properties for a user + + .PARAMETER TenantFilter + Tenant for user + + .PARAMETER UserId + One or more user ids to set properties for + + .PARAMETER Properties + Hashtable of properties to set + + #> [CmdletBinding(SupportsShouldProcess = $true)] Param( + [Parameter(Mandatory = $true)] [string]$TenantFilter, - [string]$UserId, - [hashtable]$Properties + [Parameter(Mandatory = $true)] + [object]$Users ) $Schema = Get-CIPPSchemaExtensions | Where-Object { $_.id -match '_cippUser' } - - $Body = [PSCustomObject]@{ - "$($Schema.id)" = $Properties + $int = 0 + $Requests = foreach ($User in $Users) { + @{ + id = $int++ + method = 'PATCH' + url = "users/$($User.userId)" + body = @{ + "$($Schema.id)" = $User.Properties + } + 'headers' = @{ + 'Content-Type' = 'application/json' + } + } } - $Json = ConvertTo-Json -Depth 5 -InputObject $Body - if ($PSCmdlet.ShouldProcess("User: $UserId", "Set Schema Properties to $($Properties|ConvertTo-Json -Compress)")) { - New-GraphPOSTRequest -type PATCH -Uri "https://graph.microsoft.com/beta/users/$UserId" -Body $Json -tenantid $TenantFilter + if ($PSCmdlet.ShouldProcess("User: $($Users.userId -join ', ')", 'Set Schema Properties')) { + $Requests = New-GraphBulkRequest -tenantid $tenantfilter -Requests @($Requests) } } \ No newline at end of file From 3f7d424de3600dbe384fc1ccd8e092df7b99bf94 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 13 Jun 2024 12:16:45 +0200 Subject: [PATCH 16/26] changes for CIPP From 425830028e56a7f67845980588ed3ffd289d3d07 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 13 Jun 2024 12:22:04 +0200 Subject: [PATCH 17/26] added per user MFA standard --- .../Standards/Invoke-CIPPStandardPerUserMFA.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 index 16b0c037f223..9ecd363c44a3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 @@ -15,10 +15,11 @@ function Invoke-CIPPStandardPerUserMFA { } } $UsersWithoutMFA = (New-GraphBulkRequest -tenantid $tenant -scope 'https://graph.microsoft.com/.default' -Requests @($Requests) -asapp $true).body | Where-Object { $_.perUserMfaState -ne 'enforced' } | Select-Object peruserMFAState, @{Name = 'UserPrincipalName'; Expression = { [System.Web.HttpUtility]::UrlDecode($_.'@odata.context'.split("'")[1]) } } + If ($Settings.remediate -eq $true) { if ($UsersWithoutMFA) { try { - $MFAMessage = Set-CIPPPeruserMFA -TenantFilter $Tenant -UserId $GraphRequest.UserPrincipalName -State 'Enforced' + $MFAMessage = Set-CIPPPeruserMFA -TenantFilter $Tenant -UserId $UsersWithoutMFA.UserPrincipalName -State 'Enforced' Write-LogMessage -API 'Standards' -tenant $tenant -message $MFAMessage -sev Info } catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message @@ -28,14 +29,13 @@ function Invoke-CIPPStandardPerUserMFA { } if ($Settings.alert -eq $true) { - if ($GraphRequest) { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Guests accounts with a login longer than 90 days ago: $($GraphRequest.count)" -sev Alert + if ($UsersWithoutMFA) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "The following accounts do not have Legacy MFA Enforced: $($UsersWithoutMFA.UserPrincipalName -join ', ')" -sev Alert } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'No guests accounts with a login longer than 90 days ago.' -sev Info + Write-LogMessage -API 'Standards' -tenant $tenant -message 'No accounts do not have legacy per user MFA Enforced' -sev Info } } if ($Settings.report -eq $true) { - $filtered = $GraphRequest | Select-Object -Property UserPrincipalName, id, signInActivity, mail, userType, accountEnabled - Add-CIPPBPAField -FieldName 'DisableGuests' -FieldValue $filtered -StoreAs json -Tenant $tenant + Add-CIPPBPAField -FieldName 'LegacyMFAUsers' -FieldValue $UsersWithoutMFA -StoreAs json -Tenant $tenant } } From aa755aec0c9a101b479fae135470bdf74151e25f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 13 Jun 2024 12:48:07 +0200 Subject: [PATCH 18/26] Add All Tenants to per user MFA --- .../CIPPCore/Public/Get-CIPPPerUserMFA.ps1 | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 index 86acfc501082..05e879a83496 100644 --- a/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 @@ -3,13 +3,30 @@ function Get-CIPPPerUserMFA { param( $TenantFilter, $userId, - $executingUser + $executingUser, + $AllUsers = $false ) try { - $MFAState = New-graphGetRequest -Uri "https://graph.microsoft.com/beta/users/$($userId)/authentication/requirements" -tenantid $tenantfilter - return [PSCustomObject]@{ - user = $userId - PerUserMFA = $MFAState.perUserMfaState + if ($AllUsers -eq $true) { + $AllUsers = New-graphGetRequest -Uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=UserPrincipalName,Id" -tenantid $tenantfilter + $Requests = foreach ($id in $AllUsers.userPrincipalName) { + @{ + id = $int++ + method = 'GET' + url = "users/$id/authentication/requirements" + } + } + $Requests = New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests @($Requests) -asapp $true + if ($Requests.body) { + $UsersWithoutMFA = $Requests.body | Select-Object peruserMFAState, @{Name = 'UserPrincipalName'; Expression = { [System.Web.HttpUtility]::UrlDecode($_.'@odata.context'.split("'")[1]) } } + return $UsersWithoutMFA + } + } else { + $MFAState = New-graphGetRequest -Uri "https://graph.microsoft.com/beta/users/$($userId)/authentication/requirements" -tenantid $tenantfilter + return [PSCustomObject]@{ + UserPrincipalName = $userId + PerUserMFAState = $MFAState.perUserMfaState + } } } catch { "Failed to get MFA State for $id : $_" From 72412d479b16f71434e76557d019d663f34fdc70 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 13 Jun 2024 13:56:56 +0200 Subject: [PATCH 19/26] updated MFA REport --- Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 | 20 +++++++++---------- .../CIPPCore/Public/Get-CIPPPerUserMFA.ps1 | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 b/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 index 319f9a81e726..28d526a9944d 100644 --- a/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 @@ -6,18 +6,17 @@ function Get-CIPPMFAState { $APIName = 'Get MFA Status', $ExecutingUser ) - - $users = foreach ($user in (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$select=id,UserPrincipalName,DisplayName,accountEnabled,assignedLicenses' -tenantid $TenantFilter)) { + $PerUserMFAState = Get-CIPPPerUserMFA -TenantFilter $TenantFilter -AllUsers $true + $users = foreach ($user in (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=999&$select=id,UserPrincipalName,DisplayName,accountEnabled,assignedLicenses' -tenantid $TenantFilter)) { [PSCustomObject]@{ - UserPrincipalName = $user.UserPrincipalName - isLicensed = [boolean]$user.assignedLicenses.skuid - accountEnabled = $user.accountEnabled - DisplayName = $user.DisplayName - ObjectId = $user.id - StrongAuthenticationRequirements = @{StrongAuthenticationRequirement = @{state = 'See Documentation' } } + UserPrincipalName = $user.UserPrincipalName + isLicensed = [boolean]$user.assignedLicenses.skuid + accountEnabled = $user.accountEnabled + DisplayName = $user.DisplayName + ObjectId = $user.id } } - + $SecureDefaultsState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $TenantFilter ).IsEnabled $CAState = New-Object System.Collections.ArrayList @@ -62,7 +61,6 @@ function Get-CIPPMFAState { Write-Host 'Processing users' $UserCAState = New-Object System.Collections.ArrayList foreach ($CA in $CAState) { - Write-Host 'Looping CAState' if ($CA -like '*All Users*') { if ($ExcludeAllUsers -contains $_.ObjectId) { $UserCAState.Add("Excluded from $($policy.displayName) - All Users") | Out-Null } else { $UserCAState.Add($CA) | Out-Null } @@ -75,7 +73,7 @@ function Get-CIPPMFAState { } } - $PerUser = if ($_.StrongAuthenticationRequirements.StrongAuthenticationRequirement.state -ne $null) { $_.StrongAuthenticationRequirements.StrongAuthenticationRequirement.state } else { 'Disabled' } + $PerUser = if ($PerUserMFAState -eq $null) { $null } else { ($PerUserMFAState | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).PerUserMFAState } $MFARegUser = if (($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).IsMFARegistered -eq $null) { $false } else { ($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName) } diff --git a/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 index 05e879a83496..5c525962009f 100644 --- a/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPPerUserMFA.ps1 @@ -24,8 +24,8 @@ function Get-CIPPPerUserMFA { } else { $MFAState = New-graphGetRequest -Uri "https://graph.microsoft.com/beta/users/$($userId)/authentication/requirements" -tenantid $tenantfilter return [PSCustomObject]@{ - UserPrincipalName = $userId PerUserMFAState = $MFAState.perUserMfaState + UserPrincipalName = $userId } } } catch { From 92c253dc357e11acaecde7108083b3d0a25e56c2 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 14 Jun 2024 00:14:15 +0200 Subject: [PATCH 20/26] add instant allow for sam setup --- Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 b/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 index c10a38349d7b..acdaba4e0cee 100644 --- a/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 +++ b/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 @@ -3,7 +3,7 @@ function Test-CIPPAccess { $Request, [switch]$TenantList ) - + if ($Request.Params.CIPPEndpoint -eq 'ExecSAMSetup') { return $true } if (!$Request.Headers.'x-ms-client-principal') { # Direct API Access $CustomRoles = @('CIPP-API') From 09c2cf5986b29fa32a20ca06bbe7243001e26d22 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 14 Jun 2024 12:03:02 +0200 Subject: [PATCH 21/26] added anchor conversion --- Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 index 4a05cf778fe7..d9bca21efd69 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 @@ -22,6 +22,7 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc if ($cmdparams.Identity) { $Anchor = $cmdparams.Identity } if ($cmdparams.anr) { $Anchor = $cmdparams.anr } if ($cmdparams.User) { $Anchor = $cmdparams.User } + if ($cmdparams.mailbox) { $Anchor = $cmdparams.mailbox } if (!$Anchor -or $useSystemMailbox) { if (!$Tenant.initialDomainName) { @@ -31,6 +32,15 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc } $anchor = "UPN:SystemMailbox{8cc370d3-822a-4ab8-a926-bb94bd0641a9}@$($OnMicrosoft)" } + #if the anchor is a GUID, try looking up the user. + if ($Anchor -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { + $Anchor = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$Anchor" -tenantid $tenantid -NoAuthCheck $NoAuthCheck + if ($Anchor) { + $Anchor = $Anchor.UserPrincipalName + } else { + Write-Error "Failed to find user with GUID $Anchor" + } + } } Write-Host "Using $Anchor" $Headers = @{ From 455ad75884349e21c9a6efe903f35330aef04e13 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 14 Jun 2024 12:06:02 +0200 Subject: [PATCH 22/26] added support for incorrect initial domain --- Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 index d9bca21efd69..8ef890eaf10d 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 @@ -25,7 +25,7 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc if ($cmdparams.mailbox) { $Anchor = $cmdparams.mailbox } if (!$Anchor -or $useSystemMailbox) { - if (!$Tenant.initialDomainName) { + if (!$Tenant.initialDomainName -or $Tenant.initialDomainName -notlike '*onmicrosoft.com*') { $OnMicrosoft = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains?$top=999' -tenantid $tenantid -NoAuthCheck $NoAuthCheck | Where-Object -Property isInitial -EQ $true).id } else { $OnMicrosoft = $Tenant.initialDomainName From e094d8cecbdeeb5488265d18b883fba1d2077c58 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 14 Jun 2024 12:26:27 +0200 Subject: [PATCH 23/26] fix pagination issue --- .../CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 8 ++++---- SendStats/run.ps1 | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index ebf491cec05a..9828682c6348 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -267,7 +267,7 @@ function Invoke-NinjaOneTenantSync { @{ id = 'Users' method = 'GET' - url = '/users' + url = '/users?$top=999' }, @{ id = 'TenantDetails' @@ -292,7 +292,7 @@ function Invoke-NinjaOneTenantSync { @{ id = 'Devices' method = 'GET' - url = '/deviceManagement/managedDevices' + url = '/deviceManagement/managedDevices?$top=999' }, @{ id = 'DeviceCompliancePolicies' @@ -317,12 +317,12 @@ function Invoke-NinjaOneTenantSync { @{ id = 'SecureScore' method = 'GET' - url = '/security/secureScores' + url = '/security/secureScores?$top=999' }, @{ id = 'SecureScoreControlProfiles' method = 'GET' - url = '/security/secureScoreControlProfiles' + url = '/security/secureScoreControlProfiles?$top=999' }, @{ id = 'Subscriptions' diff --git a/SendStats/run.ps1 b/SendStats/run.ps1 index b4427d230338..2e30b113ab55 100644 --- a/SendStats/run.ps1 +++ b/SendStats/run.ps1 @@ -17,6 +17,7 @@ $SendingObject = [PSCustomObject]@{ SetupComplete = $SetupComplete RunningVersionAPI = $APIVersion.trim() CountOfTotalTenants = $tenantcount + uid = $env:TenantID } | ConvertTo-Json Invoke-RestMethod -Uri 'https://management.cipp.app/api/stats' -Method POST -Body $SendingObject -ContentType 'application/json' \ No newline at end of file From 9b3b9de38e888917268bf1e70811f85802e1c4c4 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 14 Jun 2024 12:27:42 +0200 Subject: [PATCH 24/26] fixes ninja bug --- Modules/CippExtensions/Public/Get-ExtensionRateLimit.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/CippExtensions/Public/Get-ExtensionRateLimit.ps1 b/Modules/CippExtensions/Public/Get-ExtensionRateLimit.ps1 index 2a0e718402f4..242d4dd86390 100644 --- a/Modules/CippExtensions/Public/Get-ExtensionRateLimit.ps1 +++ b/Modules/CippExtensions/Public/Get-ExtensionRateLimit.ps1 @@ -24,7 +24,6 @@ function Get-ExtensionRateLimit($ExtensionName, $ExtensionPartitionKey, $RateLim } if (($ActiveJobs | Measure-Object).count -ge $RateLimit) { Write-Host "Rate Limiting. Currently $($ActiveJobs.count) Active Jobs" - Start-Sleep -Seconds $WaitTime $CurrentMap = Get-ExtensionRateLimit -ExtensionName $ExtensionName -ExtensionPartitionKey $ExtensionPartitionKey -RateLimit $RateLimit -WaitTime $WaitTime } From d5f7c5e245193be58f0281189aac556fbb373295 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 14 Jun 2024 14:59:43 +0200 Subject: [PATCH 25/26] version up --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index edb1d397cf28..a94a88fbb889 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.8.0 \ No newline at end of file +5.8.5 \ No newline at end of file From 285e24c791375e1ecfb36b434487ebde9e9ccdca Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 14 Jun 2024 15:27:54 +0200 Subject: [PATCH 26/26] final touches prerelease --- Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 | 3 ++- .../Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 index 5bb406658e37..1c92a5e9c5d2 100644 --- a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 @@ -34,7 +34,7 @@ function Set-CIPPPerUserMFA { try { $int = 0 $Body = @{ - PerUserState = $State + perUserMFAstate = $State } $Requests = foreach ($id in $userId) { @{ @@ -48,6 +48,7 @@ function Set-CIPPPerUserMFA { } } + $Requests = New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests @($Requests) -asapp $true "Successfully set Per user MFA State for $userId" diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 index 9ecd363c44a3..c83204529423 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 @@ -19,7 +19,7 @@ function Invoke-CIPPStandardPerUserMFA { If ($Settings.remediate -eq $true) { if ($UsersWithoutMFA) { try { - $MFAMessage = Set-CIPPPeruserMFA -TenantFilter $Tenant -UserId $UsersWithoutMFA.UserPrincipalName -State 'Enforced' + $MFAMessage = Set-CIPPPeruserMFA -TenantFilter $Tenant -UserId $UsersWithoutMFA.UserPrincipalName -State 'enforced' Write-LogMessage -API 'Standards' -tenant $tenant -message $MFAMessage -sev Info } catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message