From ba12192d257183c480052e1387c8d385b7c1dfad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 2 Dec 2023 16:07:50 +0100 Subject: [PATCH 01/84] progress i guess --- .../Public/Set-CIPPAuthenticationPolicy.ps1 | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 new file mode 100644 index 000000000000..f416a840ef8b --- /dev/null +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -0,0 +1,99 @@ +function Set-CIPPAuthenticationPolicy { + [CmdletBinding()] + param( + $TenantFilter, + $AuthenticationMethodId, + $EnableGroups, # Not sure if i need this, but it's for if the all_users is not the target for enablement + $OptionalInput, # Used for stuff like the + $APIName = 'Set Authentication Policy', + $ExecutingUser, + $State # enabled or disabled + ) + + switch ($AuthenticationMethodId) { + + # FIDO2 + 'FIDO2' { + + if ($State -eq 'enabled') { + # Enable FIDO2 + try { + $body = '{"@odata.type":"#microsoft.graph.fido2AuthenticationMethodConfiguration","id":"Fido2","includeTargets":[{"id":"all_users","isRegistrationRequired":false,"targetType":"group","displayName":"All users"}],"excludeTargets":[],"isAttestationEnforced":true,"isSelfServiceRegistrationAllowed":true,"keyRestrictions":{"aaGuids":[],"enforcementType":"block","isEnforced":false},"state":"enabled"}' + New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } + catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } + # Disable FIDO2 + elseif ($State -eq 'disabled') { + try { + # Get current state and disable + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/fido2' -tenantid $TenantFilter + $GraphRequest.state = $State + $body = ($GraphRequest | ConvertTo-Json -Depth 10) + $GraphRequest = New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/fido2' -Type patch -Body $body -ContentType 'application/json' + + } + catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } + # Catch invalid input + else { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + + } + + # Microsoft Authenticator + 'MicrosoftAuthenticator' { + + } + # SMS + 'SMS' { + + } + # Temporary Access Pass + 'TemporaryAccessPass' { + + } + # Hardware OATH tokens (Preview) + 'HardwareOATH' { + + } + # Third-party software OATH tokens + 'softwareOath' { + + } + # Voice call + 'Voice' { + + } + # Email OTP + 'Email' { + + } + # Certificate-based authentication + 'x509Certificate' { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + + } + Default { + Write-LogMessage -API $APIName -tenant $TenantFilter -message 'Somehow you hit the default case. You did something wrong' -sev Error + return 'Somehow you hit the default case. You did something wrong' + } + } + + + + + + + + + + + +} \ No newline at end of file From 8448137ae6375546fcbd6c7c9c1c8a11fa539dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sun, 3 Dec 2023 23:02:24 +0100 Subject: [PATCH 02/84] Meh progress --- .../Public/Set-CIPPAuthenticationPolicy.ps1 | 190 ++++++++++++++++-- 1 file changed, 178 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index f416a840ef8b..65d0737ae004 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -5,9 +5,9 @@ function Set-CIPPAuthenticationPolicy { $AuthenticationMethodId, $EnableGroups, # Not sure if i need this, but it's for if the all_users is not the target for enablement $OptionalInput, # Used for stuff like the - $APIName = 'Set Authentication Policy', + $APIName = 'Set Authentication Policy', # Should this be 'Standards' $ExecutingUser, - $State # enabled or disabled + [ValidateSet('enabled', 'disabled')]$State # enabled or disabled ) switch ($AuthenticationMethodId) { @@ -21,8 +21,7 @@ function Set-CIPPAuthenticationPolicy { $body = '{"@odata.type":"#microsoft.graph.fido2AuthenticationMethodConfiguration","id":"Fido2","includeTargets":[{"id":"all_users","isRegistrationRequired":false,"targetType":"group","displayName":"All users"}],"excludeTargets":[],"isAttestationEnforced":true,"isSelfServiceRegistrationAllowed":true,"keyRestrictions":{"aaGuids":[],"enforcementType":"block","isEnforced":false},"state":"enabled"}' New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -Type patch -Body $body -ContentType 'application/json' Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { + } catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } @@ -34,14 +33,13 @@ function Set-CIPPAuthenticationPolicy { $GraphRequest.state = $State $body = ($GraphRequest | ConvertTo-Json -Depth 10) $GraphRequest = New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/fido2' -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { + } catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } - # Catch invalid input - else { + } else { + # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } @@ -50,34 +48,202 @@ function Set-CIPPAuthenticationPolicy { # Microsoft Authenticator 'MicrosoftAuthenticator' { + if ($State -eq 'enabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } elseif ($State -eq 'disabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } else { + # Catch invalid input + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } # SMS 'SMS' { + if ($State -eq 'enabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } elseif ($State -eq 'disabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } else { + # Catch invalid input + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } } # Temporary Access Pass 'TemporaryAccessPass' { - - } + + if ($State -eq 'enabled') { + # Get the TAP config from the standards table. If it's not there, use the default value of true + $ConfigTable = Get-CippTable -tablename 'standards' + $TAPConfig = ((Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'standards' and RowKey eq '$TenantFilter'").JSON | ConvertFrom-Json).Standards.TAP.config + if (!$TAPConfig) { + $TAPConfig = ((Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'standards' and RowKey eq 'AllTenants'").JSON | ConvertFrom-Json).Standards.TAP.config + } + if (!$TAPConfig) { $TAPConfig = 'true' } + + try { + # Vaiable values + $MinimumLifetime = '60' #Minutes + $MaximumLifetime = '480' #minutes + $DefaultLifeTime = '60' #minutes + $DefaultLength = '8' + + # Build the body of the request + $CurrentInfo = [PSCustomObject]@{ + '@odata.type' = '#microsoft.graph.temporaryAccessPassAuthenticationMethodConfiguration' + id = 'TemporaryAccessPass' + includeTargets = @( + @{ + id = 'all_users' + isRegistrationRequired = $false + targetType = 'group' + displayName = 'All users' + } + ) + defaultLength = $DefaultLength + defaultLifetimeInMinutes = $DefaultLifeTime + isUsableOnce = $TAPConfig + maximumLifetimeInMinutes = $MaximumLifetime + minimumLifetimeInMinutes = $MinimumLifetime + state = $State + } + + # Convert to JSON and send the request + $body = (ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo) + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -asApp $true -Body $body -ContentType 'application/json') + + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } elseif ($State -eq 'disabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } else { + # Catch invalid input + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } + # Hardware OATH tokens (Preview) 'HardwareOATH' { + if ($State -eq 'enabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } elseif ($State -eq 'disabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } else { + # Catch invalid input + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } } # Third-party software OATH tokens 'softwareOath' { + if ($State -eq 'enabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } elseif ($State -eq 'disabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } else { + # Catch invalid input + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } } # Voice call 'Voice' { + if ($State -eq 'enabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } elseif ($State -eq 'disabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } else { + # Catch invalid input + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } } # Email OTP 'Email' { + if ($State -eq 'enabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } elseif ($State -eq 'disabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } else { + # Catch invalid input + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } } # Certificate-based authentication 'x509Certificate' { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + + if ($State -eq 'enabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } elseif ($State -eq 'disabled') { + try { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } + } else { + # Catch invalid input + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + } } Default { From 65815780b93bb5929c3ff5258089e4c9a786b8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sun, 3 Dec 2023 23:12:32 +0100 Subject: [PATCH 03/84] add small input validation --- Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index 65d0737ae004..d9fcaac75eaf 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -2,12 +2,12 @@ function Set-CIPPAuthenticationPolicy { [CmdletBinding()] param( $TenantFilter, - $AuthenticationMethodId, + [Parameter(Mandatory = $true)]$AuthenticationMethodId, $EnableGroups, # Not sure if i need this, but it's for if the all_users is not the target for enablement $OptionalInput, # Used for stuff like the - $APIName = 'Set Authentication Policy', # Should this be 'Standards' + $APIName = 'Set Authentication Policy', # Should this be 'Standards'? $ExecutingUser, - [ValidateSet('enabled', 'disabled')]$State # enabled or disabled + [Parameter(Mandatory = $true)][ValidateSet('enabled', 'disabled')]$State # enabled or disabled ) switch ($AuthenticationMethodId) { @@ -125,8 +125,9 @@ function Set-CIPPAuthenticationPolicy { state = $State } + # Convert to JSON and send the request - $body = (ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo) + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -asApp $true -Body $body -ContentType 'application/json') Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info From 12d286bcbfee0c3f22415afaf93dc513120dc045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 5 Dec 2023 00:02:05 +0100 Subject: [PATCH 04/84] Maybe progress? --- .../Public/Set-CIPPAuthenticationPolicy.ps1 | 168 ++++++++++++------ 1 file changed, 118 insertions(+), 50 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index d9fcaac75eaf..a8a5c9b019b1 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -3,13 +3,15 @@ function Set-CIPPAuthenticationPolicy { param( $TenantFilter, [Parameter(Mandatory = $true)]$AuthenticationMethodId, - $EnableGroups, # Not sure if i need this, but it's for if the all_users is not the target for enablement - $OptionalInput, # Used for stuff like the + [Parameter(Mandatory = $true)][bool]$State, # true = enabled or false = disabled + [bool]$MicrosoftAuthenticatorSoftwareOathEnabled, $APIName = 'Set Authentication Policy', # Should this be 'Standards'? - $ExecutingUser, - [Parameter(Mandatory = $true)][ValidateSet('enabled', 'disabled')]$State # enabled or disabled + $ExecutingUser ) - + + # Convert bool input to usable string + $State = if ($State) { 'enabled' } else { 'disabled' } + switch ($AuthenticationMethodId) { # FIDO2 @@ -21,7 +23,8 @@ function Set-CIPPAuthenticationPolicy { $body = '{"@odata.type":"#microsoft.graph.fido2AuthenticationMethodConfiguration","id":"Fido2","includeTargets":[{"id":"all_users","isRegistrationRequired":false,"targetType":"group","displayName":"All users"}],"excludeTargets":[],"isAttestationEnforced":true,"isSelfServiceRegistrationAllowed":true,"keyRestrictions":{"aaGuids":[],"enforcementType":"block","isEnforced":false},"state":"enabled"}' New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -Type patch -Body $body -ContentType 'application/json' Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } @@ -31,14 +34,16 @@ function Set-CIPPAuthenticationPolicy { # Get current state and disable $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/fido2' -tenantid $TenantFilter $GraphRequest.state = $State - $body = ($GraphRequest | ConvertTo-Json -Depth 10) + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $GraphRequest $GraphRequest = New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/fido2' -Type patch -Body $body -ContentType 'application/json' Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } else { + } + else { # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } @@ -50,17 +55,37 @@ function Set-CIPPAuthenticationPolicy { if ($State -eq 'enabled') { try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $TenantFilter + $CurrentInfo.featureSettings.PSObject.Properties.Remove('numberMatchingRequiredState') + $CurrentInfo.featureSettings.displayAppInformationRequiredState.state = $State + $CurrentInfo.featureSettings.displayLocationInformationRequiredState.state = $State + # Enable MS authenticator OTP if called for + if ($null -ne $MicrosoftAuthenticatorSoftwareOathEnabled ) { $CurrentInfo.isSoftwareOathEnabled = $MicrosoftAuthenticatorSoftwareOathEnabled } + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -Type patch -Body $body -ContentType 'application/json') + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } elseif ($State -eq 'disabled') { + } + elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + # Get current state and disable + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $TenantFilter + $CurrentInfo.featureSettings.PSObject.Properties.Remove('numberMatchingRequiredState') + $CurrentInfo.state = $State + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -Type patch -Body $body -ContentType 'application/json') + + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Disabled $AuthenticationMethodId Support" -sev Info + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } else { + } + else { # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } @@ -72,16 +97,20 @@ function Set-CIPPAuthenticationPolicy { if ($State -eq 'enabled') { try { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } elseif ($State -eq 'disabled') { + } + elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } else { + } + else { # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } @@ -128,19 +157,29 @@ function Set-CIPPAuthenticationPolicy { # Convert to JSON and send the request $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -asApp $true -Body $body -ContentType 'application/json') + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -asApp $true -Body $body -ContentType 'application/json') Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } elseif ($State -eq 'disabled') { + } + elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + # Get current state and disable + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/TemporaryAccessPass' -tenantid $TenantFilter + $CurrentInfo.state = $State + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -Body $body -ContentType 'application/json') + + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } else { + } + else { # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } @@ -152,16 +191,20 @@ function Set-CIPPAuthenticationPolicy { if ($State -eq 'enabled') { try { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } elseif ($State -eq 'disabled') { + } + elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } else { + } + else { # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } @@ -171,17 +214,30 @@ function Set-CIPPAuthenticationPolicy { if ($State -eq 'enabled') { try { + $CurrentInfo = new-graphgetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $TenantFilter + $CurrentInfo.state = $State + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -Type patch -Body $body -ContentType 'application/json') + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } elseif ($State -eq 'disabled') { + } + elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + $CurrentInfo = new-graphgetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $TenantFilter + $CurrentInfo.state = $State + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -Type patch -Body $body -ContentType 'application/json') + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } else { + } + else { # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } @@ -192,16 +248,20 @@ function Set-CIPPAuthenticationPolicy { if ($State -eq 'enabled') { try { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } elseif ($State -eq 'disabled') { + } + elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } else { + } + else { # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } @@ -212,16 +272,20 @@ function Set-CIPPAuthenticationPolicy { if ($State -eq 'enabled') { try { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } elseif ($State -eq 'disabled') { + } + elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } else { + } + else { # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } @@ -232,16 +296,20 @@ function Set-CIPPAuthenticationPolicy { if ($State -eq 'enabled') { try { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } elseif ($State -eq 'disabled') { + } + elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } catch { + Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + } + catch { Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } else { + } + else { # Catch invalid input Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } From fb0588cf7b74a3ca34b6f58afed9ca131e2fee21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 8 Dec 2023 00:02:17 +0100 Subject: [PATCH 05/84] Progress maybe v3 --- .../Public/Set-CIPPAuthenticationPolicy.ps1 | 192 +++++++----------- 1 file changed, 79 insertions(+), 113 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index a8a5c9b019b1..514c11589046 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -5,8 +5,8 @@ function Set-CIPPAuthenticationPolicy { [Parameter(Mandatory = $true)]$AuthenticationMethodId, [Parameter(Mandatory = $true)][bool]$State, # true = enabled or false = disabled [bool]$MicrosoftAuthenticatorSoftwareOathEnabled, - $APIName = 'Set Authentication Policy', # Should this be 'Standards'? - $ExecutingUser + $APIName = 'Set Authentication Policy', + $ExecutingUser = 'None' ) # Convert bool input to usable string @@ -16,38 +16,36 @@ function Set-CIPPAuthenticationPolicy { # FIDO2 'FIDO2' { + # Set FIDO2 state + try { - if ($State -eq 'enabled') { - # Enable FIDO2 - try { - $body = '{"@odata.type":"#microsoft.graph.fido2AuthenticationMethodConfiguration","id":"Fido2","includeTargets":[{"id":"all_users","isRegistrationRequired":false,"targetType":"group","displayName":"All users"}],"excludeTargets":[],"isAttestationEnforced":true,"isSelfServiceRegistrationAllowed":true,"keyRestrictions":{"aaGuids":[],"enforcementType":"block","isEnforced":false},"state":"enabled"}' - New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } - # Disable FIDO2 - elseif ($State -eq 'disabled') { - try { - # Get current state and disable - $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/fido2' -tenantid $TenantFilter - $GraphRequest.state = $State - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $GraphRequest - $GraphRequest = New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/fido2' -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - - } - catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + $Fido2Body = [PSCustomObject]@{ + '@odata.type' = '#microsoft.graph.fido2AuthenticationMethodConfiguration' + id = 'Fido2' + includeTargets = @(@{ + id = 'all_users' + isRegistrationRequired = $false + targetType = 'group' + displayName = 'All users' + }) + + excludeTargets = @() + isAttestationEnforced = $true + isSelfServiceRegistrationAllowed = $true + keyRestrictions = @{ + aaGuids = @() + enforcementType = 'block' + isEnforced = $false + } + state = $State } + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $Fido2Body + New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Set $AuthenticationMethodId state to $State" -sev Info } - else { - # Catch invalid input - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + catch { + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } - } # Microsoft Authenticator @@ -64,10 +62,10 @@ function Set-CIPPAuthenticationPolicy { $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -Type patch -Body $body -ContentType 'application/json') - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } elseif ($State -eq 'disabled') { @@ -79,15 +77,15 @@ function Set-CIPPAuthenticationPolicy { $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -Type patch -Body $body -ContentType 'application/json') - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Disabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Disabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } else { # Catch invalid input - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } @@ -95,25 +93,20 @@ function Set-CIPPAuthenticationPolicy { 'SMS' { if ($State -eq 'enabled') { - try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error } elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/SMS' -tenantid $TenantFilter + $CurrentInfo.state = $State + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/SMS' -Type patch -Body $body -ContentType 'application/json') + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } - else { - # Catch invalid input - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } } # Temporary Access Pass 'TemporaryAccessPass' { @@ -159,10 +152,10 @@ function Set-CIPPAuthenticationPolicy { $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -asApp $true -Body $body -ContentType 'application/json') - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } elseif ($State -eq 'disabled') { @@ -173,15 +166,15 @@ function Set-CIPPAuthenticationPolicy { $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -Body $body -ContentType 'application/json') - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } else { # Catch invalid input - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } @@ -190,133 +183,106 @@ function Set-CIPPAuthenticationPolicy { if ($State -eq 'enabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } else { # Catch invalid input - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } # Third-party software OATH tokens 'softwareOath' { - - if ($State -eq 'enabled') { - try { - $CurrentInfo = new-graphgetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $TenantFilter - $CurrentInfo.state = $State - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -Type patch -Body $body -ContentType 'application/json') - - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + # Get current state and set state + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $TenantFilter + $CurrentInfo.state = $State + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -Type patch -Body $body -ContentType 'application/json') + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Set $AuthenticationMethodId state to $State" -sev Info } - elseif ($State -eq 'disabled') { - try { - $CurrentInfo = new-graphgetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $TenantFilter - $CurrentInfo.state = $State - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -Type patch -Body $body -ContentType 'application/json') - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } - else { - # Catch invalid input - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + catch { + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } # Voice call 'Voice' { - + # Disallow enabling voice if ($State -eq 'enabled') { - try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error } - elseif ($State -eq 'disabled') { + else { + # Get current state and disable try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/Voice' -tenantid $TenantFilter + $CurrentInfo.state = $State + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/Voice' -Type patch -Body $body -ContentType 'application/json') + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } - else { - # Catch invalid input - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } } # Email OTP 'Email' { if ($State -eq 'enabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } - else { - # Catch invalid input - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } } # Certificate-based authentication 'x509Certificate' { if ($State -eq 'enabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } elseif ($State -eq 'disabled') { try { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info } catch { - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } else { # Catch invalid input - Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error } } Default { - Write-LogMessage -API $APIName -tenant $TenantFilter -message 'Somehow you hit the default case. You did something wrong' -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message 'Somehow you hit the default case. You did something wrong' -sev Error return 'Somehow you hit the default case. You did something wrong' } } From d7ff16af962b374afee26f1b776ef2a03c1b789c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 8 Dec 2023 00:07:57 +0100 Subject: [PATCH 06/84] More --- .../Public/Set-CIPPAuthenticationPolicy.ps1 | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index 514c11589046..f57929ffec8a 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -5,6 +5,10 @@ function Set-CIPPAuthenticationPolicy { [Parameter(Mandatory = $true)]$AuthenticationMethodId, [Parameter(Mandatory = $true)][bool]$State, # true = enabled or false = disabled [bool]$MicrosoftAuthenticatorSoftwareOathEnabled, + $TAPMinimumLifetime = '60', #Minutes + $TAPMaximumLifetime = '480', #minutes + $TAPDefaultLifeTime = '60', #minutes + $TAPDefaultLength = '8', #TAP password generated length in chars $APIName = 'Set Authentication Policy', $ExecutingUser = 'None' ) @@ -95,7 +99,7 @@ function Set-CIPPAuthenticationPolicy { if ($State -eq 'enabled') { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error } - elseif ($State -eq 'disabled') { + else { try { $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/SMS' -tenantid $TenantFilter $CurrentInfo.state = $State @@ -120,13 +124,7 @@ function Set-CIPPAuthenticationPolicy { } if (!$TAPConfig) { $TAPConfig = 'true' } - try { - # Vaiable values - $MinimumLifetime = '60' #Minutes - $MaximumLifetime = '480' #minutes - $DefaultLifeTime = '60' #minutes - $DefaultLength = '8' - + try { # Build the body of the request $CurrentInfo = [PSCustomObject]@{ '@odata.type' = '#microsoft.graph.temporaryAccessPassAuthenticationMethodConfiguration' @@ -139,11 +137,11 @@ function Set-CIPPAuthenticationPolicy { displayName = 'All users' } ) - defaultLength = $DefaultLength - defaultLifetimeInMinutes = $DefaultLifeTime + defaultLength = $TAPDefaultLength + defaultLifetimeInMinutes = $TAPDefaultLifeTime isUsableOnce = $TAPConfig - maximumLifetimeInMinutes = $MaximumLifetime - minimumLifetimeInMinutes = $MinimumLifetime + maximumLifetimeInMinutes = $TAPMaximumLifetime + minimumLifetimeInMinutes = $TAPMinimumLifetime state = $State } From d67f52a2d72c0dc3a8c88f822cdcff1d6a423804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 9 Dec 2023 23:06:02 +0100 Subject: [PATCH 07/84] Add id as a return value for the found users --- .../Invoke-ListSharedMailboxAccountEnabled.ps1 | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxAccountEnabled.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxAccountEnabled.ps1 index f40095596f18..e8debd027424 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxAccountEnabled.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxAccountEnabled.ps1 @@ -20,22 +20,23 @@ Function Invoke-ListSharedMailboxAccountEnabled { # Get Shared Mailbox Stuff try { $SharedMailboxList = (New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($TenantFilter)/Mailbox?`$filter=RecipientTypeDetails eq 'SharedMailbox'" -Tenantid $TenantFilter -scope ExchangeOnline) - $AllUsersAccountState = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?select=userPrincipalName,accountEnabled,displayName,givenName,surname' -tenantid $Tenantfilter + $AllUsersAccountState = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?select=id,userPrincipalName,accountEnabled,displayName,givenName,surname' -tenantid $Tenantfilter $EnabledUsersWithSharedMailbox = foreach ($SharedMailbox in $SharedMailboxList) { # Match the User - $User = $AllUsersAccountState | Where-Object { $_.userPrincipalName -eq $SharedMailbox.userPrincipalName } | Select-Object -Property userPrincipalName, accountEnabled, displayName, givenName, surname -First 1 + $User = $AllUsersAccountState | Where-Object { $_.userPrincipalName -eq $SharedMailbox.userPrincipalName } | Select-Object -Property id, userPrincipalName, accountEnabled, displayName, givenName, surname -First 1 if ($User.accountEnabled) { $User | Select-Object ` @{Name = 'UserPrincipalName'; Expression = { $User.UserPrincipalName } }, ` @{Name = 'displayName'; Expression = { $User.displayName } }, @{Name = 'givenName'; Expression = { $User.givenName } }, @{Name = 'surname'; Expression = { $User.surname } }, - @{Name = 'accountEnabled'; Expression = { $User.accountEnabled } } - + @{Name = 'accountEnabled'; Expression = { $User.accountEnabled } }, + @{Name = 'id'; Expression = { $User.id } } } } - } catch { - Write-LogMessage -API 'Tenant' -tenant $tenantfilter -message "Shared Mailbox Enabled Accounts on $($tenantfilter). Error: $($_.exception.message)" -sev 'Error' + } + catch { + Write-LogMessage -API $APIName -tenant $tenantfilter -message "Shared Mailbox Enabled Accounts on $($tenantfilter). Error: $($_.exception.message)" -sev 'Error' } $GraphRequest = $EnabledUsersWithSharedMailbox From 245673d97df55b7841ff36f5f10c95498b2afa95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 13 Dec 2023 00:20:12 +0100 Subject: [PATCH 08/84] More general for all auth types --- .../Public/Set-CIPPAuthenticationPolicy.ps1 | 307 ++++-------------- 1 file changed, 72 insertions(+), 235 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index f57929ffec8a..a57de3ecbf31 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -5,10 +5,11 @@ function Set-CIPPAuthenticationPolicy { [Parameter(Mandatory = $true)]$AuthenticationMethodId, [Parameter(Mandatory = $true)][bool]$State, # true = enabled or false = disabled [bool]$MicrosoftAuthenticatorSoftwareOathEnabled, - $TAPMinimumLifetime = '60', #Minutes - $TAPMaximumLifetime = '480', #minutes - $TAPDefaultLifeTime = '60', #minutes - $TAPDefaultLength = '8', #TAP password generated length in chars + $TAPMinimumLifetime = 60, #Minutes + $TAPMaximumLifetime = 480, #minutes + $TAPDefaultLifeTime = 60, #minutes + $TAPDefaultLength = 8, #TAP password generated length in chars + [bool]$TAPisUsableOnce = $true, $APIName = 'Set Authentication Policy', $ExecutingUser = 'None' ) @@ -16,283 +17,119 @@ function Set-CIPPAuthenticationPolicy { # Convert bool input to usable string $State = if ($State) { 'enabled' } else { 'disabled' } + # Get current state of the called authentication method and Set state of authentication method to input state + try { + $CurrentInfo = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/$AuthenticationMethodId" -tenantid $Tenant + $CurrentInfo.state = $State + } + catch { + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Could not get CurrentInfo for $AuthenticationMethodId. Error:$($_.exception.message)" -sev Error + } + switch ($AuthenticationMethodId) { - + # FIDO2 'FIDO2' { - # Set FIDO2 state - try { - - $Fido2Body = [PSCustomObject]@{ - '@odata.type' = '#microsoft.graph.fido2AuthenticationMethodConfiguration' - id = 'Fido2' - includeTargets = @(@{ - id = 'all_users' - isRegistrationRequired = $false - targetType = 'group' - displayName = 'All users' - }) + # Craft the body for FIDO2 + $CurrentInfo = [PSCustomObject]@{ + '@odata.type' = '#microsoft.graph.fido2AuthenticationMethodConfiguration' + id = 'Fido2' + includeTargets = @(@{ + id = 'all_users' + isRegistrationRequired = $false + targetType = 'group' + displayName = 'All users' + }) - excludeTargets = @() - isAttestationEnforced = $true - isSelfServiceRegistrationAllowed = $true - keyRestrictions = @{ - aaGuids = @() - enforcementType = 'block' - isEnforced = $false - } - state = $State + excludeTargets = @() + isAttestationEnforced = $true + isSelfServiceRegistrationAllowed = $true + keyRestrictions = @{ + aaGuids = @() + enforcementType = 'block' + isEnforced = $false } - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $Fido2Body - New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Set $AuthenticationMethodId state to $State" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + state = $State } } # Microsoft Authenticator 'MicrosoftAuthenticator' { - + # Remove numberMatchingRequiredState property if it exists + $CurrentInfo.featureSettings.PSObject.Properties.Remove('numberMatchingRequiredState') + if ($State -eq 'enabled') { - try { - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $TenantFilter - $CurrentInfo.featureSettings.PSObject.Properties.Remove('numberMatchingRequiredState') - $CurrentInfo.featureSettings.displayAppInformationRequiredState.state = $State - $CurrentInfo.featureSettings.displayLocationInformationRequiredState.state = $State - # Enable MS authenticator OTP if called for - if ($null -ne $MicrosoftAuthenticatorSoftwareOathEnabled ) { $CurrentInfo.isSoftwareOathEnabled = $MicrosoftAuthenticatorSoftwareOathEnabled } - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -Type patch -Body $body -ContentType 'application/json') - - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + $CurrentInfo.featureSettings.displayAppInformationRequiredState.state = $State + $CurrentInfo.featureSettings.displayLocationInformationRequiredState.state = $State + # Set MS authenticator OTP state if parameter is passed in + if ($null -ne $MicrosoftAuthenticatorSoftwareOathEnabled ) { $CurrentInfo.isSoftwareOathEnabled = $MicrosoftAuthenticatorSoftwareOathEnabled } } - elseif ($State -eq 'disabled') { - try { - # Get current state and disable - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $TenantFilter - $CurrentInfo.featureSettings.PSObject.Properties.Remove('numberMatchingRequiredState') - $CurrentInfo.state = $State - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -Type patch -Body $body -ContentType 'application/json') - - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Disabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } - else { - # Catch invalid input - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } # SMS 'SMS' { - if ($State -eq 'enabled') { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error - } - else { - try { - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/SMS' -tenantid $TenantFilter - $CurrentInfo.state = $State - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/SMS' -Type patch -Body $body -ContentType 'application/json') - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + return "Setting $AuthenticationMethodId to enabled is not allowed" } } # Temporary Access Pass 'TemporaryAccessPass' { if ($State -eq 'enabled') { - # Get the TAP config from the standards table. If it's not there, use the default value of true - $ConfigTable = Get-CippTable -tablename 'standards' - $TAPConfig = ((Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'standards' and RowKey eq '$TenantFilter'").JSON | ConvertFrom-Json).Standards.TAP.config - if (!$TAPConfig) { - $TAPConfig = ((Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'standards' and RowKey eq 'AllTenants'").JSON | ConvertFrom-Json).Standards.TAP.config - } - if (!$TAPConfig) { $TAPConfig = 'true' } - - try { - # Build the body of the request - $CurrentInfo = [PSCustomObject]@{ - '@odata.type' = '#microsoft.graph.temporaryAccessPassAuthenticationMethodConfiguration' - id = 'TemporaryAccessPass' - includeTargets = @( - @{ - id = 'all_users' - isRegistrationRequired = $false - targetType = 'group' - displayName = 'All users' - } - ) - defaultLength = $TAPDefaultLength - defaultLifetimeInMinutes = $TAPDefaultLifeTime - isUsableOnce = $TAPConfig - maximumLifetimeInMinutes = $TAPMaximumLifetime - minimumLifetimeInMinutes = $TAPMinimumLifetime - state = $State - } - - - # Convert to JSON and send the request - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -asApp $true -Body $body -ContentType 'application/json') - - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + $CurrentInfo.isUsableOnce = $TAPisUsableOnce + $CurrentInfo.minimumLifetimeInMinutes = $TAPMinimumLifetime + $CurrentInfo.maximumLifetimeInMinutes = $TAPMaximumLifetime + $CurrentInfo.defaultLifetimeInMinutes = $TAPDefaultLifeTime + $CurrentInfo.defaultLength = $TAPDefaultLength } - elseif ($State -eq 'disabled') { - try { - # Get current state and disable - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/TemporaryAccessPass' -tenantid $TenantFilter - $CurrentInfo.state = $State - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -Body $body -ContentType 'application/json') - - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } - else { - # Catch invalid input - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } + } # Hardware OATH tokens (Preview) 'HardwareOATH' { - - if ($State -eq 'enabled') { - try { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } - elseif ($State -eq 'disabled') { - try { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } - else { - # Catch invalid input - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + # Nothing special to do here } # Third-party software OATH tokens 'softwareOath' { - # Get current state and set state - try { - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $TenantFilter - $CurrentInfo.state = $State - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -Type patch -Body $body -ContentType 'application/json') - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Set $AuthenticationMethodId state to $State" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + # Nothing special to do here } # Voice call - 'Voice' { + 'Voice' { # Disallow enabling voice if ($State -eq 'enabled') { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error - } - else { - # Get current state and disable - try { - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/Voice' -tenantid $TenantFilter - $CurrentInfo.state = $State - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - (New-GraphPostRequest -tenantid $TenantFilter -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/Voice' -Type patch -Body $body -ContentType 'application/json') - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + return "Setting $AuthenticationMethodId to enabled is not allowed" } } + # Email OTP 'Email' { - if ($State -eq 'enabled') { - try { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } - elseif ($State -eq 'disabled') { - try { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error + return "Setting $AuthenticationMethodId to enabled is not allowed" } } # Certificate-based authentication 'x509Certificate' { - - if ($State -eq 'enabled') { - try { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Enabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } - elseif ($State -eq 'disabled') { - try { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Diabled $AuthenticationMethodId Support" -sev Info - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - } - else { - # Catch invalid input - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - } - + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "$AuthenticationMethodId is not yet supported in CIPP" -sev Error + return "$AuthenticationMethodId is not yet supported in CIPP" } Default { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message 'Somehow you hit the default case. You did something wrong' -sev Error - return 'Somehow you hit the default case. You did something wrong' + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message 'Somehow you hit the default case. You probably made a type in the input for AuthenticationMethodId. It''s case sensitive' -sev Error + return 'Somehow you hit the default case. You probably made a type in the input for AuthenticationMethodId. It''s case sensitive.' } - } - - - - - - - - - - + # Set state of the authentication method + try { + # Convert body to JSON and send request + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + New-GraphPostRequest -tenantid $TenantFilter -Uri "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/$AuthenticationMethodId" -Type patch -Body $body -ContentType 'application/json' + + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Set $AuthenticationMethodId state to $State" -sev Info + return "Set $AuthenticationMethodId state to $State" + } + catch { + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + return "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" + } + } } \ No newline at end of file From 519960794cbd67c24671e4aedb00a4e3701f5cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 13 Dec 2023 00:26:32 +0100 Subject: [PATCH 09/84] Spacing --- Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index a57de3ecbf31..d7da18e64d98 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -65,6 +65,7 @@ function Set-CIPPAuthenticationPolicy { if ($null -ne $MicrosoftAuthenticatorSoftwareOathEnabled ) { $CurrentInfo.isSoftwareOathEnabled = $MicrosoftAuthenticatorSoftwareOathEnabled } } } + # SMS 'SMS' { if ($State -eq 'enabled') { @@ -72,6 +73,7 @@ function Set-CIPPAuthenticationPolicy { return "Setting $AuthenticationMethodId to enabled is not allowed" } } + # Temporary Access Pass 'TemporaryAccessPass' { @@ -88,10 +90,12 @@ function Set-CIPPAuthenticationPolicy { 'HardwareOATH' { # Nothing special to do here } + # Third-party software OATH tokens 'softwareOath' { # Nothing special to do here } + # Voice call 'Voice' { # Disallow enabling voice @@ -108,6 +112,7 @@ function Set-CIPPAuthenticationPolicy { return "Setting $AuthenticationMethodId to enabled is not allowed" } } + # Certificate-based authentication 'x509Certificate' { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "$AuthenticationMethodId is not yet supported in CIPP" -sev Error From cae62d989116b5cb5b61cc0547274ef7505981fa Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 13 Dec 2023 15:01:13 -0500 Subject: [PATCH 10/84] Create Push-ExecOnboardTenantQueue.ps1 --- .../Push-ExecOnboardTenantQueue.ps1 | 251 ++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 new file mode 100644 index 000000000000..57c65d70cd8a --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 @@ -0,0 +1,251 @@ +Function Push-ExecOnboardTenantQueue { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($QueueItem, $TriggerMetadata) + try { + $Id = $QueueItem.id + Write-Host ($QueueItem.Roles | ConvertTo-Json) + $Logs = [System.Collections.Generic.List[string]]::new() + $OnboardTable = Get-CIPPTable -TableName 'TenantOnboarding' + $TenantOnboarding = Get-CIPPAzDataTableEntity @OnboardTable -Filter "RowKey eq '$Id'" + + $Logs.Add('Starting onboarding') + $OnboardingSteps = $TenantOnboarding.OnboardingSteps | ConvertFrom-Json + $OnboardingSteps.Step1.Status = 'running' + $OnboardingSteps.Step1.Message = 'Checking GDAP invite status' + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Status = 'running' + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + + try { + $Relationship = $TenantOnboarding.Relationship | ConvertFrom-Json -ErrorAction Stop + } catch { + $Relationship = '' + } + + $ExpectedRoles = @( + @{ Name = 'Application Administrator'; Id = '9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3' }, + @{ Name = 'User Administrator'; Id = 'fe930be7-5e62-47db-91af-98c3a49a38b1' }, + @{ Name = 'Intune Administrator'; Id = '3a2c62db-5318-420d-8d74-23affee5d9d5' }, + @{ Name = 'Exchange Administrator'; Id = '29232cdf-9323-42fd-ade2-1d097af3e4de' }, + @{ Name = 'Security Administrator'; Id = '194ae4cb-b126-40b2-bd5b-6091b380977d' }, + @{ Name = 'Cloud App Security Administrator'; Id = '892c5842-a9a6-463a-8041-72aa08ca3cf6' }, + @{ Name = 'Cloud Device Administrator'; Id = '7698a772-787b-4ac8-901f-60d6b08affd2' }, + @{ Name = 'Teams Administrator'; Id = '69091246-20e8-4a56-aa4d-066075b2a7a8' }, + @{ Name = 'Sharepoint Administrator'; Id = 'f28a1f50-f6e7-4571-818b-6a12f2af6b6c' }, + @{ Name = 'Authentication Policy Administrator'; Id = '0526716b-113d-4c15-b2c8-68e3c22b9f80' }, + @{ Name = 'Privileged Role Administrator'; Id = 'e8611ab8-c189-46e8-94e1-60213ab1f814' }, + @{ Name = 'Privileged Authentication Administrator'; Id = '7be44c8a-adaf-4e2a-84d6-ab2649e08a13' } + ) + + if ($OnboardingSteps.Step1.Status -ne 'succeeded') { + $Logs.Add('Checking relationship status') + $x = 0 + do { + $Relationship = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id" + $x++ + Start-Sleep -Seconds 30 + } while ($Relationship.status -ne 'active' -and $x -lt 4) + + if ($Relationship.status -eq 'active') { + $Logs.Add('GDAP Invite Accepted') + $OnboardingSteps.Step1.Status = 'succeeded' + $OnboardingSteps.Step1.Message = "GDAP Invite accepted for $($Relationship.customer.displayName)" + $TenantOnboarding.CustomerId = $Relationship.customer.tenantId + } else { + $Logs.Add('GDAP Invite Failed') + $OnboardingSteps.Step1.Status = 'failed' + $OnboardingSteps.Step1.Message = 'GDAP Invite timeout, retry onboarding after accepting the invite with a GA account in the customer tenant.' + $TenantOnboarding.Status = 'failed' + } + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Relationship = [string](ConvertTo-Json -InputObject $Relationship -Compress -Depth 10) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + } + + if ($OnboardingSteps.Step1.Status -eq 'succeeded') { + $Logs.Add('Starting role check') + $OnboardingSteps.Step2.Status = 'running' + $OnboardingSteps.Step2.Message = 'Checking role mapping' + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + + $MissingRoles = [System.Collections.Generic.List[string]]::new() + foreach ($Role in $ExpectedRoles) { + $RoleFound = $false + foreach ($AvailableRole in $Relationship.accessDetails.unifiedRoles) { + if ($AvailableRole.roleDefinitionId -eq $Role.Id) { + $RoleFound = $true + break + } + } + if (!$RoleFound) { + $MissingRoles.Add($Role.Name) + } + } + if (($MissingRoles | Measure-Object).Count -gt 0) { + $Logs.Add('Missing roles for relationship') + $TenantOnboarding.Status = 'failed' + $OnboardingSteps.Step2.Status = 'failed' + $OnboardingSteps.Step2.Message = "Your GDAP relationship is missing the following roles: $($MissingRoles -join ', ')" + } else { + $Logs.Add('Required roles found') + $OnboardingSteps.Step2.Status = 'succeeded' + $OnboardingSteps.Step2.Message = 'Your GDAP relationship has the required roles' + } + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + } + + if ($OnboardingSteps.Step2.Status -eq 'succeeded') { + $Logs.Add('Checking group mapping') + $AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id/accessAssignments" + if ($AccessAssignments.id) { + $Logs.Add('Groups mapped') + $OnboardingSteps.Step3.Status = 'succeeded' + $OnboardingSteps.Step3.Message = 'Your GDAP relationship has mapped security groups' + } else { + $Logs.Add('Starting group mapping') + $OnboardingSteps.Step3.Status = 'running' + $OnboardingSteps.Step3.Message = 'Mapping security groups' + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + + $Table = Get-CIPPTable -TableName 'GDAPInvites' + $Invite = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$Id'" + + if (!$Invite -and $QueueItem.Roles) { + $MatchingRoles = [System.Collections.Generic.List[object]]::new() + foreach ($Role in $QueueItem.Roles) { + Write-Host "##### Checking role $($Role.RoleName)" + if ($Relationship.accessDetails.unifiedRoles.roleDefinitionId -contains $Role.roleDefinitionId) { + $MatchingRoles.Add([PSCustomObject]$Role) + Write-Host '##### Found' + } + } + + if (($MatchingRoles | Measure-Object).Count -gt 0) { + $InviteTable = Get-CIPPTable -TableName 'GDAPInvites' + $Invite = [PSCustomObject]@{ + 'PartitionKey' = 'invite' + 'RowKey' = $Id + 'InviteUrl' = 'https://admin.microsoft.com/AdminPortal/Home#/partners/invitation/granularAdminRelationships/{0}' -f $Id + 'RoleMappings' = [string](@($MatchingRoles) | ConvertTo-Json -Depth 10 -Compress) + } + Add-CIPPAzDataTableEntity @InviteTable -Entity $Invite + } else { + $TenantOnboarding.Status = 'failed' + $OnboardingSteps.Step3.Status = 'failed' + $OnboardingSteps.Step3.Message = 'No matching roles found, check the relationship and try again.' + } + } + + if ($Invite) { + $GroupMapStatus = Set-CIPPGDAPInviteGroups -Relationship $Relationship + if ($GroupMapStatus) { + $Logs.Add('Groups mapped successfully') + $OnboardingSteps.Step3.Status = 'succeeded' + $OnboardingSteps.Step3.Message = 'Groups mapped successfully' + } else { + $Logs.Add('Group mapping failed') + $TenantOnboarding.Status = 'failed' + $OnboardingSteps.Step3.Status = 'failed' + $OnboardingSteps.Step3.Message = 'Group mapping failed, check the log book for details.' + } + } else { + $TenantOnboarding.Status = 'failed' + $OnboardingSteps.Step3.Status = 'failed' + $OnboardingSteps.Step3.Message = 'Failed to map security groups, no pending invite available' + } + + } + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + } + + if ($OnboardingSteps.Step3.Status -eq 'succeeded') { + $Logs.Add('Refreshing CPV permissions') + $OnboardingSteps.Step4.Status = 'running' + $OnboardingSteps.Step4.Message = 'Refreshing CPV permissions' + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + + try { + Remove-CIPPCache -tenantsOnly $true + } catch {} + + $Tenant = Get-Tenants | Where-Object { $_.customerId -eq $Relationship.customer.tenantId } + if ($Tenant) { + $y = 0 + $Refreshing = $true + $CPVSuccess = $false + do { + try { + Add-CIPPApplicationPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Tenant.defaultDomainName + Add-CIPPDelegatedPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Tenant.defaultDomainName + $CPVSuccess = $true + $Refreshing = $false + } catch { + $y++ + Start-Sleep -Seconds 30 + } + } while ($Refreshing -and $y -lt 4) + + if ($CPVSuccess) { + $Logs.Add('CPV permissions refreshed') + $OnboardingSteps.Step4.Status = 'succeeded' + $OnboardingSteps.Step4.Message = 'CPV permissions refreshed' + } else { + $Logs.Add('CPV permissions failed to refresh') + $TenantOnboarding.Status = 'failed' + $OnboardingSteps.Step4.Status = 'failed' + $OnboardingSteps.Step4.Message = 'CPV permissions failed to refresh, try again later' + } + } else { + $Logs.Add('Tenant not found') + $TenantOnboarding.Status = 'failed' + $OnboardingSteps.Step4.Status = 'failed' + $OnboardingSteps.Step4.Message = 'Tenant not found in customer list, try again later' + } + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + } + + if ($OnboardingSteps.Step4.Status -eq 'succeeded') { + $OnboardingSteps.Step5.Status = 'running' + $OnboardingSteps.Step5.Message = 'Testing API Access' + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + + try { + $UserCount = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$count=true&`$top=1" -ComplexFilter -tenantfilter $Tenant.defaultDomainName -CountOnly + } catch { + $UserCount = 0 + $ApiError = $_.Exception.Message + } + + if ($UserCount -gt 0) { + $OnboardingSteps.Step5.Status = 'succeeded' + $OnboardingSteps.Step5.Message = 'API Test Successful: {0} users found' -f $UserCount + $TenantOnboarding.Status = 'succeeded' + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + } else { + $OnboardingSteps.Step5.Status = 'failed' + $OnboardingSteps.Step5.Message = 'API Test failed: {0}' -f $ApiError + $TenantOnboarding.Status = 'succeeded' + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + } + } + } catch { + $TenantOnboarding.Status = 'failed' + $TenantOnboarding.Exception = [string]('{0} - Line {1} - {2}' -f $_.Exception.Message, $_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.ScriptName) + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + } +} From 5a4f1751f4324187b7f88df3821aa6cd13952a6a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Dec 2023 22:06:51 -0500 Subject: [PATCH 11/84] Tenant Onboarding Logging update Add missing group support --- .../Entrypoints/Invoke-ExecOnboardTenant.ps1 | 8 ++- .../Push-ExecOnboardTenantQueue.ps1 | 72 +++++++++++++------ 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 index c67be5c4de8f..e9e23c2a174e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 @@ -53,9 +53,11 @@ function Invoke-ExecOnboardTenant { Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop Push-OutputBinding -Name QueueItem -Value ([pscustomobject]@{ - FunctionName = 'ExecOnboardTenantQueue' - id = $Id - Roles = $Request.Body.gdapRoles + FunctionName = 'ExecOnboardTenantQueue' + id = $Id + Roles = $Request.Body.gdapRoles + AddMissingGroups = $Request.Body.addMissingGroups + AutoMapRoles = $Request.Body.autoMapRoles }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 index 57c65d70cd8a..00e24af25b17 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 @@ -6,13 +6,14 @@ Function Push-ExecOnboardTenantQueue { [CmdletBinding()] param($QueueItem, $TriggerMetadata) try { + $DateFormat = '%Y-%m-%d %H:%M:%S' $Id = $QueueItem.id Write-Host ($QueueItem.Roles | ConvertTo-Json) - $Logs = [System.Collections.Generic.List[string]]::new() + $Logs = [System.Collections.Generic.List[object]]::new() $OnboardTable = Get-CIPPTable -TableName 'TenantOnboarding' $TenantOnboarding = Get-CIPPAzDataTableEntity @OnboardTable -Filter "RowKey eq '$Id'" - $Logs.Add('Starting onboarding') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Starting onboarding' }) $OnboardingSteps = $TenantOnboarding.OnboardingSteps | ConvertFrom-Json $OnboardingSteps.Step1.Status = 'running' $OnboardingSteps.Step1.Message = 'Checking GDAP invite status' @@ -42,7 +43,7 @@ Function Push-ExecOnboardTenantQueue { ) if ($OnboardingSteps.Step1.Status -ne 'succeeded') { - $Logs.Add('Checking relationship status') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Checking relationship status' }) $x = 0 do { $Relationship = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id" @@ -51,12 +52,12 @@ Function Push-ExecOnboardTenantQueue { } while ($Relationship.status -ne 'active' -and $x -lt 4) if ($Relationship.status -eq 'active') { - $Logs.Add('GDAP Invite Accepted') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'GDAP Invite Accepted' }) $OnboardingSteps.Step1.Status = 'succeeded' $OnboardingSteps.Step1.Message = "GDAP Invite accepted for $($Relationship.customer.displayName)" $TenantOnboarding.CustomerId = $Relationship.customer.tenantId } else { - $Logs.Add('GDAP Invite Failed') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'GDAP Invite Failed' }) $OnboardingSteps.Step1.Status = 'failed' $OnboardingSteps.Step1.Message = 'GDAP Invite timeout, retry onboarding after accepting the invite with a GA account in the customer tenant.' $TenantOnboarding.Status = 'failed' @@ -67,7 +68,7 @@ Function Push-ExecOnboardTenantQueue { } if ($OnboardingSteps.Step1.Status -eq 'succeeded') { - $Logs.Add('Starting role check') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Starting role check' }) $OnboardingSteps.Step2.Status = 'running' $OnboardingSteps.Step2.Message = 'Checking role mapping' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) @@ -87,31 +88,33 @@ Function Push-ExecOnboardTenantQueue { } } if (($MissingRoles | Measure-Object).Count -gt 0) { - $Logs.Add('Missing roles for relationship') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Missing roles for relationship' }) $TenantOnboarding.Status = 'failed' $OnboardingSteps.Step2.Status = 'failed' $OnboardingSteps.Step2.Message = "Your GDAP relationship is missing the following roles: $($MissingRoles -join ', ')" } else { - $Logs.Add('Required roles found') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Required roles found' }) $OnboardingSteps.Step2.Status = 'succeeded' $OnboardingSteps.Step2.Message = 'Your GDAP relationship has the required roles' } $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } if ($OnboardingSteps.Step2.Status -eq 'succeeded') { - $Logs.Add('Checking group mapping') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Checking group mapping' }) $AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id/accessAssignments" if ($AccessAssignments.id) { - $Logs.Add('Groups mapped') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Groups mapped' }) $OnboardingSteps.Step3.Status = 'succeeded' $OnboardingSteps.Step3.Message = 'Your GDAP relationship has mapped security groups' } else { - $Logs.Add('Starting group mapping') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Starting group mapping' }) $OnboardingSteps.Step3.Status = 'running' $OnboardingSteps.Step3.Message = 'Mapping security groups' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop $Table = Get-CIPPTable -TableName 'GDAPInvites' @@ -120,14 +123,13 @@ Function Push-ExecOnboardTenantQueue { if (!$Invite -and $QueueItem.Roles) { $MatchingRoles = [System.Collections.Generic.List[object]]::new() foreach ($Role in $QueueItem.Roles) { - Write-Host "##### Checking role $($Role.RoleName)" if ($Relationship.accessDetails.unifiedRoles.roleDefinitionId -contains $Role.roleDefinitionId) { $MatchingRoles.Add([PSCustomObject]$Role) - Write-Host '##### Found' + } } - if (($MatchingRoles | Measure-Object).Count -gt 0) { + if (($MatchingRoles | Measure-Object).Count -gt 0 -and $QueueItem.AutoMapRoles -eq $true) { $InviteTable = Get-CIPPTable -TableName 'GDAPInvites' $Invite = [PSCustomObject]@{ 'PartitionKey' = 'invite' @@ -146,11 +148,11 @@ Function Push-ExecOnboardTenantQueue { if ($Invite) { $GroupMapStatus = Set-CIPPGDAPInviteGroups -Relationship $Relationship if ($GroupMapStatus) { - $Logs.Add('Groups mapped successfully') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Groups mapped successfully' }) $OnboardingSteps.Step3.Status = 'succeeded' $OnboardingSteps.Step3.Message = 'Groups mapped successfully' } else { - $Logs.Add('Group mapping failed') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Group mapping failed' }) $TenantOnboarding.Status = 'failed' $OnboardingSteps.Step3.Status = 'failed' $OnboardingSteps.Step3.Message = 'Group mapping failed, check the log book for details.' @@ -162,15 +164,35 @@ Function Push-ExecOnboardTenantQueue { } } + if ($QueueItem.AddMissingGroups -eq $true) { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Checking for missing groups' }) + $SamUserId = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/me?`$select=id").id + $CurrentMemberships = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/me/transitiveMemberOf?`$select=id,displayName" + foreach ($Role in $QueueItem.Roles) { + if ($CurrentMemberships.id -notcontains $Role.GroupId) { + $PostBody = @{ + '@odata.id' = 'https://graph.microsoft.com/v1.0/directoryObjects/{0}' -f $SamUserId + } | ConvertTo-Json -Compress + try { + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($Role.GroupId)/members/`$ref" -body $PostBody -AsApp $true -NoAuthCheck $true + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = "Added SAM user to $($Role.GroupName)" }) + } catch { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = "Failed to add SAM user to $($Role.GroupName) - $($_.Exception.Message)" }) + } + } + } + } $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } if ($OnboardingSteps.Step3.Status -eq 'succeeded') { - $Logs.Add('Refreshing CPV permissions') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Refreshing CPV permissions' }) $OnboardingSteps.Step4.Status = 'running' $OnboardingSteps.Step4.Message = 'Refreshing CPV permissions' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop try { @@ -195,29 +217,32 @@ Function Push-ExecOnboardTenantQueue { } while ($Refreshing -and $y -lt 4) if ($CPVSuccess) { - $Logs.Add('CPV permissions refreshed') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'CPV permissions refreshed' }) $OnboardingSteps.Step4.Status = 'succeeded' $OnboardingSteps.Step4.Message = 'CPV permissions refreshed' } else { - $Logs.Add('CPV permissions failed to refresh') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'CPV permissions failed to refresh' }) $TenantOnboarding.Status = 'failed' $OnboardingSteps.Step4.Status = 'failed' $OnboardingSteps.Step4.Message = 'CPV permissions failed to refresh, try again later' } } else { - $Logs.Add('Tenant not found') + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Tenant not found' }) $TenantOnboarding.Status = 'failed' $OnboardingSteps.Step4.Status = 'failed' $OnboardingSteps.Step4.Message = 'Tenant not found in customer list, try again later' } $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } if ($OnboardingSteps.Step4.Status -eq 'succeeded') { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Testing API access' }) $OnboardingSteps.Step5.Status = 'running' - $OnboardingSteps.Step5.Message = 'Testing API Access' + $OnboardingSteps.Step5.Message = 'Testing API access' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop try { @@ -228,20 +253,25 @@ Function Push-ExecOnboardTenantQueue { } if ($UserCount -gt 0) { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'API test successful. Onboarding complete.' }) $OnboardingSteps.Step5.Status = 'succeeded' $OnboardingSteps.Step5.Message = 'API Test Successful: {0} users found' -f $UserCount $TenantOnboarding.Status = 'succeeded' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } else { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'API Test failed: {0}' -f $ApiError }) $OnboardingSteps.Step5.Status = 'failed' $OnboardingSteps.Step5.Message = 'API Test failed: {0}' -f $ApiError $TenantOnboarding.Status = 'succeeded' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } } } catch { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Onboarding failed. Exception: {0}' -f $_.Exception.Message }) $TenantOnboarding.Status = 'failed' $TenantOnboarding.Exception = [string]('{0} - Line {1} - {2}' -f $_.Exception.Message, $_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.ScriptName) $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) From d7177d4eb78eea62794d55d5e3f785a33ab1aee6 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 15 Dec 2023 12:43:58 -0500 Subject: [PATCH 12/84] Fix webhooks --- PublicWebhooks/run.ps1 | 10 +++++----- PublicWebhooksProcess/run.ps1 | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/PublicWebhooks/run.ps1 b/PublicWebhooks/run.ps1 index 232c44458572..ad13d0546ef7 100644 --- a/PublicWebhooks/run.ps1 +++ b/PublicWebhooks/run.ps1 @@ -6,22 +6,22 @@ param($Request, $TriggerMetadata) $WebhookTable = Get-CIPPTable -TableName webhookTable $Webhooks = Get-CIPPAzDataTableEntity @WebhookTable -Write-Host "Received request" +Write-Host 'Received request' Write-Host "CIPPID: $($request.Query.CIPPID)" $url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 Write-Host $url -if ($Request.CIPPID -in $Webhooks.CIPPID) { - Write-Host "Found matching CIPPID" +if ($Request.Query.CIPPID -in $Webhooks.RowKey) { + Write-Host 'Found matching CIPPID' if ($Request.query.ValidationToken -or $Request.body.validationCode) { - Write-Host "Validation token received" + Write-Host 'Validation token received' $body = $request.query.ValidationToken } else { Push-OutputBinding -Name QueueWebhook -Value $Request $Body = 'Webhook Recieved' } } else { - $body = "This webhook is not authorized." + $body = 'This webhook is not authorized.' } # Associate values to output bindings by calling 'Push-OutputBinding'. diff --git a/PublicWebhooksProcess/run.ps1 b/PublicWebhooksProcess/run.ps1 index 8c5f91227ceb..30e692a87559 100644 --- a/PublicWebhooksProcess/run.ps1 +++ b/PublicWebhooksProcess/run.ps1 @@ -7,15 +7,15 @@ $Request = $QueueItem $WebhookTable = Get-CIPPTable -TableName webhookTable $Webhooks = Get-AzDataTableEntity @WebhookTable -Write-Host "Received request" +Write-Host 'Received request' Write-Host "CIPPID: $($request.Query.CIPPID)" $url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 Write-Host $url if ($Request.query.CIPPID -in $Webhooks.RowKey) { - Write-Host "Found matching CIPPID" + Write-Host 'Found matching CIPPID' $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID - if ($Request.Query.Type = 'GraphSubscription') { + if ($Request.Query.Type -eq 'GraphSubscription') { # Graph Subscriptions [pscustomobject]$ReceivedItem = $Request.Body.value Invoke-CippGraphWebhookProcessing -Data $ReceivedItem -CIPPID $request.Query.CIPPID -WebhookInfo $Webhookinfo @@ -28,18 +28,18 @@ if ($Request.query.CIPPID -in $Webhooks.RowKey) { $ReceivedItem = [pscustomobject]$ReceivedItem $TenantFilter = (Get-Tenants | Where-Object -Property customerId -EQ $ReceivedItem.TenantId).defaultDomainName Write-Host "TenantFilter: $TenantFilter" - $Data = New-GraphPostRequest -type GET -uri "https://manage.office.com/api/v1.0/$($ReceivedItem.tenantId)/activity/feed/audit/$($ReceivedItem.contentid)" -tenantid $TenantFilter -scope "https://manage.office.com/.default" + $Data = New-GraphPostRequest -type GET -uri "https://manage.office.com/api/v1.0/$($ReceivedItem.tenantId)/activity/feed/audit/$($ReceivedItem.contentid)" -tenantid $TenantFilter -scope 'https://manage.office.com/.default' Write-Host "Data to process found: $(($ReceivedItem.operation).count) items" Write-Host "Operations to process for this client: $($Webhookinfo.Operations)" foreach ($Item in $Data) { Write-Host "Processing $($item.operation)" if ($item.operation -in $operations) { Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations - } - if ($item.operation -eq "UserLoggedIn" -and "UserLoggedInFromUnknownLocation" -in $operations) { + } + if ($item.operation -eq 'UserLoggedIn' -and 'UserLoggedInFromUnknownLocation' -in $operations) { Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations } - if ($item.operation -eq "UserLoggedIn" -and "AdminLoggedIn" -in $operations) { + if ($item.operation -eq 'UserLoggedIn' -and 'AdminLoggedIn' -in $operations) { Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations } } From c8ac9d254a38905d3465ce9e108a3065d608f352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 16 Dec 2023 22:29:54 +0100 Subject: [PATCH 13/84] Change nonworking parts to actually work --- .../Public/Set-CIPPAuthenticationPolicy.ps1 | 73 +++++++------------ 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index d7da18e64d98..67d7c02b008b 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -1,9 +1,9 @@ function Set-CIPPAuthenticationPolicy { [CmdletBinding()] param( - $TenantFilter, - [Parameter(Mandatory = $true)]$AuthenticationMethodId, - [Parameter(Mandatory = $true)][bool]$State, # true = enabled or false = disabled + [Parameter(Mandatory = $true)]$Tenant, + [Parameter(Mandatory = $true)][ValidateSet('FIDO2', 'MicrosoftAuthenticator', 'SMS', 'TemporaryAccessPass', 'HardwareOATH', 'softwareOath', 'Voice', 'Email', 'x509Certificate')]$AuthenticationMethodId, + [Parameter(Mandatory = $true)][bool]$Enable, # true = enabled or false = disabled [bool]$MicrosoftAuthenticatorSoftwareOathEnabled, $TAPMinimumLifetime = 60, #Minutes $TAPMaximumLifetime = 480, #minutes @@ -11,45 +11,27 @@ function Set-CIPPAuthenticationPolicy { $TAPDefaultLength = 8, #TAP password generated length in chars [bool]$TAPisUsableOnce = $true, $APIName = 'Set Authentication Policy', - $ExecutingUser = 'None' + $ExecutingUser ) # Convert bool input to usable string - $State = if ($State) { 'enabled' } else { 'disabled' } - + $State = if ($Enable) { 'enabled' } else { 'disabled' } # Get current state of the called authentication method and Set state of authentication method to input state try { $CurrentInfo = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/$AuthenticationMethodId" -tenantid $Tenant $CurrentInfo.state = $State } catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Could not get CurrentInfo for $AuthenticationMethodId. Error:$($_.exception.message)" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Could not get CurrentInfo for $AuthenticationMethodId. Error:$($_.exception.message)" -sev Error } switch ($AuthenticationMethodId) { # FIDO2 'FIDO2' { - # Craft the body for FIDO2 - $CurrentInfo = [PSCustomObject]@{ - '@odata.type' = '#microsoft.graph.fido2AuthenticationMethodConfiguration' - id = 'Fido2' - includeTargets = @(@{ - id = 'all_users' - isRegistrationRequired = $false - targetType = 'group' - displayName = 'All users' - }) - - excludeTargets = @() - isAttestationEnforced = $true - isSelfServiceRegistrationAllowed = $true - keyRestrictions = @{ - aaGuids = @() - enforcementType = 'block' - isEnforced = $false - } - state = $State + if ($State -eq 'enabled') { + $CurrentInfo.isAttestationEnforced = $true + $CurrentInfo.isSelfServiceRegistrationAllowed = $true } } @@ -69,7 +51,7 @@ function Set-CIPPAuthenticationPolicy { # SMS 'SMS' { if ($State -eq 'enabled') { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error return "Setting $AuthenticationMethodId to enabled is not allowed" } } @@ -100,7 +82,7 @@ function Set-CIPPAuthenticationPolicy { 'Voice' { # Disallow enabling voice if ($State -eq 'enabled') { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error return "Setting $AuthenticationMethodId to enabled is not allowed" } } @@ -108,33 +90,32 @@ function Set-CIPPAuthenticationPolicy { # Email OTP 'Email' { if ($State -eq 'enabled') { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Setting $AuthenticationMethodId to enabled is not allowed" -sev Error return "Setting $AuthenticationMethodId to enabled is not allowed" } } # Certificate-based authentication 'x509Certificate' { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "$AuthenticationMethodId is not yet supported in CIPP" -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "$AuthenticationMethodId is not yet supported in CIPP" -sev Error return "$AuthenticationMethodId is not yet supported in CIPP" } Default { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message 'Somehow you hit the default case. You probably made a type in the input for AuthenticationMethodId. It''s case sensitive' -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message 'Somehow you hit the default case. You probably made a type in the input for AuthenticationMethodId. It''s case sensitive' -sev Error return 'Somehow you hit the default case. You probably made a type in the input for AuthenticationMethodId. It''s case sensitive.' } - - # Set state of the authentication method - try { - # Convert body to JSON and send request - $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo - New-GraphPostRequest -tenantid $TenantFilter -Uri "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/$AuthenticationMethodId" -Type patch -Body $body -ContentType 'application/json' - - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Set $AuthenticationMethodId state to $State" -sev Info - return "Set $AuthenticationMethodId state to $State" - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error - return "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" - } + } + # Set state of the authentication method + try { + # Convert body to JSON and send request + $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo + New-GraphPostRequest -tenantid $Tenant -Uri "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/$AuthenticationMethodId" -Type patch -Body $body -ContentType 'application/json' + + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Set $AuthenticationMethodId state to $State" -sev Info + return "Set $AuthenticationMethodId state to $State" + } + catch { + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error + return "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" } } \ No newline at end of file From 5d7218824bf8640db23121f1b4e36f9dbffa596e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 16 Dec 2023 22:31:10 +0100 Subject: [PATCH 14/84] Tiny formatting error --- Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index 67d7c02b008b..fa6d5ef136e8 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -58,7 +58,6 @@ function Set-CIPPAuthenticationPolicy { # Temporary Access Pass 'TemporaryAccessPass' { - if ($State -eq 'enabled') { $CurrentInfo.isUsableOnce = $TAPisUsableOnce $CurrentInfo.minimumLifetimeInMinutes = $TAPMinimumLifetime From 9b0c1b987ff3a047779cfc6e0e4132dece5863d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 16 Dec 2023 22:40:04 +0100 Subject: [PATCH 15/84] Tiny logging error --- Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index fa6d5ef136e8..5b9b0bc79208 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -96,12 +96,12 @@ function Set-CIPPAuthenticationPolicy { # Certificate-based authentication 'x509Certificate' { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "$AuthenticationMethodId is not yet supported in CIPP" -sev Error - return "$AuthenticationMethodId is not yet supported in CIPP" + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Setting $AuthenticationMethodId is not yet supported in CIPP" -sev Error + return "Setting $AuthenticationMethodId is not yet supported in CIPP" } Default { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message 'Somehow you hit the default case. You probably made a type in the input for AuthenticationMethodId. It''s case sensitive' -sev Error - return 'Somehow you hit the default case. You probably made a type in the input for AuthenticationMethodId. It''s case sensitive.' + return 'Somehow you hit the default case. You probably made a typo in the input for AuthenticationMethodId. It''s case sensitive.' } } # Set state of the authentication method From 2ab97cd6483800e2ca2f4015a773625ebed0f23f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 16 Dec 2023 22:56:47 +0100 Subject: [PATCH 16/84] Another typo and add optional log message --- .../Public/Set-CIPPAuthenticationPolicy.ps1 | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index 5b9b0bc79208..4c4c6c872bff 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -44,7 +44,10 @@ function Set-CIPPAuthenticationPolicy { $CurrentInfo.featureSettings.displayAppInformationRequiredState.state = $State $CurrentInfo.featureSettings.displayLocationInformationRequiredState.state = $State # Set MS authenticator OTP state if parameter is passed in - if ($null -ne $MicrosoftAuthenticatorSoftwareOathEnabled ) { $CurrentInfo.isSoftwareOathEnabled = $MicrosoftAuthenticatorSoftwareOathEnabled } + if ($null -ne $MicrosoftAuthenticatorSoftwareOathEnabled ) { + $CurrentInfo.isSoftwareOathEnabled = $MicrosoftAuthenticatorSoftwareOathEnabled + $OptionalLogMessage = "and Microsoft Authenticator software OTP to $MicrosoftAuthenticatorSoftwareOathEnabled" + } } } @@ -100,7 +103,7 @@ function Set-CIPPAuthenticationPolicy { return "Setting $AuthenticationMethodId is not yet supported in CIPP" } Default { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message 'Somehow you hit the default case. You probably made a type in the input for AuthenticationMethodId. It''s case sensitive' -sev Error + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message 'Somehow you hit the default case. You probably made a typo in the input for AuthenticationMethodId. It''s case sensitive' -sev Error return 'Somehow you hit the default case. You probably made a typo in the input for AuthenticationMethodId. It''s case sensitive.' } } @@ -109,9 +112,10 @@ function Set-CIPPAuthenticationPolicy { # Convert body to JSON and send request $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo New-GraphPostRequest -tenantid $Tenant -Uri "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/$AuthenticationMethodId" -Type patch -Body $body -ContentType 'application/json' - - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Set $AuthenticationMethodId state to $State" -sev Info - return "Set $AuthenticationMethodId state to $State" + + + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Set $AuthenticationMethodId state to $State $OptionalLogMessage" -sev Info + return "Set $AuthenticationMethodId state to $State $OptionalLogMessage" } catch { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error From 34675fb982c2047a311ae9e215dc1036f859e806 Mon Sep 17 00:00:00 2001 From: Roel van der Wegen Date: Sun, 17 Dec 2023 20:54:04 +0100 Subject: [PATCH 17/84] I hate here-strings --- .../CIPPCore/Public/Set-CIPPCPVConsent.ps1 | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 index d4c06d0e904f..4391f3379817 100644 --- a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 @@ -22,12 +22,20 @@ function Set-CIPPCPVConsent { } try { - $AppBody = @" -{ - "ApplicationGrants":[ {"EnterpriseApplicationId":"00000003-0000-0000-c000-000000000000","Scope":"Application.ReadWrite.all,DelegatedPermissionGrant.ReadWrite.All,Directory.ReadWrite.All"}], - "ApplicationId": "$($ENV:applicationId)" -} -"@ + $AppBody = @{ + ApplicationId = $($ENV:applicationId) + ApplicationGrants = @( + @{ + EnterpriseApplicationId = "00000003-0000-0000-c000-000000000000" + Scope = @( + "Application.ReadWrite.all", + "DelegatedPermissionGrant.ReadWrite.All", + "Directory.ReadWrite.All" + ) -Join ',' + } + ) + } | ConvertTo-Json + $CPVConsent = New-GraphpostRequest -body $AppBody -Type POST -noauthcheck $true -uri "https://api.partnercenter.microsoft.com/v1/customers/$($TenantFilter)/applicationconsents" -scope 'https://api.partnercenter.microsoft.com/.default' -tenantid $env:TenantID $Table = Get-CIPPTable -TableName cpvtenants $unixtime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds From 5832e98de3c4ad73c4a9100eda358f86a25246cb Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sun, 17 Dec 2023 21:28:55 -0500 Subject: [PATCH 18/84] Logging fix --- .../Push-ExecOnboardTenantQueue.ps1 | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 index 00e24af25b17..6a5d86893cab 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 @@ -13,12 +13,13 @@ Function Push-ExecOnboardTenantQueue { $OnboardTable = Get-CIPPTable -TableName 'TenantOnboarding' $TenantOnboarding = Get-CIPPAzDataTableEntity @OnboardTable -Filter "RowKey eq '$Id'" - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Starting onboarding' }) + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = "Starting onboarding for relationship $Id" }) $OnboardingSteps = $TenantOnboarding.OnboardingSteps | ConvertFrom-Json $OnboardingSteps.Step1.Status = 'running' $OnboardingSteps.Step1.Message = 'Checking GDAP invite status' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) $TenantOnboarding.Status = 'running' + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress -AsArray) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop try { @@ -64,6 +65,7 @@ Function Push-ExecOnboardTenantQueue { } $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) $TenantOnboarding.Relationship = [string](ConvertTo-Json -InputObject $Relationship -Compress -Depth 10) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } @@ -72,6 +74,7 @@ Function Push-ExecOnboardTenantQueue { $OnboardingSteps.Step2.Status = 'running' $OnboardingSteps.Step2.Message = 'Checking role mapping' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop $MissingRoles = [System.Collections.Generic.List[string]]::new() @@ -98,7 +101,7 @@ Function Push-ExecOnboardTenantQueue { $OnboardingSteps.Step2.Message = 'Your GDAP relationship has the required roles' } $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) - $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } @@ -114,7 +117,7 @@ Function Push-ExecOnboardTenantQueue { $OnboardingSteps.Step3.Status = 'running' $OnboardingSteps.Step3.Message = 'Mapping security groups' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) - $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop $Table = Get-CIPPTable -TableName 'GDAPInvites' @@ -183,7 +186,7 @@ Function Push-ExecOnboardTenantQueue { } } $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) - $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } @@ -192,7 +195,7 @@ Function Push-ExecOnboardTenantQueue { $OnboardingSteps.Step4.Status = 'running' $OnboardingSteps.Step4.Message = 'Refreshing CPV permissions' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) - $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop try { @@ -233,7 +236,7 @@ Function Push-ExecOnboardTenantQueue { $OnboardingSteps.Step4.Message = 'Tenant not found in customer list, try again later' } $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) - $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } @@ -242,7 +245,7 @@ Function Push-ExecOnboardTenantQueue { $OnboardingSteps.Step5.Status = 'running' $OnboardingSteps.Step5.Message = 'Testing API access' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) - $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop try { @@ -258,7 +261,7 @@ Function Push-ExecOnboardTenantQueue { $OnboardingSteps.Step5.Message = 'API Test Successful: {0} users found' -f $UserCount $TenantOnboarding.Status = 'succeeded' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) - $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } else { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'API Test failed: {0}' -f $ApiError }) @@ -266,7 +269,7 @@ Function Push-ExecOnboardTenantQueue { $OnboardingSteps.Step5.Message = 'API Test failed: {0}' -f $ApiError $TenantOnboarding.Status = 'succeeded' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) - $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } } @@ -275,7 +278,7 @@ Function Push-ExecOnboardTenantQueue { $TenantOnboarding.Status = 'failed' $TenantOnboarding.Exception = [string]('{0} - Line {1} - {2}' -f $_.Exception.Message, $_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.ScriptName) $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) - $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject $Logs -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop } } From 70adc19bdc1ccbbb3903725e075dd3b1434558e4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sun, 17 Dec 2023 21:32:58 -0500 Subject: [PATCH 19/84] Update Push-ExecOnboardTenantQueue.ps1 --- .../Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 index 6a5d86893cab..abb5a1124c25 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 @@ -256,7 +256,8 @@ Function Push-ExecOnboardTenantQueue { } if ($UserCount -gt 0) { - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'API test successful. Onboarding complete.' }) + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'API test successful' }) + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Onboarding complete' }) $OnboardingSteps.Step5.Status = 'succeeded' $OnboardingSteps.Step5.Message = 'API Test Successful: {0} users found' -f $UserCount $TenantOnboarding.Status = 'succeeded' From ded6b0d1a208c160ae9e9f2f93638ea5488f75e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 18 Dec 2023 22:27:01 +0100 Subject: [PATCH 20/84] Change all auth method standards and add new enable and disable options --- .../Public/Set-CIPPAuthenticationPolicy.ps1 | 18 ++++--- .../Invoke-CIPPStandardDisableEmail.ps1 | 28 +++++++++++ .../Invoke-CIPPStandardDisableSMS.ps1 | 28 +++++++++++ .../Invoke-CIPPStandardDisableVoice.ps1 | 28 +++++++++++ ...oke-CIPPStandardDisablex509Certificate.ps1 | 28 +++++++++++ .../Invoke-CIPPStandardEnableFIDO2.ps1 | 32 ++++++------- ...Invoke-CIPPStandardEnableHardwareOAuth.ps1 | 27 +++++++++++ ...rdPWdisplayAppInformationRequiredState.ps1 | 39 +++++++-------- .../Standards/Invoke-CIPPStandardTAP.ps1 | 47 +++++++------------ .../Invoke-CIPPStandardallowOAuthTokens.ps1 | 43 +++++++---------- .../Invoke-CIPPStandardallowOTPTokens.ps1 | 37 +++++++-------- 11 files changed, 228 insertions(+), 127 deletions(-) create mode 100644 Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 create mode 100644 Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 create mode 100644 Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 create mode 100644 Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 create mode 100644 Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index 4c4c6c872bff..2bbc84d2512a 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -3,8 +3,8 @@ function Set-CIPPAuthenticationPolicy { param( [Parameter(Mandatory = $true)]$Tenant, [Parameter(Mandatory = $true)][ValidateSet('FIDO2', 'MicrosoftAuthenticator', 'SMS', 'TemporaryAccessPass', 'HardwareOATH', 'softwareOath', 'Voice', 'Email', 'x509Certificate')]$AuthenticationMethodId, - [Parameter(Mandatory = $true)][bool]$Enable, # true = enabled or false = disabled - [bool]$MicrosoftAuthenticatorSoftwareOathEnabled, + [Parameter(Mandatory = $true)][bool]$Enabled, # true = enabled or false = disabled + $MicrosoftAuthenticatorSoftwareOathEnabled, $TAPMinimumLifetime = 60, #Minutes $TAPMaximumLifetime = 480, #minutes $TAPDefaultLifeTime = 60, #minutes @@ -15,14 +15,14 @@ function Set-CIPPAuthenticationPolicy { ) # Convert bool input to usable string - $State = if ($Enable) { 'enabled' } else { 'disabled' } + $State = if ($Enabled) { 'enabled' } else { 'disabled' } # Get current state of the called authentication method and Set state of authentication method to input state try { $CurrentInfo = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/$AuthenticationMethodId" -tenantid $Tenant $CurrentInfo.state = $State - } - catch { + } catch { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Could not get CurrentInfo for $AuthenticationMethodId. Error:$($_.exception.message)" -sev Error + Return "Could not get CurrentInfo for $AuthenticationMethodId. Error:$($_.exception.message)" } switch ($AuthenticationMethodId) { @@ -46,7 +46,7 @@ function Set-CIPPAuthenticationPolicy { # Set MS authenticator OTP state if parameter is passed in if ($null -ne $MicrosoftAuthenticatorSoftwareOathEnabled ) { $CurrentInfo.isSoftwareOathEnabled = $MicrosoftAuthenticatorSoftwareOathEnabled - $OptionalLogMessage = "and Microsoft Authenticator software OTP to $MicrosoftAuthenticatorSoftwareOathEnabled" + $OptionalLogMessage = "and MS Authenticator software OTP to $MicrosoftAuthenticatorSoftwareOathEnabled" } } } @@ -99,8 +99,7 @@ function Set-CIPPAuthenticationPolicy { # Certificate-based authentication 'x509Certificate' { - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Setting $AuthenticationMethodId is not yet supported in CIPP" -sev Error - return "Setting $AuthenticationMethodId is not yet supported in CIPP" + # Nothing special to do here } Default { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message 'Somehow you hit the default case. You probably made a typo in the input for AuthenticationMethodId. It''s case sensitive' -sev Error @@ -116,8 +115,7 @@ function Set-CIPPAuthenticationPolicy { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Set $AuthenticationMethodId state to $State $OptionalLogMessage" -sev Info return "Set $AuthenticationMethodId state to $State $OptionalLogMessage" - } - catch { + } catch { Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" -sev Error return "Failed to $State $AuthenticationMethodId Support: $($_.exception.message)" } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 new file mode 100644 index 000000000000..dcd995600259 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 @@ -0,0 +1,28 @@ +function Invoke-CIPPStandardDisableEmail { + <# + .FUNCTIONALITY + Internal + #> + param($Tenant, $Settings) + + If ($Settings.remediate) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'Email' -Enabled $false + } + + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Email' -tenantid $Tenant + $State = if ($CurrentInfo.state -eq 'enabled') { $true } else { $false } + + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Email Support is enabled' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Email Support is not enabled' -sev Info + } + } + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'DisableEmail' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + } + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 new file mode 100644 index 000000000000..d133c34deef5 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 @@ -0,0 +1,28 @@ +function Invoke-CIPPStandardDisableSMS { + <# + .FUNCTIONALITY + Internal + #> + param($Tenant, $Settings) + + If ($Settings.remediate) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'SMS' -Enabled $false + } + + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/SMS' -tenantid $Tenant + $State = if ($CurrentInfo.state -eq 'enabled') { $true } else { $false } + + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMS Support is enabled' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMS Support is not enabled' -sev Info + } + } + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'DisableSMS' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + } + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 new file mode 100644 index 000000000000..50d18e51c43f --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 @@ -0,0 +1,28 @@ +function Invoke-CIPPStandardDisableVoice { + <# + .FUNCTIONALITY + Internal + #> + param($Tenant, $Settings) + + If ($Settings.remediate) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'Voice' -Enabled $false + } + + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Voice' -tenantid $Tenant + $State = if ($CurrentInfo.state -eq 'enabled') { $true } else { $false } + + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Voice Support is enabled' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Voice Support is not enabled' -sev Info + } + } + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'DisableVoice' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + } + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 new file mode 100644 index 000000000000..94a36d9b9125 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 @@ -0,0 +1,28 @@ +function Invoke-CIPPStandardDisablex509Certificate { + <# + .FUNCTIONALITY + Internal + #> + param($Tenant, $Settings) + + If ($Settings.remediate) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'x509Certificate' -Enabled $false + } + + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/x509Certificate' -tenantid $Tenant + $State = if ($CurrentInfo.state -eq 'enabled') { $true } else { $false } + + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'x509Certificate Support is enabled' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'x509Certificate Support is not enabled' -sev Info + } + } + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'Disablex509Certificate' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + } + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 index 1c7ff1337f4e..ff80b7f264f8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 @@ -4,28 +4,24 @@ function Invoke-CIPPStandardEnableFIDO2 { Internal #> param($Tenant, $Settings) + If ($Settings.remediate) { - - - try { - $body = '{"@odata.type":"#microsoft.graph.fido2AuthenticationMethodConfiguration","id":"Fido2","includeTargets":[{"id":"all_users","isRegistrationRequired":false,"targetType":"group","displayName":"All users"}],"excludeTargets":[],"isAttestationEnforced":true,"isSelfServiceRegistrationAllowed":true,"keyRestrictions":{"aaGuids":[],"enforcementType":"block","isEnforced":false},"state":"enabled"}' - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled FIDO2 Support' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable FIDO2 Support: $($_.exception.message)" -sev Error - } + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'Fido2' -Enabled $true } - - if ($Settings.alert) { + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -tenantid $Tenant - if ($CurrentInfo.state -eq 'enabled') { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'FIDO2 Support is enabled' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'FIDO2 Support is not enabled' -sev Alert + + if ($Settings.alert) { + if ($CurrentInfo.state -eq 'enabled') { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'FIDO2 Support is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'FIDO2 Support is not enabled' -sev Alert + } + } + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'EnableFIDO2' -FieldValue [bool]$CurrentInfo.state -StoreAs bool -Tenant $tenant } - } - if ($Settings.report) { - Add-CIPPBPAField -FieldName 'EnableFIDO2' -FieldValue [bool]$CurrentInfo.state -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 new file mode 100644 index 000000000000..605a5188a47d --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 @@ -0,0 +1,27 @@ +function Invoke-CIPPStandardEnableHardwareOAuth { + <# + .FUNCTIONALITY + Internal + #> + param($Tenant, $Settings) + + If ($Settings.remediate) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'HardwareOath' -Enabled $true + } + + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/HardwareOath' -tenantid $Tenant + + if ($Settings.alert) { + if ($CurrentInfo.state -eq 'enabled') { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'HardwareOAuth Support is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'HardwareOAuth Support is not enabled' -sev Alert + } + } + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'EnableHardwareOAuth' -FieldValue [bool]$CurrentInfo.state -StoreAs bool -Tenant $tenant + } + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 index aecda655774b..4bf9ebcd3426 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 @@ -4,29 +4,24 @@ function Invoke-CIPPStandardPWdisplayAppInformationRequiredState { Internal #> param($Tenant, $Settings) - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant - + If ($Settings.remediate) { - try { - $body = @' -{"@odata.type":"#microsoft.graph.microsoftAuthenticatorAuthenticationMethodConfiguration","id":"MicrosoftAuthenticator","includeTargets":[{"id":"all_users","isRegistrationRequired":false,"targetType":"group","authenticationMode":"any"}],"excludeTargets":[],"state":"enabled","isSoftwareOathEnabled":false,"featureSettings":{"displayLocationInformationRequiredState":{"state":"enabled","includeTarget":{"id":"all_users","targetType":"group","displayName":"All users"}},"displayAppInformationRequiredState":{"state":"enabled","includeTarget":{"id":"all_users","targetType":"group","displayName":"All users"}},"companionAppAllowedState":{"state":"default","includeTarget":{"id":"all_users","targetType":"group","displayName":"All users"}}}} -'@ - (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -Type patch -Body $body -ContentType 'application/json') - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled passwordless with Information and Number Matching.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable passwordless with Information and Number Matching. Error: $($_.exception.message)" -sev 'Error' - } + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'MicrosoftAuthenticator' -Enabled $true } - if ($Settings.alert) { - - if ($CurrentInfo.featureSettings.displayAppInformationRequiredState.state -eq 'enabled') { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Passwordless with Information and Number Matching is enabled.' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Passwordless with Information and Number Matching is not enabled.' -sev Alert + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + $State = if ($CurrentInfo.featureSettings.displayAppInformationRequiredState.state -eq 'enabled') { $true } else { $false } + + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Passwordless with Information and Number Matching is enabled.' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Passwordless with Information and Number Matching is not enabled.' -sev Alert + } + } + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'PWdisplayAppInformationRequiredState' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant } } - if ($Settings.report) { - if ($CurrentInfo.featureSettings.displayAppInformationRequiredState.state -eq 'enabled') { $authstate = $true } else { $authstate = $false } - Add-CIPPBPAField -FieldName 'PWdisplayAppInformationRequiredState' -FieldValue [bool]$authstate -StoreAs bool -Tenant $tenant - } -} +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 index ed6559f29cf2..e98e7faddd01 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 @@ -4,40 +4,25 @@ function Invoke-CIPPStandardTAP { Internal #> param($Tenant, $Settings) - $CurrentInfo = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -tenantid $Tenant) - + If ($Settings.remediate) { - try { - - $CurrentInfo.state = 'enabled' - $CurrentInfo.isUsableOnce = $Settings.config - $CurrentInfo.minimumLifetimeInMinutes = '60' - $CurrentInfo.maximumLifetimeInMinutes = '480' - $CurrentInfo.defaultLifetimeInMinutes = '60' - $CurrentInfo.defaultLength = '8' - $body = ConvertTo-Json -Depth 10 -InputObject $CurrentInfo - Write-Host "Sending body $body" - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/TemporaryAccessPass' -Type patch -asApp $true -Body $body -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled Temporary Access Passwords.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable TAP. Error: $($_.exception.message)" -sev Error - } + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'TemporaryAccessPass' -Enabled $true -TAPisUsableOnce $Settings.config } - if ($Settings.alert) { - if ($CurrentInfo.state -eq 'enabled') { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Temporary Access Passwords is enabled.' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Temporary Access Passwords is not enabled.' -sev Alert + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -tenantid $Tenant + $State = if ($CurrentInfo.state -eq 'enabled') { $true } else { $false } + + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Temporary Access Passwords is enabled.' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Temporary Access Passwords is not enabled.' -sev Alert + } } - } - if ($Settings.report) { - if ($CurrentInfo.state -eq 'enabled') { - $CurrentInfo.state = $true - } else { - $CurrentInfo.state = $false + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'TemporaryAccessPass' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant } - Add-CIPPBPAField -FieldName 'TemporaryAccessPass' -FieldValue [bool]$CurrentInfo.state -StoreAs bool -Tenant $tenant } - -} +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 index 00be3942e2db..8455c93208f5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 @@ -4,33 +4,26 @@ function Invoke-CIPPStandardallowOAuthTokens { Internal #> param($Tenant, $Settings) - $CurrentInfo = new-graphgetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $Tenant - If ($Settings.remediate) { - - try { - $CurrentInfo.state = 'enabled' - $body = ($CurrentInfo | ConvertTo-Json -Depth 10) - (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -Type patch -Body $body -ContentType 'application/json') - - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled software OTP/oAuth tokens' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable software OTP/oAuth tokens. Error: $($_.exception.message)" -sev 'Error' - } + + If ($Settings.remediate) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'softwareOath' -Enabled $true } - if ($Settings.alert) { - if ($CurrentInfo.state -eq 'enabled') { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'software OTP/oAuth tokens is enabled' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'software OTP/oAuth tokens is not enabled' -sev Alert + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $Tenant + $State = if ($CurrentInfo.state -eq 'enabled') { $true } else { $false } + + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Software OTP/oAuth tokens is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Software OTP/oAuth tokens is not enabled' -sev Alert + } } - } - if ($Settings.report) { - if ($CurrentInfo.state -eq 'enabled') { - $CurrentInfo.state = $true - } else { - $CurrentInfo.state = $false + + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'softwareOath' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant } - Add-CIPPBPAField -FieldName 'softwareOath' -FieldValue [bool]$CurrentInfo.state -StoreAs bool -Tenant $tenant } -} +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 index c7a5958a45d8..cca5dc8ae8e2 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 @@ -4,30 +4,25 @@ function Invoke-CIPPStandardallowOTPTokens { Internal #> param($Tenant, $Settings) - $CurrentInfo = new-graphgetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant - + If ($Settings.remediate) { - - try { - $CurrentInfo.featureSettings.PSObject.Properties.Remove('numberMatchingRequiredState') - $CurrentInfo.isSoftwareOathEnabled = $true - $body = ($CurrentInfo | ConvertTo-Json -Depth 10) - (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -Type patch -Body $body -ContentType 'application/json') + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'MicrosoftAuthenticator' -Enabled $true -MicrosoftAuthenticatorSoftwareOathEnabled $true + } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled MS authenticator OTP/oAuth tokens' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable MS authenticator OTP/oAuth tokens. Error: $($_.exception.message)" -sev Error + # This is ugly but done to avoid a second call to the Graph API + if ($Settings.alert -or $Settings.report) { + $CurrentInfo = new-graphgetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + if ($Settings.alert) { + + if ($CurrentInfo.isSoftwareOathEnabled) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'MS authenticator OTP/oAuth tokens is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'MS authenticator OTP/oAuth tokens is not enabled' -sev Alert + } } - } - if ($Settings.alert) { - if ($CurrentInfo.isSoftwareOathEnabled) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'MS authenticator OTP/oAuth tokens is enabled' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'MS authenticator OTP/oAuth tokens is not enabled' -sev Alert + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'MSAuthenticator' -FieldValue [bool]$CurrentInfo.isSoftwareOathEnabled -StoreAs bool -Tenant $tenant } } - if ($Settings.report) { - Add-CIPPBPAField -FieldName 'MSAuthenticator' -FieldValue [bool]$CurrentInfo.isSoftwareOathEnabled -StoreAs bool -Tenant $tenant - } -} +} \ No newline at end of file From cb6a1f255a0fe6d9a6c1782178edf486394a79b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 18 Dec 2023 22:57:59 +0100 Subject: [PATCH 21/84] Spacing --- Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 index 2bbc84d2512a..ecdb1bdfc904 100644 --- a/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPAuthenticationPolicy.ps1 @@ -111,8 +111,6 @@ function Set-CIPPAuthenticationPolicy { # Convert body to JSON and send request $body = ConvertTo-Json -Compress -Depth 10 -InputObject $CurrentInfo New-GraphPostRequest -tenantid $Tenant -Uri "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/$AuthenticationMethodId" -Type patch -Body $body -ContentType 'application/json' - - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $Tenant -message "Set $AuthenticationMethodId state to $State $OptionalLogMessage" -sev Info return "Set $AuthenticationMethodId state to $State $OptionalLogMessage" } catch { From ed36d3017b2a6c94d7ff6f491e6c2a22bd64ad41 Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Wed, 20 Dec 2023 08:23:32 +0100 Subject: [PATCH 22/84] Add vendor app removal to tenant offboarding --- .../Entrypoints/Invoke-ExecOffboardTenant.ps1 | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 index 8a43ad253d09..ef42bc907663 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 @@ -37,6 +37,7 @@ Function Invoke-ExecOffboardTenant { $BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantFilter $results.Add('Succesfully removed guest users') + Write-LogMessage -user $ExecutingUser -API $APIName -message "CSP Guest users were removed" -Sev "Info" -tenant $TenantFilter } else { $results.Add('No guest users found to remove') } @@ -46,7 +47,6 @@ Function Invoke-ExecOffboardTenant { } if ($request.body.RemoveCSPnotificationContacts) { - Write-Host "DO WE GET HERE?" # Remove all email adresses that match the CSP tenants domains from the contact properties in /organization try { try { @@ -79,6 +79,7 @@ Function Invoke-ExecOffboardTenant { try { New-GraphPostRequest -type PATCH -body $patchContactBody -Uri "https://graph.microsoft.com/v1.0/organization/$($orgContacts.id)" -tenantid $Tenantfilter -ContentType "application/json" $results.Add("Succesfully removed notification contacts from $($property): $(($propertyContacts | Where-Object { $domains -contains $_.Split("@")[1] }))") + Write-LogMessage -user $ExecutingUser -API $APIName -message "Contacts were removed from $($property)" -Sev "Info" -tenant $TenantFilter } catch { $errors.Add("Failed to update property $($property): $($_.Exception.message)") } @@ -90,16 +91,17 @@ Function Invoke-ExecOffboardTenant { } - if ($request.body.RemoveMSPvendorApps) { - # 9fcfb031-1bf6-4848-8732-5573fd64fc09 - Augmentt - # 9359814a-7403-4af9-9113-d5c8cab020ed - Rewst CSP connector - # 06bfda05-2d5e-4b3b-ac5d-79f07e402973 - Rewst Prod - # c19d36e8-6537-4998-9872-ea8b962bd0b6 - Rewst Azure Integration - # d7db2a1c-c38b-4bd1-a30f-0915167ba928 - Datto Backupify/Saas Protection - # 0c3cdc94-15ba-4b89-9222-29f599727b1c - AutoTask Client Portal SSO - # 62603940-b9b0-454f-b138-eb8d571f21d3 - Eshgro Smarter 365? - # Possible others, Scapmann, PatchMyPC, Datto M365 management, Kaseya crap, Exclaimer(?), HP, Lenovo, Dell, Apple(???), resellers(all region tenants?), Action1, Liquit - # Current idea, do a filtered serviceprincipals request based on the appOwner tenantids of known MSP vendors, load that data into a multi-select on the GUI + if ($request.body.RemoveVendorApps) { + $request.body.RemoveVendorApps | ForEach-Object { + try { + $delete = (New-GraphPostRequest -type 'DELETE' -Uri "https://graph.microsoft.com/v1.0/serviceprincipals/$($_.value)" -tenantid $Tenantfilter) + $results.Add("Succesfully removed app $($_.label)") + Write-LogMessage -user $ExecutingUser -API $APIName -message "App $($_.label) was removed" -Sev "Info" -tenant $TenantFilter + } catch { + #$results.Add("Failed to removed app $($_.displayName)") + $errors.Add("Failed to removed app $($_.label)") + } + } } # All customer tenant specific actions ALWAYS have to be completed before this action! @@ -112,6 +114,7 @@ Function Invoke-ExecOffboardTenant { try { $delete = (New-GraphPostRequest -type 'DELETE' -Uri "https://graph.microsoft.com/v1.0/serviceprincipals/$($_.id)" -tenantid $Tenantfilter) $results.Add("Succesfully removed app $($_.displayName)") + Write-LogMessage -user $ExecutingUser -API $APIName -message "App $($_.displayName) was removed" -Sev "Info" -tenant $TenantFilter } catch { #$results.Add("Failed to removed app $($_.displayName)") $errors.Add("Failed to removed app $($_.displayName)") @@ -119,7 +122,7 @@ Function Invoke-ExecOffboardTenant { } } catch { #$results.Add("Failed to retrieve multitenant apps, no apps have been removed: $($_.Exception.message)") - $errors.Add("Failed to retrieve multitenant apps, no apps have been removed: $($_.Exception.message)") + $errors.Add("Failed to retrieve multitenant CSP apps, no apps have been removed: $($_.Exception.message)") } } @@ -131,6 +134,7 @@ Function Invoke-ExecOffboardTenant { try { $terminate = (New-GraphPostRequest -type 'POST' -Uri "https://graph.microsoft.com/v1.0/tenantRelationships/delegatedAdminRelationships/$($_.id)/requests" -body '{"action":"terminate"}' -ContentType 'application/json' -tenantid $env:TenantID) $results.Add("Succesfully terminated GDAP relationship $($_.displayName) from tenant $TenantFilter") + Write-LogMessage -user $ExecutingUser -API $APIName -message "GDAP Relationship $($_.displayName) has been terminated" -Sev "Info" -tenant $TenantFilter } catch { #$results.Add("Failed to terminate GDAP relationship $($_.displayName): $($_.Exception.message)") $errors.Add("Failed to terminate GDAP relationship $($_.displayName): $($_.Exception.message)") @@ -147,6 +151,7 @@ Function Invoke-ExecOffboardTenant { try { $terminate = (New-GraphPostRequest -type 'PATCH' -body '{ "relationshipToPartner": "none" }' -Uri "https://api.partnercenter.microsoft.com/v1/customers/$TenantFilter" -ContentType 'application/json' -scope 'https://api.partnercenter.microsoft.com/user_impersonation' -tenantid $env:TenantID) $results.Add('Succesfully terminated contract relationship') + Write-LogMessage -user $ExecutingUser -API $APIName -message "Contract relationship terminated" -Sev "Info" -tenant $TenantFilter } catch { #$results.Add("Failed to terminate contract relationship: $($_.Exception.message)") $errors.Add("Failed to terminate contract relationship: $($_.Exception.message)") From 94e1d27d89b0d239e635a46904397336dda81937 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 20 Dec 2023 17:29:10 -0500 Subject: [PATCH 23/84] GDAP Invite wizard Change output Add onboarding wizard URL --- .../Entrypoints/Invoke-ExecGDAPInvite.ps1 | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 index 8eb97c978ce5..e32245afb1cc 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 @@ -11,7 +11,6 @@ Function Invoke-ExecGDAPInvite { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $RoleMappings = $Request.body.gdapRoles - $Results = [System.Collections.Generic.List[string]]::new() if ($RoleMappings.roleDefinitionId -contains '62e90394-69f5-4237-9190-012177145e10') { $AutoExtendDuration = 'PT0S' @@ -48,29 +47,32 @@ Function Invoke-ExecGDAPInvite { if ($NewRelationshipRequest.action -eq 'lockForApproval') { $InviteUrl = "https://admin.microsoft.com/AdminPortal/Home#/partners/invitation/granularAdminRelationships/$($NewRelationship.id)" + $Uri = ([System.Uri]$TriggerMetadata.Headers.referer) + $OnboardingUrl = '{0}/tenant/administration/tenant-onboarding-wizard?tableFilter=Complex: id eq {1}' -f ($Uri.AbsoluteUri -replace $Uri.PathAndQuery), $NewRelationship.id $InviteEntity = [PSCustomObject]@{ - 'PartitionKey' = 'invite' - 'RowKey' = $NewRelationship.id - 'InviteUrl' = $InviteUrl - 'RoleMappings' = [string](@($RoleMappings) | ConvertTo-Json -Depth 10 -Compress) + 'PartitionKey' = 'invite' + 'RowKey' = $NewRelationship.id + 'InviteUrl' = $InviteUrl + 'OnboardingUrl' = $OnboardingUrl + 'RoleMappings' = [string](@($RoleMappings) | ConvertTo-Json -Depth 10 -Compress) } Add-CIPPAzDataTableEntity @Table -Entity $InviteEntity - $Results.add('GDAP relationship invite created. Copy the URL below and log in as a Global Admin for the new tenant to approve the invite.') + $Message = 'GDAP relationship invite created. Copy the URL below and log in as a Global Admin for the new tenant to approve the invite.' } else { - $Results.add('Error creating GDAP relationship request') + $Message = 'Error creating GDAP relationship request' } } } catch { - $Results.add('Error creating GDAP relationship') + $Message = 'Error creating GDAP relationship' Write-Host "GDAP ERROR: $($_.Exception.Message)" } Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created GDAP Invite - $InviteUrl" -Sev 'Info' $body = @{ - Results = @($Results) + Message = $Message Invite = $InviteEntity } Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ From bf340534fa6ad1ec80f187b42dbd13db3f376e57 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 20 Dec 2023 17:37:35 -0500 Subject: [PATCH 24/84] Update Invoke-ExecGDAPInvite.ps1 --- Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 index e32245afb1cc..ff0e613b6687 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 @@ -59,7 +59,7 @@ Function Invoke-ExecGDAPInvite { } Add-CIPPAzDataTableEntity @Table -Entity $InviteEntity - $Message = 'GDAP relationship invite created. Copy the URL below and log in as a Global Admin for the new tenant to approve the invite.' + $Message = 'GDAP relationship invite created. Log in as a Global Admin in the new tenant to approve the invite.' } else { $Message = 'Error creating GDAP relationship request' } From e314f61aefc6aa86999744745df64e493f3f302b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 20 Dec 2023 18:03:21 -0500 Subject: [PATCH 25/84] Update Invoke-ExecGDAPInvite.ps1 --- Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 index ff0e613b6687..b427e3e89e2b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 @@ -48,7 +48,7 @@ Function Invoke-ExecGDAPInvite { if ($NewRelationshipRequest.action -eq 'lockForApproval') { $InviteUrl = "https://admin.microsoft.com/AdminPortal/Home#/partners/invitation/granularAdminRelationships/$($NewRelationship.id)" $Uri = ([System.Uri]$TriggerMetadata.Headers.referer) - $OnboardingUrl = '{0}/tenant/administration/tenant-onboarding-wizard?tableFilter=Complex: id eq {1}' -f ($Uri.AbsoluteUri -replace $Uri.PathAndQuery), $NewRelationship.id + $OnboardingUrl = $Uri.AbsoluteUri.Replace($Uri.PathAndQuery, '/tenant/administration/tenant-onboarding-wizard?tableFilter=Complex: id eq {0}' -f $NewRelationship.id) $InviteEntity = [PSCustomObject]@{ 'PartitionKey' = 'invite' From df5eeb9c8fb1a029d9b058bfa95ff3fd5e4e3f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 21 Dec 2023 19:20:29 +0100 Subject: [PATCH 26/84] Fix OauthConsent standard not running --- .../Invoke-CIPPStandardOauthConsent.ps1 | 73 +++++++++---------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 index a3ecdb371327..e9889299cdfd 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 @@ -3,48 +3,47 @@ function Invoke-CIPPStandardOauthConsent { .FUNCTIONALITY Internal #> - param($tenant, $settings) { - $State = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant) + param($tenant, $settings) + $State = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant - If ($Settings.remediate) { - $AllowedAppIdsForTenant = $Settings.AllowedApps -split ',' - try { - if ($State.permissionGrantPolicyIdsAssignedToDefaultUserRole -notin @('ManagePermissionGrantsForSelf.cipp-1sent-policy')) { - $Existing = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies/' -tenantid $tenant) | Where-Object -Property id -EQ 'cipp-consent-policy' - if (!$Existing) { - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies' -Type POST -Body '{ "id":"cipp-consent-policy", "displayName":"Application Consent Policy", "description":"This policy controls the current application consent policies."}' -ContentType 'application/json' - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies/cipp-consent-policy/includes' -Type POST -Body '{"permissionClassification":"all","permissionType":"delegated","clientApplicationIds":["d414ee2d-73e5-4e5b-bb16-03ef55fea597"]}' -ContentType 'application/json' - } - try { - foreach ($AllowedApp in $AllowedAppIdsForTenant) { - Write-Host "$AllowedApp" - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies/cipp-consent-policy/includes' -Type POST -Body ('{"permissionType": "delegated","clientApplicationIds": ["' + $AllowedApp + '"]}') -ContentType 'application/json' - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies/cipp-consent-policy/includes' -Type POST -Body ('{ "permissionType": "Application", "clientApplicationIds": ["' + $AllowedApp + '"] }') -ContentType 'application/json' - } - } catch { - "Could not add exclusions, probably already exist: $($_)" - } - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type PATCH -Body '{"permissionGrantPolicyIdsAssignedToDefaultUserRole":["managePermissionGrantsForSelf.cipp-consent-policy"]}' -ContentType 'application/json' + If ($Settings.remediate) { + $AllowedAppIdsForTenant = $Settings.AllowedApps -split ',' + try { + if ($State.permissionGrantPolicyIdsAssignedToDefaultUserRole -notin @('ManagePermissionGrantsForSelf.cipp-1sent-policy')) { + $Existing = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies/' -tenantid $tenant) | Where-Object -Property id -EQ 'cipp-consent-policy' + if (!$Existing) { + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies' -Type POST -Body '{ "id":"cipp-consent-policy", "displayName":"Application Consent Policy", "description":"This policy controls the current application consent policies."}' -ContentType 'application/json' + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies/cipp-consent-policy/includes' -Type POST -Body '{"permissionClassification":"all","permissionType":"delegated","clientApplicationIds":["d414ee2d-73e5-4e5b-bb16-03ef55fea597"]}' -ContentType 'application/json' } - if ($AllowedAppIdsForTenant) { + try { + foreach ($AllowedApp in $AllowedAppIdsForTenant) { + Write-Host "$AllowedApp" + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies/cipp-consent-policy/includes' -Type POST -Body ('{"permissionType": "delegated","clientApplicationIds": ["' + $AllowedApp + '"]}') -ContentType 'application/json' + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/permissionGrantPolicies/cipp-consent-policy/includes' -Type POST -Body ('{ "permissionType": "Application", "clientApplicationIds": ["' + $AllowedApp + '"] }') -ContentType 'application/json' + } + } catch { + "Could not add exclusions, probably already exist: $($_)" } - - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Application Consent Mode has been enabled.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to apply Application Consent Mode Error: $($_.exception.message)" -sev Error + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type PATCH -Body '{"permissionGrantPolicyIdsAssignedToDefaultUserRole":["managePermissionGrantsForSelf.cipp-consent-policy"]}' -ContentType 'application/json' } - } - if ($Settings.alert) { - - if ($State.permissionGrantPolicyIdsAssignedToDefaultUserRole -eq 'managePermissionGrantsForSelf.cipp-consent-policy') { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Application Consent Mode is enabled.' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Application Consent Mode is not enabled.' -sev Alert + if ($AllowedAppIdsForTenant) { } + + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Application Consent Mode has been enabled.' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to apply Application Consent Mode Error: $($_.exception.message)" -sev Error } - if ($Settings.report) { - if ($State.permissionGrantPolicyIdsAssignedToDefaultUserRole -eq 'managePermissionGrantsForSelf.cipp-consent-policy') { $UserQuota = $true } else { $UserQuota = $false } - Add-CIPPBPAField -FieldName 'OauthConsent' -FieldValue [bool]$UserQuota -StoreAs bool -Tenant $tenant + } + if ($Settings.alert) { + + if ($State.permissionGrantPolicyIdsAssignedToDefaultUserRole -eq 'managePermissionGrantsForSelf.cipp-consent-policy') { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Application Consent Mode is enabled.' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Application Consent Mode is not enabled.' -sev Alert } } -} \ No newline at end of file + if ($Settings.report) { + if ($State.permissionGrantPolicyIdsAssignedToDefaultUserRole -eq 'managePermissionGrantsForSelf.cipp-consent-policy') { $UserQuota = $true } else { $UserQuota = $false } + Add-CIPPBPAField -FieldName 'OauthConsent' -FieldValue [bool]$UserQuota -StoreAs bool -Tenant $tenant + } +} From 19dae5bcbbe0336ba392efa8d0095baddbf3ff69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 21 Dec 2023 22:55:15 +0100 Subject: [PATCH 27/84] Much faster calDefaults --- .../Invoke-CIPPStandardcalDefault.ps1 | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 index 9a2ccfb0e298..f96a863c7b98 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 @@ -5,21 +5,24 @@ function Invoke-CIPPStandardcalDefault { #> param($Tenant, $Settings) If ($Settings.remediate) { + $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' - foreach ($Mailbox in $Mailboxes) { - try { + try { + # BRRRRRRRRRR + $Mailboxes | ForEach-Object -ThrottleLimit 25 -Parallel { + Import-Module CIPPcore + $Tenant = $Using:Tenant + $Settings = $Using:Settings + $Mailbox = $_ + New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxFolderStatistics' -cmdParams @{identity = $Mailbox.UserPrincipalName; FolderScope = 'Calendar' } -Anchor $Mailbox.UserPrincipalName | Where-Object { $_.FolderType -eq 'Calendar' } | ForEach-Object { New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MailboxFolderPermission' -cmdparams @{Identity = "$($Mailbox.UserPrincipalName):$($_.FolderId)"; User = 'Default'; AccessRights = $Settings.permissionlevel } -Anchor $Mailbox.UserPrincipalName - Write-LogMessage -API 'Standards' -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($Settings.permissionlevel)" -sev Info - } - } - catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error + Write-LogMessage -API 'Standards' -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($Settings.permissionlevel)" -sev Info } } - - } + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error + } Write-LogMessage -API 'Standards' -tenant $tenant -message 'Done setting default calendar permissions.' -sev Info - } } From cc0595f100c19def86f85396ce3dc8a3fd237d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 21 Dec 2023 23:28:18 +0100 Subject: [PATCH 28/84] Less parallel and fix try catch placement --- .../Invoke-CIPPStandardDisableGuests.ps1 | 2 +- .../Invoke-CIPPStandardcalDefault.ps1 | 37 ++++++++++--------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 index bcb286cc7677..e328f3d99afc 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 @@ -6,7 +6,7 @@ function Invoke-CIPPStandardDisableGuests { param($Tenant, $Settings) $lookup = (Get-Date).AddDays(-90).ToUniversalTime().ToString('o') $GraphRequest = New-GraphgetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=(signInActivity/lastSignInDateTime le $lookup)&`$select=id,UserPrincipalName,signInActivity,mail,userType,accountEnabled" -scope 'https://graph.microsoft.com/.default' -tenantid $Tenant | Where-Object { $_.userType -EQ 'Guest' -and $_.AccountEnabled -EQ $true } - + If ($Settings.remediate) { try { foreach ($guest in $GraphRequest) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 index f96a863c7b98..dcf8b868c6ab 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 @@ -7,22 +7,25 @@ function Invoke-CIPPStandardcalDefault { If ($Settings.remediate) { $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' - try { - # BRRRRRRRRRR - $Mailboxes | ForEach-Object -ThrottleLimit 25 -Parallel { - Import-Module CIPPcore - $Tenant = $Using:Tenant - $Settings = $Using:Settings - $Mailbox = $_ - - New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxFolderStatistics' -cmdParams @{identity = $Mailbox.UserPrincipalName; FolderScope = 'Calendar' } -Anchor $Mailbox.UserPrincipalName | Where-Object { $_.FolderType -eq 'Calendar' } | ForEach-Object { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MailboxFolderPermission' -cmdparams @{Identity = "$($Mailbox.UserPrincipalName):$($_.FolderId)"; User = 'Default'; AccessRights = $Settings.permissionlevel } -Anchor $Mailbox.UserPrincipalName - Write-LogMessage -API 'Standards' -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($Settings.permissionlevel)" -sev Info } + + # BRRRRRRRRRR + $Mailboxes | ForEach-Object -ThrottleLimit 15 -Parallel { + Import-Module CIPPcore + $Tenant = $Using:Tenant + $Settings = $Using:Settings + $Mailbox = $_ + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxFolderStatistics' -cmdParams @{identity = $Mailbox.UserPrincipalName; FolderScope = 'Calendar' } -Anchor $Mailbox.UserPrincipalName | + # Set permissions for each calendar found + Where-Object { $_.FolderType -eq 'Calendar' } | ForEach-Object { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MailboxFolderPermission' -cmdparams @{Identity = "$($Mailbox.UserPrincipalName):$($_.FolderId)"; User = 'Default'; AccessRights = $Settings.permissionlevel } -Anchor $Mailbox.UserPrincipalName + Write-LogMessage -API 'Standards' -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($Settings.permissionlevel)" -sev Info + } + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error + } } - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error - } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Done setting default calendar permissions.' -sev Info - } + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Done setting default calendar permissions.' -sev Info -} + } + } \ No newline at end of file From b12d7ff5a1f4835690c75123a6134f711ec81e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 22 Dec 2023 00:19:04 +0100 Subject: [PATCH 29/84] Add retry functionality for both get and set part --- .../Invoke-CIPPStandardcalDefault.ps1 | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 index dcf8b868c6ab..5f8ae73a18b9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 @@ -9,18 +9,41 @@ function Invoke-CIPPStandardcalDefault { $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' # BRRRRRRRRRR - $Mailboxes | ForEach-Object -ThrottleLimit 15 -Parallel { + $Mailboxes | ForEach-Object -ThrottleLimit 25 -Parallel { Import-Module CIPPcore $Tenant = $Using:Tenant $Settings = $Using:Settings $Mailbox = $_ try { - New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxFolderStatistics' -cmdParams @{identity = $Mailbox.UserPrincipalName; FolderScope = 'Calendar' } -Anchor $Mailbox.UserPrincipalName | - # Set permissions for each calendar found - Where-Object { $_.FolderType -eq 'Calendar' } | ForEach-Object { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MailboxFolderPermission' -cmdparams @{Identity = "$($Mailbox.UserPrincipalName):$($_.FolderId)"; User = 'Default'; AccessRights = $Settings.permissionlevel } -Anchor $Mailbox.UserPrincipalName - Write-LogMessage -API 'Standards' -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($Settings.permissionlevel)" -sev Info - } + $GetRetryCount = 0 + + do { + # Get all calendars for the mailbox, retry if it fails + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxFolderStatistics' -cmdParams @{identity = $Mailbox.UserPrincipalName; FolderScope = 'Calendar' } -Anchor $Mailbox.UserPrincipalName | + # Set permissions for each calendar found + Where-Object { $_.FolderType -eq 'Calendar' } | ForEach-Object { + $SetRetryCount = 0 + do { + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MailboxFolderPermission' -cmdparams @{Identity = "$($Mailbox.UserPrincipalName):$($_.FolderId)"; User = 'Default'; AccessRights = $Settings.permissionlevel } -Anchor $Mailbox.UserPrincipalName + Write-LogMessage -API 'Standards' -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($Settings.permissionlevel)" -sev Info + $SetRetryCount = 3 + } catch { + # Set part + Start-Sleep -Milliseconds 250 + $SetRetryCount++ + } + } Until ($SetRetryCount -ge 3 ) + } + $GetRetryCount = 3 + } catch { + # Get part + Start-Sleep -Milliseconds 250 + $GetRetryCount++ + } + + } until ($GetRetryCount -ge 3) } catch { Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error } From e3476a83a9bdba52f991609bf383a4c220090060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 22 Dec 2023 01:48:46 +0100 Subject: [PATCH 30/84] Add alternate bool success variable --- .../Invoke-CIPPStandardcalDefault.ps1 | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 index 5f8ae73a18b9..88a903319e70 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 @@ -5,10 +5,11 @@ function Invoke-CIPPStandardcalDefault { #> param($Tenant, $Settings) If ($Settings.remediate) { - $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Started setting default calendar permissions for $($Mailboxes.Count) mailboxes." -sev Info # BRRRRRRRRRR + $global:UserSuccesses = 0 $Mailboxes | ForEach-Object -ThrottleLimit 25 -Parallel { Import-Module CIPPcore $Tenant = $Using:Tenant @@ -27,28 +28,29 @@ function Invoke-CIPPStandardcalDefault { do { try { New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MailboxFolderPermission' -cmdparams @{Identity = "$($Mailbox.UserPrincipalName):$($_.FolderId)"; User = 'Default'; AccessRights = $Settings.permissionlevel } -Anchor $Mailbox.UserPrincipalName - Write-LogMessage -API 'Standards' -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($Settings.permissionlevel)" -sev Info - $SetRetryCount = 3 + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($Settings.permissionlevel)" -sev Debug + $Success = $true } catch { # Set part Start-Sleep -Milliseconds 250 $SetRetryCount++ } - } Until ($SetRetryCount -ge 3 ) + } Until ($SetRetryCount -ge 3 -or $Success -eq $true) } - $GetRetryCount = 3 + $Success = $true + $global:UserSuccesses++ } catch { # Get part Start-Sleep -Milliseconds 250 $GetRetryCount++ } - } until ($GetRetryCount -ge 3) + } until ($GetRetryCount -ge 3 -or $Success -eq $true) } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error } } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Done setting default calendar permissions.' -sev Info + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Done setting default calendar permissions for $global:UserSuccesses out of $($Mailboxes.Count) mailboxes." -sev Info } } \ No newline at end of file From 3e1b0a001b165893bc43f661c9cd39ff47221afb Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 22 Dec 2023 10:21:38 -0500 Subject: [PATCH 31/84] Tenant Onboarding - Add missing accessAssignments --- .../Push-ExecOnboardTenantQueue.ps1 | 74 +++++++++++++++---- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 index abb5a1124c25..268106eef1e2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 @@ -8,7 +8,7 @@ Function Push-ExecOnboardTenantQueue { try { $DateFormat = '%Y-%m-%d %H:%M:%S' $Id = $QueueItem.id - Write-Host ($QueueItem.Roles | ConvertTo-Json) + #Write-Host ($QueueItem.Roles | ConvertTo-Json) $Logs = [System.Collections.Generic.List[object]]::new() $OnboardTable = Get-CIPPTable -TableName 'TenantOnboarding' $TenantOnboarding = Get-CIPPAzDataTableEntity @OnboardTable -Filter "RowKey eq '$Id'" @@ -108,32 +108,60 @@ Function Push-ExecOnboardTenantQueue { if ($OnboardingSteps.Step2.Status -eq 'succeeded') { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Checking group mapping' }) $AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id/accessAssignments" - if ($AccessAssignments.id) { + if ($AccessAssignments.id -and $QueueItem.AutoMapRoles -ne $true) { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Groups mapped' }) $OnboardingSteps.Step3.Status = 'succeeded' - $OnboardingSteps.Step3.Message = 'Your GDAP relationship has mapped security groups' + $OnboardingSteps.Step3.Message = 'Your GDAP relationship already has mapped security groups' } else { - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Starting group mapping' }) + $GroupSuccess = $false $OnboardingSteps.Step3.Status = 'running' $OnboardingSteps.Step3.Message = 'Mapping security groups' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop - $Table = Get-CIPPTable -TableName 'GDAPInvites' - $Invite = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$Id'" + $InviteTable = Get-CIPPTable -TableName 'GDAPInvites' + $Invite = Get-CIPPAzDataTableEntity @InviteTable -Filter "RowKey eq '$Id'" - if (!$Invite -and $QueueItem.Roles) { + if ($AccessAssignments.id -and !$Invite) { + $MissingRoles = [System.Collections.Generic.List[object]]::new() + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Relationship has existing access assignments, checking for missing mappings' }) + #Write-Host ($AccessAssignments | ConvertTo-Json -Depth 5) + if ($QueueItem.Roles -and $QueueItem.AutoMapRoles -eq $true) { + foreach ($Role in $QueueItem.Roles) { + if ($AccessAssignments.accessContainer.accessContainerid -notcontains $Role.GroupId) { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = "Adding missing group to relationship: $($Role.GroupName)" }) + $MissingRoles.Add([PSCustomObject]$Role) + } + } + + if (($MissingRoles | Measure-Object).Count -gt 0) { + $Invite = [PSCustomObject]@{ + 'PartitionKey' = 'invite' + 'RowKey' = $Id + 'InviteUrl' = 'https://admin.microsoft.com/AdminPortal/Home#/partners/invitation/granularAdminRelationships/{0}' -f $Id + 'RoleMappings' = [string](@($MissingRoles) | ConvertTo-Json -Depth 10 -Compress) + } + Add-CIPPAzDataTableEntity @InviteTable -Entity $Invite + } else { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'All roles have been mapped to the M365 GDAP security groups' }) + $OnboardingSteps.Step3.Status = 'succeeded' + $OnboardingSteps.Step3.Message = 'Groups mapped successfully' + $GroupSuccess = $true + } + } + } + + if (!$AccessAssignments.id -and !$Invite -and $QueueItem.Roles) { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'No access assignments found, using defined role mapping.' }) $MatchingRoles = [System.Collections.Generic.List[object]]::new() foreach ($Role in $QueueItem.Roles) { if ($Relationship.accessDetails.unifiedRoles.roleDefinitionId -contains $Role.roleDefinitionId) { $MatchingRoles.Add([PSCustomObject]$Role) - } } if (($MatchingRoles | Measure-Object).Count -gt 0 -and $QueueItem.AutoMapRoles -eq $true) { - $InviteTable = Get-CIPPTable -TableName 'GDAPInvites' $Invite = [PSCustomObject]@{ 'PartitionKey' = 'invite' 'RowKey' = $Id @@ -141,6 +169,7 @@ Function Push-ExecOnboardTenantQueue { 'RoleMappings' = [string](@($MatchingRoles) | ConvertTo-Json -Depth 10 -Compress) } Add-CIPPAzDataTableEntity @InviteTable -Entity $Invite + $GroupSuccess = $true } else { $TenantOnboarding.Status = 'failed' $OnboardingSteps.Step3.Status = 'failed' @@ -149,26 +178,44 @@ Function Push-ExecOnboardTenantQueue { } if ($Invite) { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'GDAP invite found, starting group/role mapping' }) $GroupMapStatus = Set-CIPPGDAPInviteGroups -Relationship $Relationship if ($GroupMapStatus) { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Groups mapped successfully' }) - $OnboardingSteps.Step3.Status = 'succeeded' - $OnboardingSteps.Step3.Message = 'Groups mapped successfully' + $OnboardingSteps.Step3.Message = 'Groups mapped successfully, checking access assignment status' + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + } else { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Group mapping failed' }) $TenantOnboarding.Status = 'failed' $OnboardingSteps.Step3.Status = 'failed' $OnboardingSteps.Step3.Message = 'Group mapping failed, check the log book for details.' } - } else { + } elseif (!$GroupSuccess) { $TenantOnboarding.Status = 'failed' $OnboardingSteps.Step3.Status = 'failed' $OnboardingSteps.Step3.Message = 'Failed to map security groups, no pending invite available' } + $x = 0 + do { + $x++ + $AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id/accessAssignments" + Start-Sleep -Seconds 10 + } while ($AccessAssignments.status -contains 'pending' -and $x -le 12) + + if ($AccessAssignments.status -notcontains 'pending') { + $OnboardingSteps.Step3.Message = 'Group check: Access assignments are mapped and active' + $OnboardingSteps.Step3.Status = 'succeeded' + } else { + $OnboardingSteps.Step3.Message = 'Group check: Access assignments are still pending, try again later' + $OnboardingSteps.Step3.Status = 'failed' + } } if ($QueueItem.AddMissingGroups -eq $true) { - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Checking for missing groups' }) + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Checking for missing groups for SAM user' }) $SamUserId = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/me?`$select=id").id $CurrentMemberships = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/me/transitiveMemberOf?`$select=id,displayName" foreach ($Role in $QueueItem.Roles) { @@ -184,6 +231,7 @@ Function Push-ExecOnboardTenantQueue { } } } + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'SAM user group check completed' }) } $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) From 5f86b4b3bba0bbe281f690f550e37ff07134a7cd Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 22 Dec 2023 10:27:46 -0500 Subject: [PATCH 32/84] ensure role definition id is present --- .../CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 index 268106eef1e2..caf72ed72733 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 @@ -129,7 +129,7 @@ Function Push-ExecOnboardTenantQueue { #Write-Host ($AccessAssignments | ConvertTo-Json -Depth 5) if ($QueueItem.Roles -and $QueueItem.AutoMapRoles -eq $true) { foreach ($Role in $QueueItem.Roles) { - if ($AccessAssignments.accessContainer.accessContainerid -notcontains $Role.GroupId) { + if ($AccessAssignments.accessContainer.accessContainerid -notcontains $Role.GroupId -and $Relationship.accessDetails.unifiedRoles.roleDefinitionId -contains $Role.roleDefinitionId) { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = "Adding missing group to relationship: $($Role.GroupName)" }) $MissingRoles.Add([PSCustomObject]$Role) } From 3e015fbfc48cfefe08445fdded3e691bc49e35b5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 22 Dec 2023 10:41:40 -0500 Subject: [PATCH 33/84] fix graph api test --- .../Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 index caf72ed72733..687af6d40b87 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 @@ -250,7 +250,7 @@ Function Push-ExecOnboardTenantQueue { Remove-CIPPCache -tenantsOnly $true } catch {} - $Tenant = Get-Tenants | Where-Object { $_.customerId -eq $Relationship.customer.tenantId } + $Tenant = Get-Tenants | Where-Object { $_.customerId -eq $Relationship.customer.tenantId } | Select-Object -First 1 if ($Tenant) { $y = 0 $Refreshing = $true @@ -289,7 +289,7 @@ Function Push-ExecOnboardTenantQueue { } if ($OnboardingSteps.Step4.Status -eq 'succeeded') { - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Testing API access' }) + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = "Testing API access for $($Tenant.defaultDomainName)" }) $OnboardingSteps.Step5.Status = 'running' $OnboardingSteps.Step5.Message = 'Testing API access' $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) @@ -297,7 +297,8 @@ Function Push-ExecOnboardTenantQueue { Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop try { - $UserCount = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$count=true&`$top=1" -ComplexFilter -tenantfilter $Tenant.defaultDomainName -CountOnly + #Write-Host ($Tenant | ConvertTo-Json) + $UserCount = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$count=true&`$top=1" -ComplexFilter -tenantid $Tenant.defaultDomainName -CountOnly } catch { $UserCount = 0 $ApiError = $_.Exception.Message From 42319480111ef28fe82d4795257534f5127c1db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 22 Dec 2023 20:25:32 +0100 Subject: [PATCH 34/84] Add counter that works this time --- .../Standards/Invoke-CIPPStandardcalDefault.ps1 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 index 88a903319e70..6192a9c7523a 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 @@ -8,13 +8,17 @@ function Invoke-CIPPStandardcalDefault { $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' Write-LogMessage -API 'Standards' -tenant $Tenant -message "Started setting default calendar permissions for $($Mailboxes.Count) mailboxes." -sev Info - # BRRRRRRRRRR - $global:UserSuccesses = 0 + # Thread safe counter + $UserSuccesses = [HashTable]::Synchronized(@{Counter = 0 }) + + # Set default calendar permissions for each mailbox. Run in parallel to speed up the process $Mailboxes | ForEach-Object -ThrottleLimit 25 -Parallel { Import-Module CIPPcore $Tenant = $Using:Tenant $Settings = $Using:Settings $Mailbox = $_ + $UserSuccesses = $Using:UserSuccesses + try { $GetRetryCount = 0 @@ -31,16 +35,16 @@ function Invoke-CIPPStandardcalDefault { Write-LogMessage -API 'Standards' -tenant $Tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($Settings.permissionlevel)" -sev Debug $Success = $true } catch { - # Set part + # Retry Set-MailboxFolderStatistics Start-Sleep -Milliseconds 250 $SetRetryCount++ } } Until ($SetRetryCount -ge 3 -or $Success -eq $true) } $Success = $true - $global:UserSuccesses++ + $UserSuccesses.Counter++ } catch { - # Get part + # Retry Get-MailboxFolderStatistics Start-Sleep -Milliseconds 250 $GetRetryCount++ } @@ -50,7 +54,7 @@ function Invoke-CIPPStandardcalDefault { Write-LogMessage -API 'Standards' -tenant $Tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error } } - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Done setting default calendar permissions for $global:UserSuccesses out of $($Mailboxes.Count) mailboxes." -sev Info + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Done setting default calendar permissions for $($UserSuccesses.Counter) out of $($Mailboxes.Count) mailboxes." -sev Info } } \ No newline at end of file From 975935334606b01e4c15da374b29d7dd8956fca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 22 Dec 2023 20:32:03 +0100 Subject: [PATCH 35/84] Better log message --- .../CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 index 6192a9c7523a..63805abaf905 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardcalDefault.ps1 @@ -54,7 +54,7 @@ function Invoke-CIPPStandardcalDefault { Write-LogMessage -API 'Standards' -tenant $Tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error } } - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Done setting default calendar permissions for $($UserSuccesses.Counter) out of $($Mailboxes.Count) mailboxes." -sev Info + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Successfully set default calendar permissions for $($UserSuccesses.Counter) out of $($Mailboxes.Count) mailboxes." -sev Info } } \ No newline at end of file From 9009f7e064c1aac7fcac43b119b15e300b74361d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 23 Dec 2023 23:16:31 +0100 Subject: [PATCH 36/84] Clearer CPV errors --- .../GraphHelper/Get-NormalizedError.ps1 | 2 ++ .../GraphHelper/New-GraphPOSTRequest.ps1 | 10 +++++-- .../CIPPCore/Public/Set-CIPPCPVConsent.ps1 | 27 +++++++++---------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-NormalizedError.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-NormalizedError.ps1 index 98ce8bdbf8dc..7a8044bc1c5f 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-NormalizedError.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-NormalizedError.ps1 @@ -25,6 +25,8 @@ function Get-NormalizedError { '*One or more added object references already exist for the following modified properties:*' { 'This user is already a member of this group.' } '*Microsoft.Exchange.Management.Tasks.MemberAlreadyExistsException*' { 'This user is already a member of this group.' } '*The property value exceeds the maximum allowed size (64KB)*' { 'One of the values exceeds the maximum allowed size (64KB).' } + '*Unable to initialize the authorization context*' { 'Your GDAP configuration does not allow us to write to this tenant, please check your group mappings and tenant onboarding.' } + '*Providers.Common.V1.CoreException*' { '403 (Access Denied) - We cannot connect to this tenant.' } Default { $message } } diff --git a/Modules/CIPPCore/Public/GraphHelper/New-GraphPOSTRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-GraphPOSTRequest.ps1 index 9b4848253d98..533b4144c9cd 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-GraphPOSTRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-GraphPOSTRequest.ps1 @@ -19,13 +19,19 @@ function New-GraphPOSTRequest ($uri, $tenantid, $body, $type, $scope, $AsApp, $N try { $ReturnedData = (Invoke-RestMethod -Uri $($uri) -Method $TYPE -Body $body -Headers $headers -ContentType 'application/json; charset=utf-8') } catch { - $ErrorMess = $($_.Exception.Message) $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message - if (!$Message) { $Message = $ErrorMess } + if ($Message -eq $null) { + try { + $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).message + } catch { + $Message = $($_.Exception.Message) + } + } throw $Message } return $ReturnedData } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' + } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 index 4391f3379817..a04398e20ad2 100644 --- a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 @@ -15,22 +15,21 @@ function Set-CIPPCPVConsent { try { $DeleteSP = New-GraphpostRequest -Type DELETE -noauthcheck $true -uri "https://api.partnercenter.microsoft.com/v1/customers/$($TenantFilter)/applicationconsents/$($ENV:applicationId)" -scope 'https://api.partnercenter.microsoft.com/.default' -tenantid $env:TenantID $Results.add("Deleted Service Principal from $TenantName") - } - catch { + } catch { $Results.add("Error deleting SP - $($_.Exception.Message)") } } try { $AppBody = @{ - ApplicationId = $($ENV:applicationId) - ApplicationGrants = @( + ApplicationId = $($ENV:applicationId) + ApplicationGrants = @( @{ - EnterpriseApplicationId = "00000003-0000-0000-c000-000000000000" - Scope = @( - "Application.ReadWrite.all", - "DelegatedPermissionGrant.ReadWrite.All", - "Directory.ReadWrite.All" + EnterpriseApplicationId = '00000003-0000-0000-c000-000000000000' + Scope = @( + 'Application.ReadWrite.all', + 'DelegatedPermissionGrant.ReadWrite.All', + 'Directory.ReadWrite.All' ) -Join ',' } ) @@ -50,13 +49,11 @@ function Set-CIPPCPVConsent { $Results.add("Successfully added CPV Application to tenant $($TenantName)") | Out-Null Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Added our Service Principal to $($TenantName): $($_.Exception.message)" -Sev 'Info' -tenant $TenantName -tenantId $TenantFilter - } - catch { - $ErrorMessage = $_.Exception.Message - if ($ErrorMessage -like '*409 (Conflict)*') { return @("We've already added our Service Principal to $($TenantName)") } - + } catch { + $ErrorMessage = Get-NormalizedError -message $_.Exception.Message + if ($ErrorMessage -like '*Permission entry already exists*') { return @("We've already added our Service Principal to $($TenantName)") } Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add our Service Principal to the client tenant $($TenantName): $($_.Exception.message)" -Sev 'Error' -tenant $TenantName -tenantId $TenantFilter - return @("Could not add our Service Principal to the client tenant $($TenantName): $($_.Exception.message)") + return @("Could not add our Service Principal to the client tenant $($TenantName): $ErrorMessage") } return $Results } From a396ad661da24bbd64f2539e270d2ce7fefc2a24 Mon Sep 17 00:00:00 2001 From: Roel van der Wegen Date: Mon, 25 Dec 2023 12:30:56 +0100 Subject: [PATCH 37/84] Initial permissions tweak --- Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 index a04398e20ad2..8de89293b96c 100644 --- a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 @@ -27,9 +27,9 @@ function Set-CIPPCPVConsent { @{ EnterpriseApplicationId = '00000003-0000-0000-c000-000000000000' Scope = @( - 'Application.ReadWrite.all', 'DelegatedPermissionGrant.ReadWrite.All', - 'Directory.ReadWrite.All' + 'Directory.ReadWrite.All', + 'AppRoleAssignment.ReadWrite.All' ) -Join ',' } ) From 321c6be6e475500f206762369ecfd1b1d85a8287 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 27 Dec 2023 15:06:38 +0100 Subject: [PATCH 38/84] changes to classic alerts --- .../Public/Entrypoints/Invoke-AddAlert.ps1 | 41 +- .../Push-CIPPAlertAdminPassword.ps1 | 21 + .../Push-CIPPAlertApnCertExpiry.ps1 | 29 ++ .../Push-CIPPAlertAppSecretExpiry.ps1 | 37 ++ .../Push-CIPPAlertDefenderMalware.ps1 | 16 + .../Push-CIPPAlertDefenderStatus.ps1 | 16 + .../Push-CIPPAlertDepTokenExpiry.ps1 | 33 ++ .../Push-CIPPAlertExpiringLicenses.ps1 | 18 + .../Entrypoints/Push-CIPPAlertMFAAdmins.ps1 | 39 ++ .../Push-CIPPAlertMFAAlertUsers.ps1 | 29 ++ .../Entrypoints/Push-CIPPAlertNewRole.ps1 | 37 ++ .../Entrypoints/Push-CIPPAlertNoCAConfig.ps1 | 21 + .../Push-CIPPAlertOverusedLicenses.ps1 | 27 ++ .../Entrypoints/Push-CIPPAlertQuotaUsed.ps1 | 20 + .../Push-CIPPAlertSecDefaultsUpsell.ps1 | 31 ++ .../Push-CIPPAlertSharepointQuota.ps1 | 23 + .../Push-CIPPAlertUnusedLicenses.ps1 | 27 ++ .../Push-CIPPAlertVppTokenExpiry.ps1 | 35 ++ .../Public/GraphHelper/Write-AlertMessage.ps1 | 16 + .../Public/New-CIPPGraphSubscription.ps1 | 25 +- .../Invoke-CIPPStandardMailContacts.ps1 | 2 +- Scheduler_Alert/function.json | 8 +- Scheduler_Alert/run.ps1 | 393 +----------------- 23 files changed, 532 insertions(+), 412 deletions(-) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderStatus.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 create mode 100644 Modules/CIPPCore/Public/GraphHelper/Write-AlertMessage.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 index 475147d04df3..bc45ab83aa68 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 @@ -10,13 +10,12 @@ Function Invoke-AddAlert { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value + $Tenants = $Request.body.tenantFilter $Results = foreach ($Tenant in $tenants) { try { $TenantID = if ($tenant -ne 'AllTenants') { (get-tenants | Where-Object -Property defaultDomainName -EQ $Tenant).customerId - } - else { + } else { 'AllTenants' } if ($Request.body.SetAlerts) { @@ -52,38 +51,30 @@ Function Invoke-AddAlert { $URL = ($request.headers.'x-ms-original-url').split('/api') | Select-Object -First 1 if ($Tenant -eq 'AllTenants') { Get-Tenants | ForEach-Object { - foreach ($eventType in $Request.body.EventTypes.value) { - $params = @{ - TenantFilter = $_.defaultDomainName - auditLogAPI = $true - operations = ($Request.body.Operations.value -join ',') - allowedLocations = ($Request.body.AllowedLocations.value -join ',') - BaseURL = $URL - EventType = $eventType - ExecutingUser = $Request.headers.'x-ms-client-principal' - } - Push-OutputBinding -Name Subscription -Value $Params + $params = @{ + TenantFilter = $_.defaultDomainName + auditLogAPI = $true + operations = 'Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General,DLP.All' + BaseURL = $URL + ExecutingUser = $Request.headers.'x-ms-client-principal' } + Push-OutputBinding -Name Subscription -Value $Params } - } - else { + } else { foreach ($eventType in $Request.body.EventTypes.value) { $params = @{ - TenantFilter = $tenant - auditLogAPI = $true - operations = ($Request.body.Operations.value -join ',') - allowedLocations = ($Request.body.AllowedLocations.value -join ',') - BaseURL = $URL - EventType = $eventType - ExecutingUser = $Request.headers.'x-ms-client-principal' + TenantFilter = $tenant + auditLogAPI = $true + operations = 'Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General,DLP.All' + BaseURL = $URL + ExecutingUser = $Request.headers.'x-ms-client-principal' } New-CIPPGraphSubscription @params } } "Successfully added Alert for $($Tenant) to queue." Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Successfully added Alert for $($Tenant) to queue." -Sev 'Info' - } - catch { + } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Failed to add Alert for for $($Tenant) to queue" -Sev 'Error' "Failed to add Alert for for $($Tenant) to queue $($_.Exception.message)" } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 new file mode 100644 index 000000000000..ba35ab1a326f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 @@ -0,0 +1,21 @@ + +function Push-CIPPAlertAdminPassword { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $QueueItem, + + $TriggerMetadata + ) + $AlertsTable = $QueueItem.AlertsTable + try { + New-GraphGETRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'&`$expand=principal" -tenantid $($QueueItem.tenant) | Where-Object { ($_.principalOrganizationId -EQ $QueueItem.tenantid) -and ($_.principal.'@odata.type' -eq '#microsoft.graph.user') } | ForEach-Object { + $LastChanges = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.principalId)?`$select=UserPrincipalName,lastPasswordChangeDateTime" -tenant $($QueueItem.tenant) + if ($LastChanges.LastPasswordChangeDateTime -gt (Get-Date).AddDays(-1)) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Admin password has been changed for $($LastChanges.UserPrincipalName) in last 24 hours" + } + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get admin password changes for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 new file mode 100644 index 000000000000..855b61a7e12c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 @@ -0,0 +1,29 @@ +function Push-CIPPAlertApnCertExpiry { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + $LastRunTable = $QueueItem.LastRunTable + try { + $Filter = "RowKey eq 'ApnCertExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter + $Yesterday = (Get-Date).AddDays(-1) + if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { + try { + $Apn = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate' -tenantid $QueueItem.tenant + if ($Apn.expirationDateTime -lt (Get-Date).AddDays(30) -and $Apn.expirationDateTime -gt (Get-Date).AddDays(-7)) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Intune: Apple Push Notification certificate for {0} is expiring on {1}' -f $Apn.appleIdentifier, $Apn.expirationDateTime) + } + } catch {} + } + $LastRun = @{ + RowKey = 'ApnCertExpiry' + PartitionKey = $QueueItem.tenantid + } + Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force + } catch { + # Error handling + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 new file mode 100644 index 000000000000..745f51b4ada3 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 @@ -0,0 +1,37 @@ +function Push-CIPPAlertAppSecretExpiry { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + $LastRunTable = $QueueItem.LastRunTable + + + try { + $Filter = "RowKey eq 'AppSecretExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter + $Yesterday = (Get-Date).AddDays(-1) + if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications?`$select=appId,displayName,passwordCredentials" -tenantid $QueueItem.tenant | ForEach-Object { + foreach ($App in $_) { + if ($App.passwordCredentials) { + foreach ($Credential in $App.passwordCredentials) { + if ($Credential.endDateTime -lt (Get-Date).AddDays(30) -and $Credential.endDateTime -gt (Get-Date).AddDays(-7)) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message ("Application '{0}' has secrets expiring on {1}" -f $App.displayName, $Credential.endDateTime) + } + } + } + } + } + $LastRun = @{ + RowKey = 'AppSecretExpiry' + PartitionKey = $QueueItem.tenantid + } + Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force + } + } catch { + # Error handling + } +} + diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 new file mode 100644 index 000000000000..b69ed1b50ab2 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 @@ -0,0 +1,16 @@ + +function Push-CIPPAlertDefenderMalware { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + try { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsDeviceMalwareStates?`$top=999&`$filter=tenantId eq '$($QueueItem.tenantid)'" | Where-Object { $_.malwareThreatState -eq 'Active' } | ForEach-Object { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.managedDeviceName): Malware found and active. Severity: $($_.MalwareSeverity). Malware name: $($_.MalwareDisplayName)" + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get malware data for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderStatus.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderStatus.ps1 new file mode 100644 index 000000000000..e9d4e06adae0 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderStatus.ps1 @@ -0,0 +1,16 @@ + +function Push-CIPPAlertDefenderStatus { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + try { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsProtectionStates?`$top=999&`$filter=tenantId eq '$($QueueItem.tenantid)'" | Where-Object { $_.realTimeProtectionEnabled -eq $false -or $_.MalwareprotectionEnabled -eq $false } | ForEach-Object { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.managedDeviceName) - Real Time Protection: $($_.realTimeProtectionEnabled) & Malware Protection: $($_.MalwareprotectionEnabled)" + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get defender status for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 new file mode 100644 index 000000000000..f9eef0d44c1d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 @@ -0,0 +1,33 @@ +function Push-CIPPAlertDepTokenExpiry { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + $LastRunTable = $QueueItem.LastRunTable + + + try { + $Filter = "RowKey eq 'DepTokenExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter + $Yesterday = (Get-Date).AddDays(-1) + if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { + try { + $DepTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $QueueItem.tenant).value + foreach ($Dep in $DepTokens) { + if ($Dep.tokenExpirationDateTime -lt (Get-Date).AddDays(30) -and $Dep.tokenExpirationDateTime -gt (Get-Date).AddDays(-7)) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime) + } + } + } catch {} + $LastRun = @{ + RowKey = 'DepTokenExpiry' + PartitionKey = $QueueItem.tenantid + } + Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force + } + } catch { + # Error handling + } + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 new file mode 100644 index 000000000000..bfe19720a96c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 @@ -0,0 +1,18 @@ +function Push-CIPPAlertExpiringLicenses { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + + + try { + Get-CIPPLicenseOverview -TenantFilter $QueueItem.tenant | Where-Object -Property [int]TimeUntilRenew -LT 29 | ForEach-Object { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.License) will expire in $($_.TimeUntilRenew) days" + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + } + } + diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 new file mode 100644 index 000000000000..427dd44a6e6c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 @@ -0,0 +1,39 @@ +function Push-CIPPAlertMFAAdmins { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + try { + $StrongMFAMethods = '#microsoft.graph.fido2AuthenticationMethod', '#microsoft.graph.phoneAuthenticationMethod', '#microsoft.graph.passwordlessmicrosoftauthenticatorauthenticationmethod', '#microsoft.graph.softwareOathAuthenticationMethod', '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod' + $AdminList = (New-GraphGETRequest -uri "https://graph.microsoft.com/beta/directoryRoles?`$expand=members" -tenantid $($QueueItem.tenant) | Where-Object -Property roleTemplateId -NE 'd29b2b05-8046-44ba-8758-1e26182fcf32').members | Where-Object { $_.userPrincipalName -ne $null -and $_.Usertype -eq 'Member' -and $_.accountEnabled -eq $true } | Sort-Object UserPrincipalName -Unique + $CAPolicies = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies' -tenantid $QueueItem.tenant -ErrorAction Stop) + foreach ($Policy in $CAPolicies) { + if ($policy.grantControls.customAuthenticationFactors -eq 'RequireDuoMfa') { + $DuoActive = $true + } + } + if (!$DuoActive) { + $AdminList | ForEach-Object { + $CARegistered = $null + try { + New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.ID)/authentication/Methods" -tenantid $($QueueItem.tenant) | ForEach-Object { + if ($_.'@odata.type' -in $StrongMFAMethods) { + $CARegistered = $true + } + } + if ($_.StrongAuthenticationRequirements.StrongAuthenticationRequirement.state -eq $null -and $CARegistered -ne $true) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Admin $($_.UserPrincipalName) is enabled but does not have any form of MFA configured." + } + } catch { + # Error handling here if needed + } + } + } else { + Write-LogMessage -message 'Potentially using Duo for MFA, could not check MFA status for Admins with 100% accuracy' -API 'MFA Alerts - Informational' -tenant $QueueItem.tenant -sev Info + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get MFA status for admins for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 new file mode 100644 index 000000000000..db5a57fe1c40 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 @@ -0,0 +1,29 @@ +function Push-CIPPAlertMFAAlertUsers { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + + + try { + $users = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users?`$select=userPrincipalName,id" -tenantid $($QueueItem.tenant) -erroraction stop + $StrongMFAMethods = '#microsoft.graph.fido2AuthenticationMethod', '#microsoft.graph.phoneAuthenticationMethod', '#microsoft.graph.passwordlessmicrosoftauthenticatorauthenticationmethod', '#microsoft.graph.softwareOathAuthenticationMethod', '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod' + $users | Where-Object { $_.Usertype -eq 'Member' -and $_.BlockCredential -eq $false } | ForEach-Object { + trhy { + $CARegistered = $false + (New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.ObjectID)/authentication/Methods" -tenantid $($QueueItem.tenant)) | ForEach-Object { + if ($_.'@odata.type' -notin $StrongMFAMethods) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "User $($_.UserPrincipalName) is enabled but does not have any form of MFA configured." + } + } + } catch { + $CARegistered = $false + } + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get MFA status for users for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} + diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 new file mode 100644 index 000000000000..3847a3d21cce --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 @@ -0,0 +1,37 @@ +function Push-CIPPAlertNewRole { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + $Deltatable = $QueueItem.DeltaTable + try { + $Filter = "PartitionKey eq 'AdminDelta' and RowKey eq '{0}'" -f $QueueItem.tenantid + $AdminDelta = (Get-CIPPAzDataTableEntity @Deltatable -Filter $Filter).delta | ConvertFrom-Json -ErrorAction SilentlyContinue + $NewDelta = (New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/directoryRoles?`$expand=members" -tenantid $QueueItem.tenant) | Select-Object displayname, Members | ForEach-Object { + @{ + GroupName = $_.displayname + Members = $_.Members.UserPrincipalName + } + } + $NewDeltatoSave = $NewDelta | ConvertTo-Json -Depth 10 -Compress -ErrorAction SilentlyContinue | Out-String + $DeltaEntity = @{ + PartitionKey = 'AdminDelta' + RowKey = [string]$QueueItem.tenantid + delta = "$NewDeltatoSave" + } + Add-CIPPAzDataTableEntity @DeltaTable -Entity $DeltaEntity -Force + + if ($AdminDelta) { + foreach ($Group in $NewDelta) { + $OldDelta = $AdminDelta | Where-Object { $_.GroupName -eq $Group.GroupName } + $Group.members | Where-Object { $_ -notin $OldDelta.members } | ForEach-Object { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$_ has been added to the $($Group.GroupName) Role" + } + } + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get get role changes for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 new file mode 100644 index 000000000000..72bd6d90b151 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 @@ -0,0 +1,21 @@ +function Push-CIPPAlertNoCAConfig { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + + try { + $CAAvailable = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $QueueItem.Tenant -erroraction stop).serviceplans + if ('AAD_PREMIUM' -in $CAAvailable.servicePlanName) { + $CAPolicies = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies' -tenantid $QueueItem.Tenant) + if (!$CAPolicies.id) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message 'Conditional Access is available, but no policies could be found.' + } + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 new file mode 100644 index 000000000000..bd96b03266cd --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 @@ -0,0 +1,27 @@ +function Push-CIPPAlertOverusedLicenses { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + + + try { + $LicenseTable = Get-CIPPTable -TableName ExcludedLicenses + $ExcludedSkuList = Get-CIPPAzDataTableEntity @LicenseTable + New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $QueueItem.tenant | ForEach-Object { + $skuid = $_ + foreach ($sku in $skuid) { + if ($sku.skuId -in $ExcludedSkuList.GUID) { continue } + $PrettyName = ($ConvertTable | Where-Object { $_.GUID -eq $sku.skuid }).'Product_Display_Name' | Select-Object -Last 1 + if (!$PrettyName) { $PrettyName = $sku.skuPartNumber } + if ($sku.prepaidUnits.enabled - $sku.consumedUnits -lt 0) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$PrettyName has Overused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." + } + } + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 new file mode 100644 index 000000000000..9adaa80ceca6 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 @@ -0,0 +1,20 @@ +function Push-CIPPAlertQuotaUsed { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + + + try { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getMailboxUsageDetail(period='D7')?`$format=application/json" -tenantid $QueueItem.tenant | ForEach-Object { + $PercentLeft = [math]::round($_.StorageUsedInBytes / $_.prohibitSendReceiveQuotaInBytes * 100) + if ($PercentLeft -gt 90) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.UserPrincipalName): Mailbox has less than 10% space left. Mailbox is $PercentLeft% full" + } + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 new file mode 100644 index 000000000000..1d702027bfe4 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 @@ -0,0 +1,31 @@ +function Push-CIPPAlertSecDefaultsUpsell { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + $LastRunTable = $QueueItem.LastRunTable + + try { + $Filter = "RowKey eq 'SecDefaultsUpsell' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter + $Yesterday = (Get-Date).AddDays(-1) + if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { + try { + $SecDefaults = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $QueueItem.tenant) + if ($SecDefaults.isEnabled -eq $false -and $SecDefaults.securityDefaultsUpsell.action -in @('autoEnable', 'autoEnabledNotify')) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Security Defaults will be automatically enabled on {0}' -f $SecDefaults.securityDefaultsUpsell.dueDateTime) + } + } catch {} + $LastRun = @{ + RowKey = 'SecDefaultsUpsell' + PartitionKey = $QueueItem.tenantid + } + Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force + } + } catch { + # Error handling + } +} + diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 new file mode 100644 index 000000000000..1c5754b621ef --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 @@ -0,0 +1,23 @@ + +function Push-CIPPAlertSharepointQuota { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + Try { + $tenantName = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $QueueItem.Tenant | Where-Object { $_.isInitial -eq $true }).id.Split('.')[0] + $sharepointToken = (Get-GraphToken -scope "https://$($tenantName)-admin.sharepoint.com/.default" -tenantid $QueueItem.Tenant) + $sharepointToken.Add('accept', 'application/json') + $sharepointQuota = (Invoke-RestMethod -Method 'GET' -Headers $sharepointToken -Uri "https://$($tenantName)-admin.sharepoint.com/_api/StorageQuotas()?api-version=1.3.2" -ErrorAction Stop).value + if ($sharepointQuota) { + $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) + if ($UsedStoragePercentage -gt 90) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%" + } + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get SharePoint quota for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 new file mode 100644 index 000000000000..c2e7beeb6522 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 @@ -0,0 +1,27 @@ +function Push-CIPPAlertUnusedLicenses { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + + + try { + $LicenseTable = Get-CIPPTable -TableName ExcludedLicenses + $ExcludedSkuList = Get-CIPPAzDataTableEntity @LicenseTable + New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $QueueItem.tenant | ForEach-Object { + $skuid = $_ + foreach ($sku in $skuid) { + if ($sku.skuId -in $ExcludedSkuList.GUID) { continue } + $PrettyName = ($ConvertTable | Where-Object { $_.GUID -eq $sku.skuid }).'Product_Display_Name' | Select-Object -Last 1 + if (!$PrettyName) { $PrettyName = $sku.skuPartNumber } + if ($sku.prepaidUnits.enabled - $sku.consumedUnits -gt 0) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$PrettyName has unused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." + } + } + } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + } + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 new file mode 100644 index 000000000000..71844ad0865b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 @@ -0,0 +1,35 @@ +function Push-CIPPAlertVppTokenExpiry { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $QueueItem, + $TriggerMetadata + ) + $LastRunTable = $QueueItem.LastRunTable + + try { + $Filter = "RowKey eq 'VppTokenExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter + $Yesterday = (Get-Date).AddDays(-1) + if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { + try { + $VppTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/vppTokens' -tenantid $QueueItem.tenant).value + foreach ($Vpp in $VppTokens) { + if ($Vpp.state -ne 'valid') { + Write-AlertMessage -tenant $($QueueItem.tenant) -message 'Apple Volume Purchase Program Token is not valid, new token required' + } + if ($Vpp.expirationDateTime -lt (Get-Date).AddDays(30) -and $Vpp.expirationDateTime -gt (Get-Date).AddDays(-7)) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Apple Volume Purchase Program token expiring on {0}' -f $Vpp.expirationDateTime) + } + } + } catch {} + $LastRun = @{ + RowKey = 'VppTokenExpiry' + PartitionKey = $QueueItem.tenantid + } + Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force + } + } catch { + # Error handling + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/GraphHelper/Write-AlertMessage.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-AlertMessage.ps1 new file mode 100644 index 000000000000..567f1c05cb77 --- /dev/null +++ b/Modules/CIPPCore/Public/GraphHelper/Write-AlertMessage.ps1 @@ -0,0 +1,16 @@ +function Write-AlertMessage($message, $tenant = 'None', $tenantId = $null) { + <# + .FUNCTIONALITY + Internal + #> + $Table = Get-CIPPTable -tablename cachealerts + $PartitionKey = (Get-Date -UFormat '%Y%m%d').ToString() + $TableRow = @{ + 'Tenant' = [string]$tenant + 'Message' = [string]$message + 'PartitionKey' = $PartitionKey + 'RowKey' = ([guid]::NewGuid()).ToString() + } + $Table.Entity = $TableRow + Add-CIPPAzDataTableEntity @Table | Out-Null +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 649a50c452df..52600af7f182 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -9,7 +9,7 @@ function New-CIPPGraphSubscription { $operations, $Resource, $EventType, - $APIName = "Create Webhook", + $APIName = 'Create Webhook', $ExecutingUser, [Switch]$Recreate ) @@ -20,24 +20,21 @@ function New-CIPPGraphSubscription { if ($auditLogAPI) { $AuditLogParams = @{ webhook = @{ - "address" = "$BaseURL/API/Publicwebhooks?EventType=$EventType&CIPPID=$CIPPID" + 'address' = "$BaseURL/API/Publicwebhooks?EventType=$EventType&CIPPID=$CIPPID" } } | ConvertTo-Json Write-Host ($AuditLogParams) - $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=$EventType&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope "https://manage.office.com/.default" -body $AuditLogparams -verbose + $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=$EventType&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose $WebhookRow = @{ PartitionKey = [string]$TenantFilter RowKey = [string]$CIPPID - EventType = [string]$EventType - Resource = "M365AuditLogs" - Operations = [string]$operations - AllowedLocations = [string]$AllowedLocations - Expiration = "None" + Resource = 'M365AuditLogs' + Expiration = 'Does Not Expire' WebhookNotificationUrl = [string]$Auditlog.webhook.address } $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow - Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter)" -Sev "Info" -tenant $TenantFilter + Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter)" -Sev 'Info' -tenant $TenantFilter } else { # First check if there is an exsiting Webhook in place @@ -46,7 +43,7 @@ function New-CIPPGraphSubscription { $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } if (($MatchedWebhook | Measure-Object).count -eq 0 -or $Recreate) { - $expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") + $expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $params = @{ changeType = $TypeofSubscription notificationUrl = "https://$BaseURL/API/PublicWebhooks?EventType=$EventType&CIPPID=$($CIPPID)&Type=GraphSubscription" @@ -55,7 +52,7 @@ function New-CIPPGraphSubscription { } | ConvertTo-Json - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/subscriptions" -tenantid $TenantFilter -type POST -body $params -verbose + $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/subscriptions' -tenantid $TenantFilter -type POST -body $params -verbose #If creation is succesfull, we store the GUID in the storage table webhookTable to make sure we can check against this later on. #We store the GUID as rowkey, the event type, the resource, and the expiration date as properties, we also add the Tenant name so we can easily find this later on. #We don't store the return, because Ms decided that a renewal or re-authenticate does not change the url, but does change the id... @@ -72,14 +69,14 @@ function New-CIPPGraphSubscription { $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow #todo: add remove webhook function, add check webhook function, add list webhooks function #add refresh webhook function based on table. - Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Graph Webhook subscription for $($TenantFilter)" -Sev "Info" -tenant $TenantFilter + Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Graph Webhook subscription for $($TenantFilter)" -Sev 'Info' -tenant $TenantFilter } else { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Existing Graph Webhook subscription for $($TenantFilter) found" -Sev "Info" -tenant $TenantFilter + Write-LogMessage -user $ExecutingUser -API $APIName -message "Existing Graph Webhook subscription for $($TenantFilter) found" -Sev 'Info' -tenant $TenantFilter } } return "Created Webhook subscription for $($TenantFilter)" } catch { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to create Webhook Subscription: $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter + Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to create Webhook Subscription: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter Return "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)" } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 index 73e59b351a78..738420ebe73a 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 @@ -18,7 +18,7 @@ function Invoke-CIPPStandardMailContacts { { $Contacts.GeneralContact } { $body | Add-Member -NotePropertyName privacyProfile -NotePropertyValue @{contactEmail = $Contacts.GeneralContact } } } Write-Host (ConvertTo-Json -InputObject $body) - New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/organization/$($TenantID.id)" -Type patch -Body (ConvertTo-Json -InputObject $body) -ContentType 'application/json' + New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/organization/$($TenantID.id)" -asApp $true -Type patch -Body (ConvertTo-Json -InputObject $body) -ContentType 'application/json' Write-LogMessage -API 'Standards' -tenant $tenant -message "Contact email's set." -sev Info } catch { Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set contact emails: $($_.exception.message)" -sev Error diff --git a/Scheduler_Alert/function.json b/Scheduler_Alert/function.json index 2d4ea9094b24..b8758df62b9d 100644 --- a/Scheduler_Alert/function.json +++ b/Scheduler_Alert/function.json @@ -4,6 +4,12 @@ "name": "tenant", "direction": "in", "type": "activityTrigger" + }, + { + "type": "queue", + "direction": "out", + "name": "QueueItem", + "queueName": "CIPPGenericQueue" } ] -} \ No newline at end of file +} diff --git a/Scheduler_Alert/run.ps1 b/Scheduler_Alert/run.ps1 index 21f2bc61d55d..53f0ea216065 100644 --- a/Scheduler_Alert/run.ps1 +++ b/Scheduler_Alert/run.ps1 @@ -5,396 +5,47 @@ try { $Table = Get-CIPPTable -Table SchedulerConfig if ($Tenant.tag -eq 'AllTenants') { $Filter = "RowKey eq 'AllTenants' and PartitionKey eq 'Alert'" - } - else { + } else { $Filter = "RowKey eq '{0}' and PartitionKey eq 'Alert'" -f $Tenant.tenantid } $Alerts = Get-CIPPAzDataTableEntity @Table -Filter $Filter - $ConfigFilter = "RowKey eq 'CippNotifications' and PartitionKey eq 'CippNotifications'" - $Config = [pscustomobject](Get-CIPPAzDataTableEntity @Table -Filter $ConfigFilter) $DeltaTable = Get-CIPPTable -Table DeltaCompare $LastRunTable = Get-CIPPTable -Table AlertLastRun - - $ShippedAlerts = switch ($Alerts) { - { $_.'AdminPassword' -eq $true } { - try { - New-GraphGETRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'&`$expand=principal" -tenantid $($tenant.tenant) | Where-Object { ($_.principalOrganizationId -EQ $tenant.tenantid) -and ($_.principal.'@odata.type' -eq '#microsoft.graph.user') } | ForEach-Object { - $LastChanges = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.principalId)?`$select=UserPrincipalName,lastPasswordChangeDateTime" -tenant $($tenant.tenant) - if ($LastChanges.LastPasswordChangeDateTime -gt (Get-Date).AddDays(-1)) { "Admin password has been changed for $($LastChanges.UserPrincipalName) in last 24 hours" } - } - - } - catch { - "Could not get admin password changes for $($Tenant.tenant): $(Get-NormalizedError -message $_.Exception.message)" - } - } - { $_.'DefenderMalware' -eq $true } { - try { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsDeviceMalwareStates?`$top=999&`$filter=tenantId eq '$($Tenant.tenantid)'" | Where-Object { $_.malwareThreatState -eq 'Active' } | ForEach-Object { - "$($_.managedDeviceName): Malware found and active. Severity: $($_.MalwareSeverity). Malware name: $($_.MalwareDisplayName)" - } - } - catch { - - } - } - - { $_.'DefenderStatus' -eq $true } { - try { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsProtectionStates?`$top=999&`$filter=tenantId eq '$($Tenant.tenantid)'" | Where-Object { $_.realTimeProtectionEnabled -eq $false -or $_.MalwareprotectionEnabled -eq $false } | ForEach-Object { - "$($_.managedDeviceName) - Real Time Protection: $($_.realTimeProtectionEnabled) & Malware Protection: $($_.MalwareprotectionEnabled)" - } - } - catch { - - } - } - { $_.'SharepointQuota' -eq $true } { - Try { - $tenantName = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/domains" -tenantid $Tenant.Tenant | Where-Object { $_.isInitial -eq $true }).id.Split(".")[0] - $sharepointToken = (Get-GraphToken -scope "https://$($tenantName)-admin.sharepoint.com/.default" -tenantid $Tenant.Tenant) - $sharepointToken.Add('accept', 'application/json') - $sharepointQuota = (Invoke-RestMethod -Method "GET" -Headers $sharepointToken -Uri "https://$($tenantName)-admin.sharepoint.com/_api/StorageQuotas()?api-version=1.3.2" -ErrorAction Stop).value - if ($sharepointQuota) { - $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) - if ($UsedStoragePercentage -gt 90) { - "SharePoint Storage is at $($UsedStoragePercentage)%" - } - } - } - catch { - - } - } - { $_.'MFAAdmins' -eq $true } { - try { - $StrongMFAMethods = '#microsoft.graph.fido2AuthenticationMethod', '#microsoft.graph.phoneAuthenticationMethod', '#microsoft.graph.passwordlessmicrosoftauthenticatorauthenticationmethod', '#microsoft.graph.softwareOathAuthenticationMethod', '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod' - $AdminList = (New-GraphGETRequest -uri "https://graph.microsoft.com/beta/directoryRoles?`$expand=members" -tenantid $($tenant.tenant) | Where-Object -Property roleTemplateId -NE 'd29b2b05-8046-44ba-8758-1e26182fcf32').members | Where-Object { $_.userPrincipalName -ne $null -and $_.Usertype -eq 'Member' -and $_.accountEnabled -eq $true } | Sort-Object UserPrincipalName -Unique - $CAPolicies = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies' -tenantid $Tenant.tenant -ErrorAction Stop) - foreach ($Policy in $CAPolicies) { - if ($policy.grantControls.customAuthenticationFactors -eq 'RequireDuoMfa') { - $DuoActive = $true - } - } - if (!$DuoActive) { - $AdminList | ForEach-Object { - $CARegistered = $null - try { - (New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.ID)/authentication/Methods" -tenantid $($tenant.tenant)) | ForEach-Object { - if ($_.'@odata.type' -in $StrongMFAMethods) { - $CARegistered = $true; - } - } - if ($_.StrongAuthenticationRequirements.StrongAuthenticationRequirement.state -eq $null -and $CARegistered -ne $true) { "Admin $($_.UserPrincipalName) is enabled but does not have any form of MFA configured." } - } - catch { - } - } - } - else { - Write-LogMessage -message "Potentially using Duo for MFA, could not check MFA status for Admins with 100% accuracy" -API 'MFA Alerts - Informational' -tenant $tenant.tenant -sev Info - - } - } - catch { - "Could not get MFA status for admins for $($Tenant.tenant): $(Get-NormalizedError -message $_.Exception.message)" - } - } - { $_.'MFAAlertUsers' -eq $true } { - try { - $users = Get-CIPPMSolUsers -tenant $tenant.tenant - $StrongMFAMethods = '#microsoft.graph.fido2AuthenticationMethod', '#microsoft.graph.phoneAuthenticationMethod', '#microsoft.graph.passwordlessmicrosoftauthenticatorauthenticationmethod', '#microsoft.graph.softwareOathAuthenticationMethod', '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod' - - $users | Where-Object { $_.Usertype -eq 'Member' -and $_.BlockCredential -eq $false } | ForEach-Object { - try { - (New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.ObjectID)/authentication/Methods" -tenantid $($tenant.tenant)) | ForEach-Object { - if ($_.'@odata.type' -in $StrongMFAMethods -and !$CARegistered) { - $CARegistered = $true; - } } - if ($_.StrongAuthenticationRequirements.StrongAuthenticationRequirement.state -eq $null -and $CARegistered -eq $false) { "User $($_.UserPrincipalName) is enabled but does not have any form of MFA configured." } - } - catch { - $CARegistered = $false - } - } - - } - catch { - "Could not get MFA status for users for $($Tenant.tenant): $(Get-NormalizedError -message $_.Exception.message)" - - } - } - - { $_.'NewRole' -eq $true } { - try { - $Filter = "PartitionKey eq 'AdminDelta' and RowKey eq '{0}'" -f $Tenant.tenantid - $AdminDelta = (Get-CIPPAzDataTableEntity @Deltatable -Filter $Filter).delta | ConvertFrom-Json -ErrorAction SilentlyContinue - $NewDelta = (New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/directoryRoles?`$expand=members" -tenantid $Tenant.tenant) | Select-Object displayname, Members | ForEach-Object { - @{ - GroupName = $_.displayname - Members = $_.Members.UserPrincipalName - } - } - $NewDeltatoSave = $NewDelta | ConvertTo-Json -Depth 10 -Compress -ErrorAction SilentlyContinue | Out-String - $DeltaEntity = @{ - PartitionKey = 'AdminDelta' - RowKey = [string]$Tenant.tenantid - delta = "$NewDeltatoSave" - } - Add-CIPPAzDataTableEntity @DeltaTable -Entity $DeltaEntity -Force - - if ($AdminDelta) { - foreach ($Group in $NewDelta) { - $OldDelta = $AdminDelta | Where-Object { $_.GroupName -eq $Group.GroupName } - $Group.members | Where-Object { $_ -notin $OldDelta.members } | ForEach-Object { - "$_ has been added to the $($Group.GroupName) Role" - } - } - } - } - catch { - "Could not get get role changes for $($Tenant.tenant): $(Get-NormalizedError -message $_.Exception.message)" - - } - } - { $_.'QuotaUsed' -eq $true } { - try { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getMailboxUsageDetail(period='D7')?`$format=application/json" -tenantid $Tenant.tenant | ForEach-Object { - $PercentLeft = [math]::round($_.StorageUsedInBytes / $_.prohibitSendReceiveQuotaInBytes * 100) - if ($PercentLeft -gt 90) { "$($_.UserPrincipalName): Mailbox has less than 10% space left. Mailbox is $PercentLeft% full" } - } - } - catch { - - } - } - { $_.'ExpiringLicenses' -eq $true } { - try { - Get-CIPPLicenseOverview -TenantFilter $Tenant.tenant | Where-Object -Property [int]TimeUntilRenew -LT 29 | ForEach-Object { - "$($_.License) will expire in $($_.TimeUntilRenew) days" - } - } - catch { - - } - } - { $_.'NoCAConfig' -eq $true } { - try { - $CAAvailable = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $Tenant.Tenant -erroraction stop).serviceplans - if ('AAD_PREMIUM' -in $CAAvailable.servicePlanName) { - $CAPolicies = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies' -tenantid $Tenant.Tenant) - if (!$CAPolicies.id) { - 'Conditional Access is available, but no policies could be found.' - } - } - } - catch { - } - } - { $_.'UnusedLicenses' -eq $true } { - try { - #$ConvertTable = Import-Csv Conversiontable.csv - $LicenseTable = Get-CIPPTable -TableName ExcludedLicenses - $ExcludedSkuList = Get-CIPPAzDataTableEntity @LicenseTable - New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $Tenant.tenant | ForEach-Object { - $skuid = $_ - foreach ($sku in $skuid) { - if ($sku.skuId -in $ExcludedSkuList.GUID) { continue } - $PrettyName = ($ConvertTable | Where-Object { $_.GUID -eq $_.skuid }).'Product_Display_Name' | Select-Object -Last 1 - if (!$PrettyName) { $PrettyName = $sku.skuPartNumber } - if ($sku.prepaidUnits.enabled - $sku.consumedUnits -gt 0) { - "$PrettyName has unused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." - } - } - } - } - catch { - - } - } - { $_.'OverusedLicenses' -eq $true } { - try { - #$ConvertTable = Import-Csv Conversiontable.csv - $LicenseTable = Get-CIPPTable -TableName ExcludedLicenses - $ExcludedSkuList = Get-CIPPAzDataTableEntity @LicenseTable - New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $Tenant.tenant | ForEach-Object { - $skuid = $_ - foreach ($sku in $skuid) { - if ($sku.skuId -in $ExcludedSkuList.GUID) { continue } - $PrettyName = ($ConvertTable | Where-Object { $_.GUID -eq $sku.skuid }).'Product_Display_Name' | Select-Object -Last 1 - if (!$PrettyName) { $PrettyName = $sku.skuPartNumber } - if ($sku.prepaidUnits.enabled - $sku.consumedUnits -lt 0) { - "$PrettyName has Overused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." - } - } - } - } - catch { - - } - } - - { $_.'AppSecretExpiry' -eq $true } { - try { - $Filter = "RowKey eq 'AppSecretExpiry' and PartitionKey eq '{0}'" -f $Tenant.tenantid - $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter - $Yesterday = (Get-Date).AddDays(-1) - if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications?`$select=appId,displayName,passwordCredentials" -tenantid $Tenant.tenant | ForEach-Object { - foreach ($App in $_) { - if ($App.passwordCredentials) { - foreach ($Credential in $App.passwordCredentials) { - if ($Credential.endDateTime -lt (Get-Date).AddDays(30) -and $Credential.endDateTime -gt (Get-Date).AddDays(-7)) { - "Application '{0}' has secrets expiring on {1}" -f $App.displayName, $Credential.endDateTime - } - } - } - } - } - $LastRun = @{ - RowKey = 'AppSecretExpiry' - PartitionKey = $Tenant.tenantid - } - Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force - } - } - catch { - #$Message = 'Exception on line {0} - {1}' -f $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - #Write-LogMessage -message $Message -API 'Alerts' -tenant $tenant.tenant -sev Error - } - } - { $_.'ApnCertExpiry' -eq $true } { - try { - $Filter = "RowKey eq 'ApnCertExpiry' and PartitionKey eq '{0}'" -f $Tenant.tenantid - $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter - $Yesterday = (Get-Date).AddDays(-1) - if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { - try { - $Apn = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate' -tenantid $Tenant.tenant - if ($Apn.expirationDateTime -lt (Get-Date).AddDays(30) -and $Apn.expirationDateTime -gt (Get-Date).AddDays(-7)) { - 'Intune: Apple Push Notification certificate for {0} is expiring on {1}' -f $Apn.appleIdentifier, $Apn.expirationDateTime - } - } - catch {} - } - $LastRun = @{ - RowKey = 'ApnCertExpiry' - PartitionKey = $Tenant.tenantid - } - Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force - } - catch { - #$Message = 'Exception on line {0} - {1}' -f $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - #Write-LogMessage -message $Message -API 'Alerts' -tenant $tenant.tenant -sev Error - } - } - { $_.'VppTokenExpiry' -eq $true } { - try { - $Filter = "RowKey eq 'VppTokenExpiry' and PartitionKey eq '{0}'" -f $Tenant.tenantid - $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter - $Yesterday = (Get-Date).AddDays(-1) - if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { - try { - $VppTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/vppTokens' -tenantid $Tenant.tenant).value - foreach ($Vpp in $VppTokens) { - if ($Vpp.state -ne 'valid') { - 'Apple Volume Purchase Program Token is not valid, new token required' - } - if ($Vpp.expirationDateTime -lt (Get-Date).AddDays(30) -and $Vpp.expirationDateTime -gt (Get-Date).AddDays(-7)) { - 'Apple Volume Purchase Program token expiring on {0}' -f $Vpp.expirationDateTime - } - } - } - catch {} - $LastRun = @{ - RowKey = 'VppTokenExpiry' - PartitionKey = $Tenant.tenantid - } - Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force - } - } - catch { - #$Message = 'Exception on line {0} - {1}' -f $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - #Write-LogMessage -message $Message -API 'Alerts' -tenant $tenant.tenant -sev Error - } - } - { $_.'DepTokenExpiry' -eq $true } { - try { - $Filter = "RowKey eq 'DepTokenExpiry' and PartitionKey eq '{0}'" -f $Tenant.tenantid - $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter - $Yesterday = (Get-Date).AddDays(-1) - if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { - try { - $DepTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $Tenant.tenant).value - foreach ($Dep in $DepTokens) { - if ($Dep.tokenExpirationDateTime -lt (Get-Date).AddDays(30) -and $Dep.tokenExpirationDateTime -gt (Get-Date).AddDays(-7)) { - 'Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime - } - } - } - catch {} - $LastRun = @{ - RowKey = 'DepTokenExpiry' - PartitionKey = $Tenant.tenantid - } - Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force - } - } - catch { - #$Message = 'Exception on line {0} - {1}' -f $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - #Write-LogMessage -message $Message -API 'Alerts' -tenant $tenant.tenant -sev Error - } - } - { $_.'SecDefaultsUpsell' -eq $true } { - try { - $Filter = "RowKey eq 'SecDefaultsUpsell' and PartitionKey eq '{0}'" -f $Tenant.tenantid - $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter - $Yesterday = (Get-Date).AddDays(-1) - if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { - try { - $SecDefaults = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $Tenant.tenant) - if ($SecDefaults.isEnabled -eq $false -and $SecDefaults.securityDefaultsUpsell.action -in @('autoEnable', 'autoEnabledNotify')) { - 'Security Defaults will be automatically enabled on {0}' -f $SecDefaults.securityDefaultsUpsell.dueDateTime - } - } - catch {} - $LastRun = @{ - RowKey = 'SecDefaultsUpsell' - PartitionKey = $Tenant.tenantid - } - Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force - } - } - catch { - #$Message = 'Exception on line {0} - {1}' -f $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - #Write-LogMessage -message $Message -API 'Alerts' -tenant $tenant.tenant -sev Error - } - } + $AlertsTable = Get-CIPPTable -Table cachealerts + $IgnoreList = @('Etag', 'PartitionKey', 'Timestamp', 'RowKey', 'tenantid', 'tenant', 'type') + $alertList = $Alerts | Select-Object * -ExcludeProperty $IgnoreList + foreach ($task in ($AlertList.psobject.members | Where-Object { $_.MemberType -EQ 'NoteProperty' -and $_.value -eq $True }).name) { + $QueueItem = [pscustomobject]@{ + Tenant = $tenant + DeltaTable = $DeltaTable + LastRunTable = $LastRunTable + AlertsTable = $AlertsTable + FunctionName = "CIPPAlert$($Task)" + } + Push-OutputBinding -Name QueueItem -Value $QueueItem } $Table = Get-CIPPTable $PartitionKey = Get-Date -UFormat '%Y%m%d' $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $tenant.tenant - Write-Host $Filter $currentlog = Get-CIPPAzDataTableEntity @Table -Filter $Filter - $ShippedAlerts | ForEach-Object { - if ($_ -notin $currentlog.Message) { - if ($Config.includeTenantId) { - Write-LogMessage -message $_ -API 'Alerts' -tenant $tenant.tenant -sev Alert -tenantid $Tenant.tenantid - } - else { - Write-LogMessage -message $_ -API 'Alerts' -tenant $tenant.tenant -sev Alert - } + + $CurrentAlerts = (Get-CIPPAzDataTableEntity @AlertsTable -Filter $Filter) + $CurrentAlerts | ForEach-Object { + if ($_.Message -notin $currentlog.Message) { + Write-LogMessage -message $_.Message -API 'Alerts' -tenant $tenant.tenant -sev Alert -tenantid $Tenant.tenantid + } + Remove-AzDataTableEntity @AlertsTable -PartitionKey $PartitionKey -RowKey $_.RowKey } + [PSCustomObject]@{ ReturnedValues = $true } -} -catch { +} catch { $Message = 'Exception on line {0} - {1}' -f $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message Write-LogMessage -message $Message -API 'Alerts' -tenant $tenant.tenant -sev Error [PSCustomObject]@{ From 993f37e23d83de7e8eb62795c2ec7d8ce2aa8392 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 27 Dec 2023 18:35:39 +0100 Subject: [PATCH 39/84] changes to classic alerts --- .../Public/Entrypoints/Invoke-AddAlert.ps1 | 10 +++--- .../Push-CIPPAlertAdminPassword.ps1 | 4 +-- .../Push-CIPPAlertDepTokenExpiry.ps1 | 36 +++++++++---------- .../Push-CIPPAlertExpiringLicenses.ps1 | 16 ++++----- .../Entrypoints/Push-CIPPAlertMFAAdmins.ps1 | 2 +- .../Push-CIPPAlertMFAAlertUsers.ps1 | 22 ++++++------ Scheduler_Alert/run.ps1 | 14 +++----- profile.ps1 | 6 ++-- 8 files changed, 53 insertions(+), 57 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 index bc45ab83aa68..a68a429cb28b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 @@ -13,10 +13,11 @@ Function Invoke-AddAlert { $Tenants = $Request.body.tenantFilter $Results = foreach ($Tenant in $tenants) { try { - $TenantID = if ($tenant -ne 'AllTenants') { - (get-tenants | Where-Object -Property defaultDomainName -EQ $Tenant).customerId + Write-Host "Working on $Tenant" + if ($tenant -ne 'AllTenants') { + $TenantID = (get-tenants | Where-Object -Property defaultDomainName -EQ $Tenant).customerId } else { - 'AllTenants' + $TenantID = 'AllTenants' } if ($Request.body.SetAlerts) { $CompleteObject = @{ @@ -44,10 +45,11 @@ Function Invoke-AddAlert { RowKey = $TenantID PartitionKey = 'Alert' } - + Write-Host "$( $CompleteObject | ConvertTo-Json -Depth 100)" $Table = get-cipptable -TableName 'SchedulerConfig' Add-CIPPAzDataTableEntity @Table -Entity $CompleteObject -Force } + $URL = ($request.headers.'x-ms-original-url').split('/api') | Select-Object -First 1 if ($Tenant -eq 'AllTenants') { Get-Tenants | ForEach-Object { diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 index ba35ab1a326f..af03360c1973 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 @@ -3,11 +3,9 @@ function Push-CIPPAlertAdminPassword { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - $QueueItem, - + [pscustomobject]$QueueItem, $TriggerMetadata ) - $AlertsTable = $QueueItem.AlertsTable try { New-GraphGETRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'&`$expand=principal" -tenantid $($QueueItem.tenant) | Where-Object { ($_.principalOrganizationId -EQ $QueueItem.tenantid) -and ($_.principal.'@odata.type' -eq '#microsoft.graph.user') } | ForEach-Object { $LastChanges = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.principalId)?`$select=UserPrincipalName,lastPasswordChangeDateTime" -tenant $($QueueItem.tenant) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 index f9eef0d44c1d..f0079fc4e335 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 @@ -8,26 +8,26 @@ function Push-CIPPAlertDepTokenExpiry { $LastRunTable = $QueueItem.LastRunTable + try { + $Filter = "RowKey eq 'DepTokenExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter + $Yesterday = (Get-Date).AddDays(-1) + if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { try { - $Filter = "RowKey eq 'DepTokenExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid - $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter - $Yesterday = (Get-Date).AddDays(-1) - if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { - try { - $DepTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $QueueItem.tenant).value - foreach ($Dep in $DepTokens) { - if ($Dep.tokenExpirationDateTime -lt (Get-Date).AddDays(30) -and $Dep.tokenExpirationDateTime -gt (Get-Date).AddDays(-7)) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime) - } - } - } catch {} - $LastRun = @{ - RowKey = 'DepTokenExpiry' - PartitionKey = $QueueItem.tenantid + $DepTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $QueueItem.tenant).value + foreach ($Dep in $DepTokens) { + if ($Dep.tokenExpirationDateTime -lt (Get-Date).AddDays(30) -and $Dep.tokenExpirationDateTime -gt (Get-Date).AddDays(-7)) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime) } - Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } - } catch { - # Error handling + } catch {} + $LastRun = @{ + RowKey = 'DepTokenExpiry' + PartitionKey = $QueueItem.tenantid } + Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } + } catch { + # Error handling + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 index bfe19720a96c..e1f94df1fa8d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 @@ -5,14 +5,12 @@ function Push-CIPPAlertExpiringLicenses { $QueueItem, $TriggerMetadata ) - - - try { - Get-CIPPLicenseOverview -TenantFilter $QueueItem.tenant | Where-Object -Property [int]TimeUntilRenew -LT 29 | ForEach-Object { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.License) will expire in $($_.TimeUntilRenew) days" - } - } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Error occurred: $(Get-NormalizedError -message $_.Exception.message)" - } + try { + Get-CIPPLicenseOverview -TenantFilter $QueueItem.tenant | Where-Object -Property 'TimeUntilRenew' -LT 29 | ForEach-Object { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.License) will expire in $($_.TimeUntilRenew) days. The estimated term is $($_.EstTerm)" } + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 index 427dd44a6e6c..7f8c83fad40b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 @@ -23,7 +23,7 @@ function Push-CIPPAlertMFAAdmins { $CARegistered = $true } } - if ($_.StrongAuthenticationRequirements.StrongAuthenticationRequirement.state -eq $null -and $CARegistered -ne $true) { + if ($CARegistered -ne $true) { Write-AlertMessage -tenant $($QueueItem.tenant) -message "Admin $($_.UserPrincipalName) is enabled but does not have any form of MFA configured." } } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 index db5a57fe1c40..057f7a9ee553 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 @@ -5,21 +5,23 @@ function Push-CIPPAlertMFAAlertUsers { $QueueItem, $TriggerMetadata ) - - try { - $users = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users?`$select=userPrincipalName,id" -tenantid $($QueueItem.tenant) -erroraction stop + $users = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users?`$select=userPrincipalName,id,accountEnabled,userType&`$filter=userType eq 'Member' and accountEnabled eq true" -tenantid $($QueueItem.tenant) + Write-Host "found $($users.count) users" $StrongMFAMethods = '#microsoft.graph.fido2AuthenticationMethod', '#microsoft.graph.phoneAuthenticationMethod', '#microsoft.graph.passwordlessmicrosoftauthenticatorauthenticationmethod', '#microsoft.graph.softwareOathAuthenticationMethod', '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod' - $users | Where-Object { $_.Usertype -eq 'Member' -and $_.BlockCredential -eq $false } | ForEach-Object { - trhy { - $CARegistered = $false - (New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.ObjectID)/authentication/Methods" -tenantid $($QueueItem.tenant)) | ForEach-Object { - if ($_.'@odata.type' -notin $StrongMFAMethods) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "User $($_.UserPrincipalName) is enabled but does not have any form of MFA configured." + $users | ForEach-Object { + try { + $UPN = $_.UserPrincipalName + (New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.ID)/authentication/Methods" -tenantid $($QueueItem.tenant)) | ForEach-Object { + $CARegistered = $false + if ($_.'@odata.type' -in $StrongMFAMethods) { + $CARegistered = $true + } + if ($CARegistered -eq $false) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "User $UPN is enabled but does not have any form of MFA configured." } } } catch { - $CARegistered = $false } } } catch { diff --git a/Scheduler_Alert/run.ps1 b/Scheduler_Alert/run.ps1 index 53f0ea216065..d179b21144ae 100644 --- a/Scheduler_Alert/run.ps1 +++ b/Scheduler_Alert/run.ps1 @@ -13,15 +13,14 @@ try { $DeltaTable = Get-CIPPTable -Table DeltaCompare $LastRunTable = Get-CIPPTable -Table AlertLastRun - $AlertsTable = Get-CIPPTable -Table cachealerts $IgnoreList = @('Etag', 'PartitionKey', 'Timestamp', 'RowKey', 'tenantid', 'tenant', 'type') $alertList = $Alerts | Select-Object * -ExcludeProperty $IgnoreList foreach ($task in ($AlertList.psobject.members | Where-Object { $_.MemberType -EQ 'NoteProperty' -and $_.value -eq $True }).name) { $QueueItem = [pscustomobject]@{ - Tenant = $tenant + tenant = $tenant.tenant + tenantid = $tenant.tenantid DeltaTable = $DeltaTable LastRunTable = $LastRunTable - AlertsTable = $AlertsTable FunctionName = "CIPPAlert$($Task)" } Push-OutputBinding -Name QueueItem -Value $QueueItem @@ -32,14 +31,11 @@ try { $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $tenant.tenant $currentlog = Get-CIPPAzDataTableEntity @Table -Filter $Filter - + $AlertsTable = Get-CIPPTable -Table cachealerts $CurrentAlerts = (Get-CIPPAzDataTableEntity @AlertsTable -Filter $Filter) $CurrentAlerts | ForEach-Object { - if ($_.Message -notin $currentlog.Message) { - Write-LogMessage -message $_.Message -API 'Alerts' -tenant $tenant.tenant -sev Alert -tenantid $Tenant.tenantid - - } - Remove-AzDataTableEntity @AlertsTable -PartitionKey $PartitionKey -RowKey $_.RowKey + if ($_.Message -notin $currentlog.Message) { Write-LogMessage -message $_.Message -API 'Alerts' -tenant $tenant.tenant -sev Alert -tenantid $Tenant.tenantid } + Remove-AzDataTableEntity @AlertsTable -Entity $_ } [PSCustomObject]@{ diff --git a/profile.ps1 b/profile.ps1 index 5b932cfda949..f30159db12cb 100644 --- a/profile.ps1 +++ b/profile.ps1 @@ -13,11 +13,11 @@ # Remove this if you are not planning on using MSI or Azure PowerShell. # Import modules -@('CippCore','CippExtensions','Az.KeyVault','Az.Accounts') | ForEach-Object { +@('CippCore', 'CippExtensions', 'Az.KeyVault', 'Az.Accounts') | ForEach-Object { try { Import-Module -Name $_ -ErrorAction Stop } catch { - Write-LogMessage -message "Failed to import module $($_): $_.Exception.Message" -Sev 'CRITICAL' + Write-LogMessage -message "Failed to import module $($_): $_.Exception.Message" -Sev 'debug' $_.Exception.Message } } @@ -32,7 +32,7 @@ try { $Auth = Get-CIPPAuthentication } } catch { - Write-LogMessage -message "Could not retrieve keys from Keyvault: $($_.Exception.Message)" -Sev 'CRITICAL' + Write-LogMessage -message "Could not retrieve keys from Keyvault: $($_.Exception.Message)" -Sev 'debug' } # Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell. From 0e4d7b0fdf9f92eb9172d8e59a6cf50552a38749 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 27 Dec 2023 13:34:32 -0500 Subject: [PATCH 40/84] Add type of error for AdminAgents group --- Modules/CIPPCore/Public/Test-CIPPGDAPRelationships.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Test-CIPPGDAPRelationships.ps1 b/Modules/CIPPCore/Public/Test-CIPPGDAPRelationships.ps1 index 6c6a969d906f..d527bdd0ff3b 100644 --- a/Modules/CIPPCore/Public/Test-CIPPGDAPRelationships.ps1 +++ b/Modules/CIPPCore/Public/Test-CIPPGDAPRelationships.ps1 @@ -66,8 +66,9 @@ function Test-CIPPGDAPRelationships { } } if (-not $GroupFound) { + if ($Group -eq 'AdminAgents') { $Type = 'Error' } else { $Type = 'Warning' } $GDAPissues.add([PSCustomObject]@{ - Type = 'Warning' + Type = $Type Issue = "$($Group) is not assigned to the SAM user $me. If you have migrated outside of CIPP this is to be expected. Please perform an access check to make sure you have the correct set of permissions." Tenant = '*Partner Tenant' Relationship = 'None' From 044ded7b95e0e147cc71bc3fa6bfc318ea87c73a Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 28 Dec 2023 14:32:15 +0100 Subject: [PATCH 41/84] fix for duplicate profile creation --- .../Set-CIPPDefaultAPDeploymentProfile.ps1 | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPDefaultAPDeploymentProfile.ps1 b/Modules/CIPPCore/Public/Set-CIPPDefaultAPDeploymentProfile.ps1 index f3c488a4e805..f465c0f76d8c 100644 --- a/Modules/CIPPCore/Public/Set-CIPPDefaultAPDeploymentProfile.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPDefaultAPDeploymentProfile.ps1 @@ -39,9 +39,20 @@ function Set-CIPPDefaultAPDeploymentProfile { } } $Body = ConvertTo-Json -InputObject $ObjBody - Write-Host $Body - $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -body $body -tenantid $tenantfilter - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APIName -tenant $($tenantfilter) -message "Added Autopilot profile $($Displayname)" -Sev 'Info' + + $Profiles = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -tenantid $tenantfilter | Where-Object -Property displayName -EQ $displayname + if ($Profiles.count -gt 1) { + $Profiles | ForEach-Object { + if ($_.id -ne $Profiles[0].id) { + $Delete = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($_.id)" -tenantid $tenantfilter -type DELETE + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APIName -tenant $($tenantfilter) -message "Deleted duplicate Autopilot profile $($displayname)" -Sev 'Info' + } + } + } + if (!$Profiles) { + $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -body $body -tenantid $tenantfilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APIName -tenant $($tenantfilter) -message "Added Autopilot profile $($Displayname)" -Sev 'Info' + } if ($AssignTo) { $AssignBody = '{"target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"}}' $assign = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($GraphRequest.id)/assignments" -tenantid $tenantfilter -type POST -body $AssignBody From 511aee7f1dd27aad42dac4ef5ad0c5b10b89969e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 14:27:15 +0100 Subject: [PATCH 42/84] Alert Changes --- .../Public/Entrypoints/Invoke-AddAlert.ps1 | 48 ++++++++++++------- .../Entrypoints/Invoke-ListWebhookAlert.ps1 | 7 ++- .../Push-CIPPAlertMFAAlertUsers.ps1 | 44 ++++++++++++----- .../Public/Invoke-RemoveWebhookAlert.ps1 | 35 ++++++++++++-- .../Public/New-CIPPGraphSubscription.ps1 | 29 ++++++----- .../Public/Remove-CIPPGraphSubscription.ps1 | 2 +- Scheduler_GetQueue/run.ps1 | 5 +- Scheduler_Timer/function.json | 2 +- 8 files changed, 119 insertions(+), 53 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 index a68a429cb28b..543e1d195355 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 @@ -11,6 +11,8 @@ Function Invoke-AddAlert { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $Tenants = $Request.body.tenantFilter + $Table = get-cipptable -TableName 'SchedulerConfig' + $Results = foreach ($Tenant in $tenants) { try { Write-Host "Working on $Tenant" @@ -45,25 +47,29 @@ Function Invoke-AddAlert { RowKey = $TenantID PartitionKey = 'Alert' } - Write-Host "$( $CompleteObject | ConvertTo-Json -Depth 100)" $Table = get-cipptable -TableName 'SchedulerConfig' Add-CIPPAzDataTableEntity @Table -Entity $CompleteObject -Force - } - - $URL = ($request.headers.'x-ms-original-url').split('/api') | Select-Object -First 1 - if ($Tenant -eq 'AllTenants') { - Get-Tenants | ForEach-Object { - $params = @{ - TenantFilter = $_.defaultDomainName - auditLogAPI = $true - operations = 'Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General,DLP.All' - BaseURL = $URL - ExecutingUser = $Request.headers.'x-ms-client-principal' - } - Push-OutputBinding -Name Subscription -Value $Params - } } else { - foreach ($eventType in $Request.body.EventTypes.value) { + $URL = ($request.headers.'x-ms-original-url').split('/api') | Select-Object -First 1 + if ($Tenant -eq 'AllTenants') { + Get-Tenants | ForEach-Object { + $params = @{ + TenantFilter = $_.defaultDomainName + auditLogAPI = $true + operations = 'Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General,DLP.All' + BaseURL = $URL + ExecutingUser = $Request.headers.'x-ms-client-principal' + } + Push-OutputBinding -Name Subscription -Value $Params + } + $CompleteObject = @{ + tenant = 'AllTenants' + type = 'webhookcreation' + RowKey = 'AllTenantsWebhookCreation' + PartitionKey = 'webhookcreation' + } + Add-CIPPAzDataTableEntity @Table -Entity $CompleteObject -Force + } else { $params = @{ TenantFilter = $tenant auditLogAPI = $true @@ -73,6 +79,16 @@ Function Invoke-AddAlert { } New-CIPPGraphSubscription @params } + $CompleteObject = @{ + Tenant = [string]$tenant + if = [string](ConvertTo-Json -Compress -InputObject $Request.body.ifs) + execution = [string](ConvertTo-Json -Compress -InputObject $Request.body.do) + type = 'WebhookAlert' + RowKey = [string](New-Guid) + PartitionKey = 'WebhookAlert' + } + Add-CIPPAzDataTableEntity @Table -Entity $CompleteObject -Force + } "Successfully added Alert for $($Tenant) to queue." Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Successfully added Alert for $($Tenant) to queue." -Sev 'Info' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 index 6eb63f8a57ae..5ee5feb2924f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 @@ -10,10 +10,9 @@ Function Invoke-ListWebhookAlert { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $WebhookTable = Get-CIPPTable -TableName webhookTable - $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable - - # Associate values to output bindings by calling 'Push-OutputBinding'. + $Table = get-cipptable -TableName 'SchedulerConfig' + $WebhookRow = Get-CIPPAzDataTableEntity @Table | Where-Object -Property PartitionKey -EQ 'WebhookAlert' + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @($WebhookRow) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 index 057f7a9ee553..b7bf8bc6d304 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 @@ -9,23 +9,43 @@ function Push-CIPPAlertMFAAlertUsers { $users = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users?`$select=userPrincipalName,id,accountEnabled,userType&`$filter=userType eq 'Member' and accountEnabled eq true" -tenantid $($QueueItem.tenant) Write-Host "found $($users.count) users" $StrongMFAMethods = '#microsoft.graph.fido2AuthenticationMethod', '#microsoft.graph.phoneAuthenticationMethod', '#microsoft.graph.passwordlessmicrosoftauthenticatorauthenticationmethod', '#microsoft.graph.softwareOathAuthenticationMethod', '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod' - $users | ForEach-Object { - try { - $UPN = $_.UserPrincipalName - (New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.ID)/authentication/Methods" -tenantid $($QueueItem.tenant)) | ForEach-Object { - $CARegistered = $false - if ($_.'@odata.type' -in $StrongMFAMethods) { + + $UserBatches = [System.Collections.Generic.List[Object]]@() + for ($i = 0; $i -lt $users.count; $i += 20) { + $UserBatches.Add($users[$i..($i + 19)]) + } + + $UserBatches | ForEach-Object -Parallel { + Import-Module CippCore + Import-Module AzBobbyTables + $UserBatch = $_ + Write-Host "processing batch of $($UserBatch.count) users" + $BatchRequests = $UserBatch | ForEach-Object { + @{ + id = $_.id + method = 'GET' + url = "users/$($_.ID)/authentication/Methods" + } + } + $BatchResponses = New-GraphBulkRequest -tenantid $using:QueueItem.tenant -Requests $BatchRequests + foreach ($response in $BatchResponses) { + $UPN = ($UserBatch | Where-Object { $_.id -eq $response.id }).UserPrincipalName + $CARegistered = $false + + foreach ($method in $response.body.value) { + if ($method.'@odata.type' -in $using:StrongMFAMethods) { $CARegistered = $true + break } - if ($CARegistered -eq $false) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "User $UPN is enabled but does not have any form of MFA configured." - } } - } catch { + + if (-not $CARegistered) { + Write-AlertMessage -tenant $using:QueueItem.tenant -message "User $UPN is enabled but does not have any form of MFA configured." + } } - } + } -ThrottleLimit 25 + } catch { Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get MFA status for users for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } - diff --git a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 index e54f283123de..b59d09be301e 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 @@ -10,9 +10,38 @@ Function Invoke-RemoveWebhookAlert { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Table = get-cipptable -TableName 'SchedulerConfig' try { - $Results = Remove-CIPPGraphSubscription -TenantFilter $Request.query.TenantFilter -CIPPID $Request.query.CIPPID + $WebhookTable = Get-CIPPTable -TableName SchedulerConfig + $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable -Filter "PartitionKey eq 'WebhookAlert'" | Where-Object -Property Tenant -EQ $Request.query.TenantFilter + Write-Host "The webhook count is $($WebhookRow.count)" + if ($WebhookRow.count -gt 1) { + $Entity = $WebhookRow | Where-Object -Property RowKey -EQ $Request.query.ID + Remove-AzDataTableEntity @WebhookTable -Entity $Entity | Out-Null + $Results = "Removed Alert Rule for $($Request.query.TenantFilter)" + } else { + if ($Request.query.TenantFilter -eq 'AllTenants') { + $Tenants = Get-Tenants -IncludeAll -IncludeErrors | Select-Object -ExpandProperty defaultDomainName + $CompleteObject = @{ + tenant = 'AllTenants' + type = 'webhookcreation' + RowKey = 'AllTenantsWebhookCreation' + PartitionKey = 'webhookcreation' + } + RemoveAzDataTableEntity @Table -Entity $CompleteObject -ErrorAction SilentlyContinue | Out-Null + + } else { + $Tenants = $Request.query.TenantFilter + } + + $Results = foreach ($Tenant in $Tenants) { + Remove-CIPPGraphSubscription -TenantFilter $Tenant -CIPPID $Request.query.CIPPID + $Entity = $WebhookRow | Where-Object -Property RowKey -EQ $Request.query.ID + Remove-AzDataTableEntity @WebhookTable -Entity $Entity | Out-Null + "Removed Alert Rule for $($Request.query.TenantFilter)" + } + } $body = [pscustomobject]@{'Results' = $Results } } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove webhook alert. $($_.Exception.Message)" -Sev 'Error' @@ -25,6 +54,4 @@ Function Invoke-RemoveWebhookAlert { StatusCode = [HttpStatusCode]::OK Body = $body }) - - -} +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 52600af7f182..3b1259d8a696 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -23,19 +23,24 @@ function New-CIPPGraphSubscription { 'address' = "$BaseURL/API/Publicwebhooks?EventType=$EventType&CIPPID=$CIPPID" } } | ConvertTo-Json - Write-Host ($AuditLogParams) - $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=$EventType&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose - $WebhookRow = @{ - PartitionKey = [string]$TenantFilter - RowKey = [string]$CIPPID - Resource = 'M365AuditLogs' - Expiration = 'Does Not Expire' - WebhookNotificationUrl = [string]$Auditlog.webhook.address + #List existing webhook subscriptions in table + $WebhookFilter = "PartitionKey eq '$($TenantFilter)'" + $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter + $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } + if (!$MatchedWebhook) { + $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=$EventType&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose + $WebhookRow = @{ + PartitionKey = [string]$TenantFilter + RowKey = [string]$CIPPID + Resource = 'M365AuditLogsv2' + Expiration = 'Does Not Expire' + WebhookNotificationUrl = [string]$Auditlog.webhook.address + } + $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow + Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter)" -Sev 'Info' -tenant $TenantFilter + } else { + Write-LogMessage -user $ExecutingUser -API $APIName -message "No webhook creation required for $($TenantFilter). Already exists" -Sev 'Info' -tenant $TenantFilter } - - $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow - Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter)" -Sev 'Info' -tenant $TenantFilter - } else { # First check if there is an exsiting Webhook in place $WebhookFilter = "PartitionKey eq '$($TenantFilter)'" diff --git a/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 index e0ff1f4ef9ad..6a19913ecd29 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 @@ -10,7 +10,7 @@ function Remove-CIPPGraphSubscription { $WebhookTable = Get-CIPPTable -TableName webhookTable $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable | Where-Object { $_.RowKey -eq $CIPPID } $Entity = $WebhookRow | Select-Object PartitionKey, RowKey - if ($WebhookRow.Resource -eq 'M365AuditLogs') { + if ($WebhookRow.Resource -eq 'M365AuditLogsv2') { try { $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/stop?contentType=$($WebhookRow.EventType)" -scope 'https://manage.office.com/.default' -tenantid $TenantFilter -type POST -body '{}' -verbose } catch { diff --git a/Scheduler_GetQueue/run.ps1 b/Scheduler_GetQueue/run.ps1 index 9714b8421eb9..3d495bd85264 100644 --- a/Scheduler_GetQueue/run.ps1 +++ b/Scheduler_GetQueue/run.ps1 @@ -1,7 +1,7 @@ param($name) $Table = Get-CIPPTable -TableName SchedulerConfig -$Tenants = Get-CIPPAzDataTableEntity @Table +$Tenants = Get-CIPPAzDataTableEntity @Table | Where-Object -Property PartitionKey -NE 'WebhookAlert' $object = foreach ($Tenant in $Tenants) { if ($Tenant.tenant -ne 'AllTenants') { @@ -11,8 +11,7 @@ $object = foreach ($Tenant in $Tenants) { TenantID = $Tenant.tenantid Type = $Tenant.type } - } - else { + } else { Write-Host 'All tenants, doing them all' $TenantList = Get-Tenants foreach ($t in $TenantList) { diff --git a/Scheduler_Timer/function.json b/Scheduler_Timer/function.json index f30537d11b34..1d19ac7d6733 100644 --- a/Scheduler_Timer/function.json +++ b/Scheduler_Timer/function.json @@ -2,7 +2,7 @@ "bindings": [ { "name": "Timer", - "schedule": "0 */15 * * * *", + "schedule": "0 */20 * * * *", "direction": "in", "type": "timerTrigger" }, From 013e23c911ed2538808da3b3f3fe3887b0d36029 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 14:46:02 +0100 Subject: [PATCH 43/84] corrected operations --- Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 3b1259d8a696..5192efb42591 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -28,7 +28,7 @@ function New-CIPPGraphSubscription { $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } if (!$MatchedWebhook) { - $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=$EventType&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose + $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General,DLP.All&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose $WebhookRow = @{ PartitionKey = [string]$TenantFilter RowKey = [string]$CIPPID From 2e06a79c33890733a8f5a0c9edb52c3d8163c574 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 15:09:47 +0100 Subject: [PATCH 44/84] multiple event types not allowed --- .../Public/New-CIPPGraphSubscription.ps1 | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 5192efb42591..9f5aeac22b29 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -18,26 +18,29 @@ function New-CIPPGraphSubscription { try { if ($auditLogAPI) { - $AuditLogParams = @{ - webhook = @{ - 'address' = "$BaseURL/API/Publicwebhooks?EventType=$EventType&CIPPID=$CIPPID" - } - } | ConvertTo-Json - #List existing webhook subscriptions in table - $WebhookFilter = "PartitionKey eq '$($TenantFilter)'" - $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter - $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } - if (!$MatchedWebhook) { - $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General,DLP.All&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose - $WebhookRow = @{ - PartitionKey = [string]$TenantFilter - RowKey = [string]$CIPPID - Resource = 'M365AuditLogsv2' - Expiration = 'Does Not Expire' - WebhookNotificationUrl = [string]$Auditlog.webhook.address + $EventTypes = @('Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'Audit.General,DLP.All') + foreach ($EventType in $EventTypes) { + $AuditLogParams = @{ + webhook = @{ + 'address' = "$BaseURL/API/Publicwebhooks?EventType=$EventType&CIPPID=$CIPPID" + } + } | ConvertTo-Json + #List existing webhook subscriptions in table + $WebhookFilter = "PartitionKey eq '$($TenantFilter)'" + $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter + $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } + if (!$MatchedWebhook) { + $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=$EventType&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose + $WebhookRow = @{ + PartitionKey = [string]$TenantFilter + RowKey = [string]$CIPPID + Resource = 'M365AuditLogsv2' + Expiration = 'Does Not Expire' + WebhookNotificationUrl = [string]$Auditlog.webhook.address + } + $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow + Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter) for the log $($EventType)" -Sev 'Info' -tenant $TenantFilter } - $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow - Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter)" -Sev 'Info' -tenant $TenantFilter } else { Write-LogMessage -user $ExecutingUser -API $APIName -message "No webhook creation required for $($TenantFilter). Already exists" -Sev 'Info' -tenant $TenantFilter } From cbeadad195db641621218bf88aeee51165fbcdf9 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 15:18:57 +0100 Subject: [PATCH 45/84] added check for already existing entries --- .../Public/New-CIPPGraphSubscription.ps1 | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 9f5aeac22b29..c44cd58bf2c8 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -30,15 +30,23 @@ function New-CIPPGraphSubscription { $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } if (!$MatchedWebhook) { - $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=$EventType&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose - $WebhookRow = @{ - PartitionKey = [string]$TenantFilter - RowKey = [string]$CIPPID - Resource = 'M365AuditLogsv2' - Expiration = 'Does Not Expire' - WebhookNotificationUrl = [string]$Auditlog.webhook.address + try { + $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=$EventType&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose + $WebhookRow = @{ + PartitionKey = [string]$TenantFilter + RowKey = [string]$CIPPID + Resource = 'M365AuditLogsv2' + Expiration = 'Does Not Expire' + WebhookNotificationUrl = [string]$Auditlog.webhook.address + } + $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow + } catch { + if ($_.Exception.Message -like '*already exists*') { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Webhook subscription for $($TenantFilter) already exists" -Sev 'Info' -tenant $TenantFilter + } else { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter + } } - $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter) for the log $($EventType)" -Sev 'Info' -tenant $TenantFilter } } else { From fb195d53f099b3d4dae4eb250bfeaf5e2a84a772 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 15:20:04 +0100 Subject: [PATCH 46/84] new guid for each request --- Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index c44cd58bf2c8..09d06639fb13 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -20,6 +20,7 @@ function New-CIPPGraphSubscription { if ($auditLogAPI) { $EventTypes = @('Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'Audit.General,DLP.All') foreach ($EventType in $EventTypes) { + $CIPPID = (New-Guid).GUID $AuditLogParams = @{ webhook = @{ 'address' = "$BaseURL/API/Publicwebhooks?EventType=$EventType&CIPPID=$CIPPID" From 360a56abd9525fe3148e4db9e47f40f71f11c342 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 15:31:33 +0100 Subject: [PATCH 47/84] resource adding back --- Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 09d06639fb13..47f1ea625a47 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -21,6 +21,7 @@ function New-CIPPGraphSubscription { $EventTypes = @('Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'Audit.General,DLP.All') foreach ($EventType in $EventTypes) { $CIPPID = (New-Guid).GUID + $Resource = $EventType $AuditLogParams = @{ webhook = @{ 'address' = "$BaseURL/API/Publicwebhooks?EventType=$EventType&CIPPID=$CIPPID" From cafc0cbf56b49398f6bead03118a9b3566bfc57c Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 15:54:48 +0100 Subject: [PATCH 48/84] catch mistake --- .../Public/New-CIPPGraphSubscription.ps1 | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 47f1ea625a47..9a21a23ac272 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -31,8 +31,8 @@ function New-CIPPGraphSubscription { $WebhookFilter = "PartitionKey eq '$($TenantFilter)'" $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } - if (!$MatchedWebhook) { - try { + try { + if (!$MatchedWebhook) { $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/start?contentType=$EventType&PublisherIdentifier=$($TenantFilter)" -tenantid $TenantFilter -type POST -scope 'https://manage.office.com/.default' -body $AuditLogparams -verbose $WebhookRow = @{ PartitionKey = [string]$TenantFilter @@ -42,17 +42,17 @@ function New-CIPPGraphSubscription { WebhookNotificationUrl = [string]$Auditlog.webhook.address } $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow - } catch { - if ($_.Exception.Message -like '*already exists*') { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Webhook subscription for $($TenantFilter) already exists" -Sev 'Info' -tenant $TenantFilter - } else { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter - } + Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter) for the log $($EventType)" -Sev 'Info' -tenant $TenantFilter + } else { + Write-LogMessage -user $ExecutingUser -API $APIName -message "No webhook creation required for $($TenantFilter). Already exists" -Sev 'Info' -tenant $TenantFilter + } + } catch { + if ($_.Exception.Message -like '*already exists*') { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Webhook subscription for $($TenantFilter) already exists" -Sev 'Info' -tenant $TenantFilter + } else { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter } - Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter) for the log $($EventType)" -Sev 'Info' -tenant $TenantFilter } - } else { - Write-LogMessage -user $ExecutingUser -API $APIName -message "No webhook creation required for $($TenantFilter). Already exists" -Sev 'Info' -tenant $TenantFilter } } else { # First check if there is an exsiting Webhook in place From 01ae16a164b351344132ff2894a78f39011f6241 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 16:13:16 +0100 Subject: [PATCH 49/84] add receiving of all alerts --- Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 9a21a23ac272..f45884dd0c52 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -18,7 +18,7 @@ function New-CIPPGraphSubscription { try { if ($auditLogAPI) { - $EventTypes = @('Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'Audit.General,DLP.All') + $EventTypes = @('Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'Audit.General', 'DLP.All') foreach ($EventType in $EventTypes) { $CIPPID = (New-Guid).GUID $Resource = $EventType From 8a2fe156a6b0daf65c5df072dc5e1f5c8368c815 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 16:28:23 +0100 Subject: [PATCH 50/84] set resource --- Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 | 4 ++-- Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 index 543e1d195355..86d2113a7561 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 @@ -56,7 +56,7 @@ Function Invoke-AddAlert { $params = @{ TenantFilter = $_.defaultDomainName auditLogAPI = $true - operations = 'Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General,DLP.All' + operations = 'Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General' BaseURL = $URL ExecutingUser = $Request.headers.'x-ms-client-principal' } @@ -73,7 +73,7 @@ Function Invoke-AddAlert { $params = @{ TenantFilter = $tenant auditLogAPI = $true - operations = 'Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General,DLP.All' + operations = 'Audit.AzureActiveDirectory,Audit.Exchange,Audit.SharePoint,Audit.General' BaseURL = $URL ExecutingUser = $Request.headers.'x-ms-client-principal' } diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index f45884dd0c52..6af3225124cb 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -18,7 +18,7 @@ function New-CIPPGraphSubscription { try { if ($auditLogAPI) { - $EventTypes = @('Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'Audit.General', 'DLP.All') + $EventTypes = @('Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'Audit.General') foreach ($EventType in $EventTypes) { $CIPPID = (New-Guid).GUID $Resource = $EventType @@ -37,7 +37,7 @@ function New-CIPPGraphSubscription { $WebhookRow = @{ PartitionKey = [string]$TenantFilter RowKey = [string]$CIPPID - Resource = 'M365AuditLogsv2' + Resource = $Resource Expiration = 'Does Not Expire' WebhookNotificationUrl = [string]$Auditlog.webhook.address } From 74cb6e27d2baccd9ca0caaf84f63221e0fe4e837 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 16:43:21 +0100 Subject: [PATCH 51/84] resources for removal --- Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 | 2 +- Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 index b59d09be301e..799926b83be4 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 @@ -29,7 +29,7 @@ Function Invoke-RemoveWebhookAlert { RowKey = 'AllTenantsWebhookCreation' PartitionKey = 'webhookcreation' } - RemoveAzDataTableEntity @Table -Entity $CompleteObject -ErrorAction SilentlyContinue | Out-Null + Remove-AzDataTableEntity @Table -Entity $CompleteObject -ErrorAction SilentlyContinue | Out-Null } else { $Tenants = $Request.query.TenantFilter diff --git a/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 index 6a19913ecd29..eefe9569a790 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 @@ -10,7 +10,7 @@ function Remove-CIPPGraphSubscription { $WebhookTable = Get-CIPPTable -TableName webhookTable $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable | Where-Object { $_.RowKey -eq $CIPPID } $Entity = $WebhookRow | Select-Object PartitionKey, RowKey - if ($WebhookRow.Resource -eq 'M365AuditLogsv2') { + if ($WebhookRow.Resource -in @('Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'Audit.General')) { try { $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/stop?contentType=$($WebhookRow.EventType)" -scope 'https://manage.office.com/.default' -tenantid $TenantFilter -type POST -body '{}' -verbose } catch { From 42405923e4d3e0925074b3ec437886742f6dd520 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 29 Dec 2023 17:14:29 +0100 Subject: [PATCH 52/84] updated auditlog texts --- .../CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 | 2 +- .../Public/Remove-CIPPGraphSubscription.ps1 | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 index 799926b83be4..3d9b4a628914 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 @@ -36,7 +36,7 @@ Function Invoke-RemoveWebhookAlert { } $Results = foreach ($Tenant in $Tenants) { - Remove-CIPPGraphSubscription -TenantFilter $Tenant -CIPPID $Request.query.CIPPID + Remove-CIPPGraphSubscription -TenantFilter $Tenant -Type 'AuditLog' $Entity = $WebhookRow | Where-Object -Property RowKey -EQ $Request.query.ID Remove-AzDataTableEntity @WebhookTable -Entity $Entity | Out-Null "Removed Alert Rule for $($Request.query.TenantFilter)" diff --git a/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 index eefe9569a790..2847bdb24e99 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPGraphSubscription.ps1 @@ -4,17 +4,24 @@ function Remove-CIPPGraphSubscription { $TenantFilter, $CIPPID, $APIName = 'Remove Graph Webhook', + $Type, $ExecutingUser ) try { $WebhookTable = Get-CIPPTable -TableName webhookTable - $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable | Where-Object { $_.RowKey -eq $CIPPID } + if ($type -eq 'AuditLog') { + $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable | Where-Object { $_.PartitionKey -eq $TenantFilter } + } else { + $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable | Where-Object { $_.RowKey -eq $CIPPID } + } $Entity = $WebhookRow | Select-Object PartitionKey, RowKey - if ($WebhookRow.Resource -in @('Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'Audit.General')) { + if ($Type -eq 'AuditLog') { try { - $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/stop?contentType=$($WebhookRow.EventType)" -scope 'https://manage.office.com/.default' -tenantid $TenantFilter -type POST -body '{}' -verbose + foreach ($EventType in $WebhookRow.EventType) { + $AuditLog = New-GraphPOSTRequest -uri "https://manage.office.com/api/v1.0/$($TenantFilter)/activity/feed/subscriptions/stop?contentType=$($EventType)" -scope 'https://manage.office.com/.default' -tenantid $TenantFilter -type POST -body '{}' -verbose + } } catch { - #allowed to fail if the subscription is already removed + Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to remove webhook subscription at Microsoft's side: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter } $null = Remove-AzDataTableEntity @WebhookTable -Entity $Entity } else { From 4a8cd41f7f9c140199c68ee12462175f7a136097 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 17:32:46 +0100 Subject: [PATCH 53/84] updates to alerts --- ExecScheduledCommand/run.ps1 | 9 +- .../CIPPCore/Public/Get-CIPPGeoIPLocation.ps1 | 29 +- .../Public/Invoke-CIPPWebhookProcessing.ps1 | 290 ++++++++---------- .../CIPPCore/Public/New-CIPPAlertTemplate.ps1 | 150 +++++++++ PublicWebhooksProcess/run.ps1 | 10 +- 5 files changed, 287 insertions(+), 201 deletions(-) create mode 100644 Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 diff --git a/ExecScheduledCommand/run.ps1 b/ExecScheduledCommand/run.ps1 index 615b6b8471cf..180df3368b87 100644 --- a/ExecScheduledCommand/run.ps1 +++ b/ExecScheduledCommand/run.ps1 @@ -10,8 +10,7 @@ Write-Host 'started task' try { try { $results = & $QueueItem.command @commandParameters - } - catch { + } catch { $results = "Task Failed: $($_.Exception.Message)" } @@ -31,8 +30,7 @@ try { if ($StoredResults.Length -gt 64000 -or $task.Tenant -eq 'AllTenants') { $StoredResults = @{ Results = 'The results for this query are too long to store in this table, or the query was meant for All Tenants. Please use the options to send the results to another target to be able to view the results. ' } | ConvertTo-Json -Compress } -} -catch { +} catch { $errorMessage = $_.Exception.Message if ($task.Recurrence -gt 0) { $State = 'Failed - Planned' } else { $State = 'Failed' } Update-AzDataTableEntity @Table -Entity @{ @@ -71,8 +69,7 @@ if ($task.Recurrence -le '0' -or $task.Recurrence -eq $null) { Results = "$StoredResults" TaskState = 'Completed' } -} -else { +} else { $nextRun = (Get-Date).AddDays($task.Recurrence) $nextRunUnixTime = [int64]($nextRun - (Get-Date '1/1/1970')).TotalSeconds Update-AzDataTableEntity @Table -Entity @{ diff --git a/Modules/CIPPCore/Public/Get-CIPPGeoIPLocation.ps1 b/Modules/CIPPCore/Public/Get-CIPPGeoIPLocation.ps1 index 11efe5083e4a..59877c61f9da 100644 --- a/Modules/CIPPCore/Public/Get-CIPPGeoIPLocation.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPGeoIPLocation.ps1 @@ -3,32 +3,7 @@ function Get-CIPPGeoIPLocation { param ( [string]$IP ) - - if ($IP -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { - $IP = $IP -replace ':\d+$', '' # Remove the port number if present - } - - $partitionKey = "GeoIP" - $IPAsint = [System.Numerics.BigInteger]::Zero - $ipAddress = [System.Net.IPAddress]::Parse($IP) - if ($ipAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6) { - $partitionKey = "GeoIPv6" - $bytes = $ipAddress.GetAddressBytes() - for ($i = 0; $i -lt $bytes.Length; $i++) { - $IPAsint = $IPAsint -shl 8 -bor $bytes[$i] - } - } - else { - $IP.Split(".") | ForEach-Object { - $IPAddressByte = 0 - [int]::TryParse($_, [ref] $IPAddressByte) | Out-Null - $IPAsint = ([long]($IPAsint -shl 8)) -bor [byte]$_ - } - } - - $CTX = New-AzDataTableContext -TableName geoipdb -ConnectionString 'TableEndpoint=https://cyberdraingeoipdb.table.core.windows.net/;SharedAccessSignature=sv=2022-11-02&ss=t&srt=o&sp=rl&se=2025-08-08T21:05:23Z&st=2023-08-08T13:05:23Z&spr=https&sig=89Bmk2Un89xqNzZPLkryFnLRCjHs9rCWGUJjhvf5mso%3D' - $GeoTable = @{ Context = $CTX } - $location = (Get-CIPPAzDataTableEntity @GeoTable -Filter "PartitionKey eq '$partitionKey' and RowKey le '$IPAsint' and ipTo ge '$IPAsint'") | Select-Object -Last 1 - + $location = Invoke-RestMethod "https://geoipdb.azurewebsites.net/api/GetIPInfo?IP=$IP" + if ($location.status -eq 'FAIL') { throw "Could not get location for $IP" } return $location } diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 922b8307edcd..26338a5be922 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -5,15 +5,12 @@ function Invoke-CippWebhookProcessing { $Data, $Resource, $Operations, - $AllowedLocations, $CIPPPURL, $APIName = 'Process webhook', $ExecutingUser ) + $ConfigTable = get-cipptable -TableName 'SchedulerConfig' $LocationTable = Get-CIPPTable -TableName 'knownlocationdb' - - - $AllowedLocations = $AllowedLocations -split ',' if ($data.userId -eq 'Not Available') { $data.userId = $data.userKey } if ($data.Userkey -eq 'Not Available') { $data.Userkey = $data.userId } if ($data.clientip) { @@ -24,12 +21,14 @@ function Invoke-CippWebhookProcessing { Write-Host 'Using known location' $Country = $Location.CountryOrRegion $City = $Location.City - } - else { + } else { Write-Host 'We have to do a lookup' $Location = Get-CIPPGeoIPLocation -IP $data.clientip $Country = if ($Location.countryCode) { $Location.CountryCode } else { 'Unknown' } $City = if ($Location.cityName) { $Location.cityName } else { 'Unknown' } + $Proxy = if ($Location.proxy) { $Location.proxy } else { 'Unknown' } + $hosting = if ($Location.hosting) { $Location.hosting } else { 'Unknown' } + $ASName = if ($Location.asName) { $Location.asName } else { 'Unknown' } } } $TableObj = [PSCustomObject]::new() @@ -52,149 +51,135 @@ function Invoke-CippWebhookProcessing { default { break } } - #Check if the operation is allowed for this webhook. - if ($data.operation -notin $Operations) { - Write-Host "No need to process this operation, but we're saving the IP for future" - Write-Host 'Add IP and potential location to knownlocation db for this specific user' - - if ($data.ClientIP) { - $IP = $data.ClientIP - if ($IP -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { - $IP = $IP -replace ':\d+$', '' # Remove the port number if present + #Check if we actually need to do anything, and if not, break away. + $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable -Filter "Tenant eq '$tenantfilter'" + foreach ($AlertSetting in $Alertconfig) { + $ifs = $AlertSetting.If | ConvertFrom-Json + $Dos = $AlertSetting.execution | ConvertFrom-Json + if ($data.operation -notin $Ifs.selection -and $ifs.selection -ne 'AnyAlert' ) { + Write-Host 'Add IP and potential location to knownlocation db for this specific user' + if ($data.ClientIP) { + $IP = $data.ClientIP + if ($IP -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { + $IP = $IP -replace ':\d+$', '' # Remove the port number if present + } + $LocationInfo = @{ + RowKey = [string]$ip + PartitionKey = [string]$data.UserId + Tenant = [string]$TenantFilter + CountryOrRegion = "$Country" + City = "$City" + Proxy = "$Proxy" + Hosting = "$hosting" + ASName = "$ASName" + } + $null = Add-CIPPAzDataTableEntity @LocationTable -Entity $LocationInfo -Force } - $LocationInfo = @{ - RowKey = [string]$ip - PartitionKey = [string]$data.UserId - Tenant = [string]$TenantFilter - CountryOrRegion = "$Country" - City = "$City" + return '' + } else { + $ConditionMet = $true + } + foreach ($field in $ifs.field) { + $parts = $field -split ' ', 3 + $key = $parts[0] + $operator = $parts[1] + $value = $parts[2] + if (!$value) { + Write-Host 'blank value, skip' + continue + } + if ($value -contains ',') { + $valueArray = "(@($value -split ','))" + $dynamicIf = "`$data.$key -$operator $valueArray" + } else { + $dynamicIf = "`$data.$key -$operator '$value'" + } + if (Invoke-Expression $dynamicIf) { + $ConditionMet = $true + } else { + $ConditionMet = $false } - $null = Add-CIPPAzDataTableEntity @LocationTable -Entity $LocationInfo -Force - } - return '' - } - - Set-Location (Get-Item $PSScriptRoot).FullName - $Appname = '[{"Application Name":"ACOM Azure Website","Application IDs":"23523755-3a2b-41ca-9315-f81f3f566a95"},{"Application Name":"AEM-DualAuth","Application IDs":"69893ee3-dd10-4b1c-832d-4870354be3d8"},{"Application Name":"ASM Campaign Servicing","Application IDs":"0cb7b9ec-5336-483b-bc31-b15b5788de71"},{"Application Name":"Azure Advanced Threat Protection","Application IDs":"7b7531ad-5926-4f2d-8a1d-38495ad33e17"},{"Application Name":"Azure Data Lake","Application IDs":"e9f49c6b-5ce5-44c8-925d-015017e9f7ad"},{"Application Name":"Azure Lab Services Portal","Application IDs":"835b2a73-6e10-4aa5-a979-21dfda45231c"},{"Application Name":"Azure Portal","Application IDs":"c44b4083-3bb0-49c1-b47d-974e53cbdf3c"},{"Application Name":"AzureSupportCenter","Application IDs":"37182072-3c9c-4f6a-a4b3-b3f91cacffce"},{"Application Name":"Bing","Application IDs":"9ea1ad79-fdb6-4f9a-8bc3-2b70f96e34c7"},{"Application Name":"CPIM Service","Application IDs":"bb2a2e3a-c5e7-4f0a-88e0-8e01fd3fc1f4"},{"Application Name":"CRM Power BI Integration","Application IDs":"e64aa8bc-8eb4-40e2-898b-cf261a25954f"},{"Application Name":"Dataverse","Application IDs":"00000007-0000-0000-c000-000000000000"},{"Application Name":"Enterprise Roaming and Backup","Application IDs":"60c8bde5-3167-4f92-8fdb-059f6176dc0f"},{"Application Name":"IAM Supportability","Application IDs":"a57aca87-cbc0-4f3c-8b9e-dc095fdc8978"},{"Application Name":"IrisSelectionFrontDoor","Application IDs":"16aeb910-ce68-41d1-9ac3-9e1673ac9575"},{"Application Name":"MCAPI Authorization Prod","Application IDs":"d73f4b35-55c9-48c7-8b10-651f6f2acb2e"},{"Application Name":"Media Analysis and Transformation Service","Application IDs":"944f0bd1-117b-4b1c-af26-804ed95e767e
0cd196ee-71bf-4fd6-a57c-b491ffd4fb1e"},{"Application Name":"Microsoft 365 Support Service","Application IDs":"ee272b19-4411-433f-8f28-5c13cb6fd407"},{"Application Name":"Microsoft App Access Panel","Application IDs":"0000000c-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Approval Management","Application IDs":"65d91a3d-ab74-42e6-8a2f-0add61688c74
38049638-cc2c-4cde-abe4-4479d721ed44"},{"Application Name":"Microsoft Authentication Broker","Application IDs":"29d9ed98-a469-4536-ade2-f981bc1d605e"},{"Application Name":"Microsoft Azure CLI","Application IDs":"04b07795-8ddb-461a-bbee-02f9e1bf7b46"},{"Application Name":"Microsoft Azure PowerShell","Application IDs":"1950a258-227b-4e31-a9cf-717495945fc2"},{"Application Name":"Microsoft Bing Search","Application IDs":"cf36b471-5b44-428c-9ce7-313bf84528de"},{"Application Name":"Microsoft Bing Search for Microsoft Edge","Application IDs":"2d7f3606-b07d-41d1-b9d2-0d0c9296a6e8"},{"Application Name":"Microsoft Bing Default Search Engine","Application IDs":"1786c5ed-9644-47b2-8aa0-7201292175b6"},{"Application Name":"Microsoft Defender for Cloud Apps","Application IDs":"3090ab82-f1c1-4cdf-af2c-5d7a6f3e2cc7"},{"Application Name":"Microsoft Docs","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Dynamics ERP","Application IDs":"00000015-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Edge Insider Addons Prod","Application IDs":"6253bca8-faf2-4587-8f2f-b056d80998a7"},{"Application Name":"Microsoft Exchange Online Protection","Application IDs":"00000007-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Forms","Application IDs":"c9a559d2-7aab-4f13-a6ed-e7e9c52aec87"},{"Application Name":"Microsoft Graph","Application IDs":"00000003-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Intune Web Company Portal","Application IDs":"74bcdadc-2fdc-4bb3-8459-76d06952a0e9"},{"Application Name":"Microsoft Intune Windows Agent","Application IDs":"fc0f3af4-6835-4174-b806-f7db311fd2f3"},{"Application Name":"Microsoft Learn","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Office","Application IDs":"d3590ed6-52b3-4102-aeff-aad2292ab01c"},{"Application Name":"Microsoft Office 365 Portal","Application IDs":"00000006-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Office Web Apps Service","Application IDs":"67e3df25-268a-4324-a550-0de1c7f97287"},{"Application Name":"Microsoft Online Syndication Partner Portal","Application IDs":"d176f6e7-38e5-40c9-8a78-3998aab820e7"},{"Application Name":"Microsoft password reset service","Application IDs":"93625bc8-bfe2-437a-97e0-3d0060024faa"},{"Application Name":"Microsoft Power BI","Application IDs":"871c010f-5e61-4fb1-83ac-98610a7e9110"},{"Application Name":"Microsoft Storefronts","Application IDs":"28b567f6-162c-4f54-99a0-6887f387bbcc"},{"Application Name":"Microsoft Stream Portal","Application IDs":"cf53fce8-def6-4aeb-8d30-b158e7b1cf83"},{"Application Name":"Microsoft Substrate Management","Application IDs":"98db8bd6-0cc0-4e67-9de5-f187f1cd1b41"},{"Application Name":"Microsoft Support","Application IDs":"fdf9885b-dd37-42bf-82e5-c3129ef5a302"},{"Application Name":"Microsoft Teams","Application IDs":"1fec8e78-bce4-4aaf-ab1b-5451cc387264"},{"Application Name":"Microsoft Teams Services","Application IDs":"cc15fd57-2c6c-4117-a88c-83b1d56b4bbe"},{"Application Name":"Microsoft Teams Web Client","Application IDs":"5e3ce6c0-2b1f-4285-8d4b-75ee78787346"},{"Application Name":"Microsoft Whiteboard Services","Application IDs":"95de633a-083e-42f5-b444-a4295d8e9314"},{"Application Name":"O365 Suite UX","Application IDs":"4345a7b9-9a63-4910-a426-35363201d503"},{"Application Name":"Office 365 Exchange Online","Application IDs":"00000002-0000-0ff1-ce00-000000000000"},{"Application Name":"Office 365 Management","Application IDs":"00b41c95-dab0-4487-9791-b9d2c32c80f2"},{"Application Name":"Office 365 Search Service","Application IDs":"66a88757-258c-4c72-893c-3e8bed4d6899"},{"Application Name":"Office 365 SharePoint Online","Application IDs":"00000003-0000-0ff1-ce00-000000000000"},{"Application Name":"Office Delve","Application IDs":"94c63fef-13a3-47bc-8074-75af8c65887a"},{"Application Name":"Office Online Add-in SSO","Application IDs":"93d53678-613d-4013-afc1-62e9e444a0a5"},{"Application Name":"Office Online Client AAD- Augmentation Loop","Application IDs":"2abdc806-e091-4495-9b10-b04d93c3f040"},{"Application Name":"Office Online Client AAD- Loki","Application IDs":"b23dd4db-9142-4734-867f-3577f640ad0c"},{"Application Name":"Office Online Client AAD- Maker","Application IDs":"17d5e35f-655b-4fb0-8ae6-86356e9a49f5"},{"Application Name":"Office Online Client MSA- Loki","Application IDs":"b6e69c34-5f1f-4c34-8cdf-7fea120b8670"},{"Application Name":"Office Online Core SSO","Application IDs":"243c63a3-247d-41c5-9d83-7788c43f1c43"},{"Application Name":"Office Online Search","Application IDs":"a9b49b65-0a12-430b-9540-c80b3332c127"},{"Application Name":"Office.com","Application IDs":"4b233688-031c-404b-9a80-a4f3f2351f90"},{"Application Name":"Office365 Shell WCSS-Client","Application IDs":"89bee1f7-5e6e-4d8a-9f3d-ecd601259da7"},{"Application Name":"OfficeClientService","Application IDs":"0f698dd4-f011-4d23-a33e-b36416dcb1e6"},{"Application Name":"OfficeHome","Application IDs":"4765445b-32c6-49b0-83e6-1d93765276ca"},{"Application Name":"OfficeShredderWacClient","Application IDs":"4d5c2d63-cf83-4365-853c-925fd1a64357"},{"Application Name":"OMSOctopiPROD","Application IDs":"62256cef-54c0-4cb4-bcac-4c67989bdc40"},{"Application Name":"OneDrive SyncEngine","Application IDs":"ab9b8c07-8f02-4f72-87fa-80105867a763"},{"Application Name":"OneNote","Application IDs":"2d4d3d8e-2be3-4bef-9f87-7875a61c29de"},{"Application Name":"Outlook Mobile","Application IDs":"27922004-5251-4030-b22d-91ecd9a37ea4"},{"Application Name":"Partner Customer Delegated Admin Offline Processor","Application IDs":"a3475900-ccec-4a69-98f5-a65cd5dc5306"},{"Application Name":"Password Breach Authenticator","Application IDs":"bdd48c81-3a58-4ea9-849c-ebea7f6b6360"},{"Application Name":"Power BI Service","Application IDs":"00000009-0000-0000-c000-000000000000"},{"Application Name":"SharedWithMe","Application IDs":"ffcb16e8-f789-467c-8ce9-f826a080d987"},{"Application Name":"SharePoint Online Web Client Extensibility","Application IDs":"08e18876-6177-487e-b8b5-cf950c1e598c"},{"Application Name":"Signup","Application IDs":"b4bddae8-ab25-483e-8670-df09b9f1d0ea"},{"Application Name":"Skype for Business Online","Application IDs":"00000004-0000-0ff1-ce00-000000000000"},{"Application Name":"Sway","Application IDs":"905fcf26-4eb7-48a0-9ff0-8dcc7194b5ba"},{"Application Name":"Universal Store Native Client","Application IDs":"268761a2-03f3-40df-8a8b-c3db24145b6b"},{"Application Name":"Vortex [wsfed enabled]","Application IDs":"5572c4c0-d078-44ce-b81c-6cbf8d3ed39e"},{"Application Name":"Windows Azure Active Directory","Application IDs":"00000002-0000-0000-c000-000000000000"},{"Application Name":"Windows Azure Service Management API","Application IDs":"797f4846-ba00-4fd7-ba43-dac1f8f63013"},{"Application Name":"WindowsDefenderATP Portal","Application IDs":"a3b79187-70b2-4139-83f9-6016c58cd27b"},{"Application Name":"Windows Search","Application IDs":"26a7ee05-5602-4d76-a7ba-eae8b7b67941"},{"Application Name":"Windows Spotlight","Application IDs":"1b3c667f-cde3-4090-b60b-3d2abd0117f0"},{"Application Name":"Windows Store for Business","Application IDs":"45a330b1-b1ec-4cc1-9161-9f03992aa49f"},{"Application Name":"Yammer","Application IDs":"00000005-0000-0ff1-ce00-000000000000"},{"Application Name":"Yammer Web","Application IDs":"c1c74fed-04c9-4704-80dc-9f79a2e515cb"},{"Application Name":"Yammer Web Embed","Application IDs":"e1ef36fd-b883-4dbf-97f0-9ece4b576fc6"}]' | ConvertFrom-Json | Where-Object -Property 'Application IDs' -EQ $data.applicationId - $HTML = Get-Content 'TemplateEmail.HTML' -Raw | Out-String - switch ($data.Operation) { - 'New-InboxRule' { - $Title = "$($TenantFilter) - New Rule Detected for $($data.UserId)" - $RuleTable = ($TableObj | ConvertTo-Html -Fragment | Out-String).Replace('', '
') - $ParameterName - $IntroText = "

A new rule has been created for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" - $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" - $ButtonText = 'Start BEC Investigation' - $AfterButtonText = '

If you believe this is a suspect rule, you can click the button above to start the investigation.

' - } - 'Set-inboxrule' { - $Title = "$($TenantFilter) - Rule Edit Detected for $($data.UserId)" - $RuleTable = ($TableObj | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') - $ParameterName - $IntroText = "

A rule has been edited for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" - $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" - $ButtonText = 'Start BEC Investigation' - $AfterButtonText = '

If you believe this is a suspect rule, you can click the button above to start the investigation.

' - } - 'Add member to role.' { - $Title = "$($TenantFilter) - Role change detected for $($data.ObjectId)" - $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') - $IntroText = "

$($data.UserId) has added $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" - $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" - $ButtonText = 'Role Management' - $AfterButtonText = '

If this role is incorrect, or you need more information, use the button to jump to the Role Management page.

' - - } - 'Disable account.' { - $Title = "$($TenantFilter) - $($data.ObjectId) has been disabled" - $IntroText = "$($data.ObjectId) has been disabled by $($data.UserId)." - $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" - $ButtonText = 'User Management' - $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' - } - 'Enable account.' { - $Title = "$($TenantFilter) - $($data.ObjectId) has been enabled" - $IntroText = "$($data.ObjectId) has been enabled by $($data.UserId)." - $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" - $ButtonText = 'User Management' - $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' - } - 'Update StsRefreshTokenValidFrom Timestamp.' { - $Title = "$($TenantFilter) - $($data.ObjectId) has had all sessions revoked" - $IntroText = "$($data.ObjectId) has had their sessions revoked by $($data.UserId)." - $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" - $ButtonText = 'User Management' - $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' - } - 'Disable Strong Authentication.' { - $Title = "$($TenantFilter) - $($data.ObjectId) has been MFA disabled" - $IntroText = "$($data.ObjectId) MFA has been disabled by $($data.UserId)." - $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" - $ButtonText = 'User Management' - $AfterButtonText = '

If this is incorrect, use the user management screen to reenable MFA

' - } - 'Remove Member from a role.' { - $Title = "$($TenantFilter) - Role change detected for $($data.ObjectId)" - $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') - $IntroText = "

$($data.UserId) has removed $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" - $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" - $ButtonText = 'Role Management' - $AfterButtonText = '

If this role change is incorrect, or you need more information, use the button to jump to the Role Management page.

' - - } - - 'Reset user password.' { - $Title = "$($TenantFilter) - $($data.ObjectId) has had their password reset" - $IntroText = "$($data.ObjectId) has had their password reset by $($data.userId)." - $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" - $ButtonText = 'User Management' - $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' - - } - 'AdminLoggedIn' { - $Table = ($TableObj | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') - if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } - $Title = "$($TenantFilter) - an admin account has logged on" - $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). See the table below for more information. $Table" - $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserKey)&tenantDomain=$($data.OrganizationId)" - $ButtonText = 'User Management' - $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' - - } - 'UserLoggedInFromUnknownLocation' { - $Table = ($TableObj | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') - if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } - $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" - $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($Country) - $($City).

You have set up alerts to be notified when this happens. See the table below for more info.$Table" - $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" - $ButtonText = 'User Management' - $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' - } - 'Add service principal.' { - if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } - $Title = "$($TenantFilter) - Service Principal $($data.ObjectId) has been added." - $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') - $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." - $ButtonUrl = "$CIPPPURL/tenant/administration/enterprise-apps?customerId=?customerId=$($data.OrganizationId)" - $ButtonText = 'Enterprise Apps' - } - 'Remove service principal.' { - if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } - $Title = "$($TenantFilter) - Service Principal $($data.ObjectId) has been removed." - $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') - $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." - $ButtonUrl = "$CIPPPURL/tenant/administration/enterprise-apps?customerId=?customerId=$($data.OrganizationId)" - $ButtonText = 'Enterprise Apps' } - default { - break + if ($ConditionMet) { + foreach ($action in $dos) { + switch ($action.execute) { + 'generateemail' { + $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data + Send-CIPPAlert -Type 'email' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter + } + 'generatePSA' { + $GenerateEmail = New-CIPPAlertTemplate -format 'html'-data $Data + Send-CIPPAlert -Type 'psa' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter + } + 'generateWebhook' { + $GenerateJSON = New-CIPPAlertTemplate -format 'json' -data $Data + $JsonContent = @{ + Title = $GenerateJSON.Title + ActionUrl = $GenerateJSON.ButtonUrl + RawData = $Data + IP = $data.ClientIP + PotentialCountry = $Country + PotentialCity = $City + PotentialProxy = $Proxy + PotentialHosting = $hosting + PotentialASName = $ASName + } | ConvertTo-Json -Depth 15 -Compress + Send-CIPPAlert -Type 'webhook' -Title $title -JSONContent $JsonContent -TenantFilter $TenantFilter + } + 'disableUser' { + Set-CIPPSignInState -TenantFilter $TenantFilter -User $data.UserId -AccountEnabled $false -APIName 'Alert Engine' -ExecutingUser 'Alert Engine' + } + 'becremediate' { + $username = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($data.UserId)" -tenantid $TenantFilter).UserPrincipalName + Set-CIPPResetPassword -userid $username -tenantFilter $TenantFilter -APIName 'Alert Engine' -ExecutingUser 'Alert Engine' + Set-CIPPSignInState -userid $username -AccountEnabled $false -tenantFilter $TenantFilter -APIName 'Alert Engine' -ExecutingUser 'Alert Engine' + Revoke-CIPPSessions -userid $username -username $username -ExecutingUser 'Alert Engine' -APIName 'Alert Engine' -tenantFilter $TenantFilter + $RuleDisabled = 0 + New-ExoRequest -anchor $username -tenantid $TenantFilter -cmdlet 'get-inboxrule' -cmdParams @{Mailbox = $username } | ForEach-Object { + $null = New-ExoRequest -anchor $username -tenantid $TenantFilter -cmdlet 'Disable-InboxRule' -cmdParams @{Confirm = $false; Identity = $_.Identity } + "Disabled Inbox Rule $($_.Identity) for $username" + $RuleDisabled ++ + } + if ($RuleDisabled) { + "Disabled $RuleDisabled Inbox Rules for $username" + } else { + "No Inbox Rules found for $username. We have not disabled any rules." + } + Write-LogMessage -API 'BECRemediate' -tenant $tenantfilter -message "Executed Remediation for $username" -sev 'Info' + } + 'store' { + $Context = New-AzDataTableContext -ConnectionString $action.ConnectionString -TableName 'AuditLog' + New-AzDataTable -Context $Context | Out-Null + $TableObj = @{ + RowKey = [string]$data.id + PartitionKey = [string]$data.tenant + Tenant = [string]$data.tenant + Operation = [string]$data.operation + RawData = [string]($data | ConvertTo-Json -Depth 15 -Compress) + IP = [string]$data.clientip + Country = [string]$Country + City = [string]$City + Proxy = [string]$Proxy + Hosting = [string]$hosting + ASName = [string]$ASName + } + Add-CIPPAzDataTableEntity -Context $Context -Entity $TableObj + } + 'cippcommand' { + $CommandSplat = @{} + $action.parameters.psobject.properties | ForEach-Object { $CommandSplat.Add($_.name, $_.value) } + if ($CommandSplat['userid']) { $CommandSplat['userid'] = $data.userid } + if ($CommandSplat['tenantfilter']) { $CommandSplat['tenantfilter'] = $tenantfilter } + if ($CommandSplat['tenant']) { $CommandSplat['tenant'] = $tenantfilter } + if ($CommandSplat['user']) { $CommandSplat['user'] = $data.userid } + if ($CommandSplat['username']) { $CommandSplat['username'] = $data.userid } + & $action.command.value @CommandSplat + } + } + } } - } - $HTML = "$HTML" -f $Title, $IntroText, $ButtonUrl, $ButtonText, $AfterButtonText - - Write-Host 'Add IP and potential location to knownlocation db for this specific user' if ($data.ClientIP) { $IP = $data.ClientIP @@ -207,23 +192,10 @@ function Invoke-CippWebhookProcessing { Tenant = [string]$TenantFilter CountryOrRegion = "$Country" City = "$City" + Proxy = "$Proxy" + Hosting = "$hosting" + ASName = "$ASName" } $null = Add-CIPPAzDataTableEntity @LocationTable -Entity $LocationInfo -Force } - $JsonContent = @{ - Title = $Title - ActionUrl = $ButtonUrl - RawData = $Data - IP = $data.ClientIP - PotentialCountry = $Country - PotentialCity = $City - } | ConvertTo-Json -Depth 15 -Compress - if ($Title) { - Write-Host 'Sending alert to email' - Send-CIPPAlert -Type 'email' -Title $title -HTMLContent $HTML -TenantFilter $TenantFilter - Write-Host 'Sending alert to webhook' - Send-CIPPAlert -Type 'webhook' -Title $title -JSONContent $JsonContent -TenantFilter $TenantFilter - Write-Host 'Sending alert to PSA' - Send-CIPPAlert -Type 'psa' -Title $title -HTMLContent $HTML -TenantFilter $TenantFilter - } } diff --git a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 new file mode 100644 index 000000000000..056a40b8bf1c --- /dev/null +++ b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 @@ -0,0 +1,150 @@ +function New-CIPPAlertTemplate { + param( + [Parameter(Mandatory = $true)] + $Data, + [Parameter(Mandatory = $true)] + [ValidateSet('html', 'json')] + $Format + ) + + $Appname = '[{"Application Name":"ACOM Azure Website","Application IDs":"23523755-3a2b-41ca-9315-f81f3f566a95"},{"Application Name":"AEM-DualAuth","Application IDs":"69893ee3-dd10-4b1c-832d-4870354be3d8"},{"Application Name":"ASM Campaign Servicing","Application IDs":"0cb7b9ec-5336-483b-bc31-b15b5788de71"},{"Application Name":"Azure Advanced Threat Protection","Application IDs":"7b7531ad-5926-4f2d-8a1d-38495ad33e17"},{"Application Name":"Azure Data Lake","Application IDs":"e9f49c6b-5ce5-44c8-925d-015017e9f7ad"},{"Application Name":"Azure Lab Services Portal","Application IDs":"835b2a73-6e10-4aa5-a979-21dfda45231c"},{"Application Name":"Azure Portal","Application IDs":"c44b4083-3bb0-49c1-b47d-974e53cbdf3c"},{"Application Name":"AzureSupportCenter","Application IDs":"37182072-3c9c-4f6a-a4b3-b3f91cacffce"},{"Application Name":"Bing","Application IDs":"9ea1ad79-fdb6-4f9a-8bc3-2b70f96e34c7"},{"Application Name":"CPIM Service","Application IDs":"bb2a2e3a-c5e7-4f0a-88e0-8e01fd3fc1f4"},{"Application Name":"CRM Power BI Integration","Application IDs":"e64aa8bc-8eb4-40e2-898b-cf261a25954f"},{"Application Name":"Dataverse","Application IDs":"00000007-0000-0000-c000-000000000000"},{"Application Name":"Enterprise Roaming and Backup","Application IDs":"60c8bde5-3167-4f92-8fdb-059f6176dc0f"},{"Application Name":"IAM Supportability","Application IDs":"a57aca87-cbc0-4f3c-8b9e-dc095fdc8978"},{"Application Name":"IrisSelectionFrontDoor","Application IDs":"16aeb910-ce68-41d1-9ac3-9e1673ac9575"},{"Application Name":"MCAPI Authorization Prod","Application IDs":"d73f4b35-55c9-48c7-8b10-651f6f2acb2e"},{"Application Name":"Media Analysis and Transformation Service","Application IDs":"944f0bd1-117b-4b1c-af26-804ed95e767e
0cd196ee-71bf-4fd6-a57c-b491ffd4fb1e"},{"Application Name":"Microsoft 365 Support Service","Application IDs":"ee272b19-4411-433f-8f28-5c13cb6fd407"},{"Application Name":"Microsoft App Access Panel","Application IDs":"0000000c-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Approval Management","Application IDs":"65d91a3d-ab74-42e6-8a2f-0add61688c74
38049638-cc2c-4cde-abe4-4479d721ed44"},{"Application Name":"Microsoft Authentication Broker","Application IDs":"29d9ed98-a469-4536-ade2-f981bc1d605e"},{"Application Name":"Microsoft Azure CLI","Application IDs":"04b07795-8ddb-461a-bbee-02f9e1bf7b46"},{"Application Name":"Microsoft Azure PowerShell","Application IDs":"1950a258-227b-4e31-a9cf-717495945fc2"},{"Application Name":"Microsoft Bing Search","Application IDs":"cf36b471-5b44-428c-9ce7-313bf84528de"},{"Application Name":"Microsoft Bing Search for Microsoft Edge","Application IDs":"2d7f3606-b07d-41d1-b9d2-0d0c9296a6e8"},{"Application Name":"Microsoft Bing Default Search Engine","Application IDs":"1786c5ed-9644-47b2-8aa0-7201292175b6"},{"Application Name":"Microsoft Defender for Cloud Apps","Application IDs":"3090ab82-f1c1-4cdf-af2c-5d7a6f3e2cc7"},{"Application Name":"Microsoft Docs","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Dynamics ERP","Application IDs":"00000015-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Edge Insider Addons Prod","Application IDs":"6253bca8-faf2-4587-8f2f-b056d80998a7"},{"Application Name":"Microsoft Exchange Online Protection","Application IDs":"00000007-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Forms","Application IDs":"c9a559d2-7aab-4f13-a6ed-e7e9c52aec87"},{"Application Name":"Microsoft Graph","Application IDs":"00000003-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Intune Web Company Portal","Application IDs":"74bcdadc-2fdc-4bb3-8459-76d06952a0e9"},{"Application Name":"Microsoft Intune Windows Agent","Application IDs":"fc0f3af4-6835-4174-b806-f7db311fd2f3"},{"Application Name":"Microsoft Learn","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Office","Application IDs":"d3590ed6-52b3-4102-aeff-aad2292ab01c"},{"Application Name":"Microsoft Office 365 Portal","Application IDs":"00000006-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Office Web Apps Service","Application IDs":"67e3df25-268a-4324-a550-0de1c7f97287"},{"Application Name":"Microsoft Online Syndication Partner Portal","Application IDs":"d176f6e7-38e5-40c9-8a78-3998aab820e7"},{"Application Name":"Microsoft password reset service","Application IDs":"93625bc8-bfe2-437a-97e0-3d0060024faa"},{"Application Name":"Microsoft Power BI","Application IDs":"871c010f-5e61-4fb1-83ac-98610a7e9110"},{"Application Name":"Microsoft Storefronts","Application IDs":"28b567f6-162c-4f54-99a0-6887f387bbcc"},{"Application Name":"Microsoft Stream Portal","Application IDs":"cf53fce8-def6-4aeb-8d30-b158e7b1cf83"},{"Application Name":"Microsoft Substrate Management","Application IDs":"98db8bd6-0cc0-4e67-9de5-f187f1cd1b41"},{"Application Name":"Microsoft Support","Application IDs":"fdf9885b-dd37-42bf-82e5-c3129ef5a302"},{"Application Name":"Microsoft Teams","Application IDs":"1fec8e78-bce4-4aaf-ab1b-5451cc387264"},{"Application Name":"Microsoft Teams Services","Application IDs":"cc15fd57-2c6c-4117-a88c-83b1d56b4bbe"},{"Application Name":"Microsoft Teams Web Client","Application IDs":"5e3ce6c0-2b1f-4285-8d4b-75ee78787346"},{"Application Name":"Microsoft Whiteboard Services","Application IDs":"95de633a-083e-42f5-b444-a4295d8e9314"},{"Application Name":"O365 Suite UX","Application IDs":"4345a7b9-9a63-4910-a426-35363201d503"},{"Application Name":"Office 365 Exchange Online","Application IDs":"00000002-0000-0ff1-ce00-000000000000"},{"Application Name":"Office 365 Management","Application IDs":"00b41c95-dab0-4487-9791-b9d2c32c80f2"},{"Application Name":"Office 365 Search Service","Application IDs":"66a88757-258c-4c72-893c-3e8bed4d6899"},{"Application Name":"Office 365 SharePoint Online","Application IDs":"00000003-0000-0ff1-ce00-000000000000"},{"Application Name":"Office Delve","Application IDs":"94c63fef-13a3-47bc-8074-75af8c65887a"},{"Application Name":"Office Online Add-in SSO","Application IDs":"93d53678-613d-4013-afc1-62e9e444a0a5"},{"Application Name":"Office Online Client AAD- Augmentation Loop","Application IDs":"2abdc806-e091-4495-9b10-b04d93c3f040"},{"Application Name":"Office Online Client AAD- Loki","Application IDs":"b23dd4db-9142-4734-867f-3577f640ad0c"},{"Application Name":"Office Online Client AAD- Maker","Application IDs":"17d5e35f-655b-4fb0-8ae6-86356e9a49f5"},{"Application Name":"Office Online Client MSA- Loki","Application IDs":"b6e69c34-5f1f-4c34-8cdf-7fea120b8670"},{"Application Name":"Office Online Core SSO","Application IDs":"243c63a3-247d-41c5-9d83-7788c43f1c43"},{"Application Name":"Office Online Search","Application IDs":"a9b49b65-0a12-430b-9540-c80b3332c127"},{"Application Name":"Office.com","Application IDs":"4b233688-031c-404b-9a80-a4f3f2351f90"},{"Application Name":"Office365 Shell WCSS-Client","Application IDs":"89bee1f7-5e6e-4d8a-9f3d-ecd601259da7"},{"Application Name":"OfficeClientService","Application IDs":"0f698dd4-f011-4d23-a33e-b36416dcb1e6"},{"Application Name":"OfficeHome","Application IDs":"4765445b-32c6-49b0-83e6-1d93765276ca"},{"Application Name":"OfficeShredderWacClient","Application IDs":"4d5c2d63-cf83-4365-853c-925fd1a64357"},{"Application Name":"OMSOctopiPROD","Application IDs":"62256cef-54c0-4cb4-bcac-4c67989bdc40"},{"Application Name":"OneDrive SyncEngine","Application IDs":"ab9b8c07-8f02-4f72-87fa-80105867a763"},{"Application Name":"OneNote","Application IDs":"2d4d3d8e-2be3-4bef-9f87-7875a61c29de"},{"Application Name":"Outlook Mobile","Application IDs":"27922004-5251-4030-b22d-91ecd9a37ea4"},{"Application Name":"Partner Customer Delegated Admin Offline Processor","Application IDs":"a3475900-ccec-4a69-98f5-a65cd5dc5306"},{"Application Name":"Password Breach Authenticator","Application IDs":"bdd48c81-3a58-4ea9-849c-ebea7f6b6360"},{"Application Name":"Power BI Service","Application IDs":"00000009-0000-0000-c000-000000000000"},{"Application Name":"SharedWithMe","Application IDs":"ffcb16e8-f789-467c-8ce9-f826a080d987"},{"Application Name":"SharePoint Online Web Client Extensibility","Application IDs":"08e18876-6177-487e-b8b5-cf950c1e598c"},{"Application Name":"Signup","Application IDs":"b4bddae8-ab25-483e-8670-df09b9f1d0ea"},{"Application Name":"Skype for Business Online","Application IDs":"00000004-0000-0ff1-ce00-000000000000"},{"Application Name":"Sway","Application IDs":"905fcf26-4eb7-48a0-9ff0-8dcc7194b5ba"},{"Application Name":"Universal Store Native Client","Application IDs":"268761a2-03f3-40df-8a8b-c3db24145b6b"},{"Application Name":"Vortex [wsfed enabled]","Application IDs":"5572c4c0-d078-44ce-b81c-6cbf8d3ed39e"},{"Application Name":"Windows Azure Active Directory","Application IDs":"00000002-0000-0000-c000-000000000000"},{"Application Name":"Windows Azure Service Management API","Application IDs":"797f4846-ba00-4fd7-ba43-dac1f8f63013"},{"Application Name":"WindowsDefenderATP Portal","Application IDs":"a3b79187-70b2-4139-83f9-6016c58cd27b"},{"Application Name":"Windows Search","Application IDs":"26a7ee05-5602-4d76-a7ba-eae8b7b67941"},{"Application Name":"Windows Spotlight","Application IDs":"1b3c667f-cde3-4090-b60b-3d2abd0117f0"},{"Application Name":"Windows Store for Business","Application IDs":"45a330b1-b1ec-4cc1-9161-9f03992aa49f"},{"Application Name":"Yammer","Application IDs":"00000005-0000-0ff1-ce00-000000000000"},{"Application Name":"Yammer Web","Application IDs":"c1c74fed-04c9-4704-80dc-9f79a2e515cb"},{"Application Name":"Yammer Web Embed","Application IDs":"e1ef36fd-b883-4dbf-97f0-9ece4b576fc6"}]' | ConvertFrom-Json | Where-Object -Property 'Application IDs' -EQ $data.applicationId + $HTMLTemplate = Get-Content 'TemplateEmail.HTML' -Raw | Out-String + $Title = '' + $IntroText = '' + $ButtonUrl = '' + $ButtonText = '' + $AfterButtonText = '' + $RuleTable = '' + $Table = '' + + switch ($Data.Operation) { + 'New-InboxRule' { + $Title = "$($TenantFilter) - New Rule Detected for $($data.UserId)" + $RuleTable = ($TableObj | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $ParameterName + $IntroText = "

A new rule has been created for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" + $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" + $ButtonText = 'Start BEC Investigation' + $AfterButtonText = '

If you believe this is a suspect rule, you can click the button above to start the investigation.

' + } + 'Set-inboxrule' { + $Title = "$($TenantFilter) - Rule Edit Detected for $($data.UserId)" + $RuleTable = ($TableObj | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $ParameterName + $IntroText = "

A rule has been edited for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" + $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" + $ButtonText = 'Start BEC Investigation' + $AfterButtonText = '

If you believe this is a suspect rule, you can click the button above to start the investigation.

' + } + 'Add member to role.' { + $Title = "$($TenantFilter) - Role change detected for $($data.ObjectId)" + $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = "

$($data.UserId) has added $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" + $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" + $ButtonText = 'Role Management' + $AfterButtonText = '

If this role is incorrect, or you need more information, use the button to jump to the Role Management page.

' + + } + 'Disable account.' { + $Title = "$($TenantFilter) - $($data.ObjectId) has been disabled" + $IntroText = "$($data.ObjectId) has been disabled by $($data.UserId)." + $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" + $ButtonText = 'User Management' + $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' + } + 'Enable account.' { + $Title = "$($TenantFilter) - $($data.ObjectId) has been enabled" + $IntroText = "$($data.ObjectId) has been enabled by $($data.UserId)." + $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" + $ButtonText = 'User Management' + $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' + } + 'Update StsRefreshTokenValidFrom Timestamp.' { + $Title = "$($TenantFilter) - $($data.ObjectId) has had all sessions revoked" + $IntroText = "$($data.ObjectId) has had their sessions revoked by $($data.UserId)." + $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" + $ButtonText = 'User Management' + $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' + } + 'Disable Strong Authentication.' { + $Title = "$($TenantFilter) - $($data.ObjectId) has been MFA disabled" + $IntroText = "$($data.ObjectId) MFA has been disabled by $($data.UserId)." + $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" + $ButtonText = 'User Management' + $AfterButtonText = '

If this is incorrect, use the user management screen to reenable MFA

' + } + 'Remove Member from a role.' { + $Title = "$($TenantFilter) - Role change detected for $($data.ObjectId)" + $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = "

$($data.UserId) has removed $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" + $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" + $ButtonText = 'Role Management' + $AfterButtonText = '

If this role change is incorrect, or you need more information, use the button to jump to the Role Management page.

' + + } + + 'Reset user password.' { + $Title = "$($TenantFilter) - $($data.ObjectId) has had their password reset" + $IntroText = "$($data.ObjectId) has had their password reset by $($data.userId)." + $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" + $ButtonText = 'User Management' + $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' + + } + 'AdminLoggedIn' { + $Table = ($TableObj | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') + if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } + $Title = "$($TenantFilter) - an admin account has logged on" + $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). See the table below for more information. $Table" + $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserKey)&tenantDomain=$($data.OrganizationId)" + $ButtonText = 'User Management' + $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' + + } + 'UserLoggedInFromUnknownLocation' { + $Table = ($TableObj | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') + if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } + $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" + $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($Country) - $($City).

You have set up alerts to be notified when this happens. See the table below for more info.$Table" + $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" + $ButtonText = 'User Management' + $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' + } + 'Add service principal.' { + if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } + $Title = "$($TenantFilter) - Service Principal $($data.ObjectId) has been added." + $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." + $ButtonUrl = "$CIPPPURL/tenant/administration/enterprise-apps?customerId=?customerId=$($data.OrganizationId)" + $ButtonText = 'Enterprise Apps' + } + 'Remove service principal.' { + if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } + $Title = "$($TenantFilter) - Service Principal $($data.ObjectId) has been removed." + $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." + $ButtonUrl = "$CIPPPURL/tenant/administration/enterprise-apps?customerId=?customerId=$($data.OrganizationId)" + $ButtonText = 'Enterprise Apps' + } + + default { + $Title = 'A custom alert has occured' + $IntroText = "You have setup CIPP to send you a custom alert for the event $($Data.operation)" + $Table = ($data | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" + $ButtonText = 'User Management' + } + } + + if ($Format -eq 'html') { + return [pscustomobject]@{ + title = $Title + htmlcontent = $HTMLTemplate -f $Title, $IntroText, $ButtonUrl, $ButtonText, $AfterButtonText + } + } elseif ($Format -eq 'json') { + return [pscustomobject]@{ + title = $Title + buttonurl = $ButtonUrl + } + } +} diff --git a/PublicWebhooksProcess/run.ps1 b/PublicWebhooksProcess/run.ps1 index 30e692a87559..ac7e3c1ddac6 100644 --- a/PublicWebhooksProcess/run.ps1 +++ b/PublicWebhooksProcess/run.ps1 @@ -33,15 +33,7 @@ if ($Request.query.CIPPID -in $Webhooks.RowKey) { Write-Host "Operations to process for this client: $($Webhookinfo.Operations)" foreach ($Item in $Data) { Write-Host "Processing $($item.operation)" - if ($item.operation -in $operations) { - Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations - } - if ($item.operation -eq 'UserLoggedIn' -and 'UserLoggedInFromUnknownLocation' -in $operations) { - Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations - } - if ($item.operation -eq 'UserLoggedIn' -and 'AdminLoggedIn' -in $operations) { - Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations - } + Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url } } } From 69e2414c5c02ac00df282050c9a62677fbb38fa8 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 17:56:42 +0100 Subject: [PATCH 54/84] update webhook processing --- .../CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 10 +++++++--- PublicWebhooksProcess/run.ps1 | 1 - 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 26338a5be922..653ce69e4abb 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -45,19 +45,22 @@ function Invoke-CippWebhookProcessing { Write-Host 'No need to process this operation.' return '' } + + Write-Host "Operation: $($data.operation)" switch ($data.operation) { { 'UserLoggedIn' -eq $data.operation -and $Country -notin $AllowedLocations -and $data.ResultStatus -eq 'Success' -and $TableObj.ResultStatusDetail -eq 'Success' } { $data.operation = 'UserLoggedInFromUnknownLocation' } { 'UserloggedIn' -eq $data.operation -and $data.UserType -eq 2 -and $data.ResultStatus -eq 'Success' -and $TableObj.ResultStatusDetail -eq 'Success' } { $data.operation = 'AdminLoggedIn' } default { break } } - + Write-Host "Rewrote to operation: $($data.operation)" #Check if we actually need to do anything, and if not, break away. $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable -Filter "Tenant eq '$tenantfilter'" foreach ($AlertSetting in $Alertconfig) { $ifs = $AlertSetting.If | ConvertFrom-Json $Dos = $AlertSetting.execution | ConvertFrom-Json if ($data.operation -notin $Ifs.selection -and $ifs.selection -ne 'AnyAlert' ) { - Write-Host 'Add IP and potential location to knownlocation db for this specific user' + Write-Host 'Not an operation to do anything for. storing IP info' + Write-Host 'Add IP and potential location to knownlocation db for this specific user.' if ($data.ClientIP) { $IP = $data.ClientIP if ($IP -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { @@ -75,7 +78,7 @@ function Invoke-CippWebhookProcessing { } $null = Add-CIPPAzDataTableEntity @LocationTable -Entity $LocationInfo -Force } - return '' + Continue } else { $ConditionMet = $true } @@ -103,6 +106,7 @@ function Invoke-CippWebhookProcessing { if ($ConditionMet) { foreach ($action in $dos) { + Write-Host "this is our action: $($action | ConvertTo-Json -Depth 15 -Compress))" switch ($action.execute) { 'generateemail' { $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data diff --git a/PublicWebhooksProcess/run.ps1 b/PublicWebhooksProcess/run.ps1 index ac7e3c1ddac6..1f404c867df2 100644 --- a/PublicWebhooksProcess/run.ps1 +++ b/PublicWebhooksProcess/run.ps1 @@ -23,7 +23,6 @@ if ($Request.query.CIPPID -in $Webhooks.RowKey) { } else { # Auditlog Subscriptions $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID - $operations = $Webhookinfo.Operations -split ',' foreach ($ReceivedItem In ($Request.body)) { $ReceivedItem = [pscustomobject]$ReceivedItem $TenantFilter = (Get-Tenants | Where-Object -Property customerId -EQ $ReceivedItem.TenantId).defaultDomainName From 25653b396093a41575aae4f701738a9db633b998 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 18:52:12 +0100 Subject: [PATCH 55/84] move tempate email --- Modules/CIPPCore/Public/TemplateEmail.html => TemplateEmail.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Modules/CIPPCore/Public/TemplateEmail.html => TemplateEmail.html (100%) diff --git a/Modules/CIPPCore/Public/TemplateEmail.html b/TemplateEmail.html similarity index 100% rename from Modules/CIPPCore/Public/TemplateEmail.html rename to TemplateEmail.html From 0d771b76885e7d6207100535ee237b1e68a19f96 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 20:35:17 +0100 Subject: [PATCH 56/84] clientip port number removal --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 653ce69e4abb..e3d05dd4bc58 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -23,6 +23,9 @@ function Invoke-CippWebhookProcessing { $City = $Location.City } else { Write-Host 'We have to do a lookup' + if ($data.clientip -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { + $data.clientip = $data.clientip -replace ':\d+$', '' # Remove the port number if present + } $Location = Get-CIPPGeoIPLocation -IP $data.clientip $Country = if ($Location.countryCode) { $Location.CountryCode } else { 'Unknown' } $City = if ($Location.cityName) { $Location.cityName } else { 'Unknown' } @@ -61,7 +64,7 @@ function Invoke-CippWebhookProcessing { if ($data.operation -notin $Ifs.selection -and $ifs.selection -ne 'AnyAlert' ) { Write-Host 'Not an operation to do anything for. storing IP info' Write-Host 'Add IP and potential location to knownlocation db for this specific user.' - if ($data.ClientIP) { + if ($data.ClientIP -and $data.operation -like '*LoggedIn*') { $IP = $data.ClientIP if ($IP -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { $IP = $IP -replace ':\d+$', '' # Remove the port number if present From 63b0cd03109630154b43e114898463bed45f9fe3 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 21:13:17 +0100 Subject: [PATCH 57/84] fixed issue with genmerate webhook --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index e3d05dd4bc58..815af15425dc 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -132,7 +132,7 @@ function Invoke-CippWebhookProcessing { PotentialHosting = $hosting PotentialASName = $ASName } | ConvertTo-Json -Depth 15 -Compress - Send-CIPPAlert -Type 'webhook' -Title $title -JSONContent $JsonContent -TenantFilter $TenantFilter + Send-CIPPAlert -Type 'webhook' -Title $GenerateJSON.Title -JSONContent $JsonContent -TenantFilter $TenantFilter } 'disableUser' { Set-CIPPSignInState -TenantFilter $TenantFilter -User $data.UserId -AccountEnabled $false -APIName 'Alert Engine' -ExecutingUser 'Alert Engine' From 18788e4cdfa4bf81a6594dde32bd0e5e0f1bc95e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 21:55:27 +0100 Subject: [PATCH 58/84] webhook improvements --- .../Public/Invoke-CIPPWebhookProcessing.ps1 | 10 ++++---- .../CIPPCore/Public/New-CIPPAlertTemplate.ps1 | 23 +++++++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 815af15425dc..01bc94ef8812 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -51,6 +51,8 @@ function Invoke-CippWebhookProcessing { Write-Host "Operation: $($data.operation)" switch ($data.operation) { + { 'UserLoggedIn' -eq $data.operation -and $proxy -eq $true } { $data.operation = 'BadRepIP' } + { 'UserLoggedIn' -eq $data.operation -and $hosting -eq $true } { $data.operation = 'HostedIP' } { 'UserLoggedIn' -eq $data.operation -and $Country -notin $AllowedLocations -and $data.ResultStatus -eq 'Success' -and $TableObj.ResultStatusDetail -eq 'Success' } { $data.operation = 'UserLoggedInFromUnknownLocation' } { 'UserloggedIn' -eq $data.operation -and $data.UserType -eq 2 -and $data.ResultStatus -eq 'Success' -and $TableObj.ResultStatusDetail -eq 'Success' } { $data.operation = 'AdminLoggedIn' } default { break } @@ -63,8 +65,8 @@ function Invoke-CippWebhookProcessing { $Dos = $AlertSetting.execution | ConvertFrom-Json if ($data.operation -notin $Ifs.selection -and $ifs.selection -ne 'AnyAlert' ) { Write-Host 'Not an operation to do anything for. storing IP info' - Write-Host 'Add IP and potential location to knownlocation db for this specific user.' if ($data.ClientIP -and $data.operation -like '*LoggedIn*') { + Write-Host 'Add IP and potential location to knownlocation db for this specific user.' $IP = $data.ClientIP if ($IP -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { $IP = $IP -replace ':\d+$', '' # Remove the port number if present @@ -111,12 +113,12 @@ function Invoke-CippWebhookProcessing { foreach ($action in $dos) { Write-Host "this is our action: $($action | ConvertTo-Json -Depth 15 -Compress))" switch ($action.execute) { - 'generateemail' { - $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data + 'generatemail' { + $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -LocationInfo $Location Send-CIPPAlert -Type 'email' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter } 'generatePSA' { - $GenerateEmail = New-CIPPAlertTemplate -format 'html'-data $Data + $GenerateEmail = New-CIPPAlertTemplate -format 'html'-data $Data -LocationInfo $Location Send-CIPPAlert -Type 'psa' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter } 'generateWebhook' { diff --git a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 index 056a40b8bf1c..af27802a8524 100644 --- a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 @@ -4,7 +4,8 @@ function New-CIPPAlertTemplate { $Data, [Parameter(Mandatory = $true)] [ValidateSet('html', 'json')] - $Format + $Format, + $LocationInfo ) $Appname = '[{"Application Name":"ACOM Azure Website","Application IDs":"23523755-3a2b-41ca-9315-f81f3f566a95"},{"Application Name":"AEM-DualAuth","Application IDs":"69893ee3-dd10-4b1c-832d-4870354be3d8"},{"Application Name":"ASM Campaign Servicing","Application IDs":"0cb7b9ec-5336-483b-bc31-b15b5788de71"},{"Application Name":"Azure Advanced Threat Protection","Application IDs":"7b7531ad-5926-4f2d-8a1d-38495ad33e17"},{"Application Name":"Azure Data Lake","Application IDs":"e9f49c6b-5ce5-44c8-925d-015017e9f7ad"},{"Application Name":"Azure Lab Services Portal","Application IDs":"835b2a73-6e10-4aa5-a979-21dfda45231c"},{"Application Name":"Azure Portal","Application IDs":"c44b4083-3bb0-49c1-b47d-974e53cbdf3c"},{"Application Name":"AzureSupportCenter","Application IDs":"37182072-3c9c-4f6a-a4b3-b3f91cacffce"},{"Application Name":"Bing","Application IDs":"9ea1ad79-fdb6-4f9a-8bc3-2b70f96e34c7"},{"Application Name":"CPIM Service","Application IDs":"bb2a2e3a-c5e7-4f0a-88e0-8e01fd3fc1f4"},{"Application Name":"CRM Power BI Integration","Application IDs":"e64aa8bc-8eb4-40e2-898b-cf261a25954f"},{"Application Name":"Dataverse","Application IDs":"00000007-0000-0000-c000-000000000000"},{"Application Name":"Enterprise Roaming and Backup","Application IDs":"60c8bde5-3167-4f92-8fdb-059f6176dc0f"},{"Application Name":"IAM Supportability","Application IDs":"a57aca87-cbc0-4f3c-8b9e-dc095fdc8978"},{"Application Name":"IrisSelectionFrontDoor","Application IDs":"16aeb910-ce68-41d1-9ac3-9e1673ac9575"},{"Application Name":"MCAPI Authorization Prod","Application IDs":"d73f4b35-55c9-48c7-8b10-651f6f2acb2e"},{"Application Name":"Media Analysis and Transformation Service","Application IDs":"944f0bd1-117b-4b1c-af26-804ed95e767e
0cd196ee-71bf-4fd6-a57c-b491ffd4fb1e"},{"Application Name":"Microsoft 365 Support Service","Application IDs":"ee272b19-4411-433f-8f28-5c13cb6fd407"},{"Application Name":"Microsoft App Access Panel","Application IDs":"0000000c-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Approval Management","Application IDs":"65d91a3d-ab74-42e6-8a2f-0add61688c74
38049638-cc2c-4cde-abe4-4479d721ed44"},{"Application Name":"Microsoft Authentication Broker","Application IDs":"29d9ed98-a469-4536-ade2-f981bc1d605e"},{"Application Name":"Microsoft Azure CLI","Application IDs":"04b07795-8ddb-461a-bbee-02f9e1bf7b46"},{"Application Name":"Microsoft Azure PowerShell","Application IDs":"1950a258-227b-4e31-a9cf-717495945fc2"},{"Application Name":"Microsoft Bing Search","Application IDs":"cf36b471-5b44-428c-9ce7-313bf84528de"},{"Application Name":"Microsoft Bing Search for Microsoft Edge","Application IDs":"2d7f3606-b07d-41d1-b9d2-0d0c9296a6e8"},{"Application Name":"Microsoft Bing Default Search Engine","Application IDs":"1786c5ed-9644-47b2-8aa0-7201292175b6"},{"Application Name":"Microsoft Defender for Cloud Apps","Application IDs":"3090ab82-f1c1-4cdf-af2c-5d7a6f3e2cc7"},{"Application Name":"Microsoft Docs","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Dynamics ERP","Application IDs":"00000015-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Edge Insider Addons Prod","Application IDs":"6253bca8-faf2-4587-8f2f-b056d80998a7"},{"Application Name":"Microsoft Exchange Online Protection","Application IDs":"00000007-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Forms","Application IDs":"c9a559d2-7aab-4f13-a6ed-e7e9c52aec87"},{"Application Name":"Microsoft Graph","Application IDs":"00000003-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Intune Web Company Portal","Application IDs":"74bcdadc-2fdc-4bb3-8459-76d06952a0e9"},{"Application Name":"Microsoft Intune Windows Agent","Application IDs":"fc0f3af4-6835-4174-b806-f7db311fd2f3"},{"Application Name":"Microsoft Learn","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Office","Application IDs":"d3590ed6-52b3-4102-aeff-aad2292ab01c"},{"Application Name":"Microsoft Office 365 Portal","Application IDs":"00000006-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Office Web Apps Service","Application IDs":"67e3df25-268a-4324-a550-0de1c7f97287"},{"Application Name":"Microsoft Online Syndication Partner Portal","Application IDs":"d176f6e7-38e5-40c9-8a78-3998aab820e7"},{"Application Name":"Microsoft password reset service","Application IDs":"93625bc8-bfe2-437a-97e0-3d0060024faa"},{"Application Name":"Microsoft Power BI","Application IDs":"871c010f-5e61-4fb1-83ac-98610a7e9110"},{"Application Name":"Microsoft Storefronts","Application IDs":"28b567f6-162c-4f54-99a0-6887f387bbcc"},{"Application Name":"Microsoft Stream Portal","Application IDs":"cf53fce8-def6-4aeb-8d30-b158e7b1cf83"},{"Application Name":"Microsoft Substrate Management","Application IDs":"98db8bd6-0cc0-4e67-9de5-f187f1cd1b41"},{"Application Name":"Microsoft Support","Application IDs":"fdf9885b-dd37-42bf-82e5-c3129ef5a302"},{"Application Name":"Microsoft Teams","Application IDs":"1fec8e78-bce4-4aaf-ab1b-5451cc387264"},{"Application Name":"Microsoft Teams Services","Application IDs":"cc15fd57-2c6c-4117-a88c-83b1d56b4bbe"},{"Application Name":"Microsoft Teams Web Client","Application IDs":"5e3ce6c0-2b1f-4285-8d4b-75ee78787346"},{"Application Name":"Microsoft Whiteboard Services","Application IDs":"95de633a-083e-42f5-b444-a4295d8e9314"},{"Application Name":"O365 Suite UX","Application IDs":"4345a7b9-9a63-4910-a426-35363201d503"},{"Application Name":"Office 365 Exchange Online","Application IDs":"00000002-0000-0ff1-ce00-000000000000"},{"Application Name":"Office 365 Management","Application IDs":"00b41c95-dab0-4487-9791-b9d2c32c80f2"},{"Application Name":"Office 365 Search Service","Application IDs":"66a88757-258c-4c72-893c-3e8bed4d6899"},{"Application Name":"Office 365 SharePoint Online","Application IDs":"00000003-0000-0ff1-ce00-000000000000"},{"Application Name":"Office Delve","Application IDs":"94c63fef-13a3-47bc-8074-75af8c65887a"},{"Application Name":"Office Online Add-in SSO","Application IDs":"93d53678-613d-4013-afc1-62e9e444a0a5"},{"Application Name":"Office Online Client AAD- Augmentation Loop","Application IDs":"2abdc806-e091-4495-9b10-b04d93c3f040"},{"Application Name":"Office Online Client AAD- Loki","Application IDs":"b23dd4db-9142-4734-867f-3577f640ad0c"},{"Application Name":"Office Online Client AAD- Maker","Application IDs":"17d5e35f-655b-4fb0-8ae6-86356e9a49f5"},{"Application Name":"Office Online Client MSA- Loki","Application IDs":"b6e69c34-5f1f-4c34-8cdf-7fea120b8670"},{"Application Name":"Office Online Core SSO","Application IDs":"243c63a3-247d-41c5-9d83-7788c43f1c43"},{"Application Name":"Office Online Search","Application IDs":"a9b49b65-0a12-430b-9540-c80b3332c127"},{"Application Name":"Office.com","Application IDs":"4b233688-031c-404b-9a80-a4f3f2351f90"},{"Application Name":"Office365 Shell WCSS-Client","Application IDs":"89bee1f7-5e6e-4d8a-9f3d-ecd601259da7"},{"Application Name":"OfficeClientService","Application IDs":"0f698dd4-f011-4d23-a33e-b36416dcb1e6"},{"Application Name":"OfficeHome","Application IDs":"4765445b-32c6-49b0-83e6-1d93765276ca"},{"Application Name":"OfficeShredderWacClient","Application IDs":"4d5c2d63-cf83-4365-853c-925fd1a64357"},{"Application Name":"OMSOctopiPROD","Application IDs":"62256cef-54c0-4cb4-bcac-4c67989bdc40"},{"Application Name":"OneDrive SyncEngine","Application IDs":"ab9b8c07-8f02-4f72-87fa-80105867a763"},{"Application Name":"OneNote","Application IDs":"2d4d3d8e-2be3-4bef-9f87-7875a61c29de"},{"Application Name":"Outlook Mobile","Application IDs":"27922004-5251-4030-b22d-91ecd9a37ea4"},{"Application Name":"Partner Customer Delegated Admin Offline Processor","Application IDs":"a3475900-ccec-4a69-98f5-a65cd5dc5306"},{"Application Name":"Password Breach Authenticator","Application IDs":"bdd48c81-3a58-4ea9-849c-ebea7f6b6360"},{"Application Name":"Power BI Service","Application IDs":"00000009-0000-0000-c000-000000000000"},{"Application Name":"SharedWithMe","Application IDs":"ffcb16e8-f789-467c-8ce9-f826a080d987"},{"Application Name":"SharePoint Online Web Client Extensibility","Application IDs":"08e18876-6177-487e-b8b5-cf950c1e598c"},{"Application Name":"Signup","Application IDs":"b4bddae8-ab25-483e-8670-df09b9f1d0ea"},{"Application Name":"Skype for Business Online","Application IDs":"00000004-0000-0ff1-ce00-000000000000"},{"Application Name":"Sway","Application IDs":"905fcf26-4eb7-48a0-9ff0-8dcc7194b5ba"},{"Application Name":"Universal Store Native Client","Application IDs":"268761a2-03f3-40df-8a8b-c3db24145b6b"},{"Application Name":"Vortex [wsfed enabled]","Application IDs":"5572c4c0-d078-44ce-b81c-6cbf8d3ed39e"},{"Application Name":"Windows Azure Active Directory","Application IDs":"00000002-0000-0000-c000-000000000000"},{"Application Name":"Windows Azure Service Management API","Application IDs":"797f4846-ba00-4fd7-ba43-dac1f8f63013"},{"Application Name":"WindowsDefenderATP Portal","Application IDs":"a3b79187-70b2-4139-83f9-6016c58cd27b"},{"Application Name":"Windows Search","Application IDs":"26a7ee05-5602-4d76-a7ba-eae8b7b67941"},{"Application Name":"Windows Spotlight","Application IDs":"1b3c667f-cde3-4090-b60b-3d2abd0117f0"},{"Application Name":"Windows Store for Business","Application IDs":"45a330b1-b1ec-4cc1-9161-9f03992aa49f"},{"Application Name":"Yammer","Application IDs":"00000005-0000-0ff1-ce00-000000000000"},{"Application Name":"Yammer Web","Application IDs":"c1c74fed-04c9-4704-80dc-9f79a2e515cb"},{"Application Name":"Yammer Web Embed","Application IDs":"e1ef36fd-b883-4dbf-97f0-9ece4b576fc6"}]' | ConvertFrom-Json | Where-Object -Property 'Application IDs' -EQ $data.applicationId @@ -105,7 +106,25 @@ function New-CIPPAlertTemplate { $Table = ($TableObj | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" - $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($Country) - $($City).

You have set up alerts to be notified when this happens. See the table below for more info.$Table" + $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City).

You have set up alerts to be notified when this happens. See the table below for more info.$Table" + $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" + $ButtonText = 'User Management' + $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' + } + 'BadRepIP' { + $Table = ($TableObj | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') + if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } + $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" + $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City), but is a VPN, Proxy, or IP anonimizing service.

You have set up alerts to be notified when this happens. See the table below for more info.$Table" + $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" + $ButtonText = 'User Management' + $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' + } + 'HostedIP' { + $Table = ($TableObj | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') + if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } + $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" + $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City), but this IP is also belonging to a Hosting Provider, such as Microsoft, Google, or other cloud service.

You have set up alerts to be notified when this happens. See the table below for more info.$Table" $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' From 5aa9a783a298f666bfdd375982f4c0a0c385e516 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 22:09:01 +0100 Subject: [PATCH 59/84] more logging around store --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 01bc94ef8812..819c9bdce115 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -158,8 +158,11 @@ function Invoke-CippWebhookProcessing { Write-LogMessage -API 'BECRemediate' -tenant $tenantfilter -message "Executed Remediation for $username" -sev 'Info' } 'store' { + Write-Host "Using $($action.connectionstring) as connectionstring to ship data" $Context = New-AzDataTableContext -ConnectionString $action.ConnectionString -TableName 'AuditLog' - New-AzDataTable -Context $Context | Out-Null + Write-Host 'Creating table if it does not exist' + New-AzDataTable -Context $Context + Write-Host 'Uploading data to table' $TableObj = @{ RowKey = [string]$data.id PartitionKey = [string]$data.tenant From 985ca3c766cbb892f9f631b8c27bae55450fbdfd Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 22:14:34 +0100 Subject: [PATCH 60/84] fixes issues with store command --- .../CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 819c9bdce115..3bf7a552ff90 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -21,6 +21,10 @@ function Invoke-CippWebhookProcessing { Write-Host 'Using known location' $Country = $Location.CountryOrRegion $City = $Location.City + $Proxy = $Location.Proxy + $hosting = $Location.Hosting + $ASName = $Location.ASName + } else { Write-Host 'We have to do a lookup' if ($data.clientip -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { @@ -28,7 +32,7 @@ function Invoke-CippWebhookProcessing { } $Location = Get-CIPPGeoIPLocation -IP $data.clientip $Country = if ($Location.countryCode) { $Location.CountryCode } else { 'Unknown' } - $City = if ($Location.cityName) { $Location.cityName } else { 'Unknown' } + $City = if ($Location.city) { $Location.cityName } else { 'Unknown' } $Proxy = if ($Location.proxy) { $Location.proxy } else { 'Unknown' } $hosting = if ($Location.hosting) { $Location.hosting } else { 'Unknown' } $ASName = if ($Location.asName) { $Location.asName } else { 'Unknown' } @@ -161,12 +165,12 @@ function Invoke-CippWebhookProcessing { Write-Host "Using $($action.connectionstring) as connectionstring to ship data" $Context = New-AzDataTableContext -ConnectionString $action.ConnectionString -TableName 'AuditLog' Write-Host 'Creating table if it does not exist' - New-AzDataTable -Context $Context + New-AzDataTable -Context $Context | Out-Null Write-Host 'Uploading data to table' $TableObj = @{ RowKey = [string]$data.id - PartitionKey = [string]$data.tenant - Tenant = [string]$data.tenant + PartitionKey = [string]$TenantFilter + Tenant = [string]$tenantfilter Operation = [string]$data.operation RawData = [string]($data | ConvertTo-Json -Depth 15 -Compress) IP = [string]$data.clientip From 5b0abc2265fc7e6bd835b1fbd5db3f9fab6b4cf3 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 23:13:28 +0100 Subject: [PATCH 61/84] my characters need more depth --- Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 | 4 ++-- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 index 86d2113a7561..b2ac6be60384 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 @@ -81,8 +81,8 @@ Function Invoke-AddAlert { } $CompleteObject = @{ Tenant = [string]$tenant - if = [string](ConvertTo-Json -Compress -InputObject $Request.body.ifs) - execution = [string](ConvertTo-Json -Compress -InputObject $Request.body.do) + if = [string](ConvertTo-Json -Depth 10 -Compress -InputObject $Request.body.ifs) + execution = [string](ConvertTo-Json -Depth 10 -Compress -InputObject $Request.body.do) type = 'WebhookAlert' RowKey = [string](New-Guid) PartitionKey = 'WebhookAlert' diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 3bf7a552ff90..bbe5bd4a2745 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -11,6 +11,8 @@ function Invoke-CippWebhookProcessing { ) $ConfigTable = get-cipptable -TableName 'SchedulerConfig' $LocationTable = Get-CIPPTable -TableName 'knownlocationdb' + $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable -Filter "Tenant eq '$tenantfilter'" + if ($data.userId -eq 'Not Available') { $data.userId = $data.userKey } if ($data.Userkey -eq 'Not Available') { $data.Userkey = $data.userId } if ($data.clientip) { @@ -19,12 +21,11 @@ function Invoke-CippWebhookProcessing { #If we have a location, we use that. If not, we perform a lookup in the GeoIP database. if ($Location) { Write-Host 'Using known location' - $Country = $Location.CountryOrRegion + $Country = $Location.CountryCode $City = $Location.City $Proxy = $Location.Proxy $hosting = $Location.Hosting $ASName = $Location.ASName - } else { Write-Host 'We have to do a lookup' if ($data.clientip -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { @@ -53,6 +54,8 @@ function Invoke-CippWebhookProcessing { return '' } + $AllowedLocations = ($Alertconfig.if | ConvertFrom-Json).AllowedLocations.value + Write-Host "Operation: $($data.operation)" switch ($data.operation) { { 'UserLoggedIn' -eq $data.operation -and $proxy -eq $true } { $data.operation = 'BadRepIP' } @@ -63,7 +66,6 @@ function Invoke-CippWebhookProcessing { } Write-Host "Rewrote to operation: $($data.operation)" #Check if we actually need to do anything, and if not, break away. - $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable -Filter "Tenant eq '$tenantfilter'" foreach ($AlertSetting in $Alertconfig) { $ifs = $AlertSetting.If | ConvertFrom-Json $Dos = $AlertSetting.execution | ConvertFrom-Json From b7bedee6eacda0e50e683f004e137d0ce0ab45a3 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 30 Dec 2023 23:25:42 +0100 Subject: [PATCH 62/84] added logging for removal --- Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 index 3d9b4a628914..b837595407c8 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 @@ -17,7 +17,9 @@ Function Invoke-RemoveWebhookAlert { $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable -Filter "PartitionKey eq 'WebhookAlert'" | Where-Object -Property Tenant -EQ $Request.query.TenantFilter Write-Host "The webhook count is $($WebhookRow.count)" if ($WebhookRow.count -gt 1) { + Write-Host 'Multiple webhooks found' $Entity = $WebhookRow | Where-Object -Property RowKey -EQ $Request.query.ID + Write-Host "Removing $($Entity.RowKey)" Remove-AzDataTableEntity @WebhookTable -Entity $Entity | Out-Null $Results = "Removed Alert Rule for $($Request.query.TenantFilter)" } else { From a527107013307105512d820af02eea48ad9bd091 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 31 Dec 2023 00:04:23 +0100 Subject: [PATCH 63/84] why does local delete work but remote not --- Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 index b837595407c8..b8d899000074 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 @@ -16,6 +16,7 @@ Function Invoke-RemoveWebhookAlert { $WebhookTable = Get-CIPPTable -TableName SchedulerConfig $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable -Filter "PartitionKey eq 'WebhookAlert'" | Where-Object -Property Tenant -EQ $Request.query.TenantFilter Write-Host "The webhook count is $($WebhookRow.count)" + Write-Host "Here's the webhook Row: $($WebhookRow | ConvertTo-Json -Depth 10)" if ($WebhookRow.count -gt 1) { Write-Host 'Multiple webhooks found' $Entity = $WebhookRow | Where-Object -Property RowKey -EQ $Request.query.ID From f3037336249a9675d833c5ed3cf7708fa2389670 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 31 Dec 2023 00:09:41 +0100 Subject: [PATCH 64/84] troubleshooting text added --- Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 index b8d899000074..f5ee3da26668 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 @@ -14,8 +14,10 @@ Function Invoke-RemoveWebhookAlert { try { $WebhookTable = Get-CIPPTable -TableName SchedulerConfig - $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable -Filter "PartitionKey eq 'WebhookAlert'" | Where-Object -Property Tenant -EQ $Request.query.TenantFilter + + $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable -Filter "PartitionKey eq 'WebhookAlert' and Tenant eq '$($Request.query.TenantFilter)'" Write-Host "The webhook count is $($WebhookRow.count)" + Write-Host "The tenantfilter is $($Request.query.TenantFilter)" Write-Host "Here's the webhook Row: $($WebhookRow | ConvertTo-Json -Depth 10)" if ($WebhookRow.count -gt 1) { Write-Host 'Multiple webhooks found' From 8abd66e2c4e3a8ab1c4738d0c12aecf67925bb69 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 31 Dec 2023 00:15:27 +0100 Subject: [PATCH 65/84] remove writehosts --- Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 index f5ee3da26668..3d9b4a628914 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 @@ -14,15 +14,10 @@ Function Invoke-RemoveWebhookAlert { try { $WebhookTable = Get-CIPPTable -TableName SchedulerConfig - - $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable -Filter "PartitionKey eq 'WebhookAlert' and Tenant eq '$($Request.query.TenantFilter)'" + $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable -Filter "PartitionKey eq 'WebhookAlert'" | Where-Object -Property Tenant -EQ $Request.query.TenantFilter Write-Host "The webhook count is $($WebhookRow.count)" - Write-Host "The tenantfilter is $($Request.query.TenantFilter)" - Write-Host "Here's the webhook Row: $($WebhookRow | ConvertTo-Json -Depth 10)" if ($WebhookRow.count -gt 1) { - Write-Host 'Multiple webhooks found' $Entity = $WebhookRow | Where-Object -Property RowKey -EQ $Request.query.ID - Write-Host "Removing $($Entity.RowKey)" Remove-AzDataTableEntity @WebhookTable -Entity $Entity | Out-Null $Results = "Removed Alert Rule for $($Request.query.TenantFilter)" } else { From 4c12757f2b1019a1a160faaf78c9995fa2cde2aa Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 2 Jan 2024 00:19:31 -0500 Subject: [PATCH 66/84] GraphExplorer Presets --- .../Invoke-ExecGraphExplorerPreset.ps1 | 66 +++++++++++++++++++ .../Invoke-ListGraphExplorerPresets.ps1 | 41 ++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 new file mode 100644 index 000000000000..da839cf27106 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 @@ -0,0 +1,66 @@ +using namespace System.Net + +Function Invoke-ExecGraphExplorerPreset { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Username = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($request.headers.'x-ms-client-principal')) | ConvertFrom-Json).userDetails + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + switch ($Request.Body.Action) { + 'Copy' { + $Id = (New-Guid).Guid + } + 'Save' { + $Id = $Request.Body.values.reportTemplate.value + } + 'Delete' { + $Id = $Request.Body.values.reportTemplate.value + } + } + + $params = $Request.Body.values | Select-Object endpoint, '$filter', '$select', '$count', '$expand', '$search', NoPagination, '$top', IsShared + $Preset = [PSCustomObject]@{ + PartitionKey = 'Preset' + RowKey = [string]$Id + id = [string]$Id + name = [string]$Request.Body.values.name + Owner = [string]$Username + IsShared = $Request.Body.values.IsShared + params = [string](ConvertTo-Json -InputObject $params -Compress) + } + + try { + $Table = Get-CIPPTable -TableName 'GraphPresets' + if ($Request.Body.Action -eq 'Copy') { + Add-CIPPAzDataTableEntity @Table -Entity $Preset + } else { + $Entity = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$Id'" + if ($Entity.Owner -eq $Username ) { + if ($Request.Body.Action -eq 'Delete') { + Remove-AzDataTableEntity @Table -Entity $Entity + } elseif ($Request.Body.Action -eq 'Save') { + Add-CIPPAzDataTableEntity @Table -Entity $Preset -Force + } + } + } + + $StatusCode = [HttpStatusCode]::OK + } catch { + $StatusCode = [HttpStatusCode]::BadRequest + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @{ Results = ('{0} preset succeeded' -f $Request.Body.Action) } + }) +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 new file mode 100644 index 000000000000..7f5b3b09607d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 @@ -0,0 +1,41 @@ +using namespace System.Net + +Function Invoke-ListGraphExplorerPresets { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Username = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($request.headers.'x-ms-client-principal')) | ConvertFrom-Json).userDetails + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + try { + $Table = Get-CIPPTable -TableName 'GraphPresets' + $Presets = Get-CIPPAzDataTableEntity @Table -Filter "Owner eq '$Username' or IsShared eq true" + $Results = foreach ($Preset in $Presets) { + [PSCustomObject]@{ + id = $Preset.Id + name = $Preset.name + IsShared = $Preset.IsShared + params = ConvertFrom-Json -InputObject $Preset.Params + } + } + } catch { + $Presets = @() + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ + Results = @($Results) + Metadata = @{ + Count = ($Presets | Measure-Object).Count + } + } + }) +} From afde0a2b9bf65c103bed68e9bb113fed08af5ab7 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 2 Jan 2024 07:32:22 -0500 Subject: [PATCH 67/84] Add success/error messages --- .../Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 | 14 +++++++++++++- .../Invoke-ListGraphExplorerPresets.ps1 | 9 +++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 index da839cf27106..c178170db6ea 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 @@ -40,9 +40,11 @@ Function Invoke-ExecGraphExplorerPreset { } try { + $Success = $false $Table = Get-CIPPTable -TableName 'GraphPresets' if ($Request.Body.Action -eq 'Copy') { Add-CIPPAzDataTableEntity @Table -Entity $Preset + $Success = $true } else { $Entity = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$Id'" if ($Entity.Owner -eq $Username ) { @@ -51,16 +53,26 @@ Function Invoke-ExecGraphExplorerPreset { } elseif ($Request.Body.Action -eq 'Save') { Add-CIPPAzDataTableEntity @Table -Entity $Preset -Force } + $Message = '{0} preset succeeded' -f $Request.Body.Action + $Success = $true + } else { + $Message = 'Error: You can only modify your own presets.' + $Success = $false } } $StatusCode = [HttpStatusCode]::OK } catch { + $Success = $false + $Message = $_.Exception.Message $StatusCode = [HttpStatusCode]::BadRequest } # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode - Body = @{ Results = ('{0} preset succeeded' -f $Request.Body.Action) } + Body = @{ + Results = $Message + Success = $Success + } }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 index 7f5b3b09607d..100aa6a450ba 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 @@ -19,10 +19,11 @@ Function Invoke-ListGraphExplorerPresets { $Presets = Get-CIPPAzDataTableEntity @Table -Filter "Owner eq '$Username' or IsShared eq true" $Results = foreach ($Preset in $Presets) { [PSCustomObject]@{ - id = $Preset.Id - name = $Preset.name - IsShared = $Preset.IsShared - params = ConvertFrom-Json -InputObject $Preset.Params + id = $Preset.Id + name = $Preset.name + IsShared = $Preset.IsShared + IsMyPreset = $Preset.Owner -eq $Username + params = ConvertFrom-Json -InputObject $Preset.Params } } } catch { From 80540df92773d429413658b41b2517bb5d87bb77 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 2 Jan 2024 07:36:28 -0500 Subject: [PATCH 68/84] Update Invoke-ExecGraphExplorerPreset.ps1 --- .../Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 index c178170db6ea..b4d51d4f5d18 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 @@ -42,6 +42,7 @@ Function Invoke-ExecGraphExplorerPreset { try { $Success = $false $Table = Get-CIPPTable -TableName 'GraphPresets' + $Message = '{0} preset succeeded' -f $Request.Body.Action if ($Request.Body.Action -eq 'Copy') { Add-CIPPAzDataTableEntity @Table -Entity $Preset $Success = $true @@ -53,7 +54,6 @@ Function Invoke-ExecGraphExplorerPreset { } elseif ($Request.Body.Action -eq 'Save') { Add-CIPPAzDataTableEntity @Table -Entity $Preset -Force } - $Message = '{0} preset succeeded' -f $Request.Body.Action $Success = $true } else { $Message = 'Error: You can only modify your own presets.' From c1b6819ac388ed86bab1f21ed0a7101879495532 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 14:12:06 +0100 Subject: [PATCH 69/84] added changes to webhook processing --- .../Public/Invoke-CIPPWebhookProcessing.ps1 | 56 +++++++++++-------- .../CIPPCore/Public/New-CIPPAlertTemplate.ps1 | 21 ++++++- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index bbe5bd4a2745..138603a6d9a2 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -116,32 +116,11 @@ function Invoke-CippWebhookProcessing { } if ($ConditionMet) { - foreach ($action in $dos) { + #we're doing two loops, one first to collect the results of any action taken, then the second to pass those results via email etc. + + $ActionResults = foreach ($action in $dos) { Write-Host "this is our action: $($action | ConvertTo-Json -Depth 15 -Compress))" switch ($action.execute) { - 'generatemail' { - $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -LocationInfo $Location - Send-CIPPAlert -Type 'email' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter - } - 'generatePSA' { - $GenerateEmail = New-CIPPAlertTemplate -format 'html'-data $Data -LocationInfo $Location - Send-CIPPAlert -Type 'psa' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter - } - 'generateWebhook' { - $GenerateJSON = New-CIPPAlertTemplate -format 'json' -data $Data - $JsonContent = @{ - Title = $GenerateJSON.Title - ActionUrl = $GenerateJSON.ButtonUrl - RawData = $Data - IP = $data.ClientIP - PotentialCountry = $Country - PotentialCity = $City - PotentialProxy = $Proxy - PotentialHosting = $hosting - PotentialASName = $ASName - } | ConvertTo-Json -Depth 15 -Compress - Send-CIPPAlert -Type 'webhook' -Title $GenerateJSON.Title -JSONContent $JsonContent -TenantFilter $TenantFilter - } 'disableUser' { Set-CIPPSignInState -TenantFilter $TenantFilter -User $data.UserId -AccountEnabled $false -APIName 'Alert Engine' -ExecutingUser 'Alert Engine' } @@ -161,6 +140,7 @@ function Invoke-CippWebhookProcessing { } else { "No Inbox Rules found for $username. We have not disabled any rules." } + "Completed BEC Remediate for $username" Write-LogMessage -API 'BECRemediate' -tenant $tenantfilter -message "Executed Remediation for $username" -sev 'Info' } 'store' { @@ -183,6 +163,7 @@ function Invoke-CippWebhookProcessing { ASName = [string]$ASName } Add-CIPPAzDataTableEntity -Context $Context -Entity $TableObj + 'Succesfully stored log' } 'cippcommand' { $CommandSplat = @{} @@ -196,6 +177,33 @@ function Invoke-CippWebhookProcessing { } } } + foreach ($action in $dos) { + switch ($action.execute) { + 'generatemail' { + $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -LocationInfo $Location -ActionResults $ActionResults + Send-CIPPAlert -Type 'email' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter + } + 'generatePSA' { + $GenerateEmail = New-CIPPAlertTemplate -format 'html'-data $Data -LocationInfo $Location -ActionResults $ActionResults + Send-CIPPAlert -Type 'psa' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter + } + 'generateWebhook' { + $GenerateJSON = New-CIPPAlertTemplate -format 'json' -data $Data -ActionResults $ActionResults + $JsonContent = @{ + Title = $GenerateJSON.Title + ActionUrl = $GenerateJSON.ButtonUrl + RawData = $Data + IP = $data.ClientIP + PotentialCountry = $Country + PotentialCity = $City + PotentialProxy = $Proxy + PotentialHosting = $hosting + PotentialASName = $ASName + } | ConvertTo-Json -Depth 15 -Compress + Send-CIPPAlert -Type 'webhook' -Title $GenerateJSON.Title -JSONContent $JsonContent -TenantFilter $TenantFilter + } + } + } } } diff --git a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 index af27802a8524..00bab6e45157 100644 --- a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 @@ -5,7 +5,8 @@ function New-CIPPAlertTemplate { [Parameter(Mandatory = $true)] [ValidateSet('html', 'json')] $Format, - $LocationInfo + $LocationInfo, + $ActionResults ) $Appname = '[{"Application Name":"ACOM Azure Website","Application IDs":"23523755-3a2b-41ca-9315-f81f3f566a95"},{"Application Name":"AEM-DualAuth","Application IDs":"69893ee3-dd10-4b1c-832d-4870354be3d8"},{"Application Name":"ASM Campaign Servicing","Application IDs":"0cb7b9ec-5336-483b-bc31-b15b5788de71"},{"Application Name":"Azure Advanced Threat Protection","Application IDs":"7b7531ad-5926-4f2d-8a1d-38495ad33e17"},{"Application Name":"Azure Data Lake","Application IDs":"e9f49c6b-5ce5-44c8-925d-015017e9f7ad"},{"Application Name":"Azure Lab Services Portal","Application IDs":"835b2a73-6e10-4aa5-a979-21dfda45231c"},{"Application Name":"Azure Portal","Application IDs":"c44b4083-3bb0-49c1-b47d-974e53cbdf3c"},{"Application Name":"AzureSupportCenter","Application IDs":"37182072-3c9c-4f6a-a4b3-b3f91cacffce"},{"Application Name":"Bing","Application IDs":"9ea1ad79-fdb6-4f9a-8bc3-2b70f96e34c7"},{"Application Name":"CPIM Service","Application IDs":"bb2a2e3a-c5e7-4f0a-88e0-8e01fd3fc1f4"},{"Application Name":"CRM Power BI Integration","Application IDs":"e64aa8bc-8eb4-40e2-898b-cf261a25954f"},{"Application Name":"Dataverse","Application IDs":"00000007-0000-0000-c000-000000000000"},{"Application Name":"Enterprise Roaming and Backup","Application IDs":"60c8bde5-3167-4f92-8fdb-059f6176dc0f"},{"Application Name":"IAM Supportability","Application IDs":"a57aca87-cbc0-4f3c-8b9e-dc095fdc8978"},{"Application Name":"IrisSelectionFrontDoor","Application IDs":"16aeb910-ce68-41d1-9ac3-9e1673ac9575"},{"Application Name":"MCAPI Authorization Prod","Application IDs":"d73f4b35-55c9-48c7-8b10-651f6f2acb2e"},{"Application Name":"Media Analysis and Transformation Service","Application IDs":"944f0bd1-117b-4b1c-af26-804ed95e767e
0cd196ee-71bf-4fd6-a57c-b491ffd4fb1e"},{"Application Name":"Microsoft 365 Support Service","Application IDs":"ee272b19-4411-433f-8f28-5c13cb6fd407"},{"Application Name":"Microsoft App Access Panel","Application IDs":"0000000c-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Approval Management","Application IDs":"65d91a3d-ab74-42e6-8a2f-0add61688c74
38049638-cc2c-4cde-abe4-4479d721ed44"},{"Application Name":"Microsoft Authentication Broker","Application IDs":"29d9ed98-a469-4536-ade2-f981bc1d605e"},{"Application Name":"Microsoft Azure CLI","Application IDs":"04b07795-8ddb-461a-bbee-02f9e1bf7b46"},{"Application Name":"Microsoft Azure PowerShell","Application IDs":"1950a258-227b-4e31-a9cf-717495945fc2"},{"Application Name":"Microsoft Bing Search","Application IDs":"cf36b471-5b44-428c-9ce7-313bf84528de"},{"Application Name":"Microsoft Bing Search for Microsoft Edge","Application IDs":"2d7f3606-b07d-41d1-b9d2-0d0c9296a6e8"},{"Application Name":"Microsoft Bing Default Search Engine","Application IDs":"1786c5ed-9644-47b2-8aa0-7201292175b6"},{"Application Name":"Microsoft Defender for Cloud Apps","Application IDs":"3090ab82-f1c1-4cdf-af2c-5d7a6f3e2cc7"},{"Application Name":"Microsoft Docs","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Dynamics ERP","Application IDs":"00000015-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Edge Insider Addons Prod","Application IDs":"6253bca8-faf2-4587-8f2f-b056d80998a7"},{"Application Name":"Microsoft Exchange Online Protection","Application IDs":"00000007-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Forms","Application IDs":"c9a559d2-7aab-4f13-a6ed-e7e9c52aec87"},{"Application Name":"Microsoft Graph","Application IDs":"00000003-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Intune Web Company Portal","Application IDs":"74bcdadc-2fdc-4bb3-8459-76d06952a0e9"},{"Application Name":"Microsoft Intune Windows Agent","Application IDs":"fc0f3af4-6835-4174-b806-f7db311fd2f3"},{"Application Name":"Microsoft Learn","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Office","Application IDs":"d3590ed6-52b3-4102-aeff-aad2292ab01c"},{"Application Name":"Microsoft Office 365 Portal","Application IDs":"00000006-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Office Web Apps Service","Application IDs":"67e3df25-268a-4324-a550-0de1c7f97287"},{"Application Name":"Microsoft Online Syndication Partner Portal","Application IDs":"d176f6e7-38e5-40c9-8a78-3998aab820e7"},{"Application Name":"Microsoft password reset service","Application IDs":"93625bc8-bfe2-437a-97e0-3d0060024faa"},{"Application Name":"Microsoft Power BI","Application IDs":"871c010f-5e61-4fb1-83ac-98610a7e9110"},{"Application Name":"Microsoft Storefronts","Application IDs":"28b567f6-162c-4f54-99a0-6887f387bbcc"},{"Application Name":"Microsoft Stream Portal","Application IDs":"cf53fce8-def6-4aeb-8d30-b158e7b1cf83"},{"Application Name":"Microsoft Substrate Management","Application IDs":"98db8bd6-0cc0-4e67-9de5-f187f1cd1b41"},{"Application Name":"Microsoft Support","Application IDs":"fdf9885b-dd37-42bf-82e5-c3129ef5a302"},{"Application Name":"Microsoft Teams","Application IDs":"1fec8e78-bce4-4aaf-ab1b-5451cc387264"},{"Application Name":"Microsoft Teams Services","Application IDs":"cc15fd57-2c6c-4117-a88c-83b1d56b4bbe"},{"Application Name":"Microsoft Teams Web Client","Application IDs":"5e3ce6c0-2b1f-4285-8d4b-75ee78787346"},{"Application Name":"Microsoft Whiteboard Services","Application IDs":"95de633a-083e-42f5-b444-a4295d8e9314"},{"Application Name":"O365 Suite UX","Application IDs":"4345a7b9-9a63-4910-a426-35363201d503"},{"Application Name":"Office 365 Exchange Online","Application IDs":"00000002-0000-0ff1-ce00-000000000000"},{"Application Name":"Office 365 Management","Application IDs":"00b41c95-dab0-4487-9791-b9d2c32c80f2"},{"Application Name":"Office 365 Search Service","Application IDs":"66a88757-258c-4c72-893c-3e8bed4d6899"},{"Application Name":"Office 365 SharePoint Online","Application IDs":"00000003-0000-0ff1-ce00-000000000000"},{"Application Name":"Office Delve","Application IDs":"94c63fef-13a3-47bc-8074-75af8c65887a"},{"Application Name":"Office Online Add-in SSO","Application IDs":"93d53678-613d-4013-afc1-62e9e444a0a5"},{"Application Name":"Office Online Client AAD- Augmentation Loop","Application IDs":"2abdc806-e091-4495-9b10-b04d93c3f040"},{"Application Name":"Office Online Client AAD- Loki","Application IDs":"b23dd4db-9142-4734-867f-3577f640ad0c"},{"Application Name":"Office Online Client AAD- Maker","Application IDs":"17d5e35f-655b-4fb0-8ae6-86356e9a49f5"},{"Application Name":"Office Online Client MSA- Loki","Application IDs":"b6e69c34-5f1f-4c34-8cdf-7fea120b8670"},{"Application Name":"Office Online Core SSO","Application IDs":"243c63a3-247d-41c5-9d83-7788c43f1c43"},{"Application Name":"Office Online Search","Application IDs":"a9b49b65-0a12-430b-9540-c80b3332c127"},{"Application Name":"Office.com","Application IDs":"4b233688-031c-404b-9a80-a4f3f2351f90"},{"Application Name":"Office365 Shell WCSS-Client","Application IDs":"89bee1f7-5e6e-4d8a-9f3d-ecd601259da7"},{"Application Name":"OfficeClientService","Application IDs":"0f698dd4-f011-4d23-a33e-b36416dcb1e6"},{"Application Name":"OfficeHome","Application IDs":"4765445b-32c6-49b0-83e6-1d93765276ca"},{"Application Name":"OfficeShredderWacClient","Application IDs":"4d5c2d63-cf83-4365-853c-925fd1a64357"},{"Application Name":"OMSOctopiPROD","Application IDs":"62256cef-54c0-4cb4-bcac-4c67989bdc40"},{"Application Name":"OneDrive SyncEngine","Application IDs":"ab9b8c07-8f02-4f72-87fa-80105867a763"},{"Application Name":"OneNote","Application IDs":"2d4d3d8e-2be3-4bef-9f87-7875a61c29de"},{"Application Name":"Outlook Mobile","Application IDs":"27922004-5251-4030-b22d-91ecd9a37ea4"},{"Application Name":"Partner Customer Delegated Admin Offline Processor","Application IDs":"a3475900-ccec-4a69-98f5-a65cd5dc5306"},{"Application Name":"Password Breach Authenticator","Application IDs":"bdd48c81-3a58-4ea9-849c-ebea7f6b6360"},{"Application Name":"Power BI Service","Application IDs":"00000009-0000-0000-c000-000000000000"},{"Application Name":"SharedWithMe","Application IDs":"ffcb16e8-f789-467c-8ce9-f826a080d987"},{"Application Name":"SharePoint Online Web Client Extensibility","Application IDs":"08e18876-6177-487e-b8b5-cf950c1e598c"},{"Application Name":"Signup","Application IDs":"b4bddae8-ab25-483e-8670-df09b9f1d0ea"},{"Application Name":"Skype for Business Online","Application IDs":"00000004-0000-0ff1-ce00-000000000000"},{"Application Name":"Sway","Application IDs":"905fcf26-4eb7-48a0-9ff0-8dcc7194b5ba"},{"Application Name":"Universal Store Native Client","Application IDs":"268761a2-03f3-40df-8a8b-c3db24145b6b"},{"Application Name":"Vortex [wsfed enabled]","Application IDs":"5572c4c0-d078-44ce-b81c-6cbf8d3ed39e"},{"Application Name":"Windows Azure Active Directory","Application IDs":"00000002-0000-0000-c000-000000000000"},{"Application Name":"Windows Azure Service Management API","Application IDs":"797f4846-ba00-4fd7-ba43-dac1f8f63013"},{"Application Name":"WindowsDefenderATP Portal","Application IDs":"a3b79187-70b2-4139-83f9-6016c58cd27b"},{"Application Name":"Windows Search","Application IDs":"26a7ee05-5602-4d76-a7ba-eae8b7b67941"},{"Application Name":"Windows Spotlight","Application IDs":"1b3c667f-cde3-4090-b60b-3d2abd0117f0"},{"Application Name":"Windows Store for Business","Application IDs":"45a330b1-b1ec-4cc1-9161-9f03992aa49f"},{"Application Name":"Yammer","Application IDs":"00000005-0000-0ff1-ce00-000000000000"},{"Application Name":"Yammer Web","Application IDs":"c1c74fed-04c9-4704-80dc-9f79a2e515cb"},{"Application Name":"Yammer Web Embed","Application IDs":"e1ef36fd-b883-4dbf-97f0-9ece4b576fc6"}]' | ConvertFrom-Json | Where-Object -Property 'Application IDs' -EQ $data.applicationId @@ -24,6 +25,7 @@ function New-CIPPAlertTemplate { $RuleTable = ($TableObj | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') $ParameterName $IntroText = "

A new rule has been created for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'Start BEC Investigation' $AfterButtonText = '

If you believe this is a suspect rule, you can click the button above to start the investigation.

' @@ -33,6 +35,7 @@ function New-CIPPAlertTemplate { $RuleTable = ($TableObj | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') $ParameterName $IntroText = "

A rule has been edited for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'Start BEC Investigation' $AfterButtonText = '

If you believe this is a suspect rule, you can click the button above to start the investigation.

' @@ -41,6 +44,7 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - Role change detected for $($data.ObjectId)" $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') $IntroText = "

$($data.UserId) has added $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" $ButtonText = 'Role Management' $AfterButtonText = '

If this role is incorrect, or you need more information, use the button to jump to the Role Management page.

' @@ -49,6 +53,7 @@ function New-CIPPAlertTemplate { 'Disable account.' { $Title = "$($TenantFilter) - $($data.ObjectId) has been disabled" $IntroText = "$($data.ObjectId) has been disabled by $($data.UserId)." + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' @@ -57,6 +62,7 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - $($data.ObjectId) has been enabled" $IntroText = "$($data.ObjectId) has been enabled by $($data.UserId)." $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' } @@ -64,12 +70,14 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - $($data.ObjectId) has had all sessions revoked" $IntroText = "$($data.ObjectId) has had their sessions revoked by $($data.UserId)." $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' } 'Disable Strong Authentication.' { $Title = "$($TenantFilter) - $($data.ObjectId) has been MFA disabled" $IntroText = "$($data.ObjectId) MFA has been disabled by $($data.UserId)." + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to reenable MFA

' @@ -78,6 +86,7 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - Role change detected for $($data.ObjectId)" $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') $IntroText = "

$($data.UserId) has removed $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" $ButtonText = 'Role Management' $AfterButtonText = '

If this role change is incorrect, or you need more information, use the button to jump to the Role Management page.

' @@ -87,6 +96,7 @@ function New-CIPPAlertTemplate { 'Reset user password.' { $Title = "$($TenantFilter) - $($data.ObjectId) has had their password reset" $IntroText = "$($data.ObjectId) has had their password reset by $($data.userId)." + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' @@ -97,6 +107,7 @@ function New-CIPPAlertTemplate { if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } $Title = "$($TenantFilter) - an admin account has logged on" $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). See the table below for more information. $Table" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserKey)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' @@ -107,6 +118,7 @@ function New-CIPPAlertTemplate { if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City).

You have set up alerts to be notified when this happens. See the table below for more info.$Table" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' @@ -116,6 +128,7 @@ function New-CIPPAlertTemplate { if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City), but is a VPN, Proxy, or IP anonimizing service.

You have set up alerts to be notified when this happens. See the table below for more info.$Table" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' @@ -125,6 +138,7 @@ function New-CIPPAlertTemplate { if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City), but this IP is also belonging to a Hosting Provider, such as Microsoft, Google, or other cloud service.

You have set up alerts to be notified when this happens. See the table below for more info.$Table" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' @@ -133,6 +147,7 @@ function New-CIPPAlertTemplate { if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } $Title = "$($TenantFilter) - Service Principal $($data.ObjectId) has been added." $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." $ButtonUrl = "$CIPPPURL/tenant/administration/enterprise-apps?customerId=?customerId=$($data.OrganizationId)" $ButtonText = 'Enterprise Apps' @@ -141,6 +156,7 @@ function New-CIPPAlertTemplate { if ($Appname) { $AppName = $AppName.'Application Name' } else { $appName = $data.ApplicationId } $Title = "$($TenantFilter) - Service Principal $($data.ObjectId) has been removed." $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." $ButtonUrl = "$CIPPPURL/tenant/administration/enterprise-apps?customerId=?customerId=$($data.OrganizationId)" $ButtonText = 'Enterprise Apps' @@ -148,8 +164,9 @@ function New-CIPPAlertTemplate { default { $Title = 'A custom alert has occured' - $IntroText = "You have setup CIPP to send you a custom alert for the event $($Data.operation)" $Table = ($data | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = "

You have setup CIPP to send you a custom alert for the event $($Data.operation)

$Table" + if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" $ButtonText = 'User Management' } From 3e56c8b142d663bf80b9a91d42cac4c8f4bbea25 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 14:32:16 +0100 Subject: [PATCH 70/84] add action to webhook --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 138603a6d9a2..4bb92891ad8e 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -199,6 +199,7 @@ function Invoke-CippWebhookProcessing { PotentialProxy = $Proxy PotentialHosting = $hosting PotentialASName = $ASName + ActionsTaken = [string]($ActionResults | ConvertTo-Json -Depth 15 -Compress) } | ConvertTo-Json -Depth 15 -Compress Send-CIPPAlert -Type 'webhook' -Title $GenerateJSON.Title -JSONContent $JsonContent -TenantFilter $TenantFilter } From ef40e796a60ba394f7cde966fcca7f031eccaed0 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 14:55:47 +0100 Subject: [PATCH 71/84] update template to always show location info --- .../CIPPCore/Public/New-CIPPAlertTemplate.ps1 | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 index 00bab6e45157..3604e42e2728 100644 --- a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 @@ -26,6 +26,10 @@ function New-CIPPAlertTemplate { $ParameterName $IntroText = "

A new rule has been created for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'Start BEC Investigation' $AfterButtonText = '

If you believe this is a suspect rule, you can click the button above to start the investigation.

' @@ -36,6 +40,10 @@ function New-CIPPAlertTemplate { $ParameterName $IntroText = "

A rule has been edited for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'Start BEC Investigation' $AfterButtonText = '

If you believe this is a suspect rule, you can click the button above to start the investigation.

' @@ -45,6 +53,10 @@ function New-CIPPAlertTemplate { $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') $IntroText = "

$($data.UserId) has added $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" $ButtonText = 'Role Management' $AfterButtonText = '

If this role is incorrect, or you need more information, use the button to jump to the Role Management page.

' @@ -54,6 +66,10 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - $($data.ObjectId) has been disabled" $IntroText = "$($data.ObjectId) has been disabled by $($data.UserId)." if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' @@ -63,6 +79,10 @@ function New-CIPPAlertTemplate { $IntroText = "$($data.ObjectId) has been enabled by $($data.UserId)." $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' } @@ -71,6 +91,10 @@ function New-CIPPAlertTemplate { $IntroText = "$($data.ObjectId) has had their sessions revoked by $($data.UserId)." $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' } @@ -78,6 +102,10 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - $($data.ObjectId) has been MFA disabled" $IntroText = "$($data.ObjectId) MFA has been disabled by $($data.UserId)." if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to reenable MFA

' @@ -87,6 +115,10 @@ function New-CIPPAlertTemplate { $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') $IntroText = "

$($data.UserId) has removed $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" $ButtonText = 'Role Management' $AfterButtonText = '

If this role change is incorrect, or you need more information, use the button to jump to the Role Management page.

' @@ -97,6 +129,10 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - $($data.ObjectId) has had their password reset" $IntroText = "$($data.ObjectId) has had their password reset by $($data.userId)." if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to unblock the users sign-in

' @@ -108,6 +144,10 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - an admin account has logged on" $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). See the table below for more information. $Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserKey)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' @@ -119,6 +159,10 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City).

You have set up alerts to be notified when this happens. See the table below for more info.$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' @@ -129,6 +173,10 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City), but is a VPN, Proxy, or IP anonimizing service.

You have set up alerts to be notified when this happens. See the table below for more info.$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' @@ -139,6 +187,10 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - a user has logged on from a potentially unsafe location" $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City), but this IP is also belonging to a Hosting Provider, such as Microsoft, Google, or other cloud service.

You have set up alerts to be notified when this happens. See the table below for more info.$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" $ButtonText = 'User Management' $AfterButtonText = '

If this is incorrect, use the user management screen to block the user and revoke the sessions

' @@ -148,6 +200,10 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - Service Principal $($data.ObjectId) has been added." $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." $ButtonUrl = "$CIPPPURL/tenant/administration/enterprise-apps?customerId=?customerId=$($data.OrganizationId)" $ButtonText = 'Enterprise Apps' @@ -157,6 +213,10 @@ function New-CIPPAlertTemplate { $Title = "$($TenantFilter) - Service Principal $($data.ObjectId) has been removed." $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." $ButtonUrl = "$CIPPPURL/tenant/administration/enterprise-apps?customerId=?customerId=$($data.OrganizationId)" $ButtonText = 'Enterprise Apps' @@ -167,6 +227,10 @@ function New-CIPPAlertTemplate { $Table = ($data | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') $IntroText = "

You have setup CIPP to send you a custom alert for the event $($Data.operation)

$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } + if ($LocationInfo) { + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" + } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" $ButtonText = 'User Management' } From e6328bdb61255a859215f2fd0f5e959495ebc953 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 16:28:43 +0100 Subject: [PATCH 72/84] adding some troubleshooting info --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 4bb92891ad8e..84284bf6f312 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -55,7 +55,7 @@ function Invoke-CippWebhookProcessing { } $AllowedLocations = ($Alertconfig.if | ConvertFrom-Json).AllowedLocations.value - + Write-Host "These are the allowed locations: $($AllowedLocations -join ',')" Write-Host "Operation: $($data.operation)" switch ($data.operation) { { 'UserLoggedIn' -eq $data.operation -and $proxy -eq $true } { $data.operation = 'BadRepIP' } From a15476b2e4b38a1c3e159b47f15c061259550d65 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 16:35:12 +0100 Subject: [PATCH 73/84] allowed loc stuff --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 84284bf6f312..b5343745b05f 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -54,7 +54,7 @@ function Invoke-CippWebhookProcessing { return '' } - $AllowedLocations = ($Alertconfig.if | ConvertFrom-Json).AllowedLocations.value + $AllowedLocations = ($Alertconfig.if | ConvertFrom-Json) Write-Host "These are the allowed locations: $($AllowedLocations -join ',')" Write-Host "Operation: $($data.operation)" switch ($data.operation) { From ece2a162dee8a623e1f286abf995dc69e21b3498 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 16:42:44 +0100 Subject: [PATCH 74/84] added webhook --- .../CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index b5343745b05f..638375b19b56 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -32,11 +32,11 @@ function Invoke-CippWebhookProcessing { $data.clientip = $data.clientip -replace ':\d+$', '' # Remove the port number if present } $Location = Get-CIPPGeoIPLocation -IP $data.clientip - $Country = if ($Location.countryCode) { $Location.CountryCode } else { 'Unknown' } - $City = if ($Location.city) { $Location.cityName } else { 'Unknown' } - $Proxy = if ($Location.proxy) { $Location.proxy } else { 'Unknown' } - $hosting = if ($Location.hosting) { $Location.hosting } else { 'Unknown' } - $ASName = if ($Location.asName) { $Location.asName } else { 'Unknown' } + $Country = if ($Location.CountryCode) { $Location.CountryCode } else { 'Unknown' } + $City = if ($Location.City) { $Location.City } else { 'Unknown' } + $Proxy = if ($Location.Proxy) { $Location.Proxy } else { 'Unknown' } + $hosting = if ($Location.hosting) { $Location.Hosting } else { 'Unknown' } + $ASName = if ($Location.ASName) { $Location.ASName } else { 'Unknown' } } } $TableObj = [PSCustomObject]::new() @@ -55,7 +55,7 @@ function Invoke-CippWebhookProcessing { } $AllowedLocations = ($Alertconfig.if | ConvertFrom-Json) - Write-Host "These are the allowed locations: $($AllowedLocations -join ',')" + Write-Host "These are the allowed locations: $($Alertconfig.if)" Write-Host "Operation: $($data.operation)" switch ($data.operation) { { 'UserLoggedIn' -eq $data.operation -and $proxy -eq $true } { $data.operation = 'BadRepIP' } From dc4f5c4ff21786bd0ed3c980b4e210232594cf9e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 16:56:54 +0100 Subject: [PATCH 75/84] fix issue with allowed countries and case sesnitivity --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 638375b19b56..d7689db53ce4 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -54,7 +54,7 @@ function Invoke-CippWebhookProcessing { return '' } - $AllowedLocations = ($Alertconfig.if | ConvertFrom-Json) + $AllowedLocations = ($Alertconfig.if | ConvertFrom-Json).allowedcountries.value Write-Host "These are the allowed locations: $($Alertconfig.if)" Write-Host "Operation: $($data.operation)" switch ($data.operation) { From 411ac679a20beb50107fd4803ba327dd9ae8adba Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 17:06:44 +0100 Subject: [PATCH 76/84] case sensitivity --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index d7689db53ce4..21b97446629f 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -35,7 +35,7 @@ function Invoke-CippWebhookProcessing { $Country = if ($Location.CountryCode) { $Location.CountryCode } else { 'Unknown' } $City = if ($Location.City) { $Location.City } else { 'Unknown' } $Proxy = if ($Location.Proxy) { $Location.Proxy } else { 'Unknown' } - $hosting = if ($Location.hosting) { $Location.Hosting } else { 'Unknown' } + $hosting = if ($Location.Hosting) { $Location.Hosting } else { 'Unknown' } $ASName = if ($Location.ASName) { $Location.ASName } else { 'Unknown' } } } From 854efeffd6148e6ed8031209e6647ea62d00fee3 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 17:10:29 +0100 Subject: [PATCH 77/84] Make location info a list --- .../CIPPCore/Public/New-CIPPAlertTemplate.ps1 | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 index 3604e42e2728..20b8a0f2fb95 100644 --- a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 @@ -27,7 +27,7 @@ function New-CIPPAlertTemplate { $IntroText = "

A new rule has been created for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" @@ -41,7 +41,7 @@ function New-CIPPAlertTemplate { $IntroText = "

A rule has been edited for the user $($data.UserId). You should check if this rule is not malicious. The rule information can be found in the table below.

$RuleTable" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserId)&tenantDomain=$($data.OrganizationId)" @@ -54,7 +54,7 @@ function New-CIPPAlertTemplate { $IntroText = "

$($data.UserId) has added $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" @@ -67,7 +67,7 @@ function New-CIPPAlertTemplate { $IntroText = "$($data.ObjectId) has been disabled by $($data.UserId)." if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" @@ -80,7 +80,7 @@ function New-CIPPAlertTemplate { $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonText = 'User Management' @@ -92,7 +92,7 @@ function New-CIPPAlertTemplate { $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonText = 'User Management' @@ -103,7 +103,7 @@ function New-CIPPAlertTemplate { $IntroText = "$($data.ObjectId) MFA has been disabled by $($data.UserId)." if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" @@ -116,7 +116,7 @@ function New-CIPPAlertTemplate { $IntroText = "

$($data.UserId) has removed $($data.ObjectId) to the $(($data.ModifiedProperties | Where-Object -Property Name -EQ 'Role.DisplayName').NewValue) role. The information about the role can be found in the table below.

$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/roles?customerId=$($data.OrganizationId)" @@ -130,7 +130,7 @@ function New-CIPPAlertTemplate { $IntroText = "$($data.ObjectId) has had their password reset by $($data.userId)." if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" @@ -145,7 +145,7 @@ function New-CIPPAlertTemplate { $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). See the table below for more information. $Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.UserKey)&tenantDomain=$($data.OrganizationId)" @@ -160,7 +160,7 @@ function New-CIPPAlertTemplate { $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City).

You have set up alerts to be notified when this happens. See the table below for more info.$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" @@ -174,7 +174,7 @@ function New-CIPPAlertTemplate { $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City), but is a VPN, Proxy, or IP anonimizing service.

You have set up alerts to be notified when this happens. See the table below for more info.$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" @@ -188,7 +188,7 @@ function New-CIPPAlertTemplate { $IntroText = "$($data.UserId) ($($data.Userkey)) has logged on from IP $($data.ClientIP) to the application $($Appname). According to our database this is located in $($LocationInfo.Country) - $($LocationInfo.City), but this IP is also belonging to a Hosting Provider, such as Microsoft, Google, or other cloud service.

You have set up alerts to be notified when this happens. See the table below for more info.$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/ViewBec?userId=$($data.ObjectId)&tenantDomain=$($data.OrganizationId)" @@ -201,7 +201,7 @@ function New-CIPPAlertTemplate { $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." @@ -214,7 +214,7 @@ function New-CIPPAlertTemplate { $Table = ($data.ModifiedProperties | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $IntroText = "$($data.ObjectId) has been added by $($data.UserId)." @@ -228,7 +228,7 @@ function New-CIPPAlertTemplate { $IntroText = "

You have setup CIPP to send you a custom alert for the event $($Data.operation)

$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) { - $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $LocationTable = ($LocationInfo | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = $IntroText + "

The location information for this IP is as follows:

$LocationTable" } $ButtonUrl = "$CIPPPURL/identity/administration/users?customerId=$($data.OrganizationId)" From 45748b0807c0cbb75cb81d184cf784712c86961b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 17:16:50 +0100 Subject: [PATCH 78/84] true and false confusion --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index 21b97446629f..d6f350b44840 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -21,7 +21,7 @@ function Invoke-CippWebhookProcessing { #If we have a location, we use that. If not, we perform a lookup in the GeoIP database. if ($Location) { Write-Host 'Using known location' - $Country = $Location.CountryCode + $Country = $Location.CountryOrRegion $City = $Location.City $Proxy = $Location.Proxy $hosting = $Location.Hosting @@ -34,8 +34,8 @@ function Invoke-CippWebhookProcessing { $Location = Get-CIPPGeoIPLocation -IP $data.clientip $Country = if ($Location.CountryCode) { $Location.CountryCode } else { 'Unknown' } $City = if ($Location.City) { $Location.City } else { 'Unknown' } - $Proxy = if ($Location.Proxy) { $Location.Proxy } else { 'Unknown' } - $hosting = if ($Location.Hosting) { $Location.Hosting } else { 'Unknown' } + $Proxy = if ($Location.Proxy -ne $null) { $Location.Proxy } else { 'Unknown' } + $hosting = if ($Location.Hosting -ne $null) { $Location.Hosting } else { 'Unknown' } $ASName = if ($Location.ASName) { $Location.ASName } else { 'Unknown' } } } From 8b8b1aaacdb7ee2b92c80cfd165f14ace8f6b03f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 17:21:48 +0100 Subject: [PATCH 79/84] extra write logging --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index d6f350b44840..aba5497280f1 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -55,12 +55,15 @@ function Invoke-CippWebhookProcessing { } $AllowedLocations = ($Alertconfig.if | ConvertFrom-Json).allowedcountries.value - Write-Host "These are the allowed locations: $($Alertconfig.if)" + Write-Host "These are the allowed locations: $($AllowedLocations)" Write-Host "Operation: $($data.operation)" switch ($data.operation) { { 'UserLoggedIn' -eq $data.operation -and $proxy -eq $true } { $data.operation = 'BadRepIP' } { 'UserLoggedIn' -eq $data.operation -and $hosting -eq $true } { $data.operation = 'HostedIP' } - { 'UserLoggedIn' -eq $data.operation -and $Country -notin $AllowedLocations -and $data.ResultStatus -eq 'Success' -and $TableObj.ResultStatusDetail -eq 'Success' } { $data.operation = 'UserLoggedInFromUnknownLocation' } + { 'UserLoggedIn' -eq $data.operation -and $Country -notin $AllowedLocations -and $data.ResultStatus -eq 'Success' -and $TableObj.ResultStatusDetail -eq 'Success' } { + Write-Host "$($country) is not in $($AllowedLocations)" + $data.operation = 'UserLoggedInFromUnknownLocation' + } { 'UserloggedIn' -eq $data.operation -and $data.UserType -eq 2 -and $data.ResultStatus -eq 'Success' -and $TableObj.ResultStatusDetail -eq 'Success' } { $data.operation = 'AdminLoggedIn' } default { break } } From 288ae84ca9501efd64ed13fde56ea410c1d409fd Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 17:56:49 +0100 Subject: [PATCH 80/84] add all tenants part --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index aba5497280f1..b08aa37d1ce0 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -12,7 +12,10 @@ function Invoke-CippWebhookProcessing { $ConfigTable = get-cipptable -TableName 'SchedulerConfig' $LocationTable = Get-CIPPTable -TableName 'knownlocationdb' $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable -Filter "Tenant eq '$tenantfilter'" - + if (!$Alertconfig) { + $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable -Filter "Tenant eq 'AllTenants'" + } + if ($data.userId -eq 'Not Available') { $data.userId = $data.userKey } if ($data.Userkey -eq 'Not Available') { $data.Userkey = $data.userId } if ($data.clientip) { From 035341e0f76f66fd31fb60391729ce3ec6b9bc56 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 18:08:43 +0100 Subject: [PATCH 81/84] clean up webhook --- .../Public/Invoke-RemoveWebhookAlert.ps1 | 17 ++++++++++------- Scheduler_Standards/function.json | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 index 3d9b4a628914..0e9a92ac7868 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 @@ -23,14 +23,17 @@ Function Invoke-RemoveWebhookAlert { } else { if ($Request.query.TenantFilter -eq 'AllTenants') { $Tenants = Get-Tenants -IncludeAll -IncludeErrors | Select-Object -ExpandProperty defaultDomainName - $CompleteObject = @{ - tenant = 'AllTenants' - type = 'webhookcreation' - RowKey = 'AllTenantsWebhookCreation' - PartitionKey = 'webhookcreation' + try { + $CompleteObject = @{ + tenant = 'AllTenants' + type = 'webhookcreation' + RowKey = 'AllTenantsWebhookCreation' + PartitionKey = 'webhookcreation' + } + Remove-AzDataTableEntity @Table -Entity $CompleteObject -ErrorAction SilentlyContinue | Out-Null + } catch { + # } - Remove-AzDataTableEntity @Table -Entity $CompleteObject -ErrorAction SilentlyContinue | Out-Null - } else { $Tenants = $Request.query.TenantFilter } diff --git a/Scheduler_Standards/function.json b/Scheduler_Standards/function.json index 88d7d049eb48..35ec29f027f7 100644 --- a/Scheduler_Standards/function.json +++ b/Scheduler_Standards/function.json @@ -2,7 +2,7 @@ "bindings": [ { "name": "Timer", - "schedule": "0 0 */3 * * *", + "schedule": "0 0 */4 * * *", "direction": "in", "type": "timerTrigger" }, From ddbae519ab119bc538801cda416042f13c98f7a2 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 18:20:56 +0100 Subject: [PATCH 82/84] fixed logic mistake --- Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index b08aa37d1ce0..65991c17a452 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -15,7 +15,7 @@ function Invoke-CippWebhookProcessing { if (!$Alertconfig) { $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable -Filter "Tenant eq 'AllTenants'" } - + if ($data.userId -eq 'Not Available') { $data.userId = $data.userKey } if ($data.Userkey -eq 'Not Available') { $data.Userkey = $data.userId } if ($data.clientip) { @@ -75,7 +75,7 @@ function Invoke-CippWebhookProcessing { foreach ($AlertSetting in $Alertconfig) { $ifs = $AlertSetting.If | ConvertFrom-Json $Dos = $AlertSetting.execution | ConvertFrom-Json - if ($data.operation -notin $Ifs.selection -and $ifs.selection -ne 'AnyAlert' ) { + if ($data.operation -notin $Ifs.selection -and $ifs.selection -ne 'AnyAlert' -and ($ifs.count -le 1 -and $ifs.selection -ne 'customField')) { Write-Host 'Not an operation to do anything for. storing IP info' if ($data.ClientIP -and $data.operation -like '*LoggedIn*') { Write-Host 'Add IP and potential location to knownlocation db for this specific user.' From 542aa94d33c95fbb3d710cdf1b126d99cc47f8f8 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 18:36:33 +0100 Subject: [PATCH 83/84] up versions --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index 490375502c2a..b617d997d770 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -4.8.4 \ No newline at end of file +4.9.0 \ No newline at end of file From c641e6d6198f12da468fc46de285d6a492c57319 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 2 Jan 2024 18:47:48 +0100 Subject: [PATCH 84/84] one more edit pre release --- Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 index 20b8a0f2fb95..3c8c48347d2a 100644 --- a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 @@ -224,7 +224,7 @@ function New-CIPPAlertTemplate { default { $Title = 'A custom alert has occured' - $Table = ($data | ConvertTo-Html -Fragment | Out-String).Replace('
', '
') + $Table = ($data | ConvertTo-Html -Fragment -As List | Out-String).Replace('
', '
') $IntroText = "

You have setup CIPP to send you a custom alert for the event $($Data.operation)

$Table" if ($ActionResults) { $IntroText = $IntroText + "

Based on the rule, the following actions have been taken: $($ActionResults -join '
' )

" } if ($LocationInfo) {