diff --git a/Modules/CIPPCore/Public/AdditionalPermissions.json b/Modules/CIPPCore/Public/AdditionalPermissions.json index 815fe9e59248..c69644cf99bf 100644 --- a/Modules/CIPPCore/Public/AdditionalPermissions.json +++ b/Modules/CIPPCore/Public/AdditionalPermissions.json @@ -2,5 +2,12 @@ { "resourceAppId": "00000003-0000-0ff1-ce00-000000000000", "resourceAccess": [{ "id": "AllProfiles.Manage", "type": "Scope" }] + }, + { + "resourceAppId": "00000006-0000-0ff1-ce00-000000000000", + "resourceAccess": [ + { "id": "M365AdminPortal.IntegratedApps.ReadWrite", "type": "Scope" }, + { "id": "user_impersonation", "type": "Scope" } + ] } ] diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-Schedulerwebhookcreation.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-Schedulerwebhookcreation.ps1 index 47aa38b1a072..e893aac710ba 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-Schedulerwebhookcreation.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-Schedulerwebhookcreation.ps1 @@ -8,42 +8,33 @@ function Push-Schedulerwebhookcreation { ) $Table = Get-CIPPTable -TableName 'SchedulerConfig' $WebhookTable = Get-CIPPTable -TableName 'webhookTable' - - #Write-Information ($item | ConvertTo-Json -Depth 10) + $Tenant = $Item.Tenant $Row = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$($item.SchedulerRow)'" if (!$Row) { - Write-Host "No row found for $($item.SchedulerRow). Full received item was $($item | ConvertTo-Json)" + Write-Information "No row found for $($item.SchedulerRow). Full received item was $($item | ConvertTo-Json)" return } else { - if ($Row.tenantid -eq 'AllTenants') { - $Tenants = (Get-Tenants).defaultDomainName + Write-Information "Working on $Tenant - $($Item.Tenantid)" + #use the queueitem to see if we already have a webhook for this tenant + webhooktype. If we do, delete this row from SchedulerConfig. + $Webhook = Get-CIPPAzDataTableEntity @WebhookTable -Filter "PartitionKey eq '$Tenant' and Version eq '3' and Resource eq '$($Row.webhookType)'" + if ($Webhook) { + Write-Information "Found existing webhook for $Tenant - $($Row.webhookType)" + if ($Row.tenantid -ne 'AllTenants') { + Remove-AzDataTableEntity @Table -Entity $Row + } } else { - $Tenants = (Get-Tenants | Where-Object { $_.customerId -eq $Row.tenantid }).defaultDomainName - } - foreach ($Tenant in $Tenants) { - Write-Host "Working on $Tenant - $($Row.tenantid)" - #use the queueitem to see if we already have a webhook for this tenant + webhooktype. If we do, delete this row from SchedulerConfig. - $Webhook = Get-CIPPAzDataTableEntity @WebhookTable -Filter "PartitionKey eq '$Tenant' and Version eq '3' and Resource eq '$($Row.webhookType)'" - if ($Webhook) { - Write-Host "Found existing webhook for $Tenant - $($Row.webhookType)" - if ($Row.tenantid -ne 'AllTenants') { + Write-Information "No existing webhook for $Tenant - $($Row.webhookType) - Time to create." + try { + $NewSub = New-CIPPGraphSubscription -TenantFilter $Tenant -EventType $Row.webhookType -auditLogAPI $true + if ($NewSub.Success -and $Row.tenantid -ne 'AllTenants') { Remove-AzDataTableEntity @Table -Entity $Row + } else { + Write-Information "Failed to create webhook for $Tenant - $($Row.webhookType) - $($_.Exception.Message)" } - } else { - Write-Host "No existing webhook for $Tenant - $($Row.webhookType) - Time to create." - try { - $NewSub = New-CIPPGraphSubscription -TenantFilter $Tenant -EventType $Row.webhookType -auditLogAPI $true - if ($NewSub.Success -and $Row.tenantid -ne 'AllTenants') { - Remove-AzDataTableEntity @Table -Entity $Row - } else { - Write-Host "Failed to create webhook for $Tenant - $($Row.webhookType) - $($_.Exception.Message)" - } - } catch { - Write-Host "Failed to create webhook for $Tenant - $($Row.webhookType): $($_.Exception.Message)" - } - + } catch { + Write-Information "Failed to create webhook for $Tenant - $($Row.webhookType): $($_.Exception.Message)" } } } -} \ No newline at end of file +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRemoveTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRemoveTenant.ps1 new file mode 100644 index 000000000000..8e036778fb2d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRemoveTenant.ps1 @@ -0,0 +1,36 @@ +function Invoke-ExecRemoveTenant { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Tenant.Administration.ReadWrite + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + if ($Request.Body.TenantID -notmatch '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { + $Body = @{Results = "Tenant ID $($Request.Body.TenantID) is not a valid GUID." } + $StatusCode = [HttpStatusCode]::BadRequest + } else { + $Table = Get-CippTable -tablename 'Tenants' + $Tenant = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'Tenants' and RowKey eq '$($Request.Body.TenantID)'" -Property RowKey, PartitionKey, customerId, displayName + if ($Tenant) { + try { + Remove-AzDataTableEntity @Table -Entity $Tenant + $Body = @{Results = "$($Tenant.displayName) ($($Tenant.customerId)) deleted from CIPP. Note: This does not remove the GDAP relationship, see the Tenant Offboarding wizard to perform that action." } + $StatusCode = [HttpStatusCode]::OK + } catch { + $Body = @{Results = "Failed to delete $($Tenant.displayName) ($($Tenant.customerId)) from CIPP. Error: $($_.Exception.Message)" } + $StatusCode = [HttpStatusCode]::InternalServerError + } + } else { + $Body = @{Results = "Tenant $($Request.Body.TenantID) not found in CIPP." } + $StatusCode = [HttpStatusCode]::NotFound + } + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Body + }) +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 index 0a3877cfc4c9..56d168243bd4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 @@ -10,44 +10,42 @@ Function Invoke-ListLogs { [CmdletBinding()] param($Request, $TriggerMetadata) - $AllowedTenants = Test-CIPPAccess -Request $Request -TenantList $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $TenantList = Get-Tenants -IncludeErrors - if ($request.Query.Filter -eq 'True') { - $LogLevel = if ($Request.query.Severity) { ($Request.query.Severity).split(',') } else { 'Info', 'Warn', 'Error', 'Critical', 'Alert' } - $PartitionKey = $Request.query.DateFilter - $username = $Request.Query.User - } else { - $LogLevel = 'Info', 'Warn', 'Error', 'Critical', 'Alert' - $PartitionKey = Get-Date -UFormat '%Y%m%d' - $username = '*' - } $Table = Get-CIPPTable $ReturnedLog = if ($Request.Query.ListLogs) { - - Get-CIPPAzDataTableEntity @Table -Property PartitionKey | Sort-Object -Unique PartitionKey | Select-Object PartitionKey | ForEach-Object { + Get-AzDataTableEntity @Table -Property PartitionKey | Sort-Object -Unique PartitionKey | Select-Object PartitionKey | ForEach-Object { @{ value = $_.PartitionKey label = $_.PartitionKey } } } else { + if ($request.Query.Filter -eq 'True') { + $LogLevel = if ($Request.query.Severity) { ($Request.query.Severity).split(',') } else { 'Info', 'Warn', 'Error', 'Critical', 'Alert' } + $PartitionKey = $Request.query.DateFilter + $username = $Request.Query.User + } else { + $LogLevel = 'Info', 'Warn', 'Error', 'Critical', 'Alert' + $PartitionKey = Get-Date -UFormat '%Y%m%d' + $username = '*' + } + $AllowedTenants = Test-CIPPAccess -Request $Request -TenantList $Filter = "PartitionKey eq '{0}'" -f $PartitionKey - $Rows = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username } + $Rows = Get-AzDataTableEntity @Table -Filter $Filter | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username } foreach ($Row in $Rows) { - if ($AllowedTenants -notcontains 'AllTenants') { - if ($Row.Tenant -ne 'None') { + $TenantList = Get-Tenants -IncludeErrors + if ($Row.Tenant -ne 'None' -and $Row.Tenant) { $Tenant = $TenantList | Where-Object -Property defaultDomainName -EQ $Row.Tenant - if ($Tenant.customerId -notin $AllowedTenants) { + if ($Tenant -and $Tenant.customerId -notin $AllowedTenants) { continue } } } - $LogData = if ($Row.LogData -and (Test-Json -Json $Row.LogData)) { + $LogData = if ($Row.LogData -and (Test-Json -Json $Row.LogData -ErrorAction SilentlyContinue)) { $Row.LogData | ConvertFrom-Json } else { $Row.LogData } [PSCustomObject]@{ @@ -65,7 +63,6 @@ Function Invoke-ListLogs { } } } - } Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ diff --git a/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 b/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 index 0a6b0a4b3fba..d779ba65c42d 100644 --- a/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 @@ -18,7 +18,7 @@ function Get-CIPPMFAState { } $SecureDefaultsState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $TenantFilter ).IsEnabled - $CAState = New-Object System.Collections.ArrayList + $CAState = [System.Collections.Generic.List[object]]::new() Try { $MFARegistration = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails' -tenantid $TenantFilter) @@ -31,22 +31,23 @@ function Get-CIPPMFAState { $CAPolicies = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies' -tenantid $TenantFilter -ErrorAction Stop ) try { - $ExcludeAllUsers = New-Object System.Collections.ArrayList - $ExcludeSpecific = New-Object System.Collections.ArrayList - foreach ($Policy in $CAPolicies) { - if (($policy.grantControls.builtincontrols -eq 'mfa') -or ($policy.grantControls.authenticationStrength.requirementsSatisfied -eq 'mfa') -or ($policy.grantControls.customAuthenticationFactors -eq 'RequireDuoMfa')) { - if ($Policy.conditions.applications.includeApplications -ne 'All') { - Write-Host $Policy.conditions.applications.includeApplications - $CAState.Add("$($policy.displayName) - Specific Applications - $($policy.state)") | Out-Null - $Policy.conditions.users.excludeUsers.foreach({ $ExcludeSpecific.Add($_) | Out-Null }) - continue - } - if ($Policy.conditions.users.includeUsers -eq 'All') { - $CAState.Add("$($policy.displayName) - All Users - $($policy.state)") | Out-Null - $Policy.conditions.users.excludeUsers.foreach({ $ExcludeAllUsers.Add($_) | Out-Null }) - continue - } + $IsMFAControl = $policy.grantControls.builtincontrols -eq 'mfa' -or $Policy.grantControls.authenticationStrength.requirementsSatisfied -eq 'mfa' -or $Policy.grantControls.customAuthenticationFactors -eq 'RequireDuoMfa' + $IsAllApps = [bool]($Policy.conditions.applications.includeApplications -eq 'All') + $IsAllUsers = [bool]($Policy.conditions.users.includeUsers -eq 'All') + $Platforms = $Policy.conditions.clientAppTypes + + if ($IsMFAControl) { + $CAState.Add([PSCustomObject]@{ + DisplayName = $Policy.displayName + State = $Policy.state + IncludedApps = $Policy.conditions.applications.includeApplications + IncludedUsers = $Policy.conditions.users.includeUsers + ExcludedUsers = $Policy.conditions.users.excludeUsers + IsAllApps = $IsAllApps + IsAllUsers = $IsAllUsers + Platforms = $Platforms + }) } } } catch { @@ -59,18 +60,26 @@ function Get-CIPPMFAState { # Interact with query parameters or the body of the request. $GraphRequest = $Users | ForEach-Object { Write-Host 'Processing users' - $UserCAState = New-Object System.Collections.ArrayList + $UserCAState = [System.Collections.Generic.List[object]]::new() foreach ($CA in $CAState) { - if ($CA -like '*All Users*') { - if ($ExcludeAllUsers -contains $_.ObjectId) { $UserCAState.Add("Excluded from $($policy.displayName) - All Users") | Out-Null } - else { $UserCAState.Add($CA) | Out-Null } - } elseif ($CA -like '*Specific Applications*') { - if ($ExcludeSpecific -contains $_.ObjectId) { $UserCAState.Add("Excluded from $($policy.displayName) - Specific Applications") | Out-Null } - else { $UserCAState.Add($CA) | Out-Null } + if ($CA.IncludedUsers -eq 'All' -or $CA.IncludedUsers -contains $_.ObjectId) { + $UserCAState.Add([PSCustomObject]@{ + DisplayName = $CA.DisplayName + UserIncluded = ($CA.ExcludedUsers -notcontains $_.ObjectId) + AllApps = $CA.IsAllApps + PolicyState = $CA.State + Platforms = $CA.Platforms -join ', ' + }) + } + } + if ($UserCAState.UserIncluded -eq $true -and $UserCAState.PolicyState -eq 'enabled') { + if ($UserCAState.UserIncluded -eq $true -and $UserCAState.PolicyState -eq 'enabled' -and $UserCAState.AllApps) { + $CoveredByCA = 'Enforced - All Apps' } else { - Write-Host 'Adding to CA' - $UserCAState.Add($CA) | Out-Null + $CoveredByCA = 'Enforced - Specific Apps' } + } else { + $CoveredByCA = 'Not Enforced' } $PerUser = if ($PerUserMFAState -eq $null) { $null } else { ($PerUserMFAState | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).PerUserMFAState } @@ -86,8 +95,9 @@ function Get-CIPPMFAState { PerUser = $PerUser isLicensed = $_.isLicensed MFARegistration = $MFARegUser.IsMFARegistered - MFAMethods = $($MFARegUser.authMethods -join ', ') - CoveredByCA = ($UserCAState -join ', ') + MFAMethods = $MFARegUser.authMethods + CoveredByCA = $CoveredByCA + CAPolicies = $UserCAState CoveredBySD = $SecureDefaultsState RowKey = [string]($_.UserPrincipalName).replace('#', '') PartitionKey = 'users' diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 index 4b3e6790a33f..89b36c348483 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 @@ -3,43 +3,44 @@ function New-ExoRequest { .FUNCTIONALITY Internal #> - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName = 'ExoRequest')] Param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $true, ParameterSetName = 'ExoRequest')] [string]$cmdlet, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, ParameterSetName = 'ExoRequest')] $cmdParams, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, ParameterSetName = 'ExoRequest')] [string]$Select, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, ParameterSetName = 'ExoRequest')] [string]$Anchor, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, ParameterSetName = 'ExoRequest')] [bool]$useSystemMailbox, - [Parameter(Mandatory = $false)] [string]$tenantid, - [Parameter(Mandatory = $false)] [bool]$NoAuthCheck, [switch]$Compliance, [ValidateSet('v1.0', 'beta')] - [string]$ApiVersion = 'beta' + [string]$ApiVersion = 'beta', + + [Parameter(ParameterSetName = 'AvailableCmdlets')] + [switch]$AvailableCmdlets, + + $ModuleVersion = '3.5.1', + [switch]$AsApp ) if ((Get-AuthorisedRequest -TenantID $tenantid) -or $NoAuthCheck -eq $True) { - if ($Compliance.IsPresent) { $Resource = 'https://ps.compliance.protection.outlook.com' - $token = Get-GraphToken -tenantid $tenantid -scope "$Resource/.default" - $token = @{ 'access_token' = $token.Authorization -replace 'Bearer ' } } else { $Resource = 'https://outlook.office365.com' - $token = Get-ClassicAPIToken -resource $Resource -Tenantid $tenantid } + $token = Get-GraphToken -Tenantid $tenantid -scope "$Resource/.default" -AsApp:$AsApp.IsPresent if ($cmdParams) { #if cmdparams is a pscustomobject, convert to hashtable, otherwise leave as is @@ -55,7 +56,11 @@ function New-ExoRequest { } $Tenant = Get-Tenants -IncludeErrors | Where-Object { $_.defaultDomainName -eq $tenantid -or $_.customerId -eq $tenantid } - + if (-not $Tenant -and $NoAuthCheck -eq $true) { + $Tenant = [PSCustomObject]@{ + customerId = $tenantid + } + } if (!$Anchor) { if ($cmdparams.Identity) { $Anchor = $cmdparams.Identity } if ($cmdparams.anr) { $Anchor = $cmdparams.anr } @@ -74,11 +79,11 @@ function New-ExoRequest { } #if the anchor is a GUID, try looking up the user. if ($Anchor -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { - Write-Host "Anchor is a GUID, looking up user. GUID is $Anchor" + Write-Verbose "Anchor is a GUID, looking up user. GUID is $Anchor" $NewAnchor = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$Anchor/?`$select=UserPrincipalName,id" -tenantid $tenantid -NoAuthCheck $NoAuthCheck - if ($Anchor) { + if ($NewAnchor) { $Anchor = $NewAnchor.UserPrincipalName - Write-Host "Found GUID, using $Anchor" + Write-Verbose "Found GUID, using $Anchor" } else { Write-Error "Failed to find user with GUID $Anchor" } @@ -87,57 +92,74 @@ function New-ExoRequest { Write-Verbose "Using $Anchor" $Headers = @{ - Authorization = "Bearer $($token.access_token)" + Authorization = $Token.Authorization Prefer = 'odata.maxpagesize=1000' 'X-AnchorMailbox' = $anchor } # Compliance API trickery. Capture Location headers on redirect, extract subdomain and prepend to compliance URL if ($Compliance.IsPresent) { - $URL = "$Resource/adminapi/$ApiVersion/$($tenant.customerId)/EXOBanner('AutogenSession')?Version=3.4.0" - Invoke-RestMethod -ResponseHeadersVariable ComplianceHeaders -MaximumRedirection 0 -ErrorAction SilentlyContinue -Uri $URL -Headers $Headers -SkipHttpErrorCheck | Out-Null - $RedirectedHost = ([System.Uri]($ComplianceHeaders.Location | Select-Object -First 1)).Host - $RedirectedHostname = '{0}.ps.compliance.protection.outlook.com' -f ($RedirectedHost -split '\.' | Select-Object -First 1) - $Resource = "https://$($RedirectedHostname)" + if (!$Tenant.ComplianceUrl) { + Write-Verbose "Getting Compliance URL for $($tenant.defaultDomainName)" + $URL = "$Resource/adminapi/$ApiVersion/$($tenant.customerId)/EXOBanner('AutogenSession')?Version=$ModuleVersion" + Invoke-RestMethod -ResponseHeadersVariable ComplianceHeaders -MaximumRedirection 0 -ErrorAction SilentlyContinue -Uri $URL -Headers $Headers -SkipHttpErrorCheck | Out-Null + $RedirectedHost = ([System.Uri]($ComplianceHeaders.Location | Select-Object -First 1)).Host + $RedirectedHostname = '{0}.ps.compliance.protection.outlook.com' -f ($RedirectedHost -split '\.' | Select-Object -First 1) + $Resource = "https://$($RedirectedHostname)" + $Tenant | Add-Member -MemberType NoteProperty -Name ComplianceUrl -Value $Resource + $TenantTable = Get-CIPPTable -tablename 'Tenants' + Add-CIPPAzDataTableEntity @TenantTable -Entity $Tenant -Force + } else { + $Resource = $Tenant.ComplianceUrl + } Write-Verbose "Redirecting to $Resource" } - try { - if ($Select) { $Select = "?`$select=$Select" } - $URL = "$Resource/adminapi/$ApiVersion/$($tenant.customerId)/InvokeCommand$Select" - - Write-Verbose "POST [ $URL ]" - $ReturnedData = do { - $ExoRequestParams = @{ - Uri = $URL - Method = 'POST' - Body = $ExoBody - Headers = $Headers - ContentType = 'application/json' - } - - $Return = Invoke-RestMethod @ExoRequestParams - $URL = $Return.'@odata.nextLink' - $Return - } until ($null -eq $URL) + if ($PSCmdlet.ParameterSetName -eq 'AvailableCmdlets') { + $Headers.CommandName = '*' + $URL = "$Resource/adminapi/v1.0/$($tenant.customerId)/EXOModuleFile?Version=$ModuleVersion" + Write-Verbose "GET [ $URL ]" + return (Invoke-RestMethod -Uri $URL -Headers $Headers).value.exportedCmdlets -split ',' | Where-Object { $_ } | Sort-Object + } - if ($ReturnedData.'@adminapi.warnings' -and $ReturnedData.value -eq $null) { - $ReturnedData.value = $ReturnedData.'@adminapi.warnings' - } - } catch { - $ErrorMess = $($_.Exception.Message) + if ($PSCmdlet.ParameterSetName -eq 'ExoRequest') { try { - $ReportedError = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue) - $Message = if ($ReportedError.error.details.message) { - $ReportedError.error.details.message - } elseif ($ReportedError.error.innererror) { - $ReportedError.error.innererror.internalException.message - } elseif ($ReportedError.error.message) { $ReportedError.error.message } - } catch { $Message = $_.ErrorDetails } - if ($null -eq $Message) { $Message = $ErrorMess } - throw $Message + if ($Select) { $Select = "?`$select=$Select" } + $URL = "$Resource/adminapi/$ApiVersion/$($tenant.customerId)/InvokeCommand$Select" + + Write-Verbose "POST [ $URL ]" + $ReturnedData = do { + $ExoRequestParams = @{ + Uri = $URL + Method = 'POST' + Body = $ExoBody + Headers = $Headers + ContentType = 'application/json' + } + + $Return = Invoke-RestMethod @ExoRequestParams + $URL = $Return.'@odata.nextLink' + $Return + } until ($null -eq $URL) + + if ($ReturnedData.'@adminapi.warnings' -and $ReturnedData.value -eq $null) { + $ReturnedData.value = $ReturnedData.'@adminapi.warnings' + } + } catch { + $ErrorMess = $($_.Exception.Message) + try { + $ReportedError = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue) + $Message = if ($ReportedError.error.details.message) { + $ReportedError.error.details.message + } elseif ($ReportedError.error.innererror) { + $ReportedError.error.innererror.internalException.message + } elseif ($ReportedError.error.message) { $ReportedError.error.message } + } catch { $Message = $_.ErrorDetails } + if ($null -eq $Message) { $Message = $ErrorMess } + throw $Message + } + return $ReturnedData.value } - return $ReturnedData.value } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' } diff --git a/Modules/CIPPCore/Public/PermissionsTranslator.json b/Modules/CIPPCore/Public/PermissionsTranslator.json index 1a88f6dbf708..413ac8ae25e1 100644 --- a/Modules/CIPPCore/Public/PermissionsTranslator.json +++ b/Modules/CIPPCore/Public/PermissionsTranslator.json @@ -1019,7 +1019,7 @@ "displayName": "Read and write all user mailbox settings", "id": "f9156939-25cd-4ba8-abfe-7fabcf003749", "origin": "Application (Office 365 Exchange Online)", - "value": "Mailbox.Settings.ReadWrite" + "value": "MailboxSettings.ReadWrite" }, { "description": "Allows the app to read your organization's user flows, without a signed-in user.",