diff --git a/.github/workflows/dev_cippckdtz.yml b/.github/workflows/dev_cippckdtz.yml new file mode 100644 index 000000000000..6e0c53e9df0a --- /dev/null +++ b/.github/workflows/dev_cippckdtz.yml @@ -0,0 +1,30 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/azure/functions-action +# More GitHub Actions for Azure: https://github.com/Azure/actions + +name: Build and deploy Powershell project to Azure Function App - cippckdtz + +on: + push: + branches: + - dev + workflow_dispatch: + +env: + AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root + +jobs: + deploy: + runs-on: windows-latest + + steps: + - name: 'Checkout GitHub Action' + uses: actions/checkout@v4 + + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + id: fa + with: + app-name: 'cippckdtz' + slot-name: 'Production' + package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} + publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_2101C7175BFB47E58240ABD1E72E81C2 }} \ No newline at end of file diff --git a/Activity_AddOrUpdateTableRows/run.ps1 b/Activity_AddOrUpdateTableRows/run.ps1 index 6bb7e218b987..adc07df73e23 100644 --- a/Activity_AddOrUpdateTableRows/run.ps1 +++ b/Activity_AddOrUpdateTableRows/run.ps1 @@ -3,11 +3,10 @@ $TableName = ($TableParams.Context['TableName']) $Table = Get-CippTable -tablename $TableName foreach ($param in $TableParams.Entity) { - try { - #Sending each item indivually, if it fails, log an error. - Add-CIPPAzDataTableEntity @Table -Entity $param -Force - } - catch { - Write-LogMessage -API 'Activity_AddOrUpdateTableRows' -message "Unable to write to '$($TableParams.TableName)' Using RowKey $($param.RowKey) table: $($_.Exception.Message)" -sev error - } + try { + #Sending each item indivually, if it fails, log an error. + Add-CIPPAzDataTableEntity @Table -Entity $param -Force + } catch { + Write-LogMessage -API 'Activity_AddOrUpdateTableRows' -message "Unable to write to '$($TableParams.TableName)' Using RowKey $($param.RowKey)" -LogData (Get-CippException -Exception $_) -sev error + } } diff --git a/Applications_Orchestrator/run.ps1 b/Applications_Orchestrator/run.ps1 index 9c8457ff91fc..ebf60eb55628 100644 --- a/Applications_Orchestrator/run.ps1 +++ b/Applications_Orchestrator/run.ps1 @@ -17,7 +17,7 @@ try { $Outputs = Wait-ActivityFunction -Task $ParallelTasks Write-Host $Outputs } -catch { +catch { Write-Host "Applications_Orchestrator exception: $($_.Exception.Message)" } finally { diff --git a/Applications_Upload/run.ps1 b/Applications_Upload/run.ps1 index a92637a42882..b1aacdc41318 100644 --- a/Applications_Upload/run.ps1 +++ b/Applications_Upload/run.ps1 @@ -1,14 +1,14 @@ param($name) $Table = Get-CippTable -tablename 'apps' -$Filter = "PartitionKey eq 'apps' and RowKey eq '$name'" +$Filter = "PartitionKey eq 'apps' and RowKey eq '$name'" Set-Location (Get-Item $PSScriptRoot).Parent.FullName $ChocoApp = (Get-CIPPAzDataTableEntity @Table -filter $Filter).JSON | ConvertFrom-Json $intuneBody = $ChocoApp.IntuneBody -$tenants = if ($chocoapp.Tenant -eq 'AllTenants') { +$tenants = if ($chocoapp.Tenant -eq 'AllTenants') { (Get-tenants).defaultDomainName } else { $chocoapp.Tenant -} +} if ($chocoApp.type -eq 'MSPApp') { [xml]$Intunexml = Get-Content "AddMSPApp\$($ChocoApp.MSPAppName).app.xml" $intunewinFilesize = (Get-Item "AddMSPApp\$($ChocoApp.MSPAppName).intunewin") @@ -25,7 +25,7 @@ $ContentBody = ConvertTo-Json @{ name = $intunexml.ApplicationInfo.FileName size = [int64]$intunexml.ApplicationInfo.UnencryptedContentSize sizeEncrypted = [int64]($intunewinFilesize).length -} +} $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter $RemoveCacheFile = if ($chocoapp.Tenant -ne 'AllTenants') { Remove-AzDataTableEntity @Table -Entity $clearRow @@ -54,11 +54,11 @@ foreach ($tenant in $tenants) { Try { $ApplicationList = (New-graphGetRequest -Uri $baseuri -tenantid $Tenant) | Where-Object { $_.DisplayName -eq $ChocoApp.ApplicationName } - if ($ApplicationList.displayname.count -ge 1) { + if ($ApplicationList.displayname.count -ge 1) { Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) exists. Skipping this application" -Sev 'Info' continue } - if ($chocoApp.type -eq 'WinGet') { + if ($chocoApp.type -eq 'WinGet') { Write-Host 'Winget!' Write-Host ($intuneBody | ConvertTo-Json -Compress) $NewApp = New-GraphPostRequest -Uri $baseuri -Body ($intuneBody | ConvertTo-Json -Compress) -Type POST -tenantid $tenant @@ -79,8 +79,8 @@ foreach ($tenant in $tenants) { $AzFileUri = New-graphGetRequest -Uri "$($BaseURI)/$($NewApp.id)/microsoft.graph.win32lobapp/contentVersions/1/files/$($ContentReq.id)" -tenantid $tenant if ($AZfileuri.uploadState -like '*fail*') { break } Start-Sleep -Milliseconds 300 - } while ($AzFileUri.AzureStorageUri -eq $null) - + } while ($AzFileUri.AzureStorageUri -eq $null) + $chunkSizeInBytes = 4mb [byte[]]$bytes = [System.IO.File]::ReadAllBytes($($intunewinFilesize.fullname)) $chunks = [Math]::Ceiling($bytes.Length / $chunkSizeInBytes) @@ -89,15 +89,15 @@ foreach ($tenant in $tenants) { $Upload = Invoke-RestMethod -Uri "$($AzFileUri.azureStorageUri)&comp=block&blockid=$id" -Method Put -Headers @{'x-ms-blob-type' = 'BlockBlob' } -InFile $inFile -ContentType 'application/octet-stream' $ConfirmUpload = Invoke-RestMethod -Uri "$($AzFileUri.azureStorageUri)&comp=blocklist" -Method Put -Body "$id" $CommitReq = New-graphPostRequest -Uri "$($BaseURI)/$($NewApp.id)/microsoft.graph.win32lobapp/contentVersions/1/files/$($ContentReq.id)/commit" -Body $EncBody -Type POST -tenantid $tenant - + do { $CommitStateReq = New-graphGetRequest -Uri "$($BaseURI)/$($NewApp.id)/microsoft.graph.win32lobapp/contentVersions/1/files/$($ContentReq.id)" -tenantid $tenant if ($CommitStateReq.uploadState -like '*fail*') { Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) Commit failed. Please check if app uploaded succesful" -Sev 'Warning' - break + break } Start-Sleep -Milliseconds 300 - } while ($CommitStateReq.uploadState -eq 'commitFilePending') + } while ($CommitStateReq.uploadState -eq 'commitFilePending') $CommitFinalizeReq = New-graphPostRequest -Uri "$($BaseURI)/$($NewApp.id)" -tenantid $tenant -Body '{"@odata.type":"#microsoft.graph.win32lobapp","committedContentVersion":"1"}' -type PATCH Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "Added Application $($chocoApp.ApplicationName)" -Sev 'Info' if ($AssignTo -ne 'On') { @@ -108,7 +108,7 @@ foreach ($tenant in $tenants) { Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message 'Successfully added Application' -Sev 'Info' } catch { "Failed to add Application for $($Tenant): $($_.Exception.Message)" - Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "Failed adding Application $($ChocoApp.ApplicationName). Error: $($_.Exception.Message)" -Sev 'Error' + Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "Failed adding Application $($ChocoApp.ApplicationName). Error: $($_.Exception.Message)" -LogData (Get-CippException -Exception $_) -Sev 'Error' continue } diff --git a/BestPracticeAnalyser_All/run.ps1 b/BestPracticeAnalyser_All/run.ps1 index ddd92560ccda..6e90102a161a 100644 --- a/BestPracticeAnalyser_All/run.ps1 +++ b/BestPracticeAnalyser_All/run.ps1 @@ -107,7 +107,7 @@ $AddRow = foreach ($Template in $templates) { try { Add-CIPPAzDataTableEntity @Table -Entity $Result -Force } catch { - Write-LogMessage -API 'BPA' -tenant $tenant -message "Error getting saving data for $($template.Name) - $($TenantName.customerId). Error: $($_.Exception.Message)" -sev Error + Write-LogMessage -API 'BPA' -tenant $tenant -message "Error getting saving data for $($template.Name) - $($TenantName.customerId). Error: $($_.Exception.Message)" -LogData (Get-CippException -Exception $_) -sev Error } } diff --git a/BestPracticeAnalyser_Orchestration/run.ps1 b/BestPracticeAnalyser_Orchestration/run.ps1 index 11fa99a62ec4..891f7e3fb6a4 100644 --- a/BestPracticeAnalyser_Orchestration/run.ps1 +++ b/BestPracticeAnalyser_Orchestration/run.ps1 @@ -2,7 +2,7 @@ param($Context) $DurableRetryOptions = @{ FirstRetryInterval = (New-TimeSpan -Seconds 5) - MaxNumberOfAttempts = 3 + MaxNumberOfAttempts = 1 BackoffCoefficient = 2 } $RetryOptions = New-DurableRetryOptions @DurableRetryOptions diff --git a/CIPPActivityFunction/function.json b/CIPPActivityFunction/function.json new file mode 100644 index 000000000000..e8a29dde00c3 --- /dev/null +++ b/CIPPActivityFunction/function.json @@ -0,0 +1,16 @@ +{ + "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1", + "entryPoint": "Receive-CippActivityTrigger", + "bindings": [ + { + "name": "Item", + "type": "activityTrigger", + "direction": "in" + }, + { + "name": "starter", + "type": "durableClient", + "direction": "in" + } + ] +} diff --git a/CIPPOrchestrator/function.json b/CIPPOrchestrator/function.json new file mode 100644 index 000000000000..011113dbc618 --- /dev/null +++ b/CIPPOrchestrator/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1", + "entryPoint": "Receive-CippOrchestrationTrigger", + "bindings": [ + { + "name": "Context", + "type": "orchestrationTrigger", + "direction": "in" + } + ] +} diff --git a/DomainAnalyser_All/run.ps1 b/DomainAnalyser_All/run.ps1 index d0da4a3e09da..981afa4b7bbc 100644 --- a/DomainAnalyser_All/run.ps1 +++ b/DomainAnalyser_All/run.ps1 @@ -36,9 +36,11 @@ try { $Result = [PSCustomObject]@{ Tenant = $Tenant.Tenant + TenantID = $Tenant.TenantGUID GUID = $($Domain.Replace('.', '')) LastRefresh = $(Get-Date (Get-Date).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z') Domain = $Domain + NSRecords = (Read-NSRecord -Domain $Domain).Records ExpectedSPFRecord = '' ActualSPFRecord = '' SPFPassAll = '' @@ -52,6 +54,7 @@ $Result = [PSCustomObject]@{ DNSSECPresent = '' MailProvider = '' DKIMEnabled = '' + DKIMRecords = '' Score = '' MaximumScore = 160 ScorePercentage = '' @@ -114,8 +117,8 @@ try { $ScoreExplanation.Add('No SPF Record Found') | Out-Null } } catch { - $Message = 'SPF Exception: {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -sev Error + $Message = 'SPF Error' + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error throw $Message } @@ -177,8 +180,8 @@ try { } } } catch { - $Message = 'DMARC Exception: {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -sev Error + $Message = 'DMARC Error' + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error throw $Message } @@ -195,8 +198,8 @@ try { $ScoreExplanation.Add('DNSSEC Not Configured or Enabled') | Out-Null } } catch { - $Message = 'DNSSEC Exception: {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -sev Error + $Message = 'DNSSEC Error' + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error throw $Message } @@ -218,13 +221,14 @@ try { if ($DkimRecordCount -gt 0 -and $DkimFailCount -eq 0) { $Result.DKIMEnabled = $true $ScoreDomain += $Scores.DKIMActiveAndWorking + $Result.DKIMRecords = $DkimRecord.Records | Select-Object Selector, Record } else { $Result.DKIMEnabled = $false $ScoreExplanation.Add('DKIM Not Configured') | Out-Null } } catch { - $Message = 'DKIM Exception: {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -sev Error + $Message = 'DKIM Exception' + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error throw $Message } # Final Score diff --git a/DomainAnalyser_GetTenantDomains/run.ps1 b/DomainAnalyser_GetTenantDomains/run.ps1 index 4e0fd71f2be8..6c4b8b0d8110 100644 --- a/DomainAnalyser_GetTenantDomains/run.ps1 +++ b/DomainAnalyser_GetTenantDomains/run.ps1 @@ -9,10 +9,13 @@ $TenantDomains = $Tenants | ForEach-Object -Parallel { $Tenant = $_ # Get Domains to Lookup try { - $Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant.defaultDomainName | Where-Object { ($_.id -notlike '*.microsoftonline.com' -and $_.id -NotLike '*.exclaimer.cloud' -and $_.id -NotLike '*.codetwo.online' -and $_.id -NotLike '*.call2teams.com' -and $_.isVerified) } + $Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant.defaultDomainName | Where-Object { ($_.id -notlike '*.microsoftonline.com' -and $_.id -NotLike '*.exclaimer.cloud' -and $_.id -Notlike '*.excl.cloud' -and $_.id -NotLike '*.codetwo.online' -and $_.id -NotLike '*.call2teams.com' -and $_.isVerified) } + foreach ($d in $domains) { [PSCustomObject]@{ Tenant = $Tenant.defaultDomainName + TenantGUID = $Tenant.customerId + InitialDomainName = $Tenant.initialDomainName Domain = $d.id AuthenticationType = $d.authenticationType IsAdminManaged = $d.isAdminManaged @@ -24,7 +27,7 @@ $TenantDomains = $Tenants | ForEach-Object -Parallel { } } } catch { - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.defaultDomainName -message "DNS Analyser GraphGetRequest Exception: $($_.Exception.Message)" -sev Error + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.defaultDomainName -message 'DNS Analyser GraphGetRequest' -LogData (Get-CippException -Exception $_) -sev Error } } | Sort-Object -Unique -Property Domain @@ -57,11 +60,12 @@ if ($TenantCount -gt 0) { $Filter = "PartitionKey eq 'TenantDomains' and RowKey eq '{0}'" -f $Tenant.Domain $Domain = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter - if (!$Domain) { + if (!$Domain -or $null -eq $Domain.TenantGUID) { $DomainObject = [pscustomobject]@{ DomainAnalyser = '' TenantDetails = $TenantDetails TenantId = $Tenant.Tenant + TenantGUID = $Tenant.TenantGUID DkimSelectors = '' MailProviders = '' RowKey = $Tenant.Domain @@ -87,6 +91,6 @@ if ($TenantCount -gt 0) { # Batch insert all tenant domains try { Add-CIPPAzDataTableEntity @DomainTable -Entity $TenantDomainObjects -Force - } catch { Write-LogMessage -API 'DomainAnalyser' -message "Domain Analyser GetTenantDomains Error $($_.Exception.Message)" -sev info } - } catch { Write-LogMessage -API 'DomainAnalyser' -message "GetTenantDomains loop exception: $($_.Exception.Message) line $($_.InvocationInfo.ScriptLineNumber)" -sev 'Error' } + } catch { Write-LogMessage -API 'DomainAnalyser' -message 'Domain Analyser GetTenantDomains error' -sev info -LogData (Get-CippException -Exception $_) } + } catch { Write-LogMessage -API 'DomainAnalyser' -message 'GetTenantDomains loop error' -sev 'Error' -LogData (Get-CippException -Exception $_) } } diff --git a/DomainAnalyser_Orchestration/run.ps1 b/DomainAnalyser_Orchestration/run.ps1 index 74a45a92e253..e8d51585ffa7 100644 --- a/DomainAnalyser_Orchestration/run.ps1 +++ b/DomainAnalyser_Orchestration/run.ps1 @@ -1,44 +1,40 @@ param($Context) -try { +try { - $DurableRetryOptions = @{ - FirstRetryInterval = (New-TimeSpan -Seconds 5) - MaxNumberOfAttempts = 3 - BackoffCoefficient = 2 - } - $RetryOptions = New-DurableRetryOptions @DurableRetryOptions + $DurableRetryOptions = @{ + FirstRetryInterval = (New-TimeSpan -Seconds 5) + MaxNumberOfAttempts = 1 + BackoffCoefficient = 2 + } + $RetryOptions = New-DurableRetryOptions @DurableRetryOptions - # Sync tenants - try { - Invoke-ActivityFunction -FunctionName 'DomainAnalyser_GetTenantDomains' -Input 'Tenants' - } - catch { Write-Host "EXCEPTION: TenantDomains $($_.Exception.Message)" } + # Sync tenants + try { + Invoke-ActivityFunction -FunctionName 'DomainAnalyser_GetTenantDomains' -Input 'Tenants' + } catch { Write-Host "EXCEPTION: TenantDomains $($_.Exception.Message)" } - # Get list of all domains to process - $Batch = Invoke-ActivityFunction -FunctionName 'Activity_GetAllTableRows' -Input 'Domains' - - $ParallelTasks = foreach ($Item in $Batch) { - Invoke-DurableActivity -FunctionName 'DomainAnalyser_All' -Input $item -NoWait -RetryOptions $RetryOptions - } - - # Collect activity function results and send to database - $TableParams = Get-CippTable -tablename 'Domains' - $TableParams.Entity = Wait-ActivityFunction -Task $ParallelTasks - $TableParams.Force = $true - $TableParams = $TableParams | ConvertTo-Json -Compress + # Get list of all domains to process + $Batch = Invoke-ActivityFunction -FunctionName 'Activity_GetAllTableRows' -Input 'Domains' - try { - Invoke-ActivityFunction -FunctionName 'Activity_AddOrUpdateTableRows' -Input $TableParams - } - catch { - Write-Host "Orchestrator exception UpdateDomains $($_.Exception.Message)" - } -} -catch { - Write-LogMessage -API 'DomainAnalyser' -message "Domain Analyser Orchestrator Error $($_.Exception.Message)" -sev info - #Write-Host $_.Exception | ConvertTo-Json -} -finally { - Write-LogMessage -API 'DomainAnalyser' -message 'Domain Analyser has Finished' -sev Info + $ParallelTasks = foreach ($Item in $Batch) { + Invoke-DurableActivity -FunctionName 'DomainAnalyser_All' -Input $item -NoWait -RetryOptions $RetryOptions + } + + # Collect activity function results and send to database + $TableParams = Get-CippTable -tablename 'Domains' + $TableParams.Entity = Wait-ActivityFunction -Task $ParallelTasks + $TableParams.Force = $true + $TableParams = $TableParams | ConvertTo-Json -Compress + + try { + Invoke-ActivityFunction -FunctionName 'Activity_AddOrUpdateTableRows' -Input $TableParams + } catch { + Write-Host "Orchestrator exception UpdateDomains $($_.Exception.Message)" + } +} catch { + Write-LogMessage -API 'DomainAnalyser' -message 'Domain Analyser Orchestrator Error' -sev info -LogData (Get-CippException -Exception $_) + #Write-Host $_.Exception | ConvertTo-Json +} finally { + Write-LogMessage -API 'DomainAnalyser' -message 'Domain Analyser has Finished' -sev Info } \ No newline at end of file diff --git a/ExecExtensionNinjaOneQueue/function.json b/ExecExtensionNinjaOneQueue/function.json deleted file mode 100644 index 058a42bd6db9..000000000000 --- a/ExecExtensionNinjaOneQueue/function.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "NinjaOneQueue" - }, - { - "type": "queue", - "direction": "out", - "name": "NinjaProcess", - "queueName": "NinjaOneQueue" - } - ] -} diff --git a/ExecExtensionNinjaOneQueue/run.ps1 b/ExecExtensionNinjaOneQueue/run.ps1 deleted file mode 100644 index 21720a79b6a5..000000000000 --- a/ExecExtensionNinjaOneQueue/run.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -# Input bindings are passed in via param block. -param($QueueItem, $TriggerMetadata) - -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell NinjaOne queue trigger function processed work item: $($QueueItem.NinjaAction)" - - -Switch ($QueueItem.NinjaAction) { - 'StartAutoMapping' { Invoke-NinjaOneOrgMapping } - 'AutoMapTenant' { Invoke-NinjaOneOrgMappingTenant -QueueItem $QueueItem } - 'SyncTenant' { Invoke-NinjaOneTenantSync -QueueItem $QueueItem } - 'SyncTenants' {Invoke-NinjaOneSync} -} diff --git a/ExecGDAPInviteApproved_Timer/function.json b/ExecGDAPInviteApproved_Timer/function.json index 32b454a2a015..f8904bbb0a7f 100644 --- a/ExecGDAPInviteApproved_Timer/function.json +++ b/ExecGDAPInviteApproved_Timer/function.json @@ -7,10 +7,9 @@ "schedule": "0 0 */3 * * *" }, { - "type": "queue", - "direction": "out", - "name": "gdapinvitequeue", - "queueName": "gdapinvitequeue" + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/ExecGDAPInviteQueue/function.json b/ExecGDAPInviteQueue/function.json deleted file mode 100644 index e51e66299d6f..000000000000 --- a/ExecGDAPInviteQueue/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "gdapinvitequeue" - } - ] -} diff --git a/ExecGDAPInviteQueue/run.ps1 b/ExecGDAPInviteQueue/run.ps1 deleted file mode 100644 index 1b95a443bab0..000000000000 --- a/ExecGDAPInviteQueue/run.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -# Input bindings are passed in via param block. -param( $QueueItem, $TriggerMetadata) - -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell queue trigger function processed work item: $($QueueItem.customer.displayName)" - -Set-CIPPGDAPInviteGroups -Relationship $QueueItem \ No newline at end of file diff --git a/ExecScheduledCommand/function.json b/ExecScheduledCommand/function.json deleted file mode 100644 index e4c27b23b985..000000000000 --- a/ExecScheduledCommand/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "scheduledcommandprocessor" - } - ] -} diff --git a/ExecScheduledCommand/run.ps1 b/ExecScheduledCommand/run.ps1 deleted file mode 100644 index 180df3368b87..000000000000 --- a/ExecScheduledCommand/run.ps1 +++ /dev/null @@ -1,83 +0,0 @@ -# Input bindings are passed in via param block. -param($QueueItem, $TriggerMetadata) - -$Table = Get-CippTable -tablename 'ScheduledTasks' -$task = $QueueItem.TaskInfo -$commandParameters = $QueueItem.Parameters - -$tenant = $QueueItem.Parameters['TenantFilter'] -Write-Host 'started task' -try { - try { - $results = & $QueueItem.command @commandParameters - } catch { - $results = "Task Failed: $($_.Exception.Message)" - - } - - Write-Host 'ran the command' - if ($results -is [String]) { - $results = @{ Results = $results } - } - if ($results -is [array]) { - $results = $results | Where-Object { $_ -is [string] } - $results = $results | ForEach-Object { @{ Results = $_ } } - } - - $results = $results | Select-Object * -ExcludeProperty RowKey, PartitionKey - - $StoredResults = $results | ConvertTo-Json -Compress -Depth 20 | Out-String - 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 { - $errorMessage = $_.Exception.Message - if ($task.Recurrence -gt 0) { $State = 'Failed - Planned' } else { $State = 'Failed' } - Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$errorMessage" - TaskState = $State - } - Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error -} - - -$TableDesign = '' -$HTML = ($results | Select-Object * -ExcludeProperty RowKey, PartitionKey | ConvertTo-Html -Fragment) -replace '', "$TableDesign
" | Out-String -$title = "Scheduled Task $($task.Name) - $($task.ExpectedRunTime)" -Write-Host $title -switch -wildcard ($task.PostExecution) { - '*psa*' { Send-CIPPAlert -Type 'psa' -Title $title -HTMLContent $HTML } - '*email*' { Send-CIPPAlert -Type 'email' -Title $title -HTMLContent $HTML } - '*webhook*' { - $Webhook = [PSCustomObject]@{ - 'Tenant' = $tenant - 'TaskInfo' = $QueueItem.TaskInfo - 'Results' = $Results - } - Send-CIPPAlert -Type 'webhook' -Title $title -JSONContent $($Webhook | ConvertTo-Json -Depth 20) - } -} - -Write-Host 'ran the command' - -if ($task.Recurrence -le '0' -or $task.Recurrence -eq $null) { - Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$StoredResults" - TaskState = 'Completed' - } -} else { - $nextRun = (Get-Date).AddDays($task.Recurrence) - $nextRunUnixTime = [int64]($nextRun - (Get-Date '1/1/1970')).TotalSeconds - Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$StoredResults" - TaskState = 'Planned' - ScheduledTime = "$nextRunUnixTime" - } -} -Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Successfully executed task: $($task.name)" -sev Info \ No newline at end of file diff --git a/ExecSchedulerBillingRun/run.ps1 b/ExecSchedulerBillingRun/run.ps1 index ff93986817b2..3ea7e6621fac 100644 --- a/ExecSchedulerBillingRun/run.ps1 +++ b/ExecSchedulerBillingRun/run.ps1 @@ -3,20 +3,19 @@ param($QueueItem) # Get the current universal time in the default string format. try { - Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Starting billing processing." -sev Info + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message 'Starting billing processing.' -sev Info $Table = Get-CIPPTable -TableName Extensionsconfig $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 foreach ($ConfigItem in $Configuration.psobject.properties.name) { switch ($ConfigItem) { - "Gradient" { + 'Gradient' { If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { New-GradientServiceSyncRun } } } } -} -catch { - Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Could not start billing processing $($_.Exception.Message)" -sev Error +} catch { + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message 'Could not start billing processing' -sev Error -LogData (Get-CippException -Exception $_) } \ No newline at end of file diff --git a/ListCippQueue/function.json b/ListCippQueue/function.json deleted file mode 100644 index 9a02219bfcbd..000000000000 --- a/ListCippQueue/function.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "scriptFile": "../Modules/CippQueue/CippQueue.psm1", - "entryPoint": "Get-CippQueue", - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ListGenericAllTenants/run.ps1 b/ListGenericAllTenants/run.ps1 index d51627ab7aea..afda8b0570d2 100644 --- a/ListGenericAllTenants/run.ps1 +++ b/ListGenericAllTenants/run.ps1 @@ -4,13 +4,13 @@ param([string]$QueueItem, $TriggerMetadata) # Write out the queue message and metadata to the information log. Write-Host "PowerShell queue trigger function processed work item: $QueueItem" $TableURLName = ($QueueItem.tolower().split('?').Split('/') | Select-Object -First 1).toString() -$QueueKey = (Get-CippQueue | Where-Object -Property Name -EQ $TableURLName | Select-Object -Last 1).RowKey +$QueueKey = (Invoke-ListCippQueue | Where-Object -Property Name -EQ $TableURLName | Select-Object -Last 1).RowKey Update-CippQueueEntry -RowKey $QueueKey -Status 'Started' $Table = Get-CIPPTable -TableName "cache$TableURLName" $fullUrl = "https://graph.microsoft.com/beta/$QueueItem" Get-CIPPAzDataTableEntity @Table | Remove-AzDataTableEntity @table -$RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { +$RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName Import-Module CippCore try { @@ -22,7 +22,7 @@ $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { Tenant = $domainName CippStatus = "Could not connect to tenant. $($_.Exception.message)" } - } + } } Update-CippQueueEntry -RowKey $QueueKey -Status 'Processing' diff --git a/ListLicensesAllTenants/function.json b/ListLicensesAllTenants/function.json deleted file mode 100644 index eee973c7ede2..000000000000 --- a/ListLicensesAllTenants/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "licqueue" - } - ] -} diff --git a/ListLicensesAllTenants/run.ps1 b/ListLicensesAllTenants/run.ps1 deleted file mode 100644 index abc69465c094..000000000000 --- a/ListLicensesAllTenants/run.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -# Input bindings are passed in via param block. -param([string] $QueueItem, $TriggerMetadata) - -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell queue trigger function processed work item: $QueueItem" - -$RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module '.\Modules\AzBobbyTables' - Import-Module '.\Modules\CIPPCore' - try { - Write-Host "Processing $domainName" - Get-CIPPLicenseOverview -TenantFilter $domainName - } - catch { - [pscustomobject]@{ - Tenant = [string]$domainName - License = "Could not connect to client: $($_.Exception.Message)" - 'PartitionKey' = 'License' - 'RowKey' = "$($domainName)-$(New-Guid)" - } - } -} - -$Table = Get-CIPPTable -TableName cachelicenses -foreach ($GraphRequest in $RawGraphRequest) { - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null -} \ No newline at end of file diff --git a/ListMFAUsersAllTenants/function.json b/ListMFAUsersAllTenants/function.json deleted file mode 100644 index 7bb9da79d405..000000000000 --- a/ListMFAUsersAllTenants/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "mfaqueue" - } - ] -} diff --git a/ListMFAUsersAllTenants/run.ps1 b/ListMFAUsersAllTenants/run.ps1 deleted file mode 100644 index 8ab611e514fe..000000000000 --- a/ListMFAUsersAllTenants/run.ps1 +++ /dev/null @@ -1,57 +0,0 @@ -# Input bindings are passed in via param block. -param([string] $QueueItem, $TriggerMetadata) - -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell queue trigger function processed work item: $QueueItem" - - -Write-Information "Item: $QueueItem" -Write-Information ($TriggerMetadata | ConvertTo-Json) - -try { - Update-CippQueueEntry -RowKey $QueueItem -Status 'Running' - - $GraphRequest = Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module '.\modules\CippCore' - $Table = Get-CIPPTable -TableName cachemfa - Try { - $GraphRequest = Get-CIPPMFAState -TenantFilter $domainName -ErrorAction Stop - } - catch { - $GraphRequest = $null - } - if (!$GraphRequest) { - $GraphRequest = @{ - Tenant = [string]$tenantName - UPN = [string]$domainName - AccountEnabled = 'none' - PerUser = [string]'Could not connect to tenant' - MFARegistration = 'none' - CoveredByCA = [string]'Could not connect to tenant' - CoveredBySD = 'none' - RowKey = [string]"$domainName" - PartitionKey = 'users' - } - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } -} -catch { - $Table = Get-CIPPTable -TableName cachemfa - $GraphRequest = @{ - Tenant = [string]$tenantName - UPN = [string]$domainName - AccountEnabled = 'none' - PerUser = [string]'Could not connect to tenant' - MFARegistration = 'none' - CoveredByCA = [string]'Could not connect to tenant' - CoveredBySD = 'none' - RowKey = [string]"$domainName" - PartitionKey = 'users' - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null -} -finally { - Update-CippQueueEntry -RowKey $QueueItem -Status "Completed" -} diff --git a/ListMailboxRulesAllTenants/function.json b/ListMailboxRulesAllTenants/function.json deleted file mode 100644 index 776ec6ebfdcb..000000000000 --- a/ListMailboxRulesAllTenants/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "mbxrulequeue" - } - ] -} diff --git a/MailProviders/Mesh.json b/MailProviders/Mesh.json deleted file mode 100644 index 3109cc8c4876..000000000000 --- a/MailProviders/Mesh.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Name": "Mesh Email Security", - "_MxComment": "https://docs.emailsecurity.app/help-center/connection-details", - "MxMatch": "emailsecurity.app", - "_SpfComment": "https://docs.emailsecurity.app/help-center/connection-details", - "SpfInclude": "spf1.emailsecurity.app", - "_DkimComment": "No configuration found", - "Selectors": [""] -} \ No newline at end of file diff --git a/Modules/AzBobbyTables/3.1.0/AzBobbyTables.PS.dll b/Modules/AzBobbyTables/3.1.0/AzBobbyTables.PS.dll deleted file mode 100644 index 28610eb68077..000000000000 Binary files a/Modules/AzBobbyTables/3.1.0/AzBobbyTables.PS.dll and /dev/null differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/AzBobbyTables.Core.dll b/Modules/AzBobbyTables/3.1.0/dependencies/AzBobbyTables.Core.dll deleted file mode 100644 index 0349c1394f6f..000000000000 Binary files a/Modules/AzBobbyTables/3.1.0/dependencies/AzBobbyTables.Core.dll and /dev/null differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/Azure.Core.dll b/Modules/AzBobbyTables/3.1.0/dependencies/Azure.Core.dll deleted file mode 100644 index 27fcd7b909f6..000000000000 Binary files a/Modules/AzBobbyTables/3.1.0/dependencies/Azure.Core.dll and /dev/null differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/Azure.Data.Tables.dll b/Modules/AzBobbyTables/3.1.0/dependencies/Azure.Data.Tables.dll deleted file mode 100644 index 8073b79d2b68..000000000000 Binary files a/Modules/AzBobbyTables/3.1.0/dependencies/Azure.Data.Tables.dll and /dev/null differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.Bcl.AsyncInterfaces.dll b/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.Bcl.AsyncInterfaces.dll deleted file mode 100644 index 39fd1311f266..000000000000 Binary files a/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.Bcl.AsyncInterfaces.dll and /dev/null differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.VisualStudio.Threading.dll b/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.VisualStudio.Threading.dll deleted file mode 100644 index a94e9380c16f..000000000000 Binary files a/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.VisualStudio.Threading.dll and /dev/null differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.VisualStudio.Validation.dll b/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.VisualStudio.Validation.dll deleted file mode 100644 index 4966e018e7dd..000000000000 Binary files a/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.VisualStudio.Validation.dll and /dev/null differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Diagnostics.DiagnosticSource.dll b/Modules/AzBobbyTables/3.1.0/dependencies/System.Diagnostics.DiagnosticSource.dll deleted file mode 100644 index a2b54fb042de..000000000000 Binary files a/Modules/AzBobbyTables/3.1.0/dependencies/System.Diagnostics.DiagnosticSource.dll and /dev/null differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Runtime.CompilerServices.Unsafe.dll b/Modules/AzBobbyTables/3.1.0/dependencies/System.Runtime.CompilerServices.Unsafe.dll deleted file mode 100644 index 02d9849738f2..000000000000 Binary files a/Modules/AzBobbyTables/3.1.0/dependencies/System.Runtime.CompilerServices.Unsafe.dll and /dev/null differ diff --git a/Modules/AzBobbyTables/3.1.3/AzBobbyTables.PS.dll b/Modules/AzBobbyTables/3.1.3/AzBobbyTables.PS.dll new file mode 100644 index 000000000000..72910599b85e Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/AzBobbyTables.PS.dll differ diff --git a/Modules/AzBobbyTables/3.1.3/AzBobbyTables.PS.pdb b/Modules/AzBobbyTables/3.1.3/AzBobbyTables.PS.pdb new file mode 100644 index 000000000000..38ccee09c6c2 Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/AzBobbyTables.PS.pdb differ diff --git a/Modules/AzBobbyTables/3.1.0/AzBobbyTables.psd1 b/Modules/AzBobbyTables/3.1.3/AzBobbyTables.psd1 similarity index 93% rename from Modules/AzBobbyTables/3.1.0/AzBobbyTables.psd1 rename to Modules/AzBobbyTables/3.1.3/AzBobbyTables.psd1 index bd0cb30ca5a8..5bd7c13ba243 100644 --- a/Modules/AzBobbyTables/3.1.0/AzBobbyTables.psd1 +++ b/Modules/AzBobbyTables/3.1.3/AzBobbyTables.psd1 @@ -4,7 +4,7 @@ RootModule = 'AzBobbyTables.PS.dll' # Version number of this module. -ModuleVersion = '3.1.0' +ModuleVersion = '3.1.3' # Supported PSEditions CompatiblePSEditions = @('Core') @@ -108,7 +108,14 @@ PrivateData = @{ # IconUri = '' # ReleaseNotes of this module - # ReleaseNotes = '' + ReleaseNotes = '## [3.1.3] - 2024-01-20 + +### Added + +- Added Sampler ([#48](https://github.com/PalmEmanuel/AzBobbyTables/issues/48)). +- Added support for user-assigned managed identities ([#54](https://github.com/PalmEmanuel/AzBobbyTables/issues/54)). + +' # Prerelease string of this module # Prerelease = '' @@ -130,4 +137,3 @@ PrivateData = @{ # DefaultCommandPrefix = '' } - diff --git a/Modules/AzBobbyTables/3.1.3/CHANGELOG.md b/Modules/AzBobbyTables/3.1.3/CHANGELOG.md new file mode 100644 index 000000000000..c7880a5f68cf --- /dev/null +++ b/Modules/AzBobbyTables/3.1.3/CHANGELOG.md @@ -0,0 +1,28 @@ +# Changelog for the module + +The format is based on and uses the types of changes according to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Added Sampler ([#48](https://github.com/PalmEmanuel/AzBobbyTables/issues/48)). +- Added support for user-assigned managed identities ([#54](https://github.com/PalmEmanuel/AzBobbyTables/issues/54)). + +## [3.1.2] - 2024-01-05 + +### Added + +- Help documentation for a DateTime problem caused by the SDK (#43). + +### Fixed + +### Changed + +### Removed + +## 3.1.1 - 2023-05-03 + +[Unreleased]: https://github.com/PalmEmanuel/AzBobbyTables/compare/v3.1.2...HEAD + +[3.1.2]: https://github.com/PalmEmanuel/AzBobbyTables/compare/d854153aca6c5cce35a123deb86653a0d3289b07...v3.1.2 diff --git a/Modules/AzBobbyTables/3.1.3/LICENSE b/Modules/AzBobbyTables/3.1.3/LICENSE new file mode 100644 index 000000000000..d4e4667fe6da --- /dev/null +++ b/Modules/AzBobbyTables/3.1.3/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Emanuel Palm + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Modules/AzBobbyTables/3.1.0/PSGetModuleInfo.xml b/Modules/AzBobbyTables/3.1.3/PSGetModuleInfo.xml similarity index 67% rename from Modules/AzBobbyTables/3.1.0/PSGetModuleInfo.xml rename to Modules/AzBobbyTables/3.1.3/PSGetModuleInfo.xml index a2c736a1e170..775321568df1 100644 --- a/Modules/AzBobbyTables/3.1.0/PSGetModuleInfo.xml +++ b/Modules/AzBobbyTables/3.1.3/PSGetModuleInfo.xml @@ -7,13 +7,13 @@ AzBobbyTables - 3.1.0 + 3.1.3 Module A module for handling Azure Table Storage operations by wrapping the Azure Data Tables SDK. Emanuel Palm PalmEmanuel (c) Emanuel Palm. All rights reserved. -
2023-03-24T21:51:21-04:00
+
2024-01-20T16:49:22-05:00
https://github.com/PalmEmanuel/AzBobbyTables/blob/main/LICENSE @@ -43,8 +43,23 @@ - Command + RoleCapability + + + + + + Function + + + + DscResource + + + + Command + Add-AzDataTableEntity @@ -58,13 +73,6 @@ - - Function - - - - - Cmdlet @@ -83,20 +91,12 @@ Workflow - - - - DscResource - - - - RoleCapability - + - + ## [3.1.3] - 2024-01-20_x000A__x000A_### Added_x000A__x000A_- Added Sampler ([#48](https://github.com/PalmEmanuel/AzBobbyTables/issues/48))._x000A_- Added support for user-assigned managed identities ([#54](https://github.com/PalmEmanuel/AzBobbyTables/issues/54)). @@ -113,28 +113,29 @@ (c) Emanuel Palm. All rights reserved. A module for handling Azure Table Storage operations by wrapping the Azure Data Tables SDK. False + ## [3.1.3] - 2024-01-20_x000A__x000A_### Added_x000A__x000A_- Added Sampler ([#48](https://github.com/PalmEmanuel/AzBobbyTables/issues/48))._x000A_- Added support for user-assigned managed identities ([#54](https://github.com/PalmEmanuel/AzBobbyTables/issues/54)). True True - 2 - 2005 - 1261744 - 3/24/2023 9:51:21 PM -04:00 - 3/24/2023 9:51:21 PM -04:00 - 3/25/2023 4:22:25 PM -04:00 + 3242 + 12058 + 1356423 + 1/20/2024 4:49:22 PM -05:00 + 1/20/2024 4:49:22 PM -05:00 + 3/18/2024 2:41:34 PM -04:00 azure storage table cosmos cosmosdb data PSModule PSEdition_Core PSCmdlet_Add-AzDataTableEntity PSCommand_Add-AzDataTableEntity PSCmdlet_Clear-AzDataTable PSCommand_Clear-AzDataTable PSCmdlet_Get-AzDataTableEntity PSCommand_Get-AzDataTableEntity PSCmdlet_Remove-AzDataTableEntity PSCommand_Remove-AzDataTableEntity PSCmdlet_Update-AzDataTableEntity PSCommand_Update-AzDataTableEntity PSCmdlet_New-AzDataTableContext PSCommand_New-AzDataTableContext PSCmdlet_Remove-AzDataTable PSCommand_Remove-AzDataTable PSCmdlet_New-AzDataTable PSCommand_New-AzDataTable PSIncludes_Cmdlet False - 2023-03-25T16:22:25Z - 3.1.0 + 2024-03-18T14:41:34Z + 3.1.3 Emanuel Palm false Module - AzBobbyTables.nuspec|dependencies\System.Numerics.Vectors.dll|dependencies\System.Security.Principal.Windows.dll|dependencies\System.Threading.Tasks.Extensions.dll|dependencies\AzBobbyTables.Core.dll|dependencies\Microsoft.Bcl.AsyncInterfaces.dll|dependencies\Microsoft.Win32.Registry.dll|dependencies\System.Linq.Async.dll|dependencies\System.Runtime.CompilerServices.Unsafe.dll|dependencies\System.Text.Encodings.Web.dll|en-US\AzBobbyTables.PS.dll-Help.xml|AzBobbyTables.PS.dll|dependencies\Azure.Core.dll|dependencies\Microsoft.VisualStudio.Threading.dll|dependencies\System.Buffers.dll|dependencies\System.Memory.Data.dll|dependencies\System.Security.AccessControl.dll|dependencies\System.Text.Json.dll|AzBobbyTables.psd1|dependencies\Azure.Data.Tables.dll|dependencies\Microsoft.VisualStudio.Validation.dll|dependencies\System.Diagnostics.DiagnosticSource.dll|dependencies\System.Memory.dll + AzBobbyTables.nuspec|CHANGELOG.md|dependencies\AzBobbyTables.Core.pdb|dependencies\Microsoft.VisualStudio.Validation.dll|dependencies\System.Memory.Data.dll|dependencies\System.Runtime.CompilerServices.Unsafe.dll|dependencies\System.Numerics.Vectors.dll|dependencies\System.Text.Json.dll|LICENSE|dependencies\AzBobbyTables.Core.dll|dependencies\System.Threading.Tasks.Extensions.dll|dependencies\Microsoft.VisualStudio.Threading.dll|AzBobbyTables.PS.pdb|dependencies\System.Security.AccessControl.dll|dependencies\Microsoft.Win32.Registry.dll|dependencies\System.Text.Encodings.Web.dll|AzBobbyTables.psd1|dependencies\System.Buffers.dll|dependencies\Azure.Data.Tables.dll|dependencies\System.Memory.dll|AzBobbyTables.PS.dll|dependencies\System.Diagnostics.DiagnosticSource.dll|dependencies\Microsoft.Bcl.AsyncInterfaces.dll|dependencies\System.Security.Principal.Windows.dll|en-US\AzBobbyTables.PS.dll-Help.xml|dependencies\System.Linq.Async.dll|dependencies\Azure.Core.dll eead4f42-5080-4f83-8901-340c529a5a11 7.0 pipe.how
- C:\Users\jduprey.CNS\Documents\GitHub\CIPP-API\Modules\AzBobbyTables\3.1.0 + C:\GitHub\CIPP Workspace\CIPP-API\Modules\AzBobbyTables\3.1.3 diff --git a/Modules/AzBobbyTables/3.1.3/dependencies/AzBobbyTables.Core.dll b/Modules/AzBobbyTables/3.1.3/dependencies/AzBobbyTables.Core.dll new file mode 100644 index 000000000000..1a9a170b6f32 Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/dependencies/AzBobbyTables.Core.dll differ diff --git a/Modules/AzBobbyTables/3.1.3/dependencies/AzBobbyTables.Core.pdb b/Modules/AzBobbyTables/3.1.3/dependencies/AzBobbyTables.Core.pdb new file mode 100644 index 000000000000..252b9c04eaa6 Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/dependencies/AzBobbyTables.Core.pdb differ diff --git a/Modules/AzBobbyTables/3.1.3/dependencies/Azure.Core.dll b/Modules/AzBobbyTables/3.1.3/dependencies/Azure.Core.dll new file mode 100644 index 000000000000..f7369932f113 Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/dependencies/Azure.Core.dll differ diff --git a/Modules/AzBobbyTables/3.1.3/dependencies/Azure.Data.Tables.dll b/Modules/AzBobbyTables/3.1.3/dependencies/Azure.Data.Tables.dll new file mode 100644 index 000000000000..33b1aaf7903e Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/dependencies/Azure.Data.Tables.dll differ diff --git a/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.Bcl.AsyncInterfaces.dll b/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.Bcl.AsyncInterfaces.dll new file mode 100644 index 000000000000..cd1ca92a2beb Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.Bcl.AsyncInterfaces.dll differ diff --git a/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.VisualStudio.Threading.dll b/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.VisualStudio.Threading.dll new file mode 100644 index 000000000000..f3b23c328c0f Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.VisualStudio.Threading.dll differ diff --git a/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.VisualStudio.Validation.dll b/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.VisualStudio.Validation.dll new file mode 100644 index 000000000000..8c4956f420e1 Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.VisualStudio.Validation.dll differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.Win32.Registry.dll b/Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.Win32.Registry.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/Microsoft.Win32.Registry.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/Microsoft.Win32.Registry.dll diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Buffers.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Buffers.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Buffers.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Buffers.dll diff --git a/Modules/AzBobbyTables/3.1.3/dependencies/System.Diagnostics.DiagnosticSource.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Diagnostics.DiagnosticSource.dll new file mode 100644 index 000000000000..aacf2c145fa0 Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/dependencies/System.Diagnostics.DiagnosticSource.dll differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Linq.Async.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Linq.Async.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Linq.Async.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Linq.Async.dll diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Memory.Data.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Memory.Data.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Memory.Data.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Memory.Data.dll diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Memory.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Memory.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Memory.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Memory.dll diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Numerics.Vectors.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Numerics.Vectors.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Numerics.Vectors.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Numerics.Vectors.dll diff --git a/Modules/AzBobbyTables/3.1.3/dependencies/System.Runtime.CompilerServices.Unsafe.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Runtime.CompilerServices.Unsafe.dll new file mode 100644 index 000000000000..491a80a97880 Binary files /dev/null and b/Modules/AzBobbyTables/3.1.3/dependencies/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Security.AccessControl.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Security.AccessControl.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Security.AccessControl.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Security.AccessControl.dll diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Security.Principal.Windows.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Security.Principal.Windows.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Security.Principal.Windows.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Security.Principal.Windows.dll diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Text.Encodings.Web.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Text.Encodings.Web.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Text.Encodings.Web.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Text.Encodings.Web.dll diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Text.Json.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Text.Json.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Text.Json.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Text.Json.dll diff --git a/Modules/AzBobbyTables/3.1.0/dependencies/System.Threading.Tasks.Extensions.dll b/Modules/AzBobbyTables/3.1.3/dependencies/System.Threading.Tasks.Extensions.dll similarity index 100% rename from Modules/AzBobbyTables/3.1.0/dependencies/System.Threading.Tasks.Extensions.dll rename to Modules/AzBobbyTables/3.1.3/dependencies/System.Threading.Tasks.Extensions.dll diff --git a/Modules/AzBobbyTables/3.1.0/en-US/AzBobbyTables.PS.dll-Help.xml b/Modules/AzBobbyTables/3.1.3/en-US/AzBobbyTables.PS.dll-Help.xml similarity index 95% rename from Modules/AzBobbyTables/3.1.0/en-US/AzBobbyTables.PS.dll-Help.xml rename to Modules/AzBobbyTables/3.1.3/en-US/AzBobbyTables.PS.dll-Help.xml index ab60d025a0c0..ca1b9df08912 100644 --- a/Modules/AzBobbyTables/3.1.0/en-US/AzBobbyTables.PS.dll-Help.xml +++ b/Modules/AzBobbyTables/3.1.3/en-US/AzBobbyTables.PS.dll-Help.xml @@ -135,7 +135,9 @@ - + Regarding Dates, DateTime, and DateTimeOffset: + The underlying Azure.Data.Tables SDK expects to work with DateTime fields in UTC format for conversion to DateTimeOffset objects. When submitting a DateTimeOffset object to the SDK, it will be converted to UTC timezone rather than preserving the existing timezone/offset info. Similarly, if a `DateTime` object is submitted in the entity with its Kind set to "local" or "unspecified", the SDK will return an error and state that `Azure SDK requires it to be UTC`. While there isn't any change needed to get `DateTimeOffset` objects to work with AzBobbyTables, the workaround for `DateTime` objects is to set the property to a new `DateTime` object with its `Kind` property set to `Utc`. e.g. `$obj.Time = $obj.Time.ToUniversalFormat()`. Related issue (https://github.com/Azure/azure-sdk-for-net/issues/30644). + It is possible via the Azure Storage Explorer to set DateTime fields with offsets other than UTC/+00:00. Note that searches with queries set to type `DateTime` do properly calculate to a specific moment, and find equivilent moments. e.g. a `-Filter` set to `Time eq datetime'2023-12-26T18:05:40.5345634+00:00'` will match an entry where the Time property is set to `2023-12-26T17:05:40.5345634-01:00`. @@ -610,10 +612,10 @@ PS C:\> New-AzDataTable -Context $Context New-AzDataTableContext - - ConnectionString + + ClientId - The connection string to the storage account. + Specifies the client id when using a user-assigned managed identity. String @@ -623,9 +625,20 @@ PS C:\> New-AzDataTable -Context $Context None - TableName + ManagedIdentity - The name of the table. + Specifies that the command is run by a managed identity (such as in an Azure Function), and authorization will be handled using that identity. + + + SwitchParameter + + + False + + + StorageAccountName + + The name of the storage account. String @@ -634,24 +647,25 @@ PS C:\> New-AzDataTable -Context $Context None - - - New-AzDataTableContext - ManagedIdentity + TableName - Specifies that the command is run by a managed identity (such as in an Azure Function), and authorization will be handled using that identity. + The name of the table. + String - SwitchParameter + String - False + None + + + New-AzDataTableContext - StorageAccountName + ConnectionString - The name of the storage account. + The connection string to the storage account. String @@ -780,6 +794,18 @@ PS C:\> New-AzDataTable -Context $Context + + ClientId + + Specifies the client id when using a user-assigned managed identity. + + String + + String + + + None + ConnectionString diff --git a/Modules/CIPPCore/Public/Add-CIPPApplicationPermission.ps1 b/Modules/CIPPCore/Public/Add-CIPPApplicationPermission.ps1 index 52e790757b9b..f0f4c6badf6d 100644 --- a/Modules/CIPPCore/Public/Add-CIPPApplicationPermission.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPApplicationPermission.ps1 @@ -41,7 +41,7 @@ function Add-CIPPApplicationPermission { foreach ($Grant in $Grants) { try { $SettingsRequest = New-GraphPOSTRequest -body ($Grant | ConvertTo-Json) -uri "https://graph.microsoft.com/beta/servicePrincipals/$($ourSVCPrincipal.id)/appRoleAssignedTo" -tenantid $Tenantfilter -type POST - $counter ++ + $counter++ } catch { $Results.add("Failed to grant $($Grant.appRoleId) to $($Grant.resourceId): $($_.Exception.Message)") | Out-Null } diff --git a/Modules/CIPPCore/Public/Add-CIPPAzDataTableEntity.ps1 b/Modules/CIPPCore/Public/Add-CIPPAzDataTableEntity.ps1 index 6f03e28b64e2..befa8155df6c 100644 --- a/Modules/CIPPCore/Public/Add-CIPPAzDataTableEntity.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPAzDataTableEntity.ps1 @@ -9,7 +9,7 @@ function Add-CIPPAzDataTableEntity { foreach ($SingleEnt in $Entity) { try { - Add-AzDataTableEntity -context $Context -force:$Force -CreateTableIfNotExists:$CreateTableIfNotExists -Entity $SingleEnt + Add-AzDataTableEntity -context $Context -force:$Force -CreateTableIfNotExists:$CreateTableIfNotExists -Entity $SingleEnt -ErrorAction Stop } catch [System.Exception] { if ($_.Exception.ErrorCode -eq 'PropertyValueTooLarge' -or $_.Exception.ErrorCode -eq 'EntityTooLarge') { try { @@ -52,6 +52,8 @@ function Add-CIPPAzDataTableEntity { throw "Error processing entity: $($_.Exception.Message)." } } else { + Write-Host "THE ERROR IS $($_.Exception.ErrorCode)" + throw $_ } } diff --git a/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1 b/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1 index 2ff3c406102b..b29972bcce3a 100644 --- a/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1 @@ -1,29 +1,29 @@ function Add-CIPPGroupMember( [string]$ExecutingUser, - [string]$GroupType, + [string]$GroupType, [string]$GroupId, - [string]$Member, + [string]$Member, [string]$TenantFilter, [string]$APIName = 'Add Group Member' ) { try { if ($member -like '*#EXT#*') { $member = [System.Web.HttpUtility]::UrlEncode($member) } - $MemberIDs = 'https://graph.microsoft.com/v1.0/directoryObjects/' + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($member)" -tenantid $TenantFilter).id + $MemberIDs = 'https://graph.microsoft.com/v1.0/directoryObjects/' + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($member)" -tenantid $TenantFilter).id $addmemberbody = "{ `"members@odata.bind`": $(ConvertTo-Json @($MemberIDs)) }" if ($GroupType -eq 'Distribution list' -or $GroupType -eq 'Mail-Enabled Security') { $Params = @{ Identity = $GroupId; Member = $member; BypassSecurityGroupManagerCheck = $true } - New-ExoRequest -tenantid $TenantFilter -cmdlet 'Add-DistributionGroupMember' -cmdParams $params -UseSystemMailbox $true + New-ExoRequest -tenantid $TenantFilter -cmdlet 'Add-DistributionGroupMember' -cmdParams $params -UseSystemMailbox $true } else { New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($GroupId)" -tenantid $TenantFilter -type patch -body $addmemberbody -Verbose } - $Message = "Successfully added user $($Member) to $GroupId." + $Message = "Successfully added user $($Member) to $($GroupId)." Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message $Message -Sev 'Info' return $message return } catch { - $message = "Failed to add user $($Member) to $($GroupId): $($_.Exception.Message)" - Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message $message -Sev 'error' - return $message + $message = "Failed to add user $($Member) to $($GroupId)" + Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message $message -Sev 'error' -LogData (Get-CippException -Exception $_) + return $message } } diff --git a/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 b/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 index 04c5d358d599..8cd5b159d458 100644 --- a/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 @@ -18,8 +18,7 @@ function Add-CIPPScheduledTask { $ht[$p.Key] = $p.Value } $Parameters[$Key] = [PSCustomObject]$ht - } - else { + } else { $Parameters[$Key] = $Param } } @@ -30,10 +29,15 @@ function Add-CIPPScheduledTask { } $AdditionalProperties = ([PSCustomObject]$AdditionalProperties | ConvertTo-Json -Compress) if ($Parameters -eq 'null') { $Parameters = '' } + if (!$Task.RowKey) { + $RowKey = (New-Guid).Guid + } else { + $RowKey = $Task.RowKey + } $entity = @{ PartitionKey = [string]'ScheduledTask' TaskState = [string]'Planned' - RowKey = [string]"$(New-Guid)" + RowKey = [string]$RowKey Tenant = [string]$task.TenantFilter Name = [string]$task.Name Command = [string]$task.Command.value @@ -46,10 +50,9 @@ function Add-CIPPScheduledTask { Results = 'Planned' } try { - Add-CIPPAzDataTableEntity @Table -Entity $entity - } - catch { + Add-CIPPAzDataTableEntity @Table -Entity $entity -Force + } catch { return "Could not add task: $($_.Exception.Message)" } - return "Successfully added task" + return 'Successfully added task' } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/CippQueue/Invoke-ListCippQueue.ps1 b/Modules/CIPPCore/Public/CippQueue/Invoke-ListCippQueue.ps1 new file mode 100644 index 000000000000..e5f7a93a5e2a --- /dev/null +++ b/Modules/CIPPCore/Public/CippQueue/Invoke-ListCippQueue.ps1 @@ -0,0 +1,23 @@ +function Invoke-ListCippQueue { + # Input bindings are passed in via param block. + param($Request = $null, $TriggerMetadata) + + if ($Request) { + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + } + + $CippQueue = Get-CippTable -TableName 'CippQueue' + $CippQueueData = Get-CIPPAzDataTableEntity @CippQueue | Where-Object { ($_.Timestamp.DateTime) -ge (Get-Date).ToUniversalTime().AddHours(-1) } | Sort-Object -Property Timestamp -Descending + if ($request) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($CippQueueData) + }) + } else { + return $CippQueueData + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/CippQueue/Invoke-RemoveCippQueue.ps1 b/Modules/CIPPCore/Public/CippQueue/Invoke-RemoveCippQueue.ps1 new file mode 100644 index 000000000000..92212db7f6f8 --- /dev/null +++ b/Modules/CIPPCore/Public/CippQueue/Invoke-RemoveCippQueue.ps1 @@ -0,0 +1,18 @@ +function Invoke-RemoveCippQueue { + # Input bindings are passed in via param block. + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + $CippQueue = Get-CippTable -TableName 'CippQueue' + Clear-AzDataTable @CippQueue + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = @('History cleared') } + }) +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/CippQueue/New-CippQueueEntry.ps1 b/Modules/CIPPCore/Public/CippQueue/New-CippQueueEntry.ps1 new file mode 100644 index 000000000000..a64351dcfdb2 --- /dev/null +++ b/Modules/CIPPCore/Public/CippQueue/New-CippQueueEntry.ps1 @@ -0,0 +1,23 @@ +function New-CippQueueEntry { + Param( + $Name, + $Link, + $Reference + ) + + $CippQueue = Get-CippTable -TableName CippQueue + + $QueueEntry = @{ + PartitionKey = 'CippQueue' + RowKey = (New-Guid).Guid.ToString() + Name = $Name + Link = $Link + Reference = $Reference + Status = 'Queued' + } + $CippQueue.Entity = $QueueEntry + + Add-CIPPAzDataTableEntity @CippQueue + + $QueueEntry +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/CippQueue/Update-CippQueueEntry.ps1 b/Modules/CIPPCore/Public/CippQueue/Update-CippQueueEntry.ps1 new file mode 100644 index 000000000000..2662d71bf750 --- /dev/null +++ b/Modules/CIPPCore/Public/CippQueue/Update-CippQueueEntry.ps1 @@ -0,0 +1,29 @@ +function Update-CippQueueEntry { + Param( + [Parameter(Mandatory = $true)] + $RowKey, + $Status, + $Name + ) + + $CippQueue = Get-CippTable -TableName CippQueue + + if ($RowKey) { + $QueueEntry = Get-CIPPAzDataTableEntity @CippQueue -Filter ("RowKey eq '{0}'" -f $RowKey) + + if ($QueueEntry) { + if ($Status) { + $QueueEntry.Status = $Status + } + if ($Name) { + $QueueEntry.Name = $Name + } + Update-AzDataTableEntity @CippQueue -Entity $QueueEntry + $QueueEntry + } else { + return $false + } + } else { + return $false + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-AddAlertSubscription_Queue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-AddAlertSubscription_Queue.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Push-AddAlertSubscription_Queue.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-AddAlertSubscription_Queue.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertAdminPassword.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertAdminPassword.ps1 new file mode 100644 index 000000000000..63f371b10aa6 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertAdminPassword.ps1 @@ -0,0 +1,18 @@ + +function Push-CIPPAlertAdminPassword { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [pscustomobject]$Item + ) + try { + New-GraphGETRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'&`$expand=principal" -tenantid $($Item.tenant) | Where-Object { ($_.principalOrganizationId -EQ $Item.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 $($Item.tenant) + if ($LastChanges.LastPasswordChangeDateTime -gt (Get-Date).AddDays(-1)) { + Write-AlertMessage -tenant $($Item.tenant) -message "Admin password has been changed for $($LastChanges.UserPrincipalName) in last 24 hours" + } + } + } catch { + Write-AlertMessage -tenant $($Item.tenant) -message "Could not get admin password changes for $($Item.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertApnCertExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertApnCertExpiry.ps1 new file mode 100644 index 000000000000..13a411f105e9 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertApnCertExpiry.ps1 @@ -0,0 +1,17 @@ +function Push-CIPPAlertApnCertExpiry { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $Item + ) + + try { + $Apn = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate' -tenantid $Item.tenant + if ($Apn.expirationDateTime -lt (Get-Date).AddDays(30) -and $Apn.expirationDateTime -gt (Get-Date).AddDays(-7)) { + Write-AlertMessage -tenant $($Item.tenant) -message ('Intune: Apple Push Notification certificate for {0} is expiring on {1}' -f $Apn.appleIdentifier, $Apn.expirationDateTime) + } + } catch { + #no error because if a tenant does not have an APN, it'll error anyway. + #Write-AlertMessage -tenant $($Item.Tenant) -message "Failed to check APN certificate expiry for $($Item.Tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertAppSecretExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertAppSecretExpiry.ps1 new file mode 100644 index 000000000000..7b1b8b12fd4f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertAppSecretExpiry.ps1 @@ -0,0 +1,27 @@ +function Push-CIPPAlertAppSecretExpiry { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $Item + ) + + try { + Write-Host "Checking app expire for $($Item.tenant)" + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications?`$select=appId,displayName,passwordCredentials" -tenantid $Item.tenant | ForEach-Object { + foreach ($App in $_) { + Write-Host "checking $($App.displayName)" + 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-Host ("Application '{0}' has secrets expiring on {1}" -f $App.displayName, $Credential.endDateTime) + Write-AlertMessage -tenant $($Item.Tenant) -message ("Application '{0}' has secrets expiring on {1}" -f $App.displayName, $Credential.endDateTime) + } + } + } + } + } + } catch { + #Write-AlertMessage -tenant $($Item.Tenant) -message "Failed to check App registration expiry for $($Item.Tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} + diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertDefenderMalware.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertDefenderMalware.ps1 new file mode 100644 index 000000000000..0d4d1c7b02ab --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertDefenderMalware.ps1 @@ -0,0 +1,15 @@ + +function Push-CIPPAlertDefenderMalware { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $Item + ) + try { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsDeviceMalwareStates?`$top=999&`$filter=tenantId eq '$($Item.tenantid)'" | Where-Object { $_.malwareThreatState -eq 'Active' } | ForEach-Object { + Write-AlertMessage -tenant $($Item.tenant) -message "$($_.managedDeviceName): Malware found and active. Severity: $($_.MalwareSeverity). Malware name: $($_.MalwareDisplayName)" + } + } catch { + Write-AlertMessage -tenant $($Item.tenant) -message "Could not get malware data for $($Item.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertDefenderStatus.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertDefenderStatus.ps1 new file mode 100644 index 000000000000..7b42affa4e00 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertDefenderStatus.ps1 @@ -0,0 +1,14 @@ +function Push-CIPPAlertDefenderStatus { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $Item + ) + try { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsProtectionStates?`$top=999&`$filter=tenantId eq '$($Item.tenantid)'" | Where-Object { $_.realTimeProtectionEnabled -eq $false -or $_.MalwareprotectionEnabled -eq $false } | ForEach-Object { + Write-AlertMessage -tenant $($Item.tenant) -message "$($_.managedDeviceName) - Real Time Protection: $($_.realTimeProtectionEnabled) & Malware Protection: $($_.MalwareprotectionEnabled)" + } + } catch { + Write-AlertMessage -tenant $($Item.tenant) -message "Could not get defender status for $($Item.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertDepTokenExpiry.ps1 similarity index 68% rename from Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertDepTokenExpiry.ps1 index 804750e60705..8d457fcb012a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertDepTokenExpiry.ps1 @@ -2,31 +2,30 @@ function Push-CIPPAlertDepTokenExpiry { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $LastRunTable = Get-CIPPTable -Table AlertLastRun try { - $Filter = "RowKey eq 'DepTokenExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "RowKey eq 'DepTokenExpiry' and PartitionKey eq '{0}'" -f $Item.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 + $DepTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $Item.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) + Write-AlertMessage -tenant $($Item.tenant) -message ('Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime) } } } catch {} $LastRun = @{ RowKey = 'DepTokenExpiry' - PartitionKey = $QueueItem.tenantid + PartitionKey = $Item.tenantid } Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } } catch { - # Error handling + Write-AlertMessage -tenant $($Item.tenant) -message "Failed to check Apple Device Enrollment Program token expiry for $($Item.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertExpiringLicenses.ps1 similarity index 58% rename from Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertExpiringLicenses.ps1 index 99a861bfb5d7..6e2a704b9ba3 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertExpiringLicenses.ps1 @@ -2,15 +2,14 @@ function Push-CIPPAlertExpiringLicenses { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { - Get-CIPPLicenseOverview -TenantFilter $QueueItem.tenant | ForEach-Object { + Get-CIPPLicenseOverview -TenantFilter $Item.tenant | ForEach-Object { $timeTorenew = [int64]$_.TimeUntilRenew if ($timeTorenew -lt 30 -and $_.TimeUntilRenew -gt 0) { Write-Host "$($_.License) will expire in $($_.TimeUntilRenew) days. The estimated term is $($_.EstTerm)" - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.License) will expire in $($_.TimeUntilRenew) days. The estimated term is $($_.EstTerm)" + Write-AlertMessage -tenant $($Item.tenant) -message "$($_.License) will expire in $($_.TimeUntilRenew) days. The estimated term is $($_.EstTerm)" } } } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertMFAAdmins.ps1 similarity index 62% rename from Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertMFAAdmins.ps1 index b0c8056e1f03..6b933fbf64a0 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertMFAAdmins.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertMFAAdmins { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { - $CAPolicies = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?$top=999' -tenantid $QueueItem.tenant -ErrorAction Stop) + $CAPolicies = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?$top=999' -tenantid $Item.tenant -ErrorAction Stop) foreach ($Policy in $CAPolicies) { if ($policy.grantControls.customAuthenticationFactors -eq 'RequireDuoMfa') { $DuoActive = $true } } if (!$DuoActive) { - $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$top=999&$filter=IsAdmin eq true' -tenantid $($QueueItem.tenant) | Where-Object -Property 'isMfaRegistered' -EQ $false - if ($users) { - Write-AlertMessage -tenant $QueueItem.tenant -message "The following admins do not have MFA registered: $($users.UserPrincipalName -join ', ')" + $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$top=999&$filter=IsAdmin eq true' -tenantid $($Item.tenant) | Where-Object -Property 'isMfaRegistered' -EQ $false + if ($users.UserPrincipalName) { + Write-AlertMessage -tenant $Item.tenant -message "The following admins do not have MFA registered: $($users.UserPrincipalName -join ', ')" } } 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 - } + Write-LogMessage -message 'Potentially using Duo for MFA, could not check MFA status for Admins with 100% accuracy' -API 'MFA Alerts - Informational' -tenant $Item.tenant -sev Info + } } catch { - Write-LogMessage -message "Failed to check MFA status for Admins: $($_.exception.message)" -API 'MFA Alerts - Informational' -tenant $QueueItem.tenant -sev Error + Write-LogMessage -message "Failed to check MFA status for Admins: $($_.exception.message)" -API 'MFA Alerts - Informational' -tenant $Item.tenant -sev Error } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertMFAAlertUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertMFAAlertUsers.ps1 new file mode 100644 index 000000000000..6af3ca798606 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertMFAAlertUsers.ps1 @@ -0,0 +1,18 @@ +function Push-CIPPAlertMFAAlertUsers { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $Item + ) + try { + + $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$top=999&filter=isMfaRegistered eq false and userType eq ''member''&$select=userPrincipalName,lastUpdatedDateTime,isMfaRegistered' -tenantid $($Item.tenant) + if ($users.UserPrincipalName) { + Write-AlertMessage -tenant $Item.tenant -message "The following $($users.Count) users do not have MFA registered: $($users.UserPrincipalName -join ', ')" + } + + } catch { + Write-LogMessage -message "Failed to check MFA status for all users: $($_.exception.message)" -API 'MFA Alerts - Informational' -tenant $Item.tenant -sev Info + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewAppApproval.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewAppApproval.ps1 new file mode 100644 index 000000000000..f5317b9769a5 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewAppApproval.ps1 @@ -0,0 +1,15 @@ + +function Push-CIPPAlertNewAppApproval { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [pscustomobject]$Item + ) + try { + $Approvals = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identityGovernance/appConsent/appConsentRequests' -tenantid $item.tenant | Where-Object -Property requestStatus -EQ 'inProgress' + if ($Approvals.count -gt 1) { + Write-AlertMessage -tenant $($Item.tenant) -message "There is are $($Approvals.count) App Approvals waiting." + } + } catch { + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewRole.ps1 similarity index 68% rename from Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewRole.ps1 index 504bb3ea3153..8ef2727ebb66 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewRole.ps1 @@ -2,14 +2,13 @@ function Push-CIPPAlertNewRole { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $Deltatable = Get-CIPPTable -Table DeltaCompare try { - $Filter = "PartitionKey eq 'AdminDelta' and RowKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "PartitionKey eq 'AdminDelta' and RowKey eq '{0}'" -f $Item.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 { + $NewDelta = (New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/directoryRoles?`$expand=members" -tenantid $Item.tenant) | Select-Object displayname, Members | ForEach-Object { @{ GroupName = $_.displayname Members = $_.Members.UserPrincipalName @@ -18,7 +17,7 @@ function Push-CIPPAlertNewRole { $NewDeltatoSave = $NewDelta | ConvertTo-Json -Depth 10 -Compress -ErrorAction SilentlyContinue | Out-String $DeltaEntity = @{ PartitionKey = 'AdminDelta' - RowKey = [string]$QueueItem.tenantid + RowKey = [string]$Item.tenantid delta = "$NewDeltatoSave" } Add-CIPPAzDataTableEntity @DeltaTable -Entity $DeltaEntity -Force @@ -27,11 +26,11 @@ function Push-CIPPAlertNewRole { 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" + Write-AlertMessage -tenant $($Item.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)" + Write-AlertMessage -tenant $($Item.tenant) -message "Could not get get role changes for $($Item.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNoCAConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNoCAConfig.ps1 new file mode 100644 index 000000000000..c9b8309963ca --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNoCAConfig.ps1 @@ -0,0 +1,20 @@ +function Push-CIPPAlertNoCAConfig { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $Item + ) + + try { + $CAAvailable = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $Item.Tenant -erroraction stop).serviceplans + if ('AAD_PREMIUM' -in $CAAvailable.servicePlanName) { + $CAPolicies = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies' -tenantid $Item.Tenant) + if (!$CAPolicies.id) { + Write-AlertMessage -tenant $($Item.tenant) -message 'Conditional Access is available, but no policies could be found.' + } + } + } catch { + Write-AlertMessage -tenant $($Item.tenant) -message "Conditional Access Config Alert: Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertOverusedLicenses.ps1 similarity index 64% rename from Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertOverusedLicenses.ps1 index af90000fa4d0..d314a064a88a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertOverusedLicenses.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertOverusedLicenses { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { $LicenseTable = Get-CIPPTable -TableName ExcludedLicenses $ExcludedSkuList = Get-CIPPAzDataTableEntity @LicenseTable - New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $QueueItem.tenant | ForEach-Object { + New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $Item.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)." + Write-AlertMessage -tenant $($Item.tenant) -message "$PrettyName has Overused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." } } } } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Overused Licenses Alert Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + Write-AlertMessage -tenant $($Item.tenant) -message "Overused Licenses Alert Error occurred: $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertQuotaUsed.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertQuotaUsed.ps1 new file mode 100644 index 000000000000..49ae4105071c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertQuotaUsed.ps1 @@ -0,0 +1,22 @@ +function Push-CIPPAlertQuotaUsed { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $Item + ) + + + try { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getMailboxUsageDetail(period='D7')?`$format=application/json" -tenantid $Item.tenant | ForEach-Object { + if ($_.StorageUsedInBytes -eq 0) { continue } + $PercentLeft = [math]::round($_.StorageUsedInBytes / $_.prohibitSendReceiveQuotaInBytes * 100) + if ($Item.value -eq $true) { + if ($Item.value) { $Value = $Item.value } else { $Value = 90 } + if ($PercentLeft -gt 90) { + Write-AlertMessage -tenant $($Item.tenant) -message "$($_.UserPrincipalName): Mailbox is more than $($value)% full. Mailbox is $PercentLeft% full" + } + } + } + } catch { + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertSecDefaultsUpsell.ps1 similarity index 73% rename from Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertSecDefaultsUpsell.ps1 index 1380b73b4233..b89347a828a8 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertSecDefaultsUpsell.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertSecDefaultsUpsell { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $LastRunTable = Get-CIPPTable -Table AlertLastRun try { - $Filter = "RowKey eq 'SecDefaultsUpsell' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "RowKey eq 'SecDefaultsUpsell' and PartitionKey eq '{0}'" -f $Item.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) + $SecDefaults = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $Item.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) + Write-AlertMessage -tenant $($Item.tenant) -message ('Security Defaults will be automatically enabled on {0}' -f $SecDefaults.securityDefaultsUpsell.dueDateTime) } } catch {} $LastRun = @{ RowKey = 'SecDefaultsUpsell' - PartitionKey = $QueueItem.tenantid + PartitionKey = $Item.tenantid } Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertSharepointQuota.ps1 similarity index 59% rename from Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertSharepointQuota.ps1 index 0f010b2bb59c..58dedd4888a8 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertSharepointQuota.ps1 @@ -3,20 +3,22 @@ function Push-CIPPAlertSharepointQuota { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) 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) + $tenantName = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $Item.Tenant | Where-Object { $_.isInitial -eq $true }).id.Split('.')[0] + $sharepointToken = (Get-GraphToken -scope "https://$($tenantName)-admin.sharepoint.com/.default" -tenantid $Item.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) { + if ($Item.value -Is [Boolean]) { $Value = 90 } else { $Value = $Item.value } $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) - if ($UsedStoragePercentage -gt 90) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%" + if ($UsedStoragePercentage -gt $Value) { + Write-AlertMessage -tenant $($Item.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%. Your alert threshold is $($Value)%" } } } catch { } -} + + +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertUnusedLicenses.ps1 similarity index 64% rename from Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertUnusedLicenses.ps1 index 2bd58f8b6178..74be1a6e4030 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertUnusedLicenses.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertUnusedLicenses { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { $LicenseTable = Get-CIPPTable -TableName ExcludedLicenses $ExcludedSkuList = Get-CIPPAzDataTableEntity @LicenseTable - New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $QueueItem.tenant | ForEach-Object { + New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $Item.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)." + Write-AlertMessage -tenant $($Item.tenant) -message "$PrettyName has unused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." } } } } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Unused Licenses Alert Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + Write-AlertMessage -tenant $($Item.tenant) -message "Unused Licenses Alert Error occurred: $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertVppTokenExpiry.ps1 similarity index 70% rename from Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertVppTokenExpiry.ps1 index d18dd28dd11d..d9a2e70d6531 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertVppTokenExpiry.ps1 @@ -2,31 +2,30 @@ function Push-CIPPAlertVppTokenExpiry { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $LastRunTable = Get-CIPPTable -Table AlertLastRun try { - $Filter = "RowKey eq 'VppTokenExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "RowKey eq 'VppTokenExpiry' and PartitionKey eq '{0}'" -f $Item.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 + $VppTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/vppTokens' -tenantid $Item.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' + Write-AlertMessage -tenant $($Item.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) + Write-AlertMessage -tenant $($Item.tenant) -message ('Apple Volume Purchase Program token expiring on {0}' -f $Vpp.expirationDateTime) } } } catch {} $LastRun = @{ RowKey = 'VppTokenExpiry' - PartitionKey = $QueueItem.tenantid + PartitionKey = $Item.tenantid } Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPStandard.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPStandard.ps1 new file mode 100644 index 000000000000..dd9849da8514 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPStandard.ps1 @@ -0,0 +1,16 @@ +function Push-CIPPStandard { + param ( + $Item + ) + + Write-Host "Received queue item for $($Item.Tenant) and standard $($Item.Standard)." + $Tenant = $Item.Tenant + $Standard = $Item.Standard + $FunctionName = 'Invoke-CIPPStandard{0}' -f $Standard + Write-Host "We'll be running $FunctionName" + try { + & $FunctionName -Tenant $Item.Tenant -Settings $Item.Settings -ErrorAction Stop + } catch { + throw $_.Exception.Message + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecAddMultiTenantApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAddMultiTenantApp.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Push-ExecAddMultiTenantApp.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAddMultiTenantApp.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecGDAPInviteQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecGDAPInviteQueue.ps1 new file mode 100644 index 000000000000..833b498eb34e --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecGDAPInviteQueue.ps1 @@ -0,0 +1,9 @@ +function Push-ExecGDAPInviteQueue { + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell queue trigger function processed work item: $($Item.customer.displayName)" + + Set-CIPPGDAPInviteGroups -Relationship $Item +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1 similarity index 72% rename from Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1 index 6c12ad723332..221068716478 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1 @@ -4,11 +4,11 @@ Function Push-ExecOnboardTenantQueue { Entrypoint #> [CmdletBinding()] - param($QueueItem, $TriggerMetadata) + param($Item) try { $DateFormat = '%Y-%m-%d %H:%M:%S' - $Id = $QueueItem.id - #Write-Host ($QueueItem.Roles | ConvertTo-Json) + $Id = $Item.id + #Write-Host ($Item.Roles | ConvertTo-Json) $Start = Get-Date $Logs = [System.Collections.Generic.List[object]]::new() $OnboardTable = Get-CIPPTable -TableName 'TenantOnboarding' @@ -117,7 +117,7 @@ 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 -and $QueueItem.AutoMapRoles -ne $true) { + if ($AccessAssignments.id -and $Item.AutoMapRoles -ne $true) { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Groups mapped' }) $OnboardingSteps.Step3.Status = 'succeeded' $OnboardingSteps.Step3.Message = 'Your GDAP relationship already has mapped security groups' @@ -136,8 +136,8 @@ Function Push-ExecOnboardTenantQueue { $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 ($Item.Roles -and $Item.AutoMapRoles -eq $true) { + foreach ($Role in $Item.Roles) { 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) @@ -161,16 +161,16 @@ Function Push-ExecOnboardTenantQueue { } } - if (!$AccessAssignments.id -and !$Invite -and $QueueItem.Roles) { + if (!$AccessAssignments.id -and !$Invite -and $Item.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) { + foreach ($Role in $Item.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) { + if (($MatchingRoles | Measure-Object).Count -gt 0 -and $Item.AutoMapRoles -eq $true) { $Invite = [PSCustomObject]@{ 'PartitionKey' = 'invite' 'RowKey' = $Id @@ -215,39 +215,40 @@ Function Push-ExecOnboardTenantQueue { $OnboardingSteps.Step3.Status = 'failed' $OnboardingSteps.Step3.Message = 'Failed to map security groups, no pending invite available' } + } - do { - $AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id/accessAssignments" - Start-Sleep -Seconds 15 - } while ($AccessAssignments.status -contains 'pending' -and (Get-Date) -lt $Start.AddMinutes(8)) + do { + $AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id/accessAssignments" + Start-Sleep -Seconds 15 + } while ($AccessAssignments.status -contains 'pending' -and (Get-Date) -lt $Start.AddMinutes(8)) - if ($AccessAssignments.status -notcontains 'pending') { - $OnboardingSteps.Step3.Message = 'Group check: Access assignments are mapped and active' - $OnboardingSteps.Step3.Status = 'succeeded' - if ($QueueItem.AddMissingGroups -eq $true) { - $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) { - 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)" }) - } + if ($AccessAssignments.status -notcontains 'pending') { + $OnboardingSteps.Step3.Message = 'Group check: Access assignments are mapped and active' + $OnboardingSteps.Step3.Status = 'succeeded' + if ($Item.AddMissingGroups -eq $true) { + $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 $Item.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)" }) } } - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'SAM user group check completed' }) } - } else { - $OnboardingSteps.Step3.Message = 'Group check: Access assignments are still pending, try again later' - $OnboardingSteps.Step3.Status = 'failed' - $TenantOnboarding.Status = 'failed' + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'SAM user group check completed' }) } + } else { + $OnboardingSteps.Step3.Message = 'Group check: Access assignments are still pending, try again later' + $OnboardingSteps.Step3.Status = 'failed' + $TenantOnboarding.Status = 'failed' + Write-LogMessage -API 'Onboarding' -message "Tenant onboarding failed at group mapping step for $($Relationship.customer.displayName)" -Sev 'Error' } $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) @@ -263,72 +264,81 @@ Function Push-ExecOnboardTenantQueue { $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Clearing tenant cache' }) - $y = 0 - do { - try { - Remove-CIPPCache -tenantsOnly $true - } catch {} + $IsExcluded = (Get-Tenants -SkipList | Where-Object { $_.customerId -eq $Relationship.customer.tenantId } | Measure-Object).Count -gt 0 + if ($IsExcluded) { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Tenant is excluded from CIPP, onboarding cannot continue.' }) + $TenantOnboarding.Status = 'failed' + $OnboardingSteps.Step4.Status = 'failed' + $OnboardingSteps.Step4.Message = 'Tenant excluded from CIPP, remove the exclusion and retry onboarding.' + } else { - $Tenant = Get-Tenants | Where-Object { $_.customerId -eq $Relationship.customer.tenantId } | Select-Object -First 1 - $y++ - Start-Sleep -Seconds 20 - } while (!$Tenant -and $y -le 4) + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Clearing tenant cache' }) + $y = 0 + do { + $Tenant = Get-Tenants -TriggerRefresh -IncludeAll | Where-Object { $_.customerId -eq $Relationship.customer.tenantId } | Select-Object -First 1 + $y++ + Start-Sleep -Seconds 20 + } while (!$Tenant -and $y -le 4) - if ($Tenant) { - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Tenant found in customer list' }) - try { - $CPVConsentParams = @{ - TenantFilter = $Tenant.defaultDomainName - } - $Consent = Set-CIPPCPVConsent @CPVConsentParams - if ($Consent -match 'Could not add our Service Principal to the client tenant') { - throw + if ($Tenant) { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Tenant found in customer list' }) + try { + $CPVConsentParams = @{ + TenantFilter = $Relationship.customer.tenantId + } + $Consent = Set-CIPPCPVConsent @CPVConsentParams + if ($Consent -match 'Could not add our Service Principal to the client tenant') { + throw + } + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Added initial CPV consent permissions' }) + } catch { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'CPV Consent Failed' }) + $TenantOnboarding.Status = 'failed' + $OnboardingSteps.Step4.Status = 'failed' + $OnboardingSteps.Step4.Message = 'CPV Consent failed, check the App Registration in your partner tenant for missing admin consent.' + $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + Write-LogMessage -API 'Onboarding' -message "Tenant onboarding failed at CPV step for $($Relationship.customer.displayName)" -Sev 'Error' + return } - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Added initial CPV consent permissions' }) - } catch { - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'CPV Consent Failed' }) - $TenantOnboarding.Status = 'failed' - $OnboardingSteps.Step4.Status = 'failed' - $OnboardingSteps.Step4.Message = 'CPV Consent failed, check the App Registration in your partner tenant for missing admin consent.' + $Refreshing = $true + $CPVSuccess = $false + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Refreshing CPV permissions' }) + $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 - return - } - $Refreshing = $true - $CPVSuccess = $false - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Refreshing CPV permissions' }) - $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 - 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 { - Start-Sleep -Seconds 30 - } - } while ($Refreshing -and (Get-Date) -lt $Start.AddMinutes(8)) + do { + try { + Add-CIPPApplicationPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Relationship.customer.tenantId + Add-CIPPDelegatedPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Relationship.customer.tenantId + $CPVSuccess = $true + $Refreshing = $false + } catch { + Start-Sleep -Seconds 30 + } + } while ($Refreshing -and (Get-Date) -lt $Start.AddMinutes(8)) - if ($CPVSuccess) { - $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'CPV permissions refreshed' }) - $OnboardingSteps.Step4.Status = 'succeeded' - $OnboardingSteps.Step4.Message = 'CPV permissions refreshed' + if ($CPVSuccess) { + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'CPV permissions refreshed' }) + $OnboardingSteps.Step4.Status = 'succeeded' + $OnboardingSteps.Step4.Message = 'CPV permissions refreshed' + if ($Tenant.defaultDomainName -match 'Domain Error') { + $Tenant = Get-Tenants -TriggerRefresh -IncludeAll | Where-Object { $_.customerId -eq $Relationship.customer.tenantId } | Select-Object -First 1 + } + } else { + $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([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'CPV permissions failed to refresh' }) + $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Tenant not found' }) $TenantOnboarding.Status = 'failed' $OnboardingSteps.Step4.Status = 'failed' - $OnboardingSteps.Step4.Message = 'CPV permissions failed to refresh, try again later' + $OnboardingSteps.Step4.Message = 'Tenant not found in customer list, try again later' } - } else { - $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) @@ -349,6 +359,7 @@ Function Push-ExecOnboardTenantQueue { } catch { $UserCount = 0 $ApiError = $_.Exception.Message + $ApiException = $_ } if ($UserCount -gt 0) { @@ -360,6 +371,7 @@ Function Push-ExecOnboardTenantQueue { $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + Write-LogMessage -API 'Onboarding' -message "Tenant onboarding succeeded for $($Relationship.customer.displayName)" -Sev 'Info' } else { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'API Test failed: {0}' -f $ApiError }) $OnboardingSteps.Step5.Status = 'failed' @@ -368,6 +380,7 @@ Function Push-ExecOnboardTenantQueue { $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + Write-LogMessage -API 'Onboarding' -message "Tenant onboarding API test failed for $($Relationship.customer.displayName)" -Sev 'Error' -LogData (Get-CippException -Exception $ApiException) } } } catch { @@ -377,5 +390,6 @@ Function Push-ExecOnboardTenantQueue { $TenantOnboarding.OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) $TenantOnboarding.Logs = [string](ConvertTo-Json -InputObject @($Logs) -Compress) Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + Write-LogMessage -API 'Onboarding' -message "Tenant onboarding failed for $Id" -Sev 'Error' -LogData (Get-CippException -Exception $_) } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1 new file mode 100644 index 000000000000..8e53dcbd8bd4 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1 @@ -0,0 +1,85 @@ +function Push-ExecScheduledCommand { + # Input bindings are passed in via param block. + param($Item) + + $Table = Get-CippTable -tablename 'ScheduledTasks' + $task = $Item.TaskInfo + $commandParameters = $Item.Parameters + + $tenant = $Item.Parameters['TenantFilter'] + Write-Host 'started task' + try { + try { + $results = & $Item.command @commandParameters + } catch { + $results = "Task Failed: $($_.Exception.Message)" + + } + + Write-Host 'ran the command' + if ($results -is [String]) { + $results = @{ Results = $results } + } + if ($results -is [array] -and $results[0] -is [string]) { + $results = $results | Where-Object { $_ -is [string] } + $results = $results | ForEach-Object { @{ Results = $_ } } + } + + $results = $results | Select-Object * -ExcludeProperty RowKey, PartitionKey + + $StoredResults = $results | ConvertTo-Json -Compress -Depth 20 | Out-String + 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 { + $errorMessage = $_.Exception.Message + if ($task.Recurrence -gt 0) { $State = 'Failed - Planned' } else { $State = 'Failed' } + Update-AzDataTableEntity @Table -Entity @{ + PartitionKey = $task.PartitionKey + RowKey = $task.RowKey + Results = "$errorMessage" + TaskState = $State + } + Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error + } + + + $TableDesign = '' + $HTML = ($results | Select-Object * -ExcludeProperty RowKey, PartitionKey | ConvertTo-Html -Fragment) -replace '
', "$TableDesign
" | Out-String + $title = "Scheduled Task $($task.Name) - $($task.ExpectedRunTime)" + Write-Host $title + switch -wildcard ($task.PostExecution) { + '*psa*' { Send-CIPPAlert -Type 'psa' -Title $title -HTMLContent $HTML } + '*email*' { Send-CIPPAlert -Type 'email' -Title $title -HTMLContent $HTML } + '*webhook*' { + $Webhook = [PSCustomObject]@{ + 'Tenant' = $tenant + 'TaskInfo' = $Item.TaskInfo + 'Results' = $Results + } + Send-CIPPAlert -Type 'webhook' -Title $title -JSONContent $($Webhook | ConvertTo-Json -Depth 20) + } + } + + Write-Host 'ran the command' + + if ($task.Recurrence -le '0' -or $task.Recurrence -eq $null) { + Update-AzDataTableEntity @Table -Entity @{ + PartitionKey = $task.PartitionKey + RowKey = $task.RowKey + Results = "$StoredResults" + TaskState = 'Completed' + } + } else { + $nextRun = (Get-Date).AddDays($task.Recurrence) + $nextRunUnixTime = [int64]($nextRun - (Get-Date '1/1/1970')).TotalSeconds + Update-AzDataTableEntity @Table -Entity @{ + PartitionKey = $task.PartitionKey + RowKey = $task.RowKey + Results = "$StoredResults" + TaskState = 'Planned' + ScheduledTime = "$nextRunUnixTime" + } + } + Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Successfully executed task: $($task.name)" -sev Info +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-GetPendingWebhooks.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-GetPendingWebhooks.ps1 new file mode 100644 index 000000000000..11e518c782bd --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-GetPendingWebhooks.ps1 @@ -0,0 +1,8 @@ +function Push-GetPendingWebhooks { + $Table = Get-CIPPTable -TableName WebhookIncoming + $Webhooks = Get-CIPPAzDataTableEntity @Table + $WebhookCount = ($Webhooks | Measure-Object).Count + $Message = 'Processing {0} webhooks' -f $WebhookCount + Write-LogMessage -API 'Webhooks' -message $Message -sev Info + return $Webhooks +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListGraphRequestQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListGraphRequestQueue.ps1 new file mode 100644 index 000000000000..1705df9a7f70 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListGraphRequestQueue.ps1 @@ -0,0 +1,67 @@ +function Push-ListGraphRequestQueue { + <# + .FUNCTIONALITY + Entrypoint + #> + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell queue trigger function processed work item: $($Item.Endpoint) - $($Item.TenantFilter)" + + $TenantQueueName = '{0} - {1}' -f $Item.QueueName, $Item.TenantFilter + Update-CippQueueEntry -RowKey $Item.QueueId -Status 'Processing' -Name $TenantQueueName + + $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) + foreach ($Param in ($Item.Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { + $ParamCollection.Add($Param.Key, $Param.Value) + } + + $PartitionKey = $Item.PartitionKey + + $TableName = ('cache{0}' -f ($Item.Endpoint -replace '[^A-Za-z0-9]'))[0..62] -join '' + Write-Host "Queue Table: $TableName" + $Table = Get-CIPPTable -TableName $TableName + + $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $Item.TenantFilter + Write-Host "Filter: $Filter" + Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey | Remove-AzDataTableEntity @Table + + $GraphRequestParams = @{ + TenantFilter = $Item.TenantFilter + Endpoint = $Item.Endpoint + Parameters = $Item.Parameters + NoPagination = $Item.NoPagination + ReverseTenantLookupProperty = $Item.ReverseTenantLookupProperty + ReverseTenantLookup = $Item.ReverseTenantLookup + SkipCache = $true + } + + $RawGraphRequest = try { + Get-GraphRequestList @GraphRequestParams + } catch { + [PSCustomObject]@{ + Tenant = $Item.Tenant + CippStatus = "Could not connect to tenant. $($_.Exception.message)" + } + } + + $GraphResults = foreach ($Request in $RawGraphRequest) { + $Json = ConvertTo-Json -Depth 5 -Compress -InputObject $Request + [PSCustomObject]@{ + TenantFilter = [string]$Item.TenantFilter + QueueId = [string]$Item.QueueId + QueueType = [string]$Item.QueueType + RowKey = [string](New-Guid) + PartitionKey = [string]$PartitionKey + Data = [string]$Json + } + } + try { + Add-CIPPAzDataTableEntity @Table -Entity $GraphResults -Force | Out-Null + Update-CippQueueEntry -RowKey $Item.QueueId -Status 'Completed' + } catch { + Write-Host "Queue Error: $($_.Exception.Message)" + Update-CippQueueEntry -RowKey $Item.QueueId -Status 'Failed' + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListLicensesQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListLicensesQueue.ps1 new file mode 100644 index 000000000000..f587fa65fe39 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListLicensesQueue.ps1 @@ -0,0 +1,22 @@ +function Push-ListLicensesQueue { + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell queue trigger function processed work item: $($Item.defaultDomainName)" + + $domainName = $Item.defaultDomainName + $GraphRequest = try { + Write-Host "Processing $domainName" + Get-CIPPLicenseOverview -TenantFilter $domainName + } catch { + [pscustomobject]@{ + Tenant = [string]$domainName + License = "Could not connect to client: $($_.Exception.Message)" + 'PartitionKey' = 'License' + 'RowKey' = "$($domainName)-$((New-Guid).Guid)" + } + } + $Table = Get-CIPPTable -TableName cachelicenses + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListMFAUsersQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListMFAUsersQueue.ps1 new file mode 100644 index 000000000000..89e9d1dbd3bd --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListMFAUsersQueue.ps1 @@ -0,0 +1,50 @@ +function Push-ListMFAUsersQueue { + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell queue trigger function processed work item: $($Item.defaultDomainName)" + + try { + Update-CippQueueEntry -RowKey $Item.QueueId -Status 'Running' -Name $Item.displayName + $domainName = $Item.defaultDomainName + $Table = Get-CIPPTable -TableName cachemfa + Try { + $GraphRequest = Get-CIPPMFAState -TenantFilter $domainName -ErrorAction Stop + } catch { + $GraphRequest = $null + } + if (!$GraphRequest) { + $GraphRequest = @{ + Tenant = [string]$domainName + UPN = [string]$domainName + AccountEnabled = 'none' + PerUser = [string]'Could not connect to tenant' + MFARegistration = 'none' + CoveredByCA = [string]'Could not connect to tenant' + CoveredBySD = 'none' + RowKey = [string]"$domainName" + PartitionKey = 'users' + } + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + + } catch { + $Table = Get-CIPPTable -TableName cachemfa + $GraphRequest = @{ + Tenant = [string]$domainName + UPN = [string]$domainName + AccountEnabled = 'none' + PerUser = [string]'Could not connect to tenant' + MFARegistration = 'none' + CoveredByCA = [string]'Could not connect to tenant' + CoveredBySD = 'none' + RowKey = [string]"$domainName" + PartitionKey = 'users' + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + } finally { + Update-CippQueueEntry -RowKey $QueueItem -Status 'Completed' + } + +} \ No newline at end of file diff --git a/ListMailboxRulesAllTenants/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListMailboxRulesQueue.ps1 similarity index 77% rename from ListMailboxRulesAllTenants/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListMailboxRulesQueue.ps1 index 36b671ad11f6..5c7ba40b2f06 100644 --- a/ListMailboxRulesAllTenants/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ListMailboxRulesQueue.ps1 @@ -1,20 +1,12 @@ -# Input bindings are passed in via param block. -param([string] $QueueItem, $TriggerMetadata) +function Push-ListMailboxRulesQueue { + # Input bindings are passed in via param block. + param($Item) -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell queue trigger function processed work item: $QueueItem" + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell queue trigger function processed work item: $($Item.defaultDomainName)" + + $domainName = $Item.defaultDomainName -$Tenants = if ($QueueItem -ne 'AllTenants') { - [PSCustomObject]@{ - defaultDomainName = $QueueItem - } -} else { - Get-Tenants -} -$Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module CippCore - Import-Module AzBobbyTables $Table = Get-CIPPTable -TableName cachembxrules try { $Rules = New-ExoRequest -tenantid $domainName -cmdlet 'Get-Mailbox' -Select 'userPrincipalName,GUID' | ForEach-Object -Parallel { diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-PublicWebhookProcess.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-PublicWebhookProcess.ps1 new file mode 100644 index 000000000000..fa4872195b0e --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-PublicWebhookProcess.ps1 @@ -0,0 +1,19 @@ +function Push-PublicWebhookProcess { + param($Item) + + try { + if ($Item.Type -eq 'GraphSubscription') { + Invoke-CippGraphWebhookProcessing -Data ($Item.Data | ConvertFrom-Json) -CIPPID $Item.CIPPID -WebhookInfo ($Item.Webhookinfo | ConvertFrom-Json) + } elseif ($Item.Type -eq 'AuditLog') { + Invoke-CippWebhookProcessing -TenantFilter $Item.TenantFilter -Data ($Item.Data | ConvertFrom-Json) -CIPPPURL $Item.CIPPURL + } elseif ($Item.Type -eq 'PartnerCenter') { + Invoke-CippPartnerWebhookProcessing -Data ($Item.Data | ConvertFrom-Json) + } + } catch { + Write-Host "Webhook Exception: $($_.Exception.Message)" + } finally { + $WebhookIncoming = Get-CIPPTable -TableName WebhookIncoming + $Entity = $Item | Select-Object -Property RowKey, PartitionKey + Remove-AzDataTableEntity @WebhookIncoming -Entity $Entity + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerAlert.ps1 new file mode 100644 index 000000000000..43a1433ce573 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerAlert.ps1 @@ -0,0 +1,63 @@ +function Push-SchedulerAlert { + param ( + $Item + ) + + try { + $Table = Get-CIPPTable -Table SchedulerConfig + if ($Item.Tag -eq 'AllTenants') { + $Filter = "RowKey eq 'AllTenants' and PartitionKey eq 'Alert'" + } else { + $Filter = "RowKey eq '{0}' and PartitionKey eq 'Alert'" -f $Item.Tenantid + } + $Alerts = Get-CIPPAzDataTableEntity @Table -Filter $Filter + + $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 -ne $false })) { + $Table = Get-CIPPTable -TableName AlertRunCheck + $Filter = "PartitionKey eq '{0}' and RowKey eq '{1}' and Timestamp ge datetime'{2}'" -f $Item.Tenant, $task.Name, (Get-Date).AddMinutes(-10).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss') + $ExistingMessage = Get-CIPPAzDataTableEntity @Table -Filter $Filter + if (!$ExistingMessage) { + $Item = [pscustomobject]@{ + Tenant = $Item.Tenant + Tenantid = $Item.Tenantid + value = $Task.value + } + $Function = "Push-CIPPAlert$($Task.Name)" + & $Function -Item $Item + #Push-OutputBinding -Name QueueItemOut -Value $Item + $Item | Add-Member -MemberType NoteProperty -Name 'RowKey' -Value $task.Name -Force + $Item | Add-Member -MemberType NoteProperty -Name 'PartitionKey' -Value $Item.Tenant -Force + + if ($null -eq $Item.Tenant) { + Write-Host ($Item | ConvertTo-Json) + } else { + try { + $null = Add-CIPPAzDataTableEntity @Table -Entity $Item -Force -ErrorAction Stop + } catch { + Write-Host "################### Error updating alert $($_.Exception.Message) - Task:$($Task.Name) PK:$($Item.PartitionKey)" + } + } + } else { + Write-Host ('ALERTS: Duplicate run found. Ignoring. Tenant: {0}, Task: {1}' -f $Item.Tenant, $task.Name) + } + } + <#if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'AlertsOrchestrator' + SkipLog = $true + Batch = @($Batch) + } + #Write-Host ($Batch | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started alert orchestration with ID = '$InstanceId'" + #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId + } else { + Write-Host 'No alerts to process' + }#> + } catch { + $Message = 'Exception on line {0} - {1}' -f $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message + Write-LogMessage -message $Message -API 'Alerts' -tenant $Item.tenant -sev Error + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerCIPPNotifications.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Push-SchedulerCIPPNotifications.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-Schedulerwebhookcreation.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-Schedulerwebhookcreation.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Push-Schedulerwebhookcreation.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-Schedulerwebhookcreation.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-UpdatePermissionsQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-UpdatePermissionsQueue.ps1 new file mode 100644 index 000000000000..672f1f196b65 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-UpdatePermissionsQueue.ps1 @@ -0,0 +1,17 @@ +function Push-UpdatePermissionsQueue { + # Input bindings are passed in via param block. + param($Item) + Write-Host "Applying permissions for $($Item.defaultDomainName)" + $Table = Get-CIPPTable -TableName cpvtenants + $CPVRows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Tenant -EQ $Item.customerId + if (!$CPVRows -or $ENV:ApplicationID -notin $CPVRows.applicationId) { + Write-LogMessage -tenant $Item.defaultDomainName -tenantId $Item.customerId -message 'A New tenant has been added, or a new CIPP-SAM Application is in use' -Sev 'Warn' -API 'NewTenant' + Write-Host 'Adding CPV permissions' + Set-CIPPCPVConsent -Tenantfilter $Item.customerId + } + + Add-CIPPApplicationPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Item.customerId + Add-CIPPDelegatedPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Item.customerId + + Write-LogMessage -tenant $Item.defaultDomainName -tenantId $Item.customerId -message "Updated permissions for $($Item.displayName)" -Sev 'Info' -API 'UpdatePermissionsQueue' +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-UpdateTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-UpdateTenants.ps1 new file mode 100644 index 000000000000..259dc3e326d7 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-UpdateTenants.ps1 @@ -0,0 +1,18 @@ +function Push-UpdateTenants { + Param($Item) + $QueueReference = 'UpdateTenants' + $RunningQueue = Invoke-ListCippQueue | Where-Object { $_.Reference -eq $QueueReference -and $_.Status -ne 'Completed' -and $_.Status -ne 'Failed' } + if ($RunningQueue) { + Write-Host 'Update Tenants already running' + return + } + $Queue = New-CippQueueEntry -Name 'Update Tenants' -Reference $QueueReference + try { + Update-CippQueueEntry -RowKey $Queue.RowKey -Status 'Running' + Get-Tenants | Out-Null + Update-CippQueueEntry -RowKey $Queue.RowKey -Status 'Completed' + } catch { + Write-Host "Queue Error: $($_.Exception.Message)" + Update-CippQueueEntry -RowKey $Queue.RowKey -Status 'Failed' + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-Z_CIPPQueueTrigger.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-Z_CIPPQueueTrigger.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Push-Z_CIPPQueueTrigger.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-Z_CIPPQueueTrigger.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditTemplate.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGeoIPLookup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecGeoIPLookup.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGeoIPLookup.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecGeoIPLookup.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecGraphRequest.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecGraphRequest.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecPartnerWebhook.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecPartnerWebhook.ps1 new file mode 100644 index 000000000000..d529d55b1f0f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecPartnerWebhook.ps1 @@ -0,0 +1,46 @@ +function Invoke-ExecPartnerWebhook { + Param($Request, $TriggerMetadata) + + switch ($Request.Query.Action) { + 'ListEventTypes' { + $Uri = 'https://api.partnercenter.microsoft.com/webhooks/v1/registration/events' + $Results = New-GraphGetRequest -uri $Uri -tenantid $env:TenantID -NoAuthCheck $true -scope 'https://api.partnercenter.microsoft.com/.default' + } + 'ListSubscription' { + $Uri = 'https://api.partnercenter.microsoft.com/webhooks/v1/registration' + $Results = New-GraphGetRequest -uri $Uri -tenantid $env:TenantID -NoAuthCheck $true -scope 'https://api.partnercenter.microsoft.com/.default' + } + 'CreateSubscription' { + $BaseURL = ([System.Uri]$request.headers.'x-ms-original-url').Host + $Webhook = @{ + TenantFilter = $env:TenantId + PartnerCenter = $true + BaseURL = $BaseURL + EventType = $Request.body.EventType + ExecutingUser = $Request.headers.'x-ms-client-principal' + } + $Results = New-CIPPGraphSubscription @Webhook + } + 'SendTest' { + $Results = New-GraphPOSTRequest -uri 'https://api.partnercenter.microsoft.com/webhooks/v1/registration/validationEvents' -tenantid $env:TenantID -NoAuthCheck $true -scope 'https://api.partnercenter.microsoft.com/.default' + } + 'ValidateTest' { + $Results = New-GraphGetRequest -uri "https://api.partnercenter.microsoft.com/webhooks/v1/registration/validationEvents/$($Request.Query.CorrelationId)" -tenantid $env:TenantID -NoAuthCheck $true -scope 'https://api.partnercenter.microsoft.com/.default' + } + default { + $Results = 'Invalid Action' + } + } + + $Body = [PSCustomObject]@{ + Results = $Results + Metadata = [PSCustomObject]@{ + Action = $Request.Query.Action + } + } + + Push-OutputBinding -Name Response -Value @{ + StatusCode = [System.Net.HttpStatusCode]::OK + Body = $Body + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-GetCippAlerts.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-GetCippAlerts.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-GetVersion.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-GetVersion.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-GetVersion.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-GetVersion.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-AddScheduledItem.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-AddScheduledItem.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAccessChecks.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecAccessChecks.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAccessChecks.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecAccessChecks.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddTrustedIP.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecAddTrustedIP.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddTrustedIP.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecAddTrustedIP.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBackendURLs.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecBackendURLs.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBackendURLs.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecBackendURLs.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCPVPermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCPVPermissions.ps1 similarity index 68% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCPVPermissions.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCPVPermissions.ps1 index 7573e9fd209c..4703972094e4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCPVPermissions.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCPVPermissions.ps1 @@ -11,14 +11,14 @@ Function Invoke-ExecCPVPermissions { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - # Write to the Azure Functions log stream. Write-Host 'PowerShell HTTP trigger function processed a request.' - $TenantFilter = (get-tenants -IncludeAll -IncludeErrors | Where-Object -Property customerId -EQ $Request.query.Tenantfilter).defaultDomainName - Write-Host "Our Tenantfilter is $TenantFilter" + $Tenant = Get-Tenants -IncludeAll | Where-Object -Property customerId -EQ $Request.Query.TenantFilter | Select-Object -First 1 + + Write-Host "Our tenant is $($Tenant.displayName) - $($Tenant.defaultDomainName)" $CPVConsentParams = @{ - Tenantfilter = $TenantFilter + TenantFilter = $Request.Query.TenantFilter } if ($Request.Query.ResetSP -eq 'true') { $CPVConsentParams.ResetSP = $true @@ -26,15 +26,15 @@ Function Invoke-ExecCPVPermissions { $GraphRequest = try { Set-CIPPCPVConsent @CPVConsentParams - Add-CIPPApplicationPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $TenantFilter - Add-CIPPDelegatedPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $TenantFilter + Add-CIPPApplicationPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Request.Query.TenantFilter + Add-CIPPDelegatedPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Request.Query.TenantFilter $Success = $true } catch { - "Failed to update permissions for $($TenantFilter): $($_.Exception.Message)" + "Failed to update permissions for $($Tenant.displayName): $($_.Exception.Message)" $Success = $false } - $Tenant = Get-Tenants -IncludeAll -IncludeErrors | Where-Object -Property defaultDomainName -EQ $Tenantfilter + $Tenant = Get-Tenants -IncludeAll | Where-Object -Property customerId -EQ $TenantFilter # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDnsConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecDnsConfig.ps1 similarity index 99% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDnsConfig.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecDnsConfig.ps1 index 067da939c2ca..41768a704af5 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDnsConfig.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecDnsConfig.ps1 @@ -88,7 +88,7 @@ Function Invoke-ExecDnsConfig { } 'GetConfig' { $body = [pscustomobject]$Config - Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'Retrieved DNS configuration' -Sev 'Info' + Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'Retrieved DNS configuration' -Sev 'Debug' } 'RemoveDomain' { $Filter = "RowKey eq '{0}'" -f $Request.Query.Domain diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExcludeLicenses.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeLicenses.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExcludeLicenses.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExcludeTenant.ps1 similarity index 96% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeTenant.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExcludeTenant.ps1 index ca63f9f20afa..ca3e85feed9a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExcludeTenant.ps1 @@ -17,13 +17,13 @@ Function Invoke-ExecExcludeTenant { $TenantsTable = Get-CippTable -tablename Tenants if ($Request.Query.List) { - $ExcludedFilter = "PartitionKey eq 'Tenants' and Excluded eq true" - $ExcludedTenants = Get-CIPPAzDataTableEntity @TenantsTable -Filter $ExcludedFilter - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded tenants list' -Sev 'Info' + $ExcludedFilter = "PartitionKey eq 'Tenants' and Excluded eq true" + $ExcludedTenants = Get-CIPPAzDataTableEntity @TenantsTable -Filter $ExcludedFilter + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded tenants list' -Sev 'Debug' $body = @($ExcludedTenants) } elseif ($Request.query.ListAll) { - $ExcludedTenants = Get-CIPPAzDataTableEntity @TenantsTable -filter "PartitionKey eq 'Tenants'" - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded tenants list' -Sev 'Info' + $ExcludedTenants = Get-CIPPAzDataTableEntity @TenantsTable -filter "PartitionKey eq 'Tenants'" + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded tenants list' -Sev 'Debug' $body = @($ExcludedTenants) } try { @@ -31,7 +31,7 @@ Function Invoke-ExecExcludeTenant { $name = $Request.Query.TenantFilter if ($Request.Query.AddExclusion) { $Tenants = Get-Tenants -IncludeAll | Where-Object { $Request.body.value -contains $_.customerId } - + $Excluded = foreach ($Tenant in $Tenants) { $Tenant.Excluded = $true $Tenant.ExcludeUser = $username @@ -41,17 +41,17 @@ Function Invoke-ExecExcludeTenant { Write-Host ($Excluded | ConvertTo-Json) Update-AzDataTableEntity @TenantsTable -Entity ([pscustomobject]$Excluded) #Remove-CIPPCache - Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Added exclusion for customer(s): $($Excluded.defaultDomainName -join ',')" -Sev 'Info' + Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Added exclusion for customer(s): $($Excluded.defaultDomainName -join ',')" -Sev 'Info' $body = [pscustomobject]@{'Results' = "Success. Added exclusions for customer(s): $($Excluded.defaultDomainName -join ',')" } } if ($Request.Query.RemoveExclusion) { $Filter = "PartitionKey eq 'Tenants' and defaultDomainName eq '{0}'" -f $name - $Tenant = Get-CIPPAzDataTableEntity @TenantsTable -Filter $Filter + $Tenant = Get-CIPPAzDataTableEntity @TenantsTable -Filter $Filter $Tenant.Excluded = $false $Tenant.ExcludeUser = '' $Tenant.ExcludeDate = '' - Update-AzDataTableEntity @TenantsTable -Entity $Tenant + Update-AzDataTableEntity @TenantsTable -Entity $Tenant #Remove-CIPPCache Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Removed exclusion for customer $($name)" -Sev 'Info' $body = [pscustomobject]@{'Results' = "Success. We've removed $name from the excluded tenants." } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionMapping.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionMapping.ps1 new file mode 100644 index 000000000000..f35e5a38c94f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionMapping.ps1 @@ -0,0 +1,89 @@ +using namespace System.Net + +Function Invoke-ExecExtensionMapping { + <# + .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' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $Table = Get-CIPPTable -TableName CippMapping + + if ($Request.Query.List) { + switch ($Request.Query.List) { + 'Halo' { + $body = Get-HaloMapping -CIPPMapping $Table + } + + 'NinjaOrgs' { + $Body = Get-NinjaOneOrgMapping -CIPPMapping $Table + } + + 'NinjaFields' { + $Body = Get-NinjaOneFieldMapping -CIPPMapping $Table + + } + } + } + + try { + if ($Request.Query.AddMapping) { + switch ($Request.Query.AddMapping) { + 'Halo' { + $body = Set-HaloMapping -CIPPMapping $Table -APIName $APIName -Request $Request + } + + 'NinjaOrgs' { + $Body = Set-NinjaOneOrgMapping -CIPPMapping $Table -APIName $APIName -Request $Request + } + + 'NinjaFields' { + $Body = Set-NinjaOneFieldMapping -CIPPMapping $Table -APIName $APIName -Request $Request -TriggerMetadata $TriggerMetadata + } + } + } + } catch { + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + try { + if ($Request.Query.AutoMapping) { + switch ($Request.Query.AutoMapping) { + 'NinjaOrgs' { + #Push-OutputBinding -Name NinjaProcess -Value @{'NinjaAction' = 'StartAutoMapping' } + $Batch = [PSCustomObject]@{ + 'NinjaAction' = 'StartAutoMapping' + 'FunctionName' = 'NinjaOneQueue' + } + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + $Body = [pscustomobject]@{'Results' = 'Automapping Request has been queued. Exact name matches will appear first and matches on device names and serials will take longer. Please check the CIPP Logbook and refresh the page once complete.' } + } + + } + } + } catch { + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionSync.ps1 similarity index 62% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionSync.ps1 index effb15af199d..57e44b3f946f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionSync.ps1 @@ -42,28 +42,50 @@ Function Invoke-ExecExtensionSync { $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } if ($Request.Query.TenantID) { - $Tenant = $TenantsToProcess | Where-Object {$_.RowKey -eq $Request.Query.TenantID} - if (($Tenant | Measure-Object).count -eq 1){ - Push-OutputBinding -Name NinjaProcess -Value @{ + $Tenant = $TenantsToProcess | Where-Object { $_.RowKey -eq $Request.Query.TenantID } + if (($Tenant | Measure-Object).count -eq 1) { + <#Push-OutputBinding -Name NinjaProcess -Value @{ 'NinjaAction' = 'SyncTenant' 'MappedTenant' = $Tenant + }#> + $Batch = [PSCustomObject]@{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + 'FunctionName' = 'NinjaOneQueue' + } + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + $Results = [pscustomobject]@{'Results' = "NinjaOne Synchronization Queued for $($Tenant.NinjaOneName)" } } else { - $Results = [pscustomobject]@{'Results' = "Tenant was not found." } - } - + $Results = [pscustomobject]@{'Results' = 'Tenant was not found.' } + } + } else { - - Push-OutputBinding -Name NinjaProcess -Value @{ + <#Push-OutputBinding -Name NinjaProcess -Value @{ 'NinjaAction' = 'SyncTenants' + }#> + $Batch = [PSCustomObject]@{ + 'NinjaAction' = 'SyncTenants' + 'FunctionName' = 'NinjaOneQueue' } - + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" $Results = [pscustomobject]@{'Results' = "NinjaOne Synchronization Queuing $(($TenantsToProcess | Measure-Object).count) Tenants" } } - + } catch { $Results = [pscustomobject]@{'Results' = "Could not start NinjaOne Sync: $($_.Exception.Message)" } Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start NinjaOne Sync $($_.Exception.Message)" -sev Error diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionTest.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionTest.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionTest.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionTest.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionsConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionsConfig.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionsConfig.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionsConfig.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecMaintenanceScripts.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecMaintenanceScripts.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecNotificationConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecNotificationConfig.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecNotificationConfig.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecNotificationConfig.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecPasswordConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecPasswordConfig.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecPasswordConfig.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecPasswordConfig.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreBackup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreBackup.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRunBackup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRunBackup.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRunBackup.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRunBackup.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Setup/Invoke-ExecSAMSetup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Setup/Invoke-ExecSAMSetup.ps1 new file mode 100644 index 000000000000..ba829541e6dc --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Setup/Invoke-ExecSAMSetup.ps1 @@ -0,0 +1,242 @@ +using namespace System.Net + +Function Invoke-ExecSAMSetup { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $UserCreds = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($request.headers.'x-ms-client-principal')) | ConvertFrom-Json) + if ($Request.query.error) { + Add-Type -AssemblyName System.Web + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + ContentType = 'text/html' + StatusCode = [HttpStatusCode]::Forbidden + Body = Get-normalizedError -Message [System.Web.HttpUtility]::UrlDecode($Request.Query.error_description) + }) + exit + } + if ('admin' -notin $UserCreds.userRoles) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + ContentType = 'text/html' + StatusCode = [HttpStatusCode]::Forbidden + Body = 'Could not find an admin cookie in your browser. Make sure you do not have an adblocker active, use a Chromium browser, and allow cookies. If our automatic refresh does not work, try pressing the URL bar and hitting enter. We will try to refresh ourselves in 3 seconds.' + }) + exit + } + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { + $DevSecretsTable = Get-CIPPTable -tablename 'DevSecrets' + $Secret = Get-CIPPAzDataTableEntity @DevSecretsTable -Filter "PartitionKey eq 'Secret' and RowKey eq 'Secret'" + if (!$Secret) { + $Secret = [PSCustomObject]@{ + 'PartitionKey' = 'Secret' + 'RowKey' = 'Secret' + 'TenantId' = '' + 'RefreshToken' = '' + 'ApplicationId' = '' + 'ApplicationSecret' = '' + } + Add-CIPPAzDataTableEntity @DevSecretsTable -Entity $Secret -Force + } + } else { + if ($env:MSI_SECRET) { + Disable-AzContextAutosave -Scope Process | Out-Null + $AzSession = Connect-AzAccount -Identity + } + } + if (!$ENV:SetFromProfile) { + Write-Host "We're reloading from KV" + Get-CIPPAuthentication + } + + $KV = $ENV:WEBSITE_DEPLOYMENT_ID + $Table = Get-CIPPTable -TableName SAMWizard + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) + + try { + if ($Request.query.count -lt 1 ) { $Results = 'No authentication code found. Please go back to the wizard.' } + + if ($request.body.setkeys) { + if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { + if ($request.body.TenantId) { $Secret.TenantId = $Request.body.tenantid } + if ($request.body.RefreshToken) { $Secret.RefreshToken = $Request.body.RefreshToken } + if ($request.body.applicationid) { $Secret.ApplicationId = $Request.body.ApplicationId } + if ($request.body.ApplicationSecret) { $Secret.ApplicationSecret = $Request.body.ApplicationSecret } + Add-CIPPAzDataTableEntity @DevSecretsTable -Entity $Secret -Force + } else { + if ($request.body.tenantid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $request.body.tenantid -AsPlainText -Force) } + if ($request.body.RefreshToken) { Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $request.body.RefreshToken -AsPlainText -Force) } + if ($request.body.applicationid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $request.body.applicationid -AsPlainText -Force) } + if ($request.body.applicationsecret) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $request.body.applicationsecret -AsPlainText -Force) } + } + $Results = @{ Results = 'The keys have been replaced. Please perform a permissions check.' } + } + if ($Request.query.error -eq 'invalid_client') { $Results = 'Client ID was not found in Azure. Try waiting 10 seconds to try again, if you have gotten this error after 5 minutes, please restart the process.' } + if ($request.query.code) { + try { + $TenantId = $Rows.tenantid + if (!$TenantId) { $TenantId = $ENV:TenantId } + $AppID = $Rows.appid + if (!$AppID) { $appid = $env:ApplicationId } + $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 + if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { + $clientsecret = $Secret.ApplicationSecret + } else { + $clientsecret = Get-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -AsPlainText + } + if (!$clientsecret) { $clientsecret = $ENV:ApplicationSecret } + $RefreshToken = Invoke-RestMethod -Method POST -Body "client_id=$appid&scope=https://graph.microsoft.com/.default+offline_access+openid+profile&code=$($request.query.code)&grant_type=authorization_code&redirect_uri=$($url)&client_secret=$clientsecret" -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" + + if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { + $Secret.RefreshToken = $RefreshToken.refresh_token + Add-CIPPAzDataTableEntity @DevSecretsTable -Entity $Secret -Force + } else { + Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $RefreshToken.refresh_token -AsPlainText -Force) + } + + $Results = 'Authentication is now complete. You may now close this window.' + try { + $SetupPhase = $rows.validated = $true + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + } catch { + #no need. + } + } catch { + $Results = "Authentication failed. $($_.Exception.message)" + } + } + if ($request.query.CreateSAM) { + $Rows = @{ + RowKey = 'setup' + PartitionKey = 'setup' + validated = $false + SamSetup = 'NotStarted' + partnersetup = $false + appid = 'NotStarted' + tenantid = 'NotStarted' + } + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) + + if ($Request.query.partnersetup) { + $SetupPhase = $Rows.partnersetup = $true + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + } + $step = 1 + $DeviceLogon = New-DeviceLogin -clientid '1b730954-1685-4b74-9bfd-dac224a7b894' -Scope 'https://graph.microsoft.com/.default' -FirstLogon + $SetupPhase = $rows.SamSetup = [string]($DeviceLogon | ConvertTo-Json) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + $Results = @{ message = "Your code is $($DeviceLogon.user_code). Enter the code" ; step = $step; url = $DeviceLogon.verification_uri } + } + if ($Request.query.CheckSetupProcess -and $request.query.step -eq 1) { + $SAMSetup = $Rows.SamSetup | ConvertFrom-Json -ErrorAction SilentlyContinue + $Token = (New-DeviceLogin -clientid '1b730954-1685-4b74-9bfd-dac224a7b894' -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) + if ($token.Access_Token) { + $step = 2 + $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 + $PartnerSetup = $Rows.partnersetup + $TenantId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/organization' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method GET -ContentType 'application/json').value.id + $SetupPhase = $rows.tenantid = [string]($TenantId) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + if ($PartnerSetup) { + $app = Get-Content '.\Cache_SAMSetup\SAMManifest.json' | ConvertFrom-Json + $App.web.redirectUris = @($App.web.redirectUris + $URL) + $app = $app | ConvertTo-Json -Depth 15 + $AppId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/applications' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') + $rows.appid = [string]($AppId.appId) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + $attempt = 0 + do { + try { + try { + $SPNDefender = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"fc780465-2017-40d4-a0c5-307022471b92`" }" -ContentType 'application/json') + } catch { + Write-Host "didn't deploy spn for defender, probably already there." + } + try { + $SPNTeams = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"48ac35b8-9aa8-4d74-927d-1f4a14a0b239`" }" -ContentType 'application/json') + } catch { + Write-Host "didn't deploy spn for Teams, probably already there." + } + $SPN = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"$($AppId.appId)`" }" -ContentType 'application/json') + Start-Sleep 3 + $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method Get -ContentType 'application/json').value.id + Write-Host "Id is $GroupID" + $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN.id)`"}" -ContentType 'application/json') + Write-Host 'Added to adminagents' + $attempt ++ + } catch { + $attempt ++ + } + } until ($attempt -gt 5) + } else { + $app = Get-Content '.\Cache_SAMSetup\SAMManifestNoPartner.json' + $AppId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/applications' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') + $rows.appid = [string]($AppId.appId) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + } + $AppPassword = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications/$($AppID.id)/addPassword" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body '{"passwordCredential":{"displayName":"CIPPInstall"}}' -ContentType 'application/json').secretText + + + if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { + $Secret.TenantId = $Request.body.tenantid + $Secret.ApplicationId = $Request.body.ApplicationId + $Secret.ApplicationSecret = $Request.body.ApplicationSecret + Add-CIPPAzDataTableEntity @DevSecretsTable -Entity $Secret -Force + } else { + Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $TenantId -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appid -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $AppPassword -AsPlainText -Force) + } + $Results = @{'message' = 'Created application. Waiting 30 seconds for Azure propagation'; step = $step } + } else { + $step = 1 + $Results = @{ message = "Your code is $($SAMSetup.user_code). Enter the code " ; step = $step; url = $SAMSetup.verification_uri } + } + + } + switch ($request.query.step) { + 2 { + $step = 2 + $TenantId = $Rows.tenantid + $AppID = $rows.appid + $PartnerSetup = $Rows.partnersetup + $SetupPhase = $rows.SamSetup = [string]($FirstLogonRefreshtoken | ConvertTo-Json) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 + $Validated = $Rows.validated + if ($Validated) { $step = 3 } + $Results = @{ message = 'Give the next approval by clicking ' ; step = $step; url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)" } + } + 3 { + + $step = 4 + $Results = @{'message' = 'Received token.'; step = $step } + + + } + 4 { + Remove-AzDataTableEntity @Table -Entity $Rows + + $step = 5 + $Results = @{'message' = 'setup completed.'; step = $step + } + } + } + + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.InvocationInfo.ScriptLineNumber): $($_.Exception.message)" ; step = $step } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddContact.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddContact.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddContact.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddContact.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddExConnector.ps1 similarity index 81% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddExConnector.ps1 index 3686d66c124d..b3246cea773d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddExConnector.ps1 @@ -18,12 +18,12 @@ Function Invoke-AddExConnector { $Result = foreach ($Tenantfilter in $tenants) { try { $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "New-$($ConnectorType)connector" -cmdParams $RequestParams - "Successfully created transport rule for $Tenantfilter." - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Tenantfilter -message "Created transport rule for $($Tenantfilter)" -sev 'Info' + "Successfully created Connector for $Tenantfilter." + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Tenantfilter -message "Created Connector for $($Tenantfilter)" -sev 'Info' } catch { - "Could not create created transport rule for $($Tenantfilter): $($_.Exception.message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Tenantfilter -message "Could not create created transport rule for $($Tenantfilter): $($_.Exception.message)" -sev 'Error' + "Could not create created Connector for $($Tenantfilter): $($_.Exception.message)" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Tenantfilter -message "Could not create created Connector for $($Tenantfilter): $($_.Exception.message)" -sev 'Error' } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnectorTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddExConnectorTemplate.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnectorTemplate.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddExConnectorTemplate.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddSharedMailbox.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddSharedMailbox.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddSharedMailbox.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddSharedMailbox.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilter.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddSpamFilter.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilter.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddSpamFilter.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilterTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddSpamFilterTemplate.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilterTemplate.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddSpamFilterTemplate.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportRule.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddTransportRule.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportRule.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddTransportRule.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddTransportTemplate.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportTemplate.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-AddTransportTemplate.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditExConnector.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-EditExConnector.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-EditExConnector.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-EditExConnector.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditSpamFilter.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-EditSpamFilter.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-EditSpamFilter.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-EditSpamFilter.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-EditTransportRule.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-EditTransportRule.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecConverttoSharedMailbox.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecConverttoSharedMailbox.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecConverttoSharedMailbox.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecConverttoSharedMailbox.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCopyForSent.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecCopyForSent.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCopyForSent.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecCopyForSent.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecDisableEmailForward.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecDisableEmailForward.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditCalendarPermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecEditCalendarPermissions.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditCalendarPermissions.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecEditCalendarPermissions.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditMailboxPermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecEditMailboxPermissions.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditMailboxPermissions.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecEditMailboxPermissions.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEmailForward.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecEmailForward.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEmailForward.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecEmailForward.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEnableArchive.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecEnableArchive.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEnableArchive.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecEnableArchive.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDelete.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecGroupsDelete.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDelete.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecGroupsDelete.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDeliveryManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecGroupsDeliveryManagement.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDeliveryManagement.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecGroupsDeliveryManagement.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsHideFromGAL.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecGroupsHideFromGAL.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsHideFromGAL.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecGroupsHideFromGAL.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecHideFromGAL.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecHideFromGAL.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecHideFromGAL.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecHideFromGAL.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecMailTest.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecMailTest.ps1 new file mode 100644 index 000000000000..483594d83889 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecMailTest.ps1 @@ -0,0 +1,90 @@ +using namespace System.Net +Function Invoke-ExecMailTest { + <# + .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' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + try { + switch ($Request.Query.Action) { + 'CheckConfig' { + $GraphToken = Get-GraphToken -returnRefresh $true -SkipCache $true + $AccessTokenDetails = Read-JwtAccessDetails -Token $GraphToken.access_token + $Me = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/me?$select=displayName,userPrincipalName,proxyAddresses' -NoAuthCheck $true + if ($AccessTokenDetails.Scope -contains 'Mail.Read') { + $Message = 'Mail.Read - Delegated was found in the token scope.' + $HasMailRead = $true + } else { + $Message = 'Please add Mail.Read - Delegated to the API permissions for CIPP-SAM.' + $HasMailRead = $false + } + + if ($Me.proxyAddresses) { + $Emails = $Me.proxyAddresses | Select-Object @{n = 'Address'; exp = { ($_ -split ':')[1] } }, @{n = 'IsPrimary'; exp = { $_ -cmatch 'SMTP' } } + } else { + $Emails = @(@{ Address = $Me.userPrincipalName; IsPrimary = $true }) + } + + $Body = [PSCustomObject]@{ + Message = $Message + HasMailRead = $HasMailRead + MailUser = $Me.displayName + MailAddresses = @($Emails) + } + } + default { + $Messages = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/me/mailFolders/Inbox/messages?`$select=receivedDateTime,subject,sender,internetMessageHeaders,webLink" -NoAuthCheck $true + $Results = foreach ($Message in $Messages) { + if ($Message.receivedDateTime) { + $AuthResult = ($Message.internetMessageHeaders | Where-Object -Property name -EQ 'Authentication-Results').value + $AuthResult = $AuthResult -split ';\s*' + $AuthResult = $AuthResult | ForEach-Object { + if ($_ -match '^(?.+?)=\s*(?.+?)\s(?.+)$') { + [PSCustomObject]@{ + Name = $Matches.Name + Status = $Matches.Status + Info = $Matches.Info + } + } + } + [PSCustomObject]@{ + Received = $Message.receivedDateTime + Subject = $Message.subject + Sender = $Message.sender.emailAddress.name + From = $Message.sender.emailAddress.address + Link = $Message.webLink + Headers = $Message.internetMessageHeaders + AuthResult = $AuthResult + } + } + } + $Body = [PSCustomObject]@{ + Results = @($Results) + Metadata = [PSCustomObject]@{ + Count = ($Results | Measure-Object).Count + } + } + } + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::BadRequest + $Body = [PSCustomObject]@{ + Results = @($ErrorMessage) + } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Body + }) +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMailboxMobileDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecMailboxMobileDevices.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMailboxMobileDevices.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecMailboxMobileDevices.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMailboxRestore.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecMailboxRestore.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMailboxRestore.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecMailboxRestore.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecQuarantineManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecQuarantineManagement.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecQuarantineManagement.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecQuarantineManagement.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecSetMailboxQuota.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecSetMailboxQuota.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecSetOoO.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecSetOoO.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailQuarantine.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListMailQuarantine.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailQuarantine.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListMailQuarantine.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMessageTrace.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListMessageTrace.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListMessageTrace.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListMessageTrace.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOoO.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListOoO.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListOoO.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListOoO.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPhishPolicies.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListPhishPolicies.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListPhishPolicies.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListPhishPolicies.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListRecipients.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListRecipients.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListRecipients.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListRecipients.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamFilterTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListSpamFilterTemplates.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamFilterTemplates.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListSpamFilterTemplates.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamfilter.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListSpamfilter.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamfilter.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListSpamfilter.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRules.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListTransportRules.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRules.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListTransportRules.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListTransportRulesTemplates.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ListTransportRulesTemplates.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddChocoApp.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddChocoApp.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddMSPApp.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddMSPApp.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddOfficeApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddOfficeApp.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddOfficeApp.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddOfficeApp.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddWinGetApp.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddWinGetApp.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAssignApp.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignApp.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAssignApp.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListApplicationQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ListApplicationQueue.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListApplicationQueue.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ListApplicationQueue.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListApps.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ListApps.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListApps.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ListApps.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppsRepository.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ListAppsRepository.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppsRepository.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ListAppsRepository.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAPDevice.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-AddAPDevice.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddAPDevice.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-AddAPDevice.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAutopilotConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-AddAutopilotConfig.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddAutopilotConfig.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-AddAutopilotConfig.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddEnrollment.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-AddEnrollment.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddEnrollment.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-AddEnrollment.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignAPDevice.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-ExecAssignAPDevice.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignAPDevice.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-ExecAssignAPDevice.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAPDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-ListAPDevices.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListAPDevices.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Autopilot/Invoke-ListAPDevices.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddDefenderDeployment.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddDefenderDeployment.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddDefenderDeployment.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddDefenderDeployment.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddIntuneTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddIntuneTemplate.ps1 similarity index 87% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddIntuneTemplate.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddIntuneTemplate.ps1 index d8e651ae36ad..ca7815ec778b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddIntuneTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddIntuneTemplate.ps1 @@ -35,13 +35,23 @@ Function Invoke-AddIntuneTemplate { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created intune policy template named $($Request.body.displayname) with GUID $GUID" -Sev 'Debug' $body = [pscustomobject]@{'Results' = 'Successfully added template' } - } - else { + } else { $TenantFilter = $request.query.TenantFilter $URLName = $Request.query.URLName $ID = $request.query.id switch ($URLName) { - + 'deviceCompliancePolicies' { + $Type = 'deviceCompliancePolicies' + $Template = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$($urlname)/$($ID)?`$expand=scheduledActionsForRule(`$expand=scheduledActionConfigurations)" -tenantid $tenantfilter + $DisplayName = $template.displayName + $TemplateJson = ConvertTo-Json -InputObject $Template -Depth 10 -Compress + } + 'managedAppPolicies' { + $Type = 'AppProtection' + $Template = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/$($urlname)('$($ID)')" -tenantid $tenantfilter + $DisplayName = $template.displayName + $TemplateJson = ConvertTo-Json -InputObject $Template -Depth 10 -Compress + } 'configurationPolicies' { $Type = 'Catalog' $Template = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$($urlname)('$($ID)')?`$expand=settings" -tenantid $tenantfilter | Select-Object name, description, settings, platforms, technologies, templateReference @@ -112,8 +122,7 @@ Function Invoke-AddIntuneTemplate { $body = [pscustomobject]@{'Results' = 'Successfully added template' } } - } - catch { + } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Intune Template Deployment failed: $($_.Exception.Message)" -Sev 'Error' $body = [pscustomobject]@{'Results' = "Intune Template Deployment failed: $($_.Exception.Message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddPolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddPolicy.ps1 similarity index 66% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddPolicy.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddPolicy.ps1 index 34a79bee6b7e..3cf7126bd8b4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddPolicy.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddPolicy.ps1 @@ -24,6 +24,27 @@ Function Invoke-AddPolicy { } try { switch ($Request.body.TemplateType) { + 'AppProtection' { + $TemplateType = ($RawJSON | ConvertFrom-Json).'@odata.type' -replace '#microsoft.graph.', '' + $TemplateTypeURL = "$($TemplateType)s" + $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/$TemplateTypeURL" -tenantid $tenant + if ($displayname -in $CheckExististing.displayName) { + Throw "Policy with Display Name $($Displayname) Already exists" + } + $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/$TemplateTypeURL" -tenantid $tenant -type POST -body $RawJSON + } + 'deviceCompliancePolicies' { + $TemplateTypeURL = 'deviceCompliancePolicies' + $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$TemplateTypeURL" -tenantid $tenant + if ($displayname -in $CheckExististing.displayName) { + Throw "Policy with Display Name $($Displayname) Already exists" + } + $JSON = $RawJSON | ConvertFrom-Json | Select-Object * -ExcludeProperty id, createdDateTime, lastModifiedDateTime, version, 'scheduledActionsForRule@odata.context', '@odata.context' + $JSON.scheduledActionsForRule = @($JSON.scheduledActionsForRule | Select-Object * -ExcludeProperty 'scheduledActionConfigurations@odata.context') + $RawJSON = ConvertTo-Json -InputObject $JSON -Depth 20 -Compress + Write-Host $RawJSON + $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$TemplateTypeURL" -tenantid $tenant -type POST -body $RawJson + } 'Admin' { $TemplateTypeURL = 'groupPolicyConfigurations' $CreateBody = '{"description":"' + $description + '","displayName":"' + $displayname + '","roleScopeTagIds":["0"]}' @@ -42,7 +63,10 @@ Function Invoke-AddPolicy { if ($PolicyName -in $CheckExististing.displayName) { Throw "Policy with Display Name $($Displayname) Already exists" } - + $PolicyFile = $RawJSON | ConvertFrom-Json + $Null = $PolicyFile | Add-Member -MemberType NoteProperty -Name 'description' -Value $description -Force + $null = $PolicyFile | Add-Member -MemberType NoteProperty -Name 'displayName' -Value $displayname -Force + $RawJSON = ConvertTo-Json -InputObject $PolicyFile -Depth 20 $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$TemplateTypeURL" -tenantid $tenant -type POST -body $RawJSON } 'Catalog' { @@ -62,8 +86,7 @@ Function Invoke-AddPolicy { Set-CIPPAssignedPolicy -GroupName $AssignTo -PolicyId $CreateRequest.id -Type $TemplateTypeURL -TenantFilter $tenant } "Successfully added policy for $($Tenant)" - } - catch { + } catch { "Failed to add policy for $($Tenant): $($_.Exception.Message)" Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Failed adding policy $($Displayname). Error: $($_.Exception.Message)" -Sev 'Error' continue diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditPolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-EditPolicy.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-EditPolicy.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-EditPolicy.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignPolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecAssignPolicy.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignPolicy.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecAssignPolicy.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceAction.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceAction.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetLocalAdminPassword.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecGetLocalAdminPassword.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetLocalAdminPassword.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecGetLocalAdminPassword.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetRecoveryKey.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecGetRecoveryKey.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetRecoveryKey.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecGetRecoveryKey.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Reports/Invoke-ListDevices.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListDevices.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Reports/Invoke-ListDevices.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Devices/Invoke-ExecDeviceDelete.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Devices/Invoke-ExecDeviceDelete.ps1 new file mode 100644 index 000000000000..25e64be478b3 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Devices/Invoke-ExecDeviceDelete.ps1 @@ -0,0 +1,38 @@ +using namespace System.Net + +Function Invoke-ExecDeviceDelete { + <# + .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' + + # Interact with query parameters or the body of the request. + + + try { + $url = "https://graph.microsoft.com/beta/devices/$($request.query.id)" + if ($Request.query.action -eq 'delete') { + $ActionResult = New-GraphPOSTRequest -uri $url -type DELETE -tenantid $Request.Query.TenantFilter + } elseif ($Request.query.action -eq 'disable') { + $ActionResult = New-GraphPOSTRequest -uri $url -type PATCH -tenantid $Request.Query.TenantFilter -body '{"accountEnabled": false }' + } elseif ($Request.query.action -eq 'enable') { + $ActionResult = New-GraphPOSTRequest -uri $url -type PATCH -tenantid $Request.Query.TenantFilter -body '{"accountEnabled": true }' + } + Write-Host $ActionResult + $body = [pscustomobject]@{'Results' = "Executed action $($Request.query.action) on $($Request.query.id)" } + } catch { + $body = [pscustomobject]@{'Results' = "Failed to queue action $($Request.query.action) on $($request.query.id): $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroup.ps1 similarity index 78% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroup.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroup.ps1 index 9794b0d51f3c..2a9d176a365e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroup.ps1 @@ -45,13 +45,23 @@ Function Invoke-AddGroup { } $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/groups' -tenantid $tenant -type POST -body (ConvertTo-Json -InputObject $BodyToship -Depth 10) -verbose } else { - $Params = @{ - Name = $groupobj.Displayname - Alias = $groupobj.username - Description = $groupobj.Description - PrimarySmtpAddress = $email - Type = $groupobj.groupType - RequireSenderAuthenticationEnabled = [bool]!$groupobj.AllowExternal + if ($groupobj.groupType -eq 'dynamicdistribution') { + $Params = @{ + Name = $groupobj.Displayname + RecipientFilter = $groupobj.membershipRules + PrimarySmtpAddress = $email + } + $GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DynamicDistributionGroup' -cmdParams $params + } else { + $Params = @{ + Name = $groupobj.Displayname + Alias = $groupobj.username + Description = $groupobj.Description + PrimarySmtpAddress = $email + Type = $groupobj.groupType + RequireSenderAuthenticationEnabled = [bool]!$groupobj.AllowExternal + } + $GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DistributionGroup' -cmdParams $params } $GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DistributionGroup' -cmdParams $params # At some point add logic to use AddOwner/AddMember for New-DistributionGroup, but idk how we're going to brr that - rvdwegen diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroupTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroupTemplate.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroupTemplate.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroupTemplate.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditGroup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 similarity index 99% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-EditGroup.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 index 135ddf5a44ba..6990f6baa46f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditGroup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 @@ -69,6 +69,7 @@ Function Invoke-EditGroup { if ($RemoveMembers) { $RemoveMembers | ForEach-Object { $member = $_ + if ($member -like '*#EXT#*') { $member = [System.Web.HttpUtility]::UrlEncode($member) } if ($GroupType -eq 'Distribution list' -or $GroupType -eq 'Mail-Enabled Security') { $Params = @{ Identity = $userobj.groupid; Member = $member ; BypassSecurityGroupManagerCheck = $true } New-ExoRequest -tenantid $Userobj.tenantid -cmdlet 'Remove-DistributionGroupMember' -cmdParams $params -UseSystemMailbox $true diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGuest.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddGuest.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddGuest.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddGuest.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUser.ps1 similarity index 88% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUser.ps1 index 8845a0a8987c..41c1004a8cf6 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUser.ps1 @@ -8,7 +8,7 @@ Function Invoke-AddUser { [CmdletBinding()] param($Request, $TriggerMetadata) - $APIName = "AddUser" + $APIName = 'AddUser' Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $Results = [System.Collections.ArrayList]@() @@ -56,10 +56,10 @@ Function Invoke-AddUser { $results.add('Created user.') $results.add("Username: $($UserprincipalName)") $results.add("Password: $password") - } - catch { + } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($userobj.tenantid) -message "Failed to create user. Error:$($_.Exception.Message)" -Sev 'Error' $body = $results.add("Failed to create user. $($_.Exception.Message)" ) + exit 1 } try { @@ -70,8 +70,7 @@ Function Invoke-AddUser { $LicenseBody = if ($licenses.count -ge 2) { $liclist = foreach ($license in $Licenses) { '{"disabledPlans": [],"skuId": "' + $license + '" },' } '{"addLicenses": [' + $LicList + '], "removeLicenses": [ ] }' - } - else { + } else { '{"addLicenses": [ {"disabledPlans": [],"skuId": "' + $licenses + '" }],"removeLicenses": [ ]}' } Write-Host $LicenseBody @@ -97,8 +96,7 @@ Function Invoke-AddUser { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($userobj.tenantid) -message "Added alias $($Alias) to $($userobj.displayname)" -Sev 'Info' $body = $results.add("Added Aliases: $($Aliases -join ',')") } - } - catch { + } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($userobj.tenantid) -message "Failed to create the Aliases. Error:$($_.Exception.Message)" -Sev 'Error' $body = $results.add("Failed to create the Aliases: $($_.Exception.Message)") } @@ -107,7 +105,15 @@ Function Invoke-AddUser { $results.Add($CopyFrom.Success -join ', ') $results.Add($CopyFrom.Error -join ', ') } - + + if ($Request.body.setManager) { + $ManagerBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Request.body.setManager.value)" } + $ManagerBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $ManagerBody + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($GraphRequest.id)/manager/`$ref" -tenantid $Userobj.tenantid -type PUT -body $ManagerBodyJSON -Verbose + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Userobj.tenantid -message "Set $($UserObj.DisplayName)'s manager to $($Request.body.setManager.label)" -Sev 'Info' + $results.add("Success. Set $($UserObj.DisplayName)'s manager to $($Request.body.setManager.label)") + } + $copyFromResults = @{ 'Success' = $CopyFrom.Success 'Error' = $CopyFrom.Error @@ -119,6 +125,8 @@ Function Invoke-AddUser { 'Password' = $password 'CopyFrom' = $copyFromResults } + + # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUserBulk.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUserBulk.ps1 new file mode 100644 index 000000000000..14d52620943d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUserBulk.ps1 @@ -0,0 +1,52 @@ +using namespace System.Net + +Function Invoke-AddUserBulk { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = 'AddUserBulk' + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $TenantFilter = $Request.body.TenantFilter + $Results = [System.Collections.ArrayList]@() + foreach ($userobj in $request.body.BulkUser) { + Write-Host 'PowerShell HTTP trigger function processed a request.' + try { + $password = if ($userobj.password) { $userobj.password } else { New-passwordString } + $UserprincipalName = "$($UserObj.mailNickName)@$($UserObj.domain)" + $BodyToship = $userobj + #Remove domain from body to ship + $BodyToship = $BodyToship | Select-Object * -ExcludeProperty password, domain + $BodyToship | Add-Member -NotePropertyName accountEnabled -NotePropertyValue $true -Force + $BodyToship | Add-Member -NotePropertyName userPrincipalName -NotePropertyValue $UserprincipalName -Force + $BodyToship | Add-Member -NotePropertyName passwordProfile -NotePropertyValue @{'password' = $password; 'forceChangePasswordNextSignIn' = $true } -Force + Write-Host "body is now: $($BodyToship | ConvertTo-Json -Depth 10 -Compress)" + if ($userobj.businessPhones) { $bodytoShip.businessPhones = @($userobj.businessPhones) } + $bodyToShip = ConvertTo-Json -Depth 10 -InputObject $BodyToship -Compress + Write-Host "Our body to ship is $bodyToShip" + $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/users' -tenantid $TenantFilter -type POST -body $BodyToship -verbose + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($TenantFilter) -message "Created user $($userobj.displayname) with id $($GraphRequest.id) " -Sev 'Info' + $results.add("Created user $($UserprincipalName). Password is $password") + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($TenantFilter) -message "Failed to create user. Error:$($_.Exception.Message)" -Sev 'Error' + $body = $results.add("Failed to create user. $($_.Exception.Message)" ) + } + } + $body = [pscustomobject] @{ + 'Results' = @($results) + 'Username' = $UserprincipalName + 'Password' = $password + 'CopyFrom' = $copyFromResults + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 similarity index 93% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 index d5d7b4e1b961..5a29285bd695 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 @@ -42,7 +42,6 @@ Function Invoke-EditUser { 'displayName' = $UserObj.Displayname 'postalCode' = $userobj.postalCode 'companyName' = $userobj.companyName - 'mailNickname' = $UserObj.username 'jobTitle' = $UserObj.JobTitle 'userPrincipalName' = $Email 'usageLocation' = $UserObj.usageLocation @@ -161,7 +160,14 @@ Function Invoke-EditUser { } } - + if ($Request.body.setManager) { + $ManagerBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Request.body.setManager.value)" } + $ManagerBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $ManagerBody + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)/manager/`$ref" -tenantid $Userobj.tenantid -type PUT -body $ManagerBodyJSON -Verbose + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Userobj.tenantid -message "Set $($UserObj.DisplayName)'s manager to $($Request.body.setManager.label)" -Sev 'Info' + $results.add("Success. Set $($UserObj.DisplayName)'s manager to $($Request.body.setManager.label)") + } + if ($RemoveFromGroups) { $RemoveFromGroups | ForEach-Object { diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECCheck.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecBECCheck.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECCheck.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecBECCheck.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECRemediate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecBECRemediate.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECRemediate.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecBECRemediate.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecClrImmId.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecClrImmId.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecClrImmId.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecClrImmId.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCreateTAP.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecCreateTAP.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCreateTAP.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecCreateTAP.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecDisableUser.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableUser.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecDisableUser.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecOffboardUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecOffboardUser.ps1 new file mode 100644 index 000000000000..02516f3148bf --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecOffboardUser.ps1 @@ -0,0 +1,53 @@ +using namespace System.Net + +Function Invoke-ExecOffboardUser { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + if ($Request.body.user.value) { $AllUsers = $Request.body.user.value } else { $AllUsers = @($Request.body.user) } + $Results = foreach ($username in $AllUsers) { + try { + $APIName = 'ExecOffboardUser' + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Tenantfilter = $request.body.tenantfilter + if ($Request.body.Scheduled.enabled) { + $taskObject = [PSCustomObject]@{ + TenantFilter = $Tenantfilter + Name = "Offboarding: $Username" + Command = @{ + value = 'Invoke-CIPPOffboardingJob' + } + Parameters = @{ + Username = $Username + APIName = 'Scheduled Offboarding' + options = $request.body + } + ScheduledTime = $Request.body.scheduled.date + PostExecution = @{ + Webhook = [bool]$Request.Body.PostExecution.webhook + Email = [bool]$Request.Body.PostExecution.email + PSA = [bool]$Request.Body.PostExecution.psa + } + } + Add-CIPPScheduledTask -Task $taskObject -hidden $false + } else { + Invoke-CIPPOffboardingJob -Username $Username -TenantFilter $Tenantfilter -Options $Request.body -APIName $APIName -ExecutingUser $request.headers.'x-ms-client-principal' + } + $StatusCode = [HttpStatusCode]::OK + + } catch { + $StatusCode = [HttpStatusCode]::Forbidden + $body = $_.Exception.message + } + } + $body = [pscustomobject]@{'Results' = @($results) } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboard_Mailboxpermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecOffboard_Mailboxpermissions.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboard_Mailboxpermissions.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecOffboard_Mailboxpermissions.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOneDriveShortCut.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecOneDriveShortCut.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOneDriveShortCut.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecOneDriveShortCut.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetMFA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecResetMFA.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetMFA.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecResetMFA.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetPass.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecResetPass.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetPass.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecResetPass.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRevokeSessions.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecRevokeSessions.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRevokeSessions.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecRevokeSessions.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendPush.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecSendPush.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendPush.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecSendPush.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserConditionalAccessPolicies.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserConditionalAccessPolicies.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserConditionalAccessPolicies.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserConditionalAccessPolicies.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserCounts.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserCounts.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserCounts.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserCounts.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserDevices.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserDevices.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserDevices.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserGroups.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserGroups.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserGroups.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserGroups.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxDetails.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxRules.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxRules.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxRules.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxRules.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserPhoto.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserPhoto.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserPhoto.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserPhoto.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserSettings.ps1 similarity index 82% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserSettings.ps1 index 274c5d8383f1..50781d922e7b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserSettings.ps1 @@ -14,8 +14,8 @@ function Invoke-ListUserSettings { try { $Table = Get-CippTable -tablename 'UserSettings' $UserSettings = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq 'allUsers'" - if (!$UserSettings) { Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$username'" } - $UserSettings = $UserSettings | Select-Object -ExpandProperty JSON | ConvertFrom-Json -Depth 10 -ErrorAction SilentlyContinue + if (!$UserSettings) { $userSettings = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$username'" } + $UserSettings = $UserSettings.JSON | ConvertFrom-Json -Depth 10 -ErrorAction SilentlyContinue $StatusCode = [HttpStatusCode]::OK $Results = $UserSettings } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserSigninLogs.ps1 similarity index 93% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserSigninLogs.ps1 index d4fe27a93764..d421b79441d5 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserSigninLogs.ps1 @@ -40,9 +40,14 @@ Function Invoke-ListUserSigninLogs { @{ Name = 'FailureReason'; Expression = { $_.status.failureReason } }, @{ Name = 'FullDetails'; Expression = { $_ } } # Associate values to output bindings by calling 'Push-OutputBinding'. + if ($GraphRequest.FullDetails -eq $null) { + $GraphRequest = $null + } else { + $GraphRequest = @($GraphRequest) + } Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) + Body = $GraphRequest }) } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve Sign In report: $($_.Exception.message) " -Sev 'Error' -tenant $TenantFilter diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUsers.ps1 similarity index 95% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUsers.ps1 index 50c2d13f4525..8431d441a320 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUsers.ps1 @@ -10,7 +10,7 @@ Function Invoke-ListUsers { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $selectlist = 'id', 'accountEnabled', 'displayName', 'userPrincipalName', 'userType', 'createdDateTime', 'companyName', 'country', 'department', 'businessPhones', 'city', 'faxNumber', 'givenName', 'isResourceAccount', 'jobTitle', 'mobilePhone', 'officeLocation', 'postalCode', 'preferredDataLocation', 'preferredLanguage', 'mail', 'mailNickname', 'proxyAddresses', 'Aliases', 'otherMails', 'showInAddressList', 'state', 'streetAddress', 'surname', 'usageLocation', 'LicJoined', 'assignedLicenses', 'onPremisesSyncEnabled', 'OnPremisesImmutableId', 'onPremisesDistinguishedName', 'onPremisesLastSyncDateTime', 'primDomain', 'Tenant', 'CippStatus' + $selectlist = 'id', 'accountEnabled', 'displayName', 'userPrincipalName', 'username', 'userType', 'createdDateTime', 'companyName', 'country', 'department', 'businessPhones', 'city', 'faxNumber', 'givenName', 'isResourceAccount', 'jobTitle', 'mobilePhone', 'officeLocation', 'postalCode', 'preferredDataLocation', 'preferredLanguage', 'mail', 'mailNickname', 'proxyAddresses', 'Aliases', 'otherMails', 'showInAddressList', 'state', 'streetAddress', 'surname', 'usageLocation', 'LicJoined', 'assignedLicenses', 'onPremisesSyncEnabled', 'OnPremisesImmutableId', 'onPremisesDistinguishedName', 'onPremisesLastSyncDateTime', 'primDomain', 'Tenant', 'CippStatus' # Write to the Azure Functions log stream. Write-Host 'PowerShell HTTP trigger function processed a request.' $ConvertTable = Import-Csv Conversiontable.csv | Sort-Object -Property 'guid' -Unique @@ -22,6 +22,7 @@ Function Invoke-ListUsers { $GraphRequest = if ($TenantFilter -ne 'AllTenants') { New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userid)?`$top=999&`$select=$($selectlist -join ',')&`$filter=$GraphFilter&`$count=true" -tenantid $TenantFilter -ComplexFilter | Select-Object $selectlist | ForEach-Object { $_.onPremisesSyncEnabled = [bool]($_.onPremisesSyncEnabled) + $_.UserName = $_.userPrincipalName -split '@' | Select-Object -First 1 $_.Aliases = $_.Proxyaddresses -join ', ' $SkuID = $_.AssignedLicenses.skuid $_.LicJoined = ($ConvertTable | Where-Object { $_.guid -in $skuid }).'Product_Display_Name' -join ', ' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuth.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Reports/Invoke-ListBasicAuth.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuth.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Reports/Invoke-ListBasicAuth.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Reports/Invoke-ListBasicAuthAllTenants.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Reports/Invoke-ListBasicAuthAllTenants.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-Z_CIPPHttpTrigger.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Invoke-Z_CIPPHttpTrigger.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-Z_CIPPHttpTrigger.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Invoke-Z_CIPPHttpTrigger.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecAlertsListAllTenants.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecAlertsListAllTenants.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsList.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecIncidentsList.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsList.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecIncidentsList.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecIncidentsListAllTenants.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecIncidentsListAllTenants.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecSetSecurityAlert.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityAlert.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecSetSecurityAlert.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityIncident.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecSetSecurityIncident.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityIncident.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Security/Invoke-ExecSetSecurityIncident.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddTeam.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-AddTeam.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddTeam.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-AddTeam.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSharePointMember.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecSetSharePointMember.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSharePointMember.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecSetSharePointMember.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSharePointOwner.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecSharePointOwner.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSharePointOwner.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecSharePointOwner.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointSettings.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharepointSettings.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointSettings.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharepointSettings.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSites.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSites.ps1 similarity index 92% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListSites.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSites.ps1 index 9a0d7a228765..48f3f8badb80 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSites.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSites.ps1 @@ -52,10 +52,14 @@ Function Invoke-ListSites { $StatusCode = [HttpStatusCode]::Forbidden $GraphRequest = $ErrorMessage } + if ($Request.query.URLOnly -eq 'true') { + $GraphRequest = $GraphRequest | Where-Object { $null -ne $_.URL } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode - Body = @($GraphRequest) + Body = @($GraphRequest | Sort-Object -Property UPN) }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeams.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListTeams.ps1 similarity index 96% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeams.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListTeams.ps1 index 5c83df245e5f..c5969ad1d8ae 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeams.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListTeams.ps1 @@ -18,7 +18,7 @@ Function Invoke-ListTeams { # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.TenantFilter if ($request.query.type -eq 'List') { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')&`$select=id,displayname,description,visibility,mailNickname" -tenantid $TenantFilter + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')&`$select=id,displayname,description,visibility,mailNickname" -tenantid $TenantFilter | Sort-Object -Property displayName } $TeamID = $request.query.ID Write-Host $TeamID @@ -37,7 +37,7 @@ Function Invoke-ListTeams { Members = @($Members) Owners = @($owners) InstalledApps = @($AppsList) - } + } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsActivity.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListTeamsActivity.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsActivity.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListTeamsActivity.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsVoice.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListTeamsVoice.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsVoice.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListTeamsVoice.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-AddAlert.ps1 similarity index 95% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-AddAlert.ps1 index 9bc6df14e424..6cc440842493 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-AddAlert.ps1 @@ -32,7 +32,7 @@ Function Invoke-AddAlert { MFAAlertUsers = [bool]$Request.body.MFAAlertUsers NewGA = [bool]$Request.body.NewGA NewRole = [bool]$Request.body.NewRole - QuotaUsed = [bool]$Request.body.QuotaUsed + QuotaUsed = [int]$Request.body.QuotaUsedQuota UnusedLicenses = [bool]$Request.body.UnusedLicenses OverusedLicenses = [bool]$Request.body.OverusedLicenses AppSecretExpiry = [bool]$Request.body.AppSecretExpiry @@ -41,8 +41,9 @@ Function Invoke-AddAlert { DepTokenExpiry = [bool]$Request.body.DepTokenExpiry NoCAConfig = [bool]$Request.body.NoCAConfig SecDefaultsUpsell = [bool]$Request.body.SecDefaultsUpsell - SharePointQuota = [bool]$Request.body.SharePointQuota + SharePointQuota = [int]$Request.body.SharePointQuotaQuota ExpiringLicenses = [bool]$Request.body.ExpiringLicenses + NewAppApproval = [bool]$Request.body.NewAppApproval type = 'Alert' RowKey = $TenantID PartitionKey = 'Alert' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsList.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAlertsList.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsList.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAlertsList.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListWebhookAlert.ps1 similarity index 67% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListWebhookAlert.ps1 index 5ee5feb2924f..d585504f4254 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListWebhookAlert.ps1 @@ -11,12 +11,14 @@ Function Invoke-ListWebhookAlert { $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' - $WebhookRow = Get-CIPPAzDataTableEntity @Table | Where-Object -Property PartitionKey -EQ 'WebhookAlert' - + $WebhookRow = foreach ($Webhook in Get-CIPPAzDataTableEntity @Table | Where-Object -Property PartitionKey -EQ 'WebhookAlert') { + $Webhook.If = $Webhook.If | ConvertFrom-Json + $Webhook.execution = $Webhook.execution | ConvertFrom-Json + $Webhook + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @($WebhookRow) }) - - } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1 new file mode 100644 index 000000000000..9f843768ab9b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1 @@ -0,0 +1,149 @@ +using namespace System.Net +function Invoke-PublicWebhooks { + # Input bindings are passed in via param block. + param($Request, $TriggerMetadata) + + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + $WebhookTable = Get-CIPPTable -TableName webhookTable + $WebhookIncoming = Get-CIPPTable -TableName WebhookIncoming + $Webhooks = Get-CIPPAzDataTableEntity @WebhookTable + 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 ($Webhooks.Resource -eq 'M365AuditLogs') { + Write-Host "Found M365AuditLogs - This is an old entry, we'll deny so Microsoft stops sending it." + $body = 'This webhook is not authorized, its an old entry.' + $StatusCode = [HttpStatusCode]::Forbidden + } + if ($Request.query.ValidationToken) { + Write-Host 'Validation token received - query ValidationToken' + $body = $request.query.ValidationToken + $StatusCode = [HttpStatusCode]::OK + } elseif ($Request.body.validationCode) { + Write-Host 'Validation token received - body validationCode' + $body = $request.body.validationCode + $StatusCode = [HttpStatusCode]::OK + } elseif ($Request.query.validationCode) { + Write-Host 'Validation token received - query validationCode' + $body = $request.query.validationCode + $StatusCode = [HttpStatusCode]::OK + } elseif ($Request.Query.CIPPID -in $Webhooks.RowKey) { + Write-Host 'Found matching CIPPID' + + 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 + + $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID + + if ($Request.Query.Type -eq 'GraphSubscription') { + # Graph Subscriptions + [pscustomobject]$ReceivedItem = $Request.Body.value + $Entity = [PSCustomObject]@{ + PartitionKey = 'Webhook' + RowKey = [string](New-Guid).Guid + Type = $Request.Query.Type + Data = [string]($ReceivedItem | ConvertTo-Json -Depth 10) + CIPPID = $Request.Query.CIPPID + WebhookInfo = [string]($WebhookInfo | ConvertTo-Json -Depth 10) + FunctionName = 'PublicWebhookProcess' + } + Add-CIPPAzDataTableEntity @WebhookIncoming -Entity $Entity + ## Push webhook data to queue + #Invoke-CippGraphWebhookProcessing -Data $ReceivedItem -CIPPID $request.Query.CIPPID -WebhookInfo $Webhookinfo + + } elseif ($Request.Query.Type -eq 'PartnerCenter') { + [pscustomobject]$ReceivedItem = $Request.Body + $Entity = [PSCustomObject]@{ + PartitionKey = 'Webhook' + RowKey = [string](New-Guid).Guid + Type = $Request.Query.Type + Data = [string]($ReceivedItem | ConvertTo-Json -Depth 10) + CIPPID = $Request.Query.CIPPID + WebhookInfo = [string]($WebhookInfo | ConvertTo-Json -Depth 10) + FunctionName = 'PublicWebhookProcess' + } + Add-CIPPAzDataTableEntity @WebhookIncoming -Entity $Entity + } else { + # Auditlog Subscriptions + try { + foreach ($ReceivedItem In ($Request.body)) { + $ReceivedItem = [pscustomobject]$ReceivedItem + Write-Host "Received Item: $($ReceivedItem | ConvertTo-Json -Depth 15 -Compress))" + $TenantFilter = (Get-Tenants | Where-Object -Property customerId -EQ $ReceivedItem.TenantId).defaultDomainName + Write-Host "Webhook TenantFilter: $TenantFilter" + $ConfigTable = get-cipptable -TableName 'SchedulerConfig' + $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable | Where-Object { $_.Tenant -eq $TenantFilter -or $_.Tenant -eq 'AllTenants' } + $Operations = @(($AlertConfig.if | ConvertFrom-Json -ErrorAction SilentlyContinue).selection) + 'UserLoggedIn' + $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID + #Increased download efficiency: only download the data we need for processing. Todo: Change this to load from table or dynamic source. + $MappingTable = [pscustomobject]@{ + 'UserLoggedIn' = 'Audit.AzureActiveDirectory' + 'Add member to role.' = 'Audit.AzureActiveDirectory' + 'Disable account.' = 'Audit.AzureActiveDirectory' + 'Update StsRefreshTokenValidFrom Timestamp.' = 'Audit.AzureActiveDirectory' + 'Enable account.' = 'Audit.AzureActiveDirectory' + 'Disable Strong Authentication.' = 'Audit.AzureActiveDirectory' + 'Reset user password.' = 'Audit.AzureActiveDirectory' + 'Add service principal.' = 'Audit.AzureActiveDirectory' + 'HostedIP' = 'Audit.AzureActiveDirectory' + 'badRepIP' = 'Audit.AzureActiveDirectory' + 'UserLoggedInFromUnknownLocation' = 'Audit.AzureActiveDirectory' + 'customfield' = 'AnyLog' + 'anyAlert' = 'AnyLog' + 'New-InboxRule' = 'Audit.Exchange' + 'Set-InboxRule' = 'Audit.Exchange' + } + #Compare $Operations to $MappingTable. If there is a match, we make a new variable called $LogsToDownload + #Example: $Operations = 'UserLoggedIn', 'Set-InboxRule' makes : $LogsToDownload = @('Audit.AzureActiveDirectory',Audit.Exchange) + $LogsToDownload = $Operations | Where-Object { $MappingTable.$_ } | ForEach-Object { $MappingTable.$_ } + Write-Host "Our operations: $Operations" + Write-Host "Logs to download: $LogsToDownload" + if ($ReceivedItem.ContentType -in $LogsToDownload -or 'AnyLog' -in $LogsToDownload) { + if ($ReceivedItem.ContentType -eq 'Audit.SharePoint') { continue } + $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' + } else { + Write-Host "No data to download for $($ReceivedItem.ContentType)" + continue + } + Write-Host "Data found: $($data.count) items" + $DataToProcess = if ('anylog' -NotIn $LogsToDownload) { $Data | Where-Object -Property Operation -In $Operations } else { $Data } + Write-Host "Data to process found: $($DataToProcess.count) items" + foreach ($Item in $DataToProcess) { + Write-Host "Processing $($item.operation)" + + ## Push webhook data to table + $Entity = [PSCustomObject]@{ + PartitionKey = 'Webhook' + RowKey = [string](New-Guid).Guid + Type = 'AuditLog' + Data = [string]($Item | ConvertTo-Json -Depth 10) + CIPPURL = $CIPPURL + TenantFilter = $TenantFilter + FunctionName = 'PublicWebhookProcess' + } + Add-CIPPAzDataTableEntity @WebhookIncoming -Entity $Entity -Force + #Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url + } + } + } catch { + Write-Host "Webhook Failed: $($_.Exception.Message). Line number $($_.InvocationInfo.ScriptLineNumber)" + } + } + + $Body = 'Webhook Recieved' + $StatusCode = [HttpStatusCode]::OK + + } else { + $Body = 'This webhook is not authorized.' + $StatusCode = [HttpStatusCode]::Forbidden + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Body + }) +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddMultiTenantApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAddMultiTenantApp.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddMultiTenantApp.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAddMultiTenantApp.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAppApproval.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApproval.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAppApproval.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApproval.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddSPN.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecAddSPN.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddSPN.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecAddSPN.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecOffboardTenant.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecOffboardTenant.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecOnboardTenant.ps1 similarity index 83% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecOnboardTenant.ps1 index e9e23c2a174e..41365297ca4f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecOnboardTenant.ps1 @@ -52,13 +52,19 @@ 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 - AddMissingGroups = $Request.Body.addMissingGroups - AutoMapRoles = $Request.Body.autoMapRoles - }) + $Item = [pscustomobject]@{ + FunctionName = 'ExecOnboardTenantQueue' + id = $Id + Roles = $Request.Body.gdapRoles + AddMissingGroups = $Request.Body.addMissingGroups + AutoMapRoles = $Request.Body.autoMapRoles + } + + $InputObject = @{ + OrchestratorName = 'OnboardingOrchestrator' + Batch = @($Item) + } + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) } $Steps = $TenantOnboarding.OnboardingSteps | ConvertFrom-Json diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ListAppConsentRequests.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ListAppConsentRequests.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-EditTenant.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-EditTenant.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenantDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenantDetails.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenantDetails.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenantDetails.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenants.ps1 similarity index 82% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenants.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenants.ps1 index 93425c195e45..3d2967edb591 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenants.ps1 @@ -21,16 +21,22 @@ Function Invoke-ListTenants { StatusCode = [HttpStatusCode]::OK Body = $GraphRequest }) + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'UpdateTenantsOrchestrator' + Batch = @(@{'FunctionName' = 'UpdateTenants' }) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) exit } try { $tenantfilter = $Request.Query.TenantFilter - $Tenants = Get-Tenants -IncludeErrors + $Tenants = Get-Tenants -IncludeErrors -SkipDomains if ($null -eq $TenantFilter -or $TenantFilter -eq 'null') { $TenantList = [system.collections.generic.list[object]]::new() - if ($Request.Query.AllTenantSelector -eq $true) { + if ($Request.Query.AllTenantSelector -eq $true) { $TenantList.Add(@{ customerId = 'AllTenants' defaultDomainName = 'AllTenants' @@ -55,7 +61,7 @@ Function Invoke-ListTenants { Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $Tenantfilter -API $APINAME -message 'Listed Tenant Details' -Sev 'Debug' } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $Tenantfilter -API $APINAME -message "List Tenant failed. The error is: $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{ + $body = [pscustomobject]@{ 'Results' = "Failed to retrieve tenants: $($_.Exception.Message)" defaultDomainName = '' displayName = 'Failed to retrieve tenants. Perform a permission check.' @@ -68,6 +74,6 @@ Function Invoke-ListTenants { StatusCode = [HttpStatusCode]::OK Body = @($Body) }) - + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddCAPolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddCAPolicy.ps1 similarity index 83% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddCAPolicy.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddCAPolicy.ps1 index ab6635459e4b..547828a337f4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddCAPolicy.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddCAPolicy.ps1 @@ -16,11 +16,10 @@ Function Invoke-AddCAPolicy { $results = foreach ($Tenant in $tenants) { try { - $CAPolicy = New-CIPPCAPolicy -TenantFilter $tenant -state $request.body.NewState -RawJSON $Request.body.RawJSON -APIName $APIName -ExecutingUser $request.headers.'x-ms-client-principal' + $CAPolicy = New-CIPPCAPolicy -replacePattern $Request.body.replacename -Overwrite $request.body.overwrite -TenantFilter $tenant -state $request.body.NewState -RawJSON $Request.body.RawJSON -APIName $APIName -ExecutingUser $request.headers.'x-ms-client-principal' Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Added Conditional Access Policy $($Displayname)" -Sev 'Info' "Successfully added Conditional Access Policy for $($Tenant)" - } - catch { + } catch { "Failed to add policy for $($Tenant): $($_.Exception.Message)" Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Failed to add Conditional Access Policy $($Displayname). Error: $($_.Exception.Message)" -Sev 'Error' continue diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddCATemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddCATemplate.ps1 similarity index 68% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddCATemplate.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddCATemplate.ps1 index 7fc95e0d7af1..3f4b8d651650 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddCATemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddCATemplate.ps1 @@ -40,6 +40,32 @@ Function Invoke-AddCATemplate { } if ($excludelocations) { $JSON.conditions.locations.excludeLocations = $excludelocations } + if ($JSON.conditions.users.includeUsers) { + $JSON.conditions.users.includeUsers = @($JSON.conditions.users.includeUsers | ForEach-Object { + if ($_ -in 'All', 'None', 'GuestOrExternalUsers') { return $_ } + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($_)" -tenantid $TenantFilter).displayName + }) + } + + if ($JSON.conditions.users.excludeUsers) { + $JSON.conditions.users.excludeUsers = @($JSON.conditions.users.excludeUsers | ForEach-Object { + if ($_ -in 'All', 'None', 'GuestOrExternalUsers') { return $_ } + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($_)" -tenantid $TenantFilter).displayName + }) + } + + if ($JSON.conditions.users.includeGroups) { + $JSON.conditions.users.includeGroups = @($JSON.conditions.users.includeGroups | ForEach-Object { + if ($_ -in 'All', 'None', 'GuestOrExternalUsers') { return $_ } + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/$($_)" -tenantid $TenantFilter).displayName + }) + } + if ($JSON.conditions.users.excludeGroups) { + $JSON.conditions.users.excludeGroups = @($JSON.conditions.users.excludeGroups | ForEach-Object { + if ($_ -in 'All', 'None', 'GuestOrExternalUsers') { return $_ } + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/$($_)" -tenantid $TenantFilter).displayName + }) + } $JSON | Add-Member -NotePropertyName 'LocationInfo' -NotePropertyValue @($IncludeJSON, $ExcludeJSON) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddNamedLocation.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddNamedLocation.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddNamedLocation.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddNamedLocation.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-EditCAPolicy.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-EditCAPolicy.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCACheck.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCACheck.ps1 new file mode 100644 index 000000000000..137c55a9c28b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCACheck.ps1 @@ -0,0 +1,58 @@ +using namespace System.Net + +Function Invoke-ExecCaCheck { + <# + .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' + + $Tenant = $request.body.tenantFilter + $UserID = $request.body.userId.value + if ($Request.body.IncludeApplications.value) { + $IncludeApplications = $Request.body.IncludeApplications.value + } else { + $IncludeApplications = '67ad5377-2d78-4ac2-a867-6300cda00e85' + } + $results = try { + $CAContext = @{ + '@odata.type' = '#microsoft.graph.whatIfApplicationContext' + 'includeApplications' = @($IncludeApplications) + } + $ConditionalAccessWhatIfDefinition = @{ + 'conditionalAccessWhatIfSubject' = @{ + '@odata.type' = '#microsoft.graph.userSubject' + 'userId' = "$userId" + } + 'conditionalAccessContext' = $CAContext + 'conditionalAccessWhatIfConditions' = @{} + } + $whatIfConditions = $ConditionalAccessWhatIfDefinition.conditionalAccessWhatIfConditions + if ($Request.body.UserRiskLevel) { $whatIfConditions.userRiskLevel = $Request.body.UserRiskLevel.value } + if ($Request.body.SignInRiskLevel) { $whatIfConditions.signInRiskLevel = $Request.body.SignInRiskLevel.value } + if ($Request.body.ClientAppType) { $whatIfConditions.clientAppType = $Request.body.ClientAppType.value } + if ($Request.body.DevicePlatform) { $whatIfConditions.devicePlatform = $Request.body.DevicePlatform.value } + if ($Request.body.Country) { $whatIfConditions.country = $Request.body.Country.value } + if ($Request.body.IpAddress) { $whatIfConditions.ipAddress = $Request.body.IpAddress.value } + + $JSONBody = $ConditionalAccessWhatIfDefinition | ConvertTo-Json -Depth 10 + Write-Host $JSONBody + $Request = New-GraphPOSTRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/evaluate' -tenantid $tenant -type POST -body $JsonBody -AsApp $true + $Request + } catch { + "Failed to execute check: $($_.Exception.Message)" + } + + $body = [pscustomobject]@{'Results' = $results } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCAExclusion.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCAExclusion.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCAExclusion.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCAExclusion.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListCAtemplates.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListCAtemplates.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListConditionalAccessPolicies.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListConditionalAccessPolicies.ps1 similarity index 99% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListConditionalAccessPolicies.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListConditionalAccessPolicies.ps1 index affec8e72291..c093a8c4f009 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListConditionalAccessPolicies.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListConditionalAccessPolicies.ps1 @@ -243,7 +243,7 @@ function Get-ApplicationNameFromId { 4660504c-45b3-4674-a709-71951a6b0763 { $return = 'Microsoft Invitation Acceptance Portal' } ba23cd2a-306c-48f2-9d62-d3ecd372dfe4 { $return = 'OfficeGraph' } d52485ee-4609-4f6b-b3a3-68b6f841fa23 { $return = 'On-Premises Data Gateway Connector' } - 996def3d-b36c-4153-8607-a6fd3c01b89f { $return = 'Dynamics 365 for Financials' } + 996def3d-b36c-4153-8607-a6fd3c01b89f { $return = 's 365 for Financials' } b6b84568-6c01-4981-a80f-09da9a20bbed { $return = 'Microsoft Invoicing' } 9d3e55ba-79e0-4b7c-af50-dc460b81dca1 { $return = 'Microsoft Azure Data Catalog' } 4345a7b9-9a63-4910-a426-35363201d503 { $return = 'O365 Suite UX' } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListConditionalAccessPolicyChanges.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListConditionalAccessPolicyChanges.ps1 new file mode 100644 index 000000000000..89e2ecfb2bff --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListConditionalAccessPolicyChanges.ps1 @@ -0,0 +1,47 @@ +using namespace System.Net + +Function Invoke-ListConditionalAccessPolicyChanges { + <# + .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' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $policyId = $Request.body.id + $policyDisplayName = $Request.body.displayName + + try { + [array]$changes = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?`$filter=targetResources/any(s:s/id eq '$($policyId)')" -tenantid $TenantFilter | ForEach-Object { + [pscustomobject]@{ + policy = $policyDisplayName + policyId = $policyId + typeFriendlyName = $_.activityDisplayName + type = $_.operationType + initiatedBy = if ($_.initiatedBy.user.userPrincipalName) { $_.initiatedBy.user.userPrincipalName } else { $_.initiatedBy.app.displayName } + date = $_.activityDateTime + oldValue = ($_.targetResources[0].modifiedProperties.oldValue | ConvertFrom-Json) # targetResources is an array, can we ever get more than 1 object in it? + newValue = ($_.targetResources[0].modifiedProperties.newValue | ConvertFrom-Json) + } + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $StatusCode = [HttpStatusCode]::BadRequest + Write-Host $($_.Exception.message) + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APIName -message "Failed to request audit logs for policy $($policyDisplayName): $($_.Exception.message)" -Sev "Error" -tenant $TenantFilter + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $changes + }) +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddGDAPRole.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecAddGDAPRole.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddGDAPRole.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecAddGDAPRole.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAutoExtendGDAP.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecAutoExtendGDAP.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAutoExtendGDAP.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecAutoExtendGDAP.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecDeleteGDAPRelationship.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecDeleteGDAPRelationship.ps1 new file mode 100644 index 000000000000..bdfe01455cd4 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecDeleteGDAPRelationship.ps1 @@ -0,0 +1,31 @@ +using namespace System.Net + +Function Invoke-ExecDeleteGDAPRelationship { + <# + .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' + + # Interact with query parameters or the body of the request. + $GDAPID = $request.query.GDAPId + try { + $DELETE = New-GraphPostRequest -NoAuthCheck $True -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$($GDAPID)/requests" -type POST -body '{"action":"terminate"}' -tenantid $env:TenantID + $Results = [pscustomobject]@{'Results' = "Success. GDAP relationship for $($GDAPID) been revoked" } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Success. GDAP relationship for $($GDAPID) been revoked" -Sev 'Info' + + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRoleMapping.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecDeleteGDAPRoleMapping.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRoleMapping.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecDeleteGDAPRoleMapping.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPInvite.ps1 similarity index 96% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPInvite.ps1 index d6a5965f2055..4739df9c2df1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPInvite.ps1 @@ -64,6 +64,8 @@ Function Invoke-ExecGDAPInvite { } else { $Message = 'Error creating GDAP relationship request' } + + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created GDAP Invite - $InviteUrl" -Sev 'Info' } } catch { $Message = 'Error creating GDAP relationship' @@ -71,8 +73,6 @@ Function Invoke-ExecGDAPInvite { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $env:TenantID -message "$($Message): $($_.Exception.Message)" -Sev 'Error' } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created GDAP Invite - $InviteUrl" -Sev 'Info' - $body = @{ Message = $Message Invite = $InviteEntity diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteApproved.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPInviteApproved.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteApproved.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPInviteApproved.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPInvite.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPInvite.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPInvite.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPInvite.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPQueue.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPQueue.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPQueue.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPRoles.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPRoles.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPRoles.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPRoles.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddStandardsDeploy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-AddStandardsDeploy.ps1 similarity index 87% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-AddStandardsDeploy.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-AddStandardsDeploy.ps1 index 127c285bbc1e..a97bb06cf6de 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddStandardsDeploy.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-AddStandardsDeploy.ps1 @@ -15,7 +15,7 @@ Function Invoke-AddStandardsDeploy { $username = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($user)) | ConvertFrom-Json).userDetails try { - $Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value + $Tenants = $Request.body.Tenant $Settings = ($request.body | Select-Object -Property *, v2* -ExcludeProperty Select_*, None ) $Settings | Add-Member -NotePropertyName 'v2.1' -NotePropertyValue $true -Force if ($Settings.phishProtection.remediate) { @@ -26,6 +26,12 @@ Function Invoke-AddStandardsDeploy { URL = $URL } } + #Get all subobjects in $Settings that are set to false, and remove them. + $Settings.psobject.properties.name | Where-Object { $Settings.$_ -eq $false -and $_ -ne 'v2.1' -and $_ -in 'Alert', 'Remediate', 'Report' } | ForEach-Object { + $Settings.psobject.properties.remove($_) + } + + foreach ($Tenant in $tenants) { $object = [PSCustomObject]@{ diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-AddStandardsTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-AddStandardsTemplate.ps1 new file mode 100644 index 000000000000..27c8774bae3c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-AddStandardsTemplate.ps1 @@ -0,0 +1,32 @@ +using namespace System.Net + +Function Invoke-AddStandardsTemplate { + <# + .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' + $GUID = (New-Guid).GUID + $JSON = (ConvertTo-Json -Depth 100 -InputObject ($Request.body | Select-Object standards, name)) + $Table = Get-CippTable -tablename 'templates' + $Table.Force = $true + Add-CIPPAzDataTableEntity @Table -Entity @{ + JSON = "$JSON" + RowKey = "$GUID" + PartitionKey = 'StandardsTemplate' + GUID = "$GUID" + } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created CA Template $($Request.body.name) with GUID $GUID" -Sev 'Debug' + $body = [pscustomobject]@{'Results' = 'Successfully added template' } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-BestPracticeAnalyser_List.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-BestPracticeAnalyser_List.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-BestPracticeAnalyser_List.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-BestPracticeAnalyser_List.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecStandardsRun.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListBPA.ps1 similarity index 95% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListBPA.ps1 index e9f9f9a1fbf4..0e632d4fba35 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListBPA.ps1 @@ -13,7 +13,7 @@ Function Invoke-ListBPA { $Table = get-cipptable 'cachebpav2' $name = $Request.query.Report - if ($name -eq $null) { $name = 'CIPP Best Practices v1.0 - Table view' } + if ($name -eq $null) { $name = 'CIPP Best Practices v1.5 - Table view' } # Get all possible JSON files for reports, find the correct one, select the Columns $JSONFields = @() @@ -77,7 +77,7 @@ Function Invoke-ListBPA { # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK - Body = ($Results | ConvertTo-Json -Depth 15) + Body = (ConvertTo-Json -Depth 15 -InputObject $Results) }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListBPATemplates.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListBPATemplates.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListDomainAnalyser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListDomainAnalyser.ps1 new file mode 100644 index 000000000000..76fe08536529 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListDomainAnalyser.ps1 @@ -0,0 +1,19 @@ + +using namespace System.Net + +Function Invoke-ListDomainAnalyser { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $Results = Get-CIPPDomainAnalyser -TenantFilter $Request.query.tenantFilter + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Results) + }) +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomainHealth.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListDomainHealth.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomainHealth.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListDomainHealth.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 new file mode 100644 index 000000000000..acc984d6e0ab --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 @@ -0,0 +1,27 @@ +using namespace System.Net + +Function Invoke-listStandardTemplates { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'StandardsTemplate'" + $Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { + $data = $_.JSON | ConvertFrom-Json -Depth 100 + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.GUID -Force + $data + } | Sort-Object -Property displayName + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Templates) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/invoke-DomainAnalyser_List.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/invoke-DomainAnalyser_List.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/invoke-DomainAnalyser_List.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/invoke-DomainAnalyser_List.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 similarity index 83% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 index b4d51d4f5d18..f04c365c5ccf 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphExplorerPreset.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 @@ -21,21 +21,30 @@ Function Invoke-ExecGraphExplorerPreset { $Id = (New-Guid).Guid } 'Save' { - $Id = $Request.Body.values.reportTemplate.value + $Id = $Request.Body.preset.reportTemplate.value } 'Delete' { - $Id = $Request.Body.values.reportTemplate.value + $Id = $Request.Body.preset.reportTemplate.value } + default { + $Request.Body.Action = 'Copy' + $Id = (New-Guid).Guid + } + } + + $params = $Request.Body.preset | Select-Object endpoint, '$filter', '$select', '$count', '$expand', '$search', NoPagination, '$top', IsShared + + if ($params.'$select'.value) { + $params.'$select' = ($params.'$select').value -join ',' } - $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 + name = [string]$Request.Body.preset.name Owner = [string]$Username - IsShared = $Request.Body.values.IsShared + IsShared = $Request.Body.preset.IsShared params = [string](ConvertTo-Json -InputObject $params -Compress) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 deleted file mode 100644 index fbbcb29a1dce..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -using namespace System.Net - -Function Invoke-AddChocoApp_OrchestrationStarter { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - Write-LogMessage -API 'ChocoApps' -message 'Attempted to start upload but an instance was already running.' -sev Info - $InstanceId = Start-NewOrchestration -FunctionName 'Applications_Orchestrator' - Write-Host "Started orchestration with ID = '$InstanceId'" - $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId - Write-LogMessage -API 'ChocoApps' -message 'Started uploading applications to tenants' -sev Info - $Results = [pscustomobject]@{'Results' = 'Started application queue' } - - Write-Host ($Orchestrator | ConvertTo-Json) - - - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $results - }) - -} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRelationship.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRelationship.ps1 deleted file mode 100644 index b51a86b0098d..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRelationship.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net - -Function Invoke-ExecDeleteGDAPRelationship { - <# - .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' - - # Interact with query parameters or the body of the request. - $GDAPID = $request.query.GDAPId - try { - $DELETE = New-GraphPostRequest -NoAuthCheck $True -uri "https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web/v1/delegatedAdminRelationships/$($GDAPID)/requests" -type POST -body '{"action":"terminate"}' -tenantid $env:TenantID -scope 'https://api.partnercustomeradministration.microsoft.com/.default' - $Results = [pscustomobject]@{'Results' = "Success. GDAP relationship for $($GDAPID) been revoked" } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Success. GDAP relationship for $($GDAPID) been revoked" -Sev 'Info' - - } catch { - $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } - } - - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 deleted file mode 100644 index 1e4c8c8677e3..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 +++ /dev/null @@ -1,81 +0,0 @@ - using namespace System.Net - - Function Invoke-ExecExtensionMapping { - <# - .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' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$Table = Get-CIPPTable -TableName CippMapping - -if ($Request.Query.List) { - switch ($Request.Query.List) { - 'Halo' { - $body = Get-HaloMapping -CIPPMapping $Table - } - - 'NinjaOrgs' { - $Body = Get-NinjaOneOrgMapping -CIPPMapping $Table - } - - 'NinjaFields' { - $Body = Get-NinjaOneFieldMapping -CIPPMapping $Table - - } - } -} - -try { - if ($Request.Query.AddMapping) { - switch ($Request.Query.AddMapping) { - 'Halo' { - $body = Set-HaloMapping -CIPPMapping $Table -APIName $APIName -Request $Request - } - - 'NinjaOrgs' { - $Body = Set-NinjaOneOrgMapping -CIPPMapping $Table -APIName $APIName -Request $Request - } - - 'NinjaFields' { - $Body = Set-NinjaOneFieldMapping -CIPPMapping $Table -APIName $APIName -Request $Request -TriggerMetadata $TriggerMetadata - } - } - } -} -catch { - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } -} - -try { - if ($Request.Query.AutoMapping) { - switch ($Request.Query.AutoMapping) { - 'NinjaOrgs' { - Push-OutputBinding -Name NinjaProcess -Value @{'NinjaAction' = 'StartAutoMapping' } - $Body = [pscustomobject]@{'Results' = 'Automapping Request has been queued. Exact name matches will appear first and matches on device names and serials will take longer. Please check the CIPP Logbook and refresh the page once complete.' } - } - - - } - } -} -catch { - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - - } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteQueue.ps1 deleted file mode 100644 index 60d7ddbb29d2..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteQueue.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -using namespace System.Net - -Function Invoke-ExecGDAPInviteQueue { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - #$TenantFilter = $env:TenantID - - $Table = Get-CIPPTable -TableName 'GDAPInvites' - $Invite = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$QueueItem'" - $APINAME = 'GDAPInvites' - $RoleMappings = $Invite.RoleMappings | ConvertFrom-Json - Write-Host ($Invite | ConvertTo-Json -Compress) - - foreach ($role in $RoleMappings) { - try { - $Mappingbody = ConvertTo-Json -Depth 10 -InputObject @{ - 'accessContainer' = @{ - 'accessContainerId' = "$($Role.GroupId)" - 'accessContainerType' = 'securityGroup' - } - 'accessDetails' = @{ - 'unifiedRoles' = @(@{ - 'roleDefinitionId' = "$($Role.roleDefinitionId)" - }) - } - } - New-GraphPostRequest -NoAuthCheck $True -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$($QueueItem)/accessAssignments" -tenantid $env:TenantID -type POST -body $MappingBody -verbose - Start-Sleep -Milliseconds 100 - } catch { - Write-LogMessage -API $APINAME -message "GDAP Group mapping failed - $($role.GroupId): $($_.Exception.Message)" -Sev Error - exit 1 - } - Write-LogMessage -API $APINAME -message "Groups mapped for GDAP Relationship: $($GdapInvite.RowKey)" -Sev Info - } - Remove-AzDataTableEntity @Table -Entity $Invite - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigration.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigration.ps1 deleted file mode 100644 index 7b5ce0a31a1d..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigration.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -using namespace System.Net - -Function Invoke-ExecGDAPMigration { - <# - .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' - - $Groups = $Request.body.gdapRoles - $Tenants = $Request.body.selectedTenants - $Results = [System.Collections.ArrayList]@() - - foreach ($Tenant in $Tenants) { - $obj = [PSCustomObject]@{ - tenant = $Tenant - gdapRoles = $Groups - } - Push-OutputBinding -Name gdapqueue -Value $obj - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Started GDAP Migration for $($tenant.displayName)" -Sev 'Debug' - $results.add("Started GDAP Migration for $($tenant.displayName)") | Out-Null - } - $body = @{Results = @($Results) } - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigrationQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigrationQueue.ps1 deleted file mode 100644 index bb59a42fed9c..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigrationQueue.ps1 +++ /dev/null @@ -1,99 +0,0 @@ - using namespace System.Net - - Function Invoke-ExecGDAPMigrationQueue { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - #$TenantFilter = $env:TenantID -$RoleMappings = $QueueItem.gdapRoles -$tenant = $queueitem.tenant -$Table = Get-CIPPTable -TableName 'gdapmigration' -Write-Host ($QueueItem.tenant | ConvertTo-Json -Compress) -$logRequest = @{ - status = 'Started migration' - tenant = "$($tenant.displayName)" - RowKey = "$($tenant.customerId)" - PartitionKey = 'alert' - startAt = "$((Get-Date).ToString('s'))" -} - -Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null - -if ($RoleMappings) { - $LogRequest['status'] = 'Step 2: Roles selected, creating new GDAP relationship.' - Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null -} -else { - $LogRequest['status'] = 'Migration failed at Step 2: No role mappings created.' - Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null - exit 1 -} -try { - $JSONBody = @{ - 'displayName' = "$((New-Guid).GUID)" - 'partner' = @{ - 'tenantId' = "$env:tenantid" - } - - 'customer' = @{ - 'displayName' = "$($tenant.displayName)" - 'tenantId' = "$($tenant.customerId)" - } - 'accessDetails' = @{ - 'unifiedRoles' = @($RoleMappings | Select-Object roleDefinitionId) - } - 'duration' = 'P730D' - } | ConvertTo-Json -Depth 5 -Compress - Write-Host $JSONBody - $MigrateRequest = New-GraphPostRequest -NoAuthCheck $True -uri 'https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web//v1/delegatedAdminRelationships/migrate' -type POST -body $JSONBody -verbose -tenantid $env:TenantID -scope 'https://api.partnercustomeradministration.microsoft.com/.default' - Start-Sleep -Milliseconds 100 - do { - $CheckActive = New-GraphGetRequest -NoAuthCheck $True -uri "https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web//v1/delegatedAdminRelationships/$($MigrateRequest.id)" -tenantid $env:TenantID -scope 'https://api.partnercustomeradministration.microsoft.com/.default' - Start-Sleep -Milliseconds 200 - } until ($CheckActive.status -eq 'Active') -} -catch { - $LogRequest['status'] = "Migration Failed. Could not create relationship: $($_.Exception.Message)" - Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null -} - - -if ($CheckActive.status -eq 'Active') { - $LogRequest['status'] = 'Step 3: GDAP Relationship active. Mapping groups.' - Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null - foreach ($role in $RoleMappings) { - try { - $Mappingbody = ConvertTo-Json -Depth 10 -InputObject @{ - 'accessContainer' = @{ - 'accessContainerId' = "$($Role.GroupId)" - 'accessContainerType' = 'securityGroup' - } - 'accessDetails' = @{ - 'unifiedRoles' = @(@{ - 'roleDefinitionId' = "$($Role.roleDefinitionId)" - }) - } - } - $RoleActiveID = New-GraphPostRequest -NoAuthCheck $True -uri "https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web//v1/delegatedAdminRelationships/$($MigrateRequest.id)/accessAssignments" -tenantid $env:TenantID -type POST -body $MappingBody -verbose -scope 'https://api.partnercustomeradministration.microsoft.com/.default' - Start-Sleep -Milliseconds 400 - $LogRequest['status'] = "Step 3: GDAP Relationship active. Mapping group: $($Role.GroupId)" - Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null - } - catch { - $LogRequest['status'] = "Migration Failed. Could not create group mapping for group $($role.GroupId): $($_.Exception.Message)" - Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null - exit 1 - } - #$CheckActiveRole = New-GraphGetRequest -NoAuthCheck $True -uri "https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web//v1/delegatedAdminRelationships/$($MigrateRequest.id)/accessAssignments/$($RoleActiveID.id)" -tenantid $env:TenantId -scope 'https://api.partnercustomeradministration.microsoft.com/.default' - } - $LogRequest['status'] = 'Migration Complete' - Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null - -} - - - } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardUser.ps1 deleted file mode 100644 index 2391bd643995..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardUser.ps1 +++ /dev/null @@ -1,51 +0,0 @@ -using namespace System.Net - -Function Invoke-ExecOffboardUser { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - try { - $APIName = 'ExecOffboardUser' - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $Username = $request.body.user - $Tenantfilter = $request.body.tenantfilter - $Results = if ($Request.body.Scheduled.enabled) { - $taskObject = [PSCustomObject]@{ - TenantFilter = $Tenantfilter - Name = "Offboarding: $Username" - Command = @{ - value = 'Invoke-CIPPOffboardingJob' - } - Parameters = @{ - Username = $Username - APIName = 'Scheduled Offboarding' - options = $request.body - } - ScheduledTime = $Request.body.scheduled.date - PostExecution = @{ - Webhook = [bool]$Request.Body.PostExecution.webhook - Email = [bool]$Request.Body.PostExecution.email - PSA = [bool]$Request.Body.PostExecution.psa - } - } - - Add-CIPPScheduledTask -Task $taskObject -hidden $false - } else { - Invoke-CIPPOffboardingJob -Username $Username -TenantFilter $Tenantfilter -Options $Request.body -APIName $APIName -ExecutingUser $request.headers.'x-ms-client-principal' - } - $StatusCode = [HttpStatusCode]::OK - $body = [pscustomobject]@{'Results' = @($results) } - } catch { - $StatusCode = [HttpStatusCode]::Forbidden - $body = $_.Exception.message - } - $Request.Body.PostExecution - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $Body - }) - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 deleted file mode 100644 index a224182bf1c9..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 +++ /dev/null @@ -1,198 +0,0 @@ -using namespace System.Net - -Function Invoke-ExecSAMSetup { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - $UserCreds = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($request.headers.'x-ms-client-principal')) | ConvertFrom-Json) - if ($Request.query.error) { - Add-Type -AssemblyName System.Web - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - ContentType = 'text/html' - StatusCode = [HttpStatusCode]::Forbidden - Body = Get-normalizedError -Message [System.Web.HttpUtility]::UrlDecode($Request.Query.error_description) - }) - exit - } - if ('admin' -notin $UserCreds.userRoles) { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - ContentType = 'text/html' - StatusCode = [HttpStatusCode]::Forbidden - Body = 'Could not find an admin cookie in your browser. Make sure you do not have an adblocker active, use a Chromium browser, and allow cookies. If our automatic refresh does not work, try pressing the URL bar and hitting enter. We will try to refresh ourselves in 3 seconds.' - }) - exit - } - - $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - if ($env:MSI_SECRET) { - Disable-AzContextAutosave -Scope Process | Out-Null - $AzSession = Connect-AzAccount -Identity - } - if (!$ENV:SetFromProfile) { - Write-Host "We're reloading from KV" - Get-CIPPAuthentication - } - - $KV = $ENV:WEBSITE_DEPLOYMENT_ID - $Table = Get-CIPPTable -TableName SAMWizard - $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) - - try { - if ($Request.query.count -lt 1 ) { $Results = 'No authentication code found. Please go back to the wizard.' } - - if ($request.body.setkeys) { - if ($request.body.tenantid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $request.body.tenantid -AsPlainText -Force) } - if ($request.body.RefreshToken) { Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $request.body.RefreshToken -AsPlainText -Force) } - if ($request.body.applicationid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $request.body.applicationid -AsPlainText -Force) } - if ($request.body.applicationsecret) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $request.body.applicationsecret -AsPlainText -Force) } - $Results = @{ Results = 'The keys have been replaced. Please perform a permissions check.' } - } - if ($Request.query.error -eq 'invalid_client') { $Results = 'Client ID was not found in Azure. Try waiting 10 seconds to try again, if you have gotten this error after 5 minutes, please restart the process.' } - if ($request.query.code) { - try { - $TenantId = $Rows.tenantid - if (!$TenantId) { $TenantId = $ENV:TenantId } - $AppID = $Rows.appid - if (!$AppID) { $appid = $env:ApplicationId } - $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 - $clientsecret = Get-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -AsPlainText - if (!$clientsecret) { $clientsecret = $ENV:ApplicationSecret } - $RefreshToken = Invoke-RestMethod -Method POST -Body "client_id=$appid&scope=https://graph.microsoft.com/.default+offline_access+openid+profile&code=$($request.query.code)&grant_type=authorization_code&redirect_uri=$($url)&client_secret=$clientsecret" -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" - Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $RefreshToken.refresh_token -AsPlainText -Force) - $Results = 'Authentication is now complete. You may now close this window.' - try { - $SetupPhase = $rows.validated = $true - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - } catch { - #no need. - } - } catch { - $Results = "Authentication failed. $($_.Exception.message)" - } - } - if ($request.query.CreateSAM) { - $Rows = @{ - RowKey = 'setup' - PartitionKey = 'setup' - validated = $false - SamSetup = 'NotStarted' - partnersetup = $false - appid = 'NotStarted' - tenantid = 'NotStarted' - } - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) - - if ($Request.query.partnersetup) { - $SetupPhase = $Rows.partnersetup = $true - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - } - $step = 1 - $DeviceLogon = New-DeviceLogin -clientid '1b730954-1685-4b74-9bfd-dac224a7b894' -Scope 'https://graph.microsoft.com/.default' -FirstLogon - $SetupPhase = $rows.SamSetup = [string]($DeviceLogon | ConvertTo-Json) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - $Results = @{ message = "Your code is $($DeviceLogon.user_code). Enter the code" ; step = $step; url = $DeviceLogon.verification_uri } - } - if ($Request.query.CheckSetupProcess -and $request.query.step -eq 1) { - $SAMSetup = $Rows.SamSetup | ConvertFrom-Json -ErrorAction SilentlyContinue - $Token = (New-DeviceLogin -clientid '1b730954-1685-4b74-9bfd-dac224a7b894' -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) - if ($token.Access_Token) { - $step = 2 - $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 - $PartnerSetup = $Rows.partnersetup - $TenantId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/organization' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method GET -ContentType 'application/json').value.id - $SetupPhase = $rows.tenantid = [string]($TenantId) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - if ($PartnerSetup) { - $app = Get-Content '.\Cache_SAMSetup\SAMManifest.json' | ConvertFrom-Json - $App.web.redirectUris = @($App.web.redirectUris + $URL) - $app = $app | ConvertTo-Json -Depth 15 - $AppId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/applications' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') - $rows.appid = [string]($AppId.appId) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - $attempt = 0 - do { - try { - try { - $SPNDefender = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"fc780465-2017-40d4-a0c5-307022471b92`" }" -ContentType 'application/json') - } catch { - Write-Host "didn't deploy spn for defender, probably already there." - } - try { - $SPNTeams = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"48ac35b8-9aa8-4d74-927d-1f4a14a0b239`" }" -ContentType 'application/json') - } catch { - Write-Host "didn't deploy spn for Teams, probably already there." - } - $SPN = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"$($AppId.appId)`" }" -ContentType 'application/json') - Start-Sleep 3 - $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method Get -ContentType 'application/json').value.id - Write-Host "Id is $GroupID" - $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN.id)`"}" -ContentType 'application/json') - Write-Host 'Added to adminagents' - $attempt ++ - } catch { - $attempt ++ - } - } until ($attempt -gt 5) - } else { - $app = Get-Content '.\Cache_SAMSetup\SAMManifestNoPartner.json' - $AppId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/applications' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') - $rows.appid = [string]($AppId.appId) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - } - $AppPassword = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications/$($AppID.id)/addPassword" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body '{"passwordCredential":{"displayName":"CIPPInstall"}}' -ContentType 'application/json').secretText - Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $TenantId -AsPlainText -Force) - Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appid -AsPlainText -Force) - Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $AppPassword -AsPlainText -Force) - $Results = @{'message' = 'Created application. Waiting 30 seconds for Azure propagation'; step = $step } - } else { - $step = 1 - $Results = @{ message = "Your code is $($SAMSetup.user_code). Enter the code " ; step = $step; url = $SAMSetup.verification_uri } - } - - } - switch ($request.query.step) { - 2 { - $step = 2 - $TenantId = $Rows.tenantid - $AppID = $rows.appid - $PartnerSetup = $Rows.partnersetup - $SetupPhase = $rows.SamSetup = [string]($FirstLogonRefreshtoken | ConvertTo-Json) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 - $Validated = $Rows.validated - if ($Validated) { $step = 3 } - $Results = @{ message = 'Give the next approval by clicking ' ; step = $step; url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)" } - } - 3 { - - $step = 4 - $Results = @{'message' = 'Received token.'; step = $step } - - - } - 4 { - Remove-AzDataTableEntity @Table -Entity $Rows - - $step = 5 - $Results = @{'message' = 'setup completed.'; step = $step - } - } - } - - } catch { - $Results = [pscustomobject]@{'Results' = "Failed. $($_.InvocationInfo.ScriptLineNumber): $($_.Exception.message)" ; step = $step } - } - - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecScheduledCommand.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecScheduledCommand.ps1 deleted file mode 100644 index ce73476b5c97..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecScheduledCommand.ps1 +++ /dev/null @@ -1,93 +0,0 @@ - using namespace System.Net - - Function Invoke-ExecScheduledCommand { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - $commandParameters = $QueueItem.Parameters - -$tenant = $QueueItem.Parameters['TenantFilter'] -Write-Host 'started task' -try { - try { - $results = & $QueueItem.command @commandParameters - } - catch { - $results = "Task Failed: $($_.Exception.Message)" - - } - - Write-Host 'ran the command' - if ($results -is [String]) { - $results = @{ Results = $results } - } - if ($results -is [array]) { - $results = $results | Where-Object { $_ -is [string] } - $results = $results | ForEach-Object { @{ Results = $_ } } - } - - $results = $results | Select-Object * -ExcludeProperty RowKey, PartitionKey - - $StoredResults = $results | ConvertTo-Json -Compress -Depth 20 | Out-String - 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 { - $errorMessage = $_.Exception.Message - if ($task.Recurrence -gt 0) { $State = 'Failed - Planned' } else { $State = 'Failed' } - Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$errorMessage" - TaskState = $State - } - Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error -} - - -$TableDesign = '' -$HTML = ($results | Select-Object * -ExcludeProperty RowKey, PartitionKey | ConvertTo-Html -Fragment) -replace '
', "$TableDesign
" | Out-String -$title = "Scheduled Task $($task.Name) - $($task.ExpectedRunTime)" -Write-Host $title -switch -wildcard ($task.PostExecution) { - '*psa*' { Send-CIPPAlert -Type 'psa' -Title $title -HTMLContent $HTML } - '*email*' { Send-CIPPAlert -Type 'email' -Title $title -HTMLContent $HTML } - '*webhook*' { - $Webhook = [PSCustomObject]@{ - 'Tenant' = $tenant - 'TaskInfo' = $QueueItem.TaskInfo - 'Results' = $Results - } - Send-CIPPAlert -Type 'webhook' -Title $title -JSONContent $($Webhook | ConvertTo-Json -Depth 20) - } -} - -Write-Host 'ran the command' - -if ($task.Recurrence -le '0' -or $task.Recurrence -eq $null) { - Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$StoredResults" - TaskState = 'Completed' - } -} -else { - $nextRun = (Get-Date).AddDays($task.Recurrence) - $nextRunUnixTime = [int64]($nextRun - (Get-Date '1/1/1970')).TotalSeconds - Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$StoredResults" - TaskState = 'Planned' - ScheduledTime = "$nextRunUnixTime" - } -} -Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Successfully executed task: $($task.name)" -sev Info - - } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUniversalSearch.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUniversalSearch.ps1 index 838f23ee3cd6..eda323666fa1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUniversalSearch.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUniversalSearch.ps1 @@ -19,8 +19,29 @@ Function Invoke-ExecUniversalSearch { try { $tenantfilter = Get-Tenants - $payload = '{ "returnsPartialResults":true, "displayName":"getUsers", "target": { "allTenants":true }, "operationDefinition": { "values":["@sys.normalize([ConsistencyLevel: eventual GET /v1.0/users?$top=5&$search=\"userPrincipalName:' + $request.query.name + '\" OR \"displayName:' + $request.query.name + '\"])"] }, "aggregationDefinition": { "values":["@sys.append([/result],50)"] } }' - $GraphRequest = (New-GraphPOSTRequest -noauthcheck $true -type 'POST' -uri 'https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedTenantOperations' -tenantid $env:TenantID -body $payload).result.Results | ConvertFrom-Json | Where-Object { $_.'_TenantId' -in $tenantfilter.customerId } + $payload = [PSCustomObject]@{ + returnsPartialResults = $false + displayName = 'getUsers' + target = [PSCustomObject]@{ + allTenants = $true + } + operationDefinition = [PSCustomObject]@{ + values = @( + "@sys.normalize([ConsistencyLevel: eventual GET /v1.0/users?`$top=5&`$search=`"userPrincipalName:$($Request.query.name)`" OR `"displayName:$($Request.query.name)`"])" + ) + } + aggregationDefinition = [PSCustomObject]@{ + values = @( + '@sys.append([/result],50)' + ) + } + } | ConvertTo-Json -Depth 10 + $GraphRequest = (New-GraphPOSTRequest -noauthcheck $true -type 'POST' -uri 'https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedTenantOperations' -tenantid $env:TenantID -body $payload -IgnoreErrors $true) + if (!$GraphRequest.result.results) { + $GraphRequest = ($GraphRequest.error.message | ConvertFrom-Json).result.results | ConvertFrom-Json | Where-Object { $_.'_TenantId' -in $tenantfilter.customerId } + } else { + $GraphRequest.result.Results | ConvertFrom-Json -ErrorAction SilentlyContinue | Where-Object { $_.'_TenantId' -in $tenantfilter.customerId } + } $StatusCode = [HttpStatusCode]::OK } catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUserSettings.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUserSettings.ps1 index 76469b49b477..ab9092f13c1d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUserSettings.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUserSettings.ps1 @@ -11,18 +11,17 @@ function Invoke-ExecUserSettings { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' try { - $object = $request.body.currentSettings | Select-Object * -ExcludeProperty CurrentTenant, pageSizes, sidebarShow, sidebarUnfoldable, _persist | ConvertTo-Json -Compress + $object = $request.body.currentSettings | Select-Object * -ExcludeProperty CurrentTenant, pageSizes, sidebarShow, sidebarUnfoldable, _persist | ConvertTo-Json -Compress -Depth 10 $Table = Get-CippTable -tablename 'UserSettings' $Table.Force = $true Add-CIPPAzDataTableEntity @Table -Entity @{ JSON = "$object" RowKey = "$($Request.body.user)" - PartitionKey = "UserSettings" + PartitionKey = 'UserSettings' } $StatusCode = [HttpStatusCode]::OK - $Results = [pscustomobject]@{"Results" = "Successfully added user settings" } - } - catch { + $Results = [pscustomobject]@{'Results' = 'Successfully added user settings' } + } catch { $ErrorMsg = Get-NormalizedError -message $($_.Exception.Message) $Results = "Function Error: $ErrorMsg" $StatusCode = [HttpStatusCode]::BadRequest diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAlertsQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAlertsQueue.ps1 index 8d12a5c8f94d..cb26d77ec876 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAlertsQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAlertsQueue.ps1 @@ -20,26 +20,29 @@ Function Invoke-ListAlertsQueue { $CurrentStandards = foreach ($QueueFile in $QueuedApps) { [PSCustomObject]@{ - tenantName = $QueueFile.tenant - AdminPassword = [bool]$QueueFile.AdminPassword - DefenderMalware = [bool]$QueueFile.DefenderMalware - DefenderStatus = [bool]$QueueFile.DefenderStatus - MFAAdmins = [bool]$QueueFile.MFAAdmins - MFAAlertUsers = [bool]$QueueFile.MFAAlertUsers - NewGA = [bool]$QueueFile.NewGA - NewRole = [bool]$QueueFile.NewRole - QuotaUsed = [bool]$QueueFile.QuotaUsed - UnusedLicenses = [bool]$QueueFile.UnusedLicenses - OverusedLicenses = [bool]$QueueFile.OverusedLicenses - AppSecretExpiry = [bool]$QueueFile.AppSecretExpiry - ApnCertExpiry = [bool]$QueueFile.ApnCertExpiry - VppTokenExpiry = [bool]$QueueFile.VppTokenExpiry - DepTokenExpiry = [bool]$QueueFile.DepTokenExpiry - NoCAConfig = [bool]$QueueFile.NoCAConfig - SecDefaultsUpsell = [bool]$QueueFile.SecDefaultsUpsell - SharePointQuota = [bool]$QueueFile.SharePointQuota - ExpiringLicenses = [bool]$QueueFile.ExpiringLicenses - tenantId = $QueueFile.tenantid + tenantName = $QueueFile.tenant + AdminPassword = [bool]$QueueFile.AdminPassword + DefenderMalware = [bool]$QueueFile.DefenderMalware + DefenderStatus = [bool]$QueueFile.DefenderStatus + MFAAdmins = [bool]$QueueFile.MFAAdmins + MFAAlertUsers = [bool]$QueueFile.MFAAlertUsers + NewGA = [bool]$QueueFile.NewGA + NewRole = [bool]$QueueFile.NewRole + QuotaUsed = [bool]$QueueFile.QuotaUsed + UnusedLicenses = [bool]$QueueFile.UnusedLicenses + OverusedLicenses = [bool]$QueueFile.OverusedLicenses + AppSecretExpiry = [bool]$QueueFile.AppSecretExpiry + ApnCertExpiry = [bool]$QueueFile.ApnCertExpiry + VppTokenExpiry = [bool]$QueueFile.VppTokenExpiry + DepTokenExpiry = [bool]$QueueFile.DepTokenExpiry + NoCAConfig = [bool]$QueueFile.NoCAConfig + SecDefaultsUpsell = [bool]$QueueFile.SecDefaultsUpsell + SharePointQuota = [bool]$QueueFile.SharePointQuota + ExpiringLicenses = [bool]$QueueFile.ExpiringLicenses + NewAppApproval = [bool]$QueueFile.NewAppApproval + SharePointQuotaQuota = [int]$QueueFile.SharePointQuota + QuotaUsedQuota = [int]$QueueFile.QuotaUsed + tenantId = $QueueFile.tenantid } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExConnectorTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExConnectorTemplates.ps1 index 28dc15275c83..897fcf53b3ed 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExConnectorTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExConnectorTemplates.ps1 @@ -22,7 +22,7 @@ Function Invoke-ListExConnectorTemplates { $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $GUID $data | Add-Member -NotePropertyName 'cippconnectortype' -NotePropertyValue $Direction $data - } + } | Sort-Object -Property displayName if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property RowKey -EQ $Request.query.id } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 index 2bec443954b5..55fb22f3f99e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 @@ -58,20 +58,20 @@ Function Invoke-ListExternalTenantInfo { # Invoke $response = Invoke-RestMethod -UseBasicParsing -Method Post -Uri 'https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc' -Body $body -Headers $headers - + # Return $TenantDomains = $response.Envelope.body.GetFederationInformationResponseMessage.response.Domains.Domain | Sort-Object } $results = [PSCustomObject]@{ GraphRequest = $GraphRequest - Domains = $TenantDomains + Domains = @($TenantDomains) } # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $results + StatusCode = $StatusCode + Body = $results }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionStats.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionStats.ps1 new file mode 100644 index 000000000000..7a0ca462e60e --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionStats.ps1 @@ -0,0 +1,96 @@ +using namespace System.Net + +Function Invoke-ListFunctionStats { + <# + .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' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + # Interact with query parameters or the body of the request. + + try { + $TenantFilter = $Request.Query.TenantFilter + $PartitionKey = $Request.Query.FunctionType + + $Timestamp = if (![string]::IsNullOrEmpty($Request.Query.Interval) -and ![string]::IsNullOrEmpty($Request.Query.Time)) { + switch ($Request.Query.Interval) { + 'Days' { + (Get-Date).AddDays(-$Request.Query.Time).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffK') + } + 'Hours' { + (Get-Date).AddHours(-$Request.Query.Time).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffK') + } + 'Minutes' { + (Get-Date).AddMinutes(-$Request.Query.Time).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffK') + } + } + } else { + (Get-Date).AddDays(-1).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffK') + } + $Table = Get-CIPPTable -tablename 'CippFunctionStats' + + if (!$PartitionKey) { $PartitionKey = 'Durable' } + if (![string]::IsNullOrEmpty($TenantFilter) -and $TenantFilter -ne 'AllTenants') { + $TenantQuery = " and (tenant eq '{0}' or Tenant eq '{0}' or Tenantid eq '{0}' or tenantid eq '{0}')" -f $TenantFilter + } else { + $TenantQuery = '' + } + $Filter = "PartitionKey eq '{0}' and Start ge datetime'{1}'{2}" -f $PartitionKey, $Timestamp, $TenantQuery + + $Entries = Get-CIPPAzDataTableEntity @Table -Filter $Filter + $FunctionList = $Entries | Group-Object -Property FunctionName + $StandardList = $Entries | Where-Object { $_.Standard } | Group-Object -Property Standard + $FunctionStats = foreach ($Function in $FunctionList) { + $Stats = $Function.Group | Measure-Object -Property Duration -AllStats + [PSCustomObject]@{ + 'Name' = $Function.Name + 'ExecutionCount' = $Function.Count + 'TotalSeconds' = $Stats.Sum + 'MaxSeconds' = $Stats.Maximum + 'AvgSeconds' = $Stats.Average + } + } + $StandardStats = foreach ($Standard in $StandardList) { + $Stats = $Standard.Group | Measure-Object -Property Duration -AllStats + [PSCustomObject]@{ + 'Name' = $Standard.Name + 'ExecutionCount' = $Standard.Count + 'TotalSeconds' = $Stats.Sum + 'MaxSeconds' = $Stats.Maximum + 'AvgSeconds' = $Stats.Average + } + } + $Status = [HttpStatusCode]::OK + $Body = @{ + Results = @{ + Functions = @($FunctionStats) + Standards = @($StandardStats) + } + Metadata = @{ + Filter = $Filter + } + } + } catch { + $Status = [HttpStatusCode]::BadRequest + $Body = @{ + Results = @() + Metadata = @{ + Filter = $Filter + Exception = $_.Exception.Message + } + } + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $Status + Body = $Body + }) -Clobber + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 index 00ddc0156cbb..ddb0c55b1444 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 @@ -9,13 +9,13 @@ Function Invoke-ListGenericAllTenants { param($Request, $TriggerMetadata) $TableURLName = ($QueueItem.tolower().split('?').Split('/') | Select-Object -First 1).toString() - $QueueKey = (Get-CippQueue | Where-Object -Property Name -EQ $TableURLName | Select-Object -Last 1).RowKey + $QueueKey = (Invoke-ListCippQueue | Where-Object -Property Name -EQ $TableURLName | Select-Object -Last 1).RowKey Update-CippQueueEntry -RowKey $QueueKey -Status 'Started' $Table = Get-CIPPTable -TableName "cache$TableURLName" $fullUrl = "https://graph.microsoft.com/beta/$QueueItem" Get-CIPPAzDataTableEntity @Table | Remove-AzDataTableEntity @table - $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { + $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName Import-Module '.\Modules\AzBobbyTables' Import-Module '.\Modules\CIPPCore' @@ -27,7 +27,7 @@ Function Invoke-ListGenericAllTenants { Tenant = $domainName CippStatus = "Could not connect to tenant. $($_.Exception.message)" } - } + } } Update-CippQueueEntry -RowKey $QueueKey -Status 'Processing' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphRequest.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphRequest.ps1 index e6f2f4e3ff40..95866c41c395 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphRequest.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphRequest.ps1 @@ -3,6 +3,9 @@ function Invoke-ListGraphRequest { <# .FUNCTIONALITY Entrypoint + + .ROLE + Core.Read #> [CmdletBinding()] param($Request, $TriggerMetadata) @@ -47,6 +50,10 @@ function Invoke-ListGraphRequest { $Parameters.'$search' = $Request.Query.'$search' } + if ($Request.Query.'$format') { + $Parameters.'$format' = $Request.Query.'$format' + } + $GraphRequestParams = @{ Endpoint = $Request.Query.Endpoint Parameters = $Parameters @@ -89,17 +96,31 @@ function Invoke-ListGraphRequest { $GraphRequestParams.SkipCache = [System.Boolean]$Request.Query.SkipCache } + if ($Request.Query.ListProperties) { + $GraphRequestParams.NoPagination = $true + $GraphRequestParams.Parameters.'$select' = '' + if ($Request.Query.TenantFilter -eq 'AllTenants') { + $GraphRequestParams.TenantFilter = (Get-Tenants | Select-Object -First 1).customerId + } + } + Write-Host ($GraphRequestParams | ConvertTo-Json) $Metadata = $GraphRequestParams try { $Results = Get-GraphRequestList @GraphRequestParams - if ($Results.Queued -eq $true) { - $Metadata.Queued = $Results.Queued - $Metadata.QueueMessage = $Results.QueueMessage - $Metadata.QueuedId = $Results.QueueId - $Results = @() + + if ($Request.Query.ListProperties) { + $Columns = ($Results | Select-Object -First 1).PSObject.Properties.Name + $Results = $Columns | Where-Object { @('Tenant', 'CippStatus') -notcontains $_ } + } else { + if ($Results.Queued -eq $true) { + $Metadata.Queued = $Results.Queued + $Metadata.QueueMessage = $Results.QueueMessage + $Metadata.QueuedId = $Results.QueueId + $Results = @() + } } $GraphRequestData = [PSCustomObject]@{ Results = @($Results) @@ -108,11 +129,17 @@ function Invoke-ListGraphRequest { $StatusCode = [HttpStatusCode]::OK } catch { $GraphRequestData = "Graph Error: $($_.Exception.Message) - Endpoint: $($Request.Query.Endpoint)" - $StatusCode = [HttpStatusCode]::BadRequest + if ($Request.Query.IgnoreErrors) { $StatusCode = [HttpStatusCode]::OK } + else { $StatusCode = [HttpStatusCode]::BadRequest } } + if ($request.Query.Sort) { + $GraphRequestData.Results = $GraphRequestData.Results | Sort-Object -Property $request.Query.Sort + } + $Outputdata = $GraphRequestData | ConvertTo-Json -Depth 20 -Compress + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode - Body = $GraphRequestData | ConvertTo-Json -Depth 20 -Compress + Body = $Outputdata }) } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 index e3270a1834b0..d9c02d090fb2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 @@ -32,11 +32,11 @@ Function Invoke-ListIntuneTemplates { $data = $_.RAWJson | ConvertFrom-Json $data | Add-Member -NotePropertyName 'displayName' -NotePropertyValue $_.Displayname -Force $data | Add-Member -NotePropertyName 'description' -NotePropertyValue $_.Description -Force - $data | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_.Type - $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.GUID + $data | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_.Type -Force + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.GUID -Force $data - } - } + } | Sort-Object -Property displayName + } if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property guid -EQ $Request.query.id } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 index 27c2db9767a3..6870b020be6e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 @@ -18,18 +18,38 @@ Function Invoke-ListLicenses { # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.TenantFilter $RawGraphRequest = if ($TenantFilter -ne 'AllTenants') { - $GraphRequest = Get-CIPPLicenseOverview -TenantFilter $TenantFilter + $GraphRequest = Get-CIPPLicenseOverview -TenantFilter $TenantFilter | ForEach-Object { + $TermInfo = $_.TermInfo | ConvertFrom-Json -ErrorAction SilentlyContinue + $_.TermInfo = $TermInfo + $_ + } } else { $Table = Get-CIPPTable -TableName cachelicenses $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) if (!$Rows) { - Push-OutputBinding -Name LicenseQueue -Value (Get-Date).ToString() + #Push-OutputBinding -Name LicenseQueue -Value (Get-Date).ToString() $GraphRequest = [PSCustomObject]@{ Tenant = 'Loading data for all tenants. Please check back in 1 minute' License = 'Loading data for all tenants. Please check back in 1 minute' } + $Tenants = Get-Tenants -IncludeErrors | ForEach-Object { $_ | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'ListLicensesQueue'; $_ } + + if (($Tenants | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'ListLicensesOrchestrator' + Batch = @($Tenants) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } } else { - $GraphRequest = $Rows + $GraphRequest = $Rows | ForEach-Object { + $TermInfo = $_.TermInfo | ConvertFrom-Json -ErrorAction SilentlyContinue + $_.TermInfo = $TermInfo + $_ + } } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 deleted file mode 100644 index 7d6c25d9daa7..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -using namespace System.Net - -Function Invoke-ListLicensesAllTenants { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - - $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - - Import-Module '.\Modules\AzBobbyTables' - Import-Module '.\Modules\CIPPCore' - try { - Write-Host "Processing $domainName" - Get-CIPPLicenseOverview -TenantFilter $domainName - } catch { - [pscustomobject]@{ - Tenant = [string]$domainName - License = "Could not connect to client: $($_.Exception.Message)" - 'PartitionKey' = 'License' - 'RowKey' = "$($domainName)-$(New-Guid)" - } - } - } - - $Table = Get-CIPPTable -TableName cachelicenses - foreach ($GraphRequest in $RawGraphRequest) { - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 index 319b43a00995..a3963bd0bc94 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 @@ -12,7 +12,7 @@ Function Invoke-ListLogs { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' if ($request.Query.Filter -eq 'True') { - $LogLevel = if ($Request.query.Severity) { ($Request.query.Severity).split(',') } else { 'Info', 'Warn', 'Error', 'Critical', 'Alert' } + $LogLevel = if ($Request.query.Severity) { ($Request.query.Severity).split(',') } else { 'Info', 'Warn', 'Error', 'Critical', 'Alert' } $PartitionKey = $Request.query.DateFilter $username = $Request.Query.User } else { @@ -25,7 +25,7 @@ Function Invoke-ListLogs { $ReturnedLog = if ($Request.Query.ListLogs) { Get-CIPPAzDataTableEntity @Table -Property PartitionKey | Sort-Object -Unique PartitionKey | Select-Object PartitionKey | ForEach-Object { - @{ + @{ value = $_.PartitionKey label = $_.PartitionKey } @@ -34,13 +34,17 @@ Function Invoke-ListLogs { $Filter = "PartitionKey eq '{0}'" -f $PartitionKey $Rows = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username } foreach ($Row in $Rows) { - @{ + $LogData = if ($Row.LogData -and (Test-Json -Json $Row.LogData)) { + $Row.LogData | ConvertFrom-Json + } else { $Row.LogData } + [PSCustomObject]@{ DateTime = $Row.Timestamp Tenant = $Row.Tenant API = $Row.API Message = $Row.Message User = $Row.Username Severity = $Row.Severity + LogData = $LogData TenantID = if ($Row.TenantID -ne $null) { $Row.TenantID } else { diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 index 32314d2298d8..99209bfc9c8f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 @@ -24,9 +24,24 @@ Function Invoke-ListMFAUsers { if (!$Rows) { $Queue = New-CippQueueEntry -Name 'MFA Users - All Tenants' -Link '/identity/reports/mfa-report?customerId=AllTenants' Write-Information ($Queue | ConvertTo-Json) - Push-OutputBinding -Name mfaqueue -Value $Queue.RowKey + #Push-OutputBinding -Name mfaqueue -Value $Queue.RowKey $GraphRequest = [PSCustomObject]@{ - UPN = 'Loading data for all tenants. Please check back in 10 minutes' + UPN = 'Loading data for all tenants. Please check back in a few minutes' + } + $Batch = Get-Tenants -IncludeErrors | ForEach-Object { + $_ | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'ListMFAUsersQueue' + $_ | Add-Member -NotePropertyName QueueId -NotePropertyValue $Queue.RowKey + $_ + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'ListMFAUsersOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" } } else { $GraphRequest = $Rows diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 deleted file mode 100644 index 8123a963e1ff..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 +++ /dev/null @@ -1,63 +0,0 @@ -using namespace System.Net - -Function Invoke-ListMFAUsersAllTenants { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - - - Write-Information "Item: $QueueItem" - Write-Information ($TriggerMetadata | ConvertTo-Json) - - try { - Update-CippQueueEntry -RowKey $QueueItem -Status 'Running' - - $GraphRequest = Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module '.\modules\CippCore' - Import-Module '.\Modules\AzBobbyTables' - - $Table = Get-CIPPTable -TableName cachemfa - Try { - $GraphRequest = Get-CIPPMFAState -TenantFilter $domainName -ErrorAction Stop - } catch { - $GraphRequest = $null - } - if (!$GraphRequest) { - $GraphRequest = @{ - Tenant = [string]$tenantName - UPN = [string]$domainName - AccountEnabled = 'none' - PerUser = [string]'Could not connect to tenant' - MFARegistration = 'none' - CoveredByCA = [string]'Could not connect to tenant' - CoveredBySD = 'none' - RowKey = [string]"$domainName" - PartitionKey = 'users' - } - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } - } catch { - $Table = Get-CIPPTable -TableName cachemfa - $GraphRequest = @{ - Tenant = [string]$tenantName - UPN = [string]$domainName - AccountEnabled = 'none' - PerUser = [string]'Could not connect to tenant' - MFARegistration = 'none' - CoveredByCA = [string]'Could not connect to tenant' - CoveredBySD = 'none' - RowKey = [string]"$domainName" - PartitionKey = 'users' - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } finally { - Update-CippQueueEntry -RowKey $QueueItem -Status 'Completed' - } - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 index a4ca980ba7d3..d1de06beada8 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 @@ -19,14 +19,36 @@ Function Invoke-ListMailboxRules { $TenantFilter = $Request.Query.TenantFilter $Table = Get-CIPPTable -TableName cachembxrules + if ($TenantFilter -ne 'AllTenants') { + $Table.Filter = "Tenant eq '$TenantFilter'" + } $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).Addhours(-1) if (!$Rows) { - Push-OutputBinding -Name mbxrulequeue -Value $TenantFilter + #Push-OutputBinding -Name mbxrulequeue -Value $TenantFilter $GraphRequest = [PSCustomObject]@{ Tenant = 'Loading data. Please check back in 1 minute' Licenses = 'Loading data. Please check back in 1 minute' } + $Batch = if ($TenantFilter -eq 'AllTenants') { + Get-Tenants -IncludeErrors | ForEach-Object { $_ | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'ListMailboxRulesQueue'; $_ } + } else { + [PSCustomObject]@{ + defaultDomainName = $TenantFilter + FunctionName = 'ListMailboxRulesQueue' + } + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'ListMailboxRulesOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } + } else { if ($TenantFilter -ne 'AllTenants') { $Rows = $Rows | Where-Object -Property Tenant -EQ $TenantFilter diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 deleted file mode 100644 index b3909976d4c9..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -using namespace System.Net - -Function Invoke-ListMailboxRulesAllTenants { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - $Tenants = if ($QueueItem -ne 'AllTenants') { - [PSCustomObject]@{ - defaultDomainName = $QueueItem - } - } else { - Get-Tenants - } - $Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module '.\Modules\CIPPcore' - Import-Module '.\Modules\AzBobbyTables' - - try { - - $Rules = New-ExoRequest -tenantid $domainName -cmdlet 'Get-Mailbox' | ForEach-Object -Parallel { - New-ExoRequest -Anchor $_.UserPrincipalName -tenantid $domainName -cmdlet 'Get-InboxRule' -cmdParams @{Mailbox = $_.GUID } - } - foreach ($Rule in $Rules) { - $GraphRequest = @{ - Rules = [string]($Rule | ConvertTo-Json) - RowKey = [string](New-Guid).guid - Tenant = [string]$domainName - PartitionKey = 'mailboxrules' - } - $Table = Get-CIPPTable -TableName cachembxrules - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } - } catch { - $Rules = @{ - Name = "Could not connect to tenant $($_.Exception.message)" - } | ConvertTo-Json - $GraphRequest = @{ - Rules = [string]$Rules - RowKey = [string]$domainName - Tenant = [string]$domainName - - PartitionKey = 'mailboxrules' - } - $Table = Get-CIPPTable -TableName cachembxrules - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } - } - - - - $Table = Get-CIPPTable -TableName cachembxrules - Write-Host "$($GraphRequest.RowKey) - $($GraphRequest.tenant)" - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPendingWebhooks.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPendingWebhooks.ps1 new file mode 100644 index 000000000000..24c7020f0e31 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPendingWebhooks.ps1 @@ -0,0 +1,41 @@ +using namespace System.Net + +Function Invoke-ListPendingWebhooks { + <# + .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' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + try { + $Table = Get-CIPPTable -TableName 'WebhookIncoming' + $Webhooks = Get-CIPPAzDataTableEntity @Table + $Results = $Webhooks | Select-Object -ExcludeProperty RowKey, PartitionKey, ETag, Timestamp + $PendingWebhooks = foreach ($Result in $Results) { + foreach ($Property in $Result.PSObject.Properties.Name) { + if (Test-Json -Json $Result.$Property -ErrorAction SilentlyContinue) { + $Result.$Property = $Result.$Property | ConvertFrom-Json + } + } + $Result + } + } catch { + $PendingWebhooks = @() + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ + Results = @($PendingWebhooks) + Metadata = @{ + Count = ($PendingWebhooks | Measure-Object).Count + } + } + }) +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListScheduledItems.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListScheduledItems.ps1 index d23de674c779..17a650f11196 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListScheduledItems.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListScheduledItems.ps1 @@ -11,7 +11,10 @@ Function Invoke-ListScheduledItems { # Write to the Azure Functions log stream. Write-Host 'PowerShell HTTP trigger function processed a request.' $Table = Get-CIPPTable -TableName 'ScheduledTasks' - $ScheduledTasks = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'ScheduledTask' and Hidden ne 'True'" + $ScheduledTasks = foreach ($Task in Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'ScheduledTask' and Hidden ne 'True'") { + $Task.Parameters = $Task.Parameters | ConvertFrom-Json + $Task + } # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicPhishingCheck.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicPhishingCheck.ps1 index 32541d596cbf..37136c230bdd 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicPhishingCheck.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicPhishingCheck.ps1 @@ -13,8 +13,10 @@ Function Invoke-PublicPhishingCheck { $validList = @( 'https://login.microsoftonline.com', 'https://login.microsoft.net', - 'https://login.microsoft.com' - 'https://autologon.microsoftazuread-sso.com' + 'https://login.microsoft.com', + 'https://autologon.microsoftazuread-sso.com', + 'https://tasks.office.com', + 'https://login.windows.net' ) $matchedUrls = $validList | Where-Object { ([uri]$_).Host -in ([uri]$($request.headers.Referer)).Host } @@ -23,7 +25,13 @@ Function Invoke-PublicPhishingCheck { Write-Host 'Not being Phished, no issue' } else { $bytes = [Convert]::FromBase64String('iVBORw0KGgoAAAANSUhEUgAAAbEAAAFUCAIAAAAlO5XXAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAALCCSURBVHhe7X0HoCVZVW2/9zr36zQ9OTLDEERRklkx54AR9ZsjoIjpGwBBFBWzoKKIfFHMqKgfRBQUUD5iQJSkwDA5MDOdu18O96+w965z7+tmhmGGSWd13XN2WHufU6eq9q0b+r6p0Useuqmjo6OjQ5iOvqOjo6Oj18SOjo6OFr0mdnR0dAzoNbGjo6NjQK+JHR0dHQN6Tezo6OgY0GtiR0dHx4BeEzs6OjoG9JrY0dHRMaDXxI6Ojo4BvSZ2dHR0DOg1saOjo2NAr4kdHR0dA3pN7Ojo6BjQa2JHR0fHgF4TOzo6Ogb0mtjR0dExoNfEjo6OjgG9JnZ0dHQM6DWxo6OjIzHqNbGjo+N+jtGmTetoRxR6Tezo6Lg/guVvSi0ewjpUlsVeEzs6Ou4HcPlDi1tC3hViW6ewNjVY1Paa2NHRcZ8Ey56b3OJOUNtIN4airdvoijnqNbGjo+M+BJc51jjdAFJgpVPJQ/mDqiLoV8q8Q5yKMLv6584dHR33ZriiZe1j4VOZYysLqx66NE4w8YjKOJB7Tezo6Li3oaob7/hcy7Sx2JWcN4OwrFm1ME4rMjfWzV4TOzo67iWIMqeK5uqGB9Vsw7s+/upYTGwW4nW0+eUSX22viR0dHfdMqHKpV8GCgFfKKmR0aQs7NtU2yGuwu6yt0w7II1m6Q9ooNsoMYb3fJ3Z0dNxzoBoVBcs3eqxxUHXrR0HtGkoYSpsI0Ckkn0lUHMPoSofWRiq0OI9eLOd9pY39/cSOjo57AlieXKeoDDULYLWS4LqGlgSUNqvYUNQUnnWNRruCAAEElVFuolUbriD3mtjR0XF3wDWIm6uYChketLhstVsSvBWHeWxXICS6tNEChwS2qKH6kraUqIMRVRxuvSZ2dHR8SODKFa1qUCFKmD45AcMyKPWSlq5ptt6cwXZu+T6jiprUlKliAxk1FOUuP34hWXeXYDCVaGp7Tezo6LjroHLDDQ+0vjVDKwvlEetgFClFWPWLWdPCpbu8iLJX2QDnYQgDxDG5Kp35NXQaTYgRTWbh7Ojo6LjzMBQdtK41KFIqPZRVyOCKOgVVluCjyxSDUXbwaUSjnOViHmzTyfeI5mgg2ykrZ4wiOTZPAEaY+3dxOjo6PkhMuaCoRcWhgKrUtJum+Emx1fCmi2/zqd7RBaMLpQjORg6YCNetH4udQmyPPMl0nlBxB1pl1K1rn5Lz3jBDaHQqbP1z546OjjuAKjSsL6p3/oEZSFFligALFaqAK5GI8opGb5YzchyohCTYZRmVEbeElhXLljGDChc6RKFlEk0vOJpbxHpzNRSHgf39xI6OjtsP1hTVEbaqWaHabouLTlaZ4NjuAiQBJocwKmtZ1C9ZJHHQ8PqmD4TKY75kEIbX5jKGHYJvRc1X7eaOKDkJIjOzjGv9tXNHR8fpgDLBtopLtq5E3qqasPToZq0KDQVnKKNkC21Oy3bHq9oslN7CYjWj0FGusdKOafhDauhVbUXnBljFXkAo2ZPP/wTT0dHRYahsoHG5icKkosPykZXOLYUqcChDVWXqM2Kp2CBRSDJfbmcho9fGad3rqai1sRCoglllMUO4eYatJd9G9C2hLU7oPDBZNo0GTNhz6/eJHR0dRlQHbKoUrBGoHU0ZclWKsiImrDRKpl00mlVffAcHAQmDjCbTOXNtzCkCHhiFFpc2qA5MQoQ0EwuLZJuitormyZBvIfNHiO9t09K/i9PRcb+Ea5Y2FoJqXX2wqYui5lKCTWq0Uv25CjaSFYsHU7mVS5kosE7pdoyjOHOSsQFFpgX8/Ooiw6v1LZ7GhYAHVYdIgj1ypp3DeXNsbsOIaVLTa2JHx/0JLgHcJKMooGvrCGtWuibINqIDBy3sbGWxC5YgnCqtW4CyvihDrzl+nQumX3QrSjmUUy5mk+zaCj0ymyaBLg1kvmPtbWXkMQFtlXVuSNjfT+zouI9DhSOKglrfZMkjb9YmVAfT0Na9VdURgMZ0kZwtqwmEZMbNHewKc5Q3MmHSDSBa+sWhN/OQIAsE2qXAa9k5Kee07UUgwAw1H9OqdGpKnBuJEhTlcZkEDdT+fmJHx30QvsJ1tbMcqK3NRYpGlw9ZIbN1NATVFDMppEwvihE2FxTY1TJVDkeOvIAtHstDMI9DxDSBW1XDSuXRFcgRJTNE9mgzAx4Ml9GtXQz0raWM3FQZ2zl4aLv6/3fu6LjvoK75qFNZs9AN1zyMfpXaFo7yWlXJ820jVZOhgiCZDo/XvoCVwJ87rDyisZERgjeWJAsqRlGz/GafZshJmuNUGhoSm6bUUshJ1qBhlLveQMSjaF4WKHQ1n4+zZZJeEzs67s3gxYwLvClDVLX5Itd1npY08pVjU5u8oZSYBiXqpmUTlIe0TEXBxUs5EWILWlgdhSFQ+Fz7IoMiKafATYEACd5q2oiFnHOz3eG0eCARIDhDbdwLeZkfQrM5YcjOH5PsNbGj414CXL1uY3NRSIHeLC6WbRxcvvJbY8rc4IViGvpMZdk11Hy7gubYoaCQYzseFFT1pCdHZAe6jNrPhJkZne1o0cHCPCrTAz8Hiihs6cWj7NE2hIhKlclrPpxmr4kdHfdk6DL19YwOV7gvXWz+fINGMCSb5o3GIrsumKMkdKYXD8q+pRq3uKLF3VZVFlUQGys/Q1KGi7eHVk3WWCSbpjlAYKuyCIUq+xQU4iE4E02ek3QqpTW7hmCLbH67UN5YLtNMkGqL8zCnXkGTQ7HXxI6Oex58AfOK9U9gqRxEIYBdVzsFFRRe4L7CIaTF5SCM423UFwgVhVy2SOVdoThkWsiN/GyhQqKQfGeg3YI2PMqO1vxhc1pxADNZ1JAwyXiE7FiHSOATgypabEWArMlDgp00W0wQOZgg2GtLr4kdHfccxBWLh1uo+X09X6+4ekuN67/KIjZXCgmASwB0W0oGB5tyyN5UBOQ06JLKTeXYUVV0QlXRYZJ8jUyCp5Ec5lHrbTBqXMairRtAuQAIkEwAM8q0vObAjkpHTptHo3NNJAwJLatlBq+MZKpoc537b4V1dNyd4DXpi1lXpq9VtL7+YTEHAooC7558zZspO4uRBG/mD2UiaxlVbR6CglTyMWLOgVt55WY4LCZkrDyZ2UOo5e2YBJMBJkcHu+YZm5LgQUFTZQZsssRwVb/Mx5aLANCo/BBYLoupFh2FZrZVDS2zAnqeymwXHhD6/3fu6LgbwCtTW1y0LgRyULWlaYPmEMeqIkDilS+jtzWlcglwLLOmik1WblSTaQ5pVdok+yUnN9jTSKZTWXAGyZHHat55MVb3dFAhMCRltppzZPZw8lKQ14JSBhzFEBsdLoGWGkhjRes7SjYxOumyQEXHENv7fWJHx10NX5O8ArXxusWWF2Rcw2jhkmxXbDBO648ryRjZsK3H/REUh1gYCNqYWWktu40bK09ARhas5NhCWR0a5vSHLZI9SW6VNquny1wIsCAkN1gdztZGyRBUlIbNSTyKCUGWQKN4zk+m0taswljzgT1pdjkWi+CVpxHknG3/jKWj464CLzlddbgI60Ll5UdDXJB2iS1j0oKgyxWt7SawbWTaTVMS05hEAh682iXTLqYJtEBWfrvhssDGFhlcUzyHCNSUYmtV87FpPia7+pQLNLvsDb4FJaGMmeRrbXoRDruTSHB+E7BhCHQ1AQgy5LgK557mNPBgHtGYB+rw0n5q9JKH0tHR0fHBgxezrrS4LKf0t0pgTEv6dfmVYq9pVqHpCg+j5Cld0nhQppkCLYKNVtk63JDDRsged5gqG9lRHjyEIG4Injwh6zBbp2qpZfS+p2onRXst0xyqgbIVUIZwZAgEjIV1cDkrDFqTKsK9R7JK02K2rnKo7587d3TcccQV5XsZ33H4Ew9dmdws6JaEZN0E8R5Hgr1xy6Prk0KG+6aGsmMhO7k4EwI2EsTkfZkE5yTBw8lCQTkHo0g0gu9AqTQ6ecrclCHmBlVvGgLBtDf3Lt6OzFjAdqo5E/KtaoOvDYktVc4kW8e6pWo5d3nYENvYOdwEJ+eAtJD7+4kdHR8gcNn4WnJd8JUpE1UY00Kj3qqzbC9d9eEDNl2H3mCEKVw2Sh7ICCdJxrySQaMFqvmi0qWxSMjNcnkVFqMMZdRGNA4XAXAdcUIT4AqmE1q1BZtvxOoFaQ5acuviSkqhS3K5wLRMwQRtNuLhgcJbHPPrw2UN533BxqhMyBYjalAz++fOHR23C7yU8nJlBcGmqxrwxYYrKq46e3l1sSBC8uVHjhzMo2uSm2W1TmJCWGorl6Jal/MMsgPRSrexbSl4MpKrZIddAjrLgGm0SLWdWwYCGJRt2gHm1144PAgqTKTl6JTRmuxw55clkpuTa1IhXiUIOBxOW0wAAjcPJ5eN8PrwAU5IDrLJRHK/T+zo2AhdVrpydMHEdZXlgObmYqvq4Erka5JRcusyCybtuozjutWlWN4hIXONBbYbjCF7CCZqMuSIvuDpcip73WYtMGwPl1KxUOYodsHC4SBDKBoIUqGYhq2iLJTXRgq1XNg0T1IkOGG5aMxYdB5UooyNBbDsUTxuqXZxF2SB4qXDWgHkwJXe9f4ZS0dHABdnXmNsLfmKlYhLizAtr2RpkuXhFS7dRsJqyy+OBWTDrYkF0QgT8FB5MujFVauqYQxCpip+uKzWiLKUjKj4JCds7ILTWNiImZpmAUl1hPlsVRQs06g4VjO5ZQBNTDURGSB59yFpBylqubykwVHnPULLWdlusgs0nLkXaJ3Emc0MeDjDCUXe1F87d9yfwcsgt7gPwiYdF4xlvtSCqivKFl+ltviFmAkMh+K2+JmnbnlIboZjNr/I1XVuAlqoaJ3fqjkV5YRB9gTSiw0Ijo1oaybJh2QyZc8KnCwfMDqnZUT5bhFTgmkYS0N4FHiBWBOmJxwLtV0re9ECjJKFLVJJ9lsT5JisaXDasJijiuYkVjkcFFvE8XwqiV0OYUIYZYeMuWWqXhM77mfwZTDR+rKhN7e4chwCwUwL4tuCR1xLslDFlvWCtOadfraOlYqOE/DnsxJ4JTvKluSbzPyVB7HmqDTQmEmcn5XLJU95OCuncpsDVf3yHCCBxgKhKKd2FJ0aFJLDza/XpOR4DjTLa7tDyg5jjkUjNk8mczpVqM6Aze8J5EDRKtz7FZacDI1ZNAG4KtbGEDxhedlS7a+dO+4H4PmPR5aPgCRfxvDg8qiXh7yMcd34Ja0tiJaRNJvTZSBk7HWoSBBMpkWXHDkwpcUgYWJ0u2zRT+NUcmSkYFcyw5UoNQR0WWLClNMDsEd4tWsvpwETVCgeSAjBk5diCzSsU9rUWhHTRiAmnxb69VKXo6TF46JjFZNgp12WAYeEqgzt6BjIx9HzrDY4ZKQgoKfX0xO40r0mdtyH0V6xgAW0w2Uji0nlDZhjfqYKY7Yy0WcmMIRLHsum3nFAeEu3RSoalgYBxqCIltxBACyjbZJpwhoUOeOyh1toixHbHM9M132qmaEtNAWoLkmUxzzK35Q8y4xuBAd4ks5iOluPJdDpyUNujDbZLhJb5x+ySfaIYVFboxsgODPN/T6x4z6GOtV9ovMsr+swHCEMF0V7SaQdra4QJcm7D9sNE6pyGRFVNOmUU2hnUq0tE4Eel6Kvc4ECeJY3tKfIrJ11KhuB8J6qWATNI1a6qolpS/MgAJZrhuHCAuE+N3U2TVr3NRam5BZjuS6TMEGWtXIMbQZSbFYM1JAhoE1azZMWedFp0F4TO+7VyDPeF4nEOO9D1PWDcz19zcUA+HoQfLGFakJeLXEvQ0PAZG6YQCanVhez5sBrz3w8WloVUylsxPe+mNmmdaqSbTfNeZzQSahqGkEbegmylwWA7Hlu9FqI0WHXmwmtNwLbQQ2RSPCeWgozu9oLWzgEWtOcM8TAINghwR80B012NOyVocKR3AtLu/PbJ36QigxWr4kd9y7oZM6TO2xptGylBF0DFH0BFS0DrA40PhhLeyVFLzsFqeYPgtrhapTFhUnNAMitClBVmC9da2wzsghoY1Ag5QgSIZgCDW2dsidnG9miV6zUQo3uFurghaS06xaS4N23XOzW0hJqbt7rpCc8z7Jj9Dx8QB0L08hp4i2SIhqLcoMis6nY2rv+2rnj3gGcsjr7eZb7avF53LRGyGijyXPdyFhfbzQkP8hNXTvlrVYxAQgxpSS0LsMCs0mAyvwe3alylGFijlHbVjR7mUFOz9NcwuESw6hu+PKjbREcKiEXzRqL6+zkVnOgNoRCKdo1Z2WnDGxtdGev7NUjPzNjq9vPyEJ4ntDAGVQd62Al2RFubYzimTntQh4sJrMpDzAWWEKviR33ZAxXlE5YNlJ9/vok5nWF8z7tNo5dPGlMncLYZWOzvbBM847SGUQKDkBBxnBp6InLjAJ5gRIppMI+wzlPhzuDZTHRxzyzpBIghCRAQRIJUcQdUFPKdYDaThWIGiEXGW7Sol6qJyktanT4crYak8S69WOj/ZLA1oxm9MGVKLlc4HIFMg8w8JXKHGmTLrcRm4I3z8WwQKOZ6HtN7LiHACdlXHLNuUtZp2pcS5YlFqiKbTtF3SzEuV+XdzJptjEugyAWDQhvZrClSgOnZEmgy4L4lgNS2ODRzAQS5wC+6u+Q1gIeThWODMwkHsgrll8Zkl2E0G1R8lhDO0WgQTo5ttMUXtql0pEzRNvm58Fa11eUHJsCyTlzEstCwwCoZRnCpQKTrtR9hjibPUA4awjtFFUFovFTC2IZqLAa0TEOVNtrYsfdDZ+ccdaq9QkKhOBzPc/g8gKM1ak/KcCnRLS4CGojSjnlO032ZCo8IMR/VkvCWDZAnBIcW2oI4jvQUoVPCDVbI4xqicwJi3PHLmiIMMnHlcww9jJaqAyBmiegIgutQswjwUNQUatsg6UlaM2JzIyG8zRNfTBhUFRMwHz5gqP1tFotOnpxO29XQ/DQzFlPFCLENLx3TRtfzNSgaBk41Wtix4cePonz8uCjqRFAyHWalgUCzvgkV8tTOhlOC6tP9LBY1VUBG0Oai5+xjgJEUB9ezs11SiZa0oecFDMkjI4yzJFd15ttkc0qIZOjYjJSamLhUovOgU5IjuBRKFSCDUOMPQfAiyYKAfMwqllwhmqV1FP3WwrWHZIjyS415iPVUe1nI4D9HE5kqwWoHsIfiLmS+ogHU0NbgUARFqji2OOWFM9HeuzmxgyagzSj18SODwl8zuWpONYakEPVxckrQh1P3zBz85ltnae4YsAkX7KTsLUKsmgRCGNznZhsC7z2p3WsgthWSaimwF4Xm68ugKn0P16cudIONy/KMzFhZq4kIhBSIcZkGiP7SE1wBdDVvsgezrwzKr4JYVQgXZgzZih+wXYLJpeTghKGpdlZu6KG5rhsAbH9hITMYYRZnCIzG62R1nnh8hzscWt6Ka0XnbNVzmonloscW/pnLB13EeIMk+BuONdlYmNLmNlaQEdZ5zftjk3QKIJjKIgcV2AZGw7asDQXtj0yDwg5Y2NcqwnK0ulVtWXRMDM87IZYCbCTHl3SIGpKvkS9MjGuZDTBrACIjZexUENp8ssYQdHRCAL96a1bP1OqVQ61MnlEt3YZMe3GQjT7ng0BAWYV2CGVVR6Uia92Cx49hnBXu2CCU6A1R8aAFhkEJKdWMTkoMKyenKT0mthxl0BXBc/AONFs5bnoc9QtT+b0oiFX57Hhu6pQFaLevDib0UL1TYfLgfluiSbQye2ibTyQBircHEIh+kAQLLuThXI7kFU0sqDhRViBmja9oZNBWbqHCI7CrYbgXhxusjvWLkLZrHogAwLoLgcD2YTK08D1iEK2fA2rGsrxcw6eG73iscRIIE3ecqFRP4AnQJMBD2d21fPxrRDSMk+IktDInKoUEyimxbCIllPCywvrSpLoNbHjg0ee+nj4nLbFLja2yABAaE9ZuiCNV0DaTZCpskGzc8gpgi3M48AJQW1dG+6dwSpFZFPIQG4IBoWipb8Cx658ZYgE7dCZk3K1eIAjtaqAc1pGR4rywN7e3KF3mavEbCVZtQMyNpOLCdAohwl4OHMxOFyGY3OsBXoh2TJRv6KXUMUxyaGmYGuE5DqERVHtjMJrQYppFmoObtExPx05Q8vjXjRNde41seMOoU41nE3oecLZrkecc7bnmYenfX6AK9mhpkUqStWMn7g4a6XSkuF8WDYzOYYtbNNbxhjY79/XlSyLhZgzZEEettw2uKpjk/OpNoDMkG1MVCw9SistQEEZYpKqZPb6qrYyJJyYlcfSuOQ2QzvUm10YmgdFPhonUimgzUCIYVq0tQvmywhQwEOxwfSUcncI8WGMG0Mp9DaD0htz8SMGAsJeq5T2iQxMa8GjNDtLD9RYq14TO2434qTS+RQnXr78lDic7lB0eulUg08nHDVZIMUZiRNa3XBqNqAqZsgNSNYQlDOhSRBwbfiPxEvXMKkBnpWkrAWAJlBoyZ6AwVD5Yq8zqubgHTEghCwvECo6L4stCiGlaKdaEMtkes3NxENrG3BaPMDxN89bO+AuP5IaBsKj5dhek5QFCmfowMzEKCmDS8IpMjvA/IZAOQOtkoP5meM4OTjn0NlwIHTKE2VRDtoNMQEHYtw0RKDV1qjYXhM7Tg+dOfnsXZbm3PJ53HoDkPSrf3LL4BNOqmkRpfd0UMF0FRCO4KB+hVgZ9HAsABcziGCZBc5utxqo+EDrxHyUlWhpECoNgFlNzk1XIABaVUYO7UDN0AI9slMY66JPbRAMXsO5OOgiSe6swV7yxBCxDkaFeCkkRGZAKx+5TZsYK8QABSnhqjcWQc5feCR8A66ZAENC9fB6ZQD02OjXcJRFY0g4Ys6SIhWHG2wN06JpMkas+AYFu6wqkM+g3hGa8K/XxI4GODN4QTZVwOeKzyGeMe1FZVecTTrV0oGeZnW21QlKOa8lymwICKT4nE4LW1obHkSp1fragzxRC2AvC5JA9K5RrVZJQhYoaAd9Z0GMCzGiBJsLNVXaNVIEAsmnvx20kguVofIUmd5W1oMWcGtP02KauWy9Gpk7OPmmpOGERgyUc5M4rB5gYtDN8d5pziSLatWIM0ruYloOsyaPEX0zyAwio4XdfJMBCuVFo+EgOchwNkJeZpbXtqIhc4wl9JrYofOpTpASdSJSUYs+zstUjfDjUReYOLZLi85GPJTANsKqt5JjIAGqfVUvaCkkM4zqcIrz/ydoxLgSKrVeUw/VwWNZ9QyBsoRGhXl88QyRQYvfhkl+ICcWgnxBkEpZgRW1cQcR6KUIsiMqc9jCWDtFiySr9KokOTD2wjztF1vG0BAjUgxAFiWiINfa6kEM5VIJzWdmtHJ4PoUQa6DM7CyVwYActBLU2W4aDd6XBJOUT7ONdRNngu8+jCD2mni/hq8QI8/R4WRKQISRZ1RyCEt+rQRZJ1x7IlJN2VwKzanpy88hzD/xKkZSqONnMLY4xQseC73Gai8Ag1Gy2FbhVCVDix0R0EcCeFFJbVIbmR0uwXNjkvQmNzoasal4gKxQemm3pGnXPhpDrTFZ6uQ6qCU8DekWQkuZ8ExClFHeiWkbY4FWYah55sC1C9TKaEJoQzsM5EFtzyja841gNOTbiMacZNoLFUlqlTzDyJADOcRk+WVEk/aYkgC118T7E3BO+KTIcwJngs8n2HlyQJWRfp0dpllTr6rRQJTkAz7tJKPhED4dJUNiA45Lg+zsJPvkhuyQ8CZ8jakJlzljJ7Q5WXG4L00qM0UJJpTBIoJvhQDPloK8FhgFNK7y0Zta3TbaNETBILmSlzwM55Bm3ejVRU41o4AIkVdBPDYw2s5GacOlA2fj8J4AVJHNYTJJEeIRTVAIxFgfuoMAF6fqtGH2g7QYVBpaHwLvWhmNEJLtGbLRWOFVeCoERGxOG7qPe7mKAEFKZbDXQihB6zXxvg6dAEQJAM45n0Y+n9rTwmK16OI08kmjc3ooNynAy1YxZcTGIGcotaFZiMwSAQjBURRdKUTTjsWGGGZS/MYbRhHQ89o2Kkl6qSoVBbvwEJ8XapvEWtYLE6sFgk9JRsk0sst7z/H3HAIpg1s5AKt4wMLRMbQm4ZSGZUeFXV2VJ8c6algx/Z6C+bYQCoi1IjvM6Di9UOwZXD43KDZDBGfcAthIcuohiICNhFxeThvQ6LaTL45dsQtNQqvtrlkohL2snECvifc98HxhH+eQtLSpTb0s5QXKaAebPNXa87XNAPA0hYoNduphNKiJzCAHFs3WRnQSsbgVYIcxLrnMEIQmoZN43ytP8kQTBw4yk29/tbSbWb7yyoKmlhdowwmmYIs+xInVE5IVAquPfdYllIHQnPmQzpyZIULbadt+yl3AhpXRDjhJQFJlQCwo1DIDA9HJNRwmx3sHoZclguSykGmtJotGhChUadFB8diCDzrteKQRgEY+LLan3I4SRovyoqlj5310LCBCr4n3CWw8/Dy4ssZpDbl5p4YPnIg+F+teAPD5ZL5OLITTokCgTiaS/G0MnXweV0Hs4iR2WhHk0HCi0ZghEZV2KB69ODSXRX1MgwFqahqSBzva1qV2mFgaCXEYC1FWq5wwWulOLGe0QAjoTBOcgQIeCisXZUzJrtyRU0xJHRpsMa4JbSrAtBpCnGH+DUqFwAlUHs/Hh8xqmJMggSNolLK4i1kBfuUOQW3lz4jAYCRpcA/28bYFLTmivR6F808vwBO+nViSaSE7BTUUxaTB5zOie02894JXrA+qT8G8LOtK5rkuiwEmCY6iW4EwSvCZBLOTBCdhMQzw5Ys+8xFL4wTNszJyoOn2C9XqiwOBOcTUqR0u21saOfUumyGJgRbwQPjglgWPImRyIFxAE8J+QlXLPU3VBMoqKx69ncOY0RZ1cUtoFywlCCVQ2viuXCMbISjDRq9B1QTvVH0yBtRSRFc9Ia7eJIUMqVATzoRhaVQCxsw/MTe2loQSIWDj0uUhphxOCZpGHRoAIkbxofHaOiTImoNpcUQMy4qKOVPrNfHeAx8/HFQ/pfNQ4qC2P5SfoJgHGxgOvFU8ZIEw5JELsJrnR7Sk1ckk3S50YRSBrTOmEbKftyuEootCA7o8hwyv3WFC7KAujzjRHVsEWXyuO8o56JIURE+1iQIvZiIjkzSF3mYmTEt1ZOZwcMazjgJMCwIl2sMIMedgghdhMCYtxo6m2alxGjSubU7DdgACLbIzxHCAD8cEOUTNCitgQbEhZDZy5EWHhudhzscdD5MGIBktmmbCLaCKMj5J02RxYAiecc5BFMVqa1U8nM12GiVFhrJ78WV0lKxS+2cs93D4YBE+eDhsgg8kWh5HnXwB02Rvz362eaZS4bFPH3pnqEtL3iQK48w4X6GLFBb/xxVYYKNPQjMKQNFDVxLJ5lR4cSRKRdsyU4wLRnJwXKI0TxLQ0hEzMUeivJ6DaSQN86kJhEy3G6o0pMVOC7ZYoSAMq+FWAWiqHNPYjiiEpxmdmjllhIAyBOhgt0ecQuWEIIsJbS1zHqiAazRAsgnyomWgstEosmHRmYkmykMzWiQzqaqdUC23qkFZuo0xW6nYhikVPAEz2alFT3ZqJXuSyQG0Mr0m3iNRBy9OLJ0HFHQI0fqUDZqPq576fJag5fmd9jrwFc4gfxkQdp0f8rN1hgFSy06aqZBpDTXsmYrm6MKoh5togTGhTaXOQnEAz98CZy5tIHjmOX/a8WhlNxOrl0Z0FPDw/kKUxUvE9aznDNHaUTifxsVGXsh1UEiTDQgBRlcpWWjMWM/QOR0LhaeB7JWcRonogqaGHvGDCdhFx8CkJcVhkpkcgoczSgAqCoAw0DToMCt4MWenTSaNJiSJrlwl2z3B8EPRKjkzraJRlRcCw81PGYIPmSjJp4PyQKYhkgCIhdJr4j0DeUh8kHB+8IlXh5DQkQ5BPUBBx9IHOOxShiQZFU2bxOcBNgsKZJNzoE3MGFoWd5G5mRWbOq3Nq6ikOWGhtQMQ6yR25hjdhglX3ZhA1ss9CPa7cyVyW6kYq4E8LlWlHVT34ltwgP9fsL3GMFWBYoaEOVWnAnhd51hpIyBji18Msp4EuvKvwbRTcto1tKLZEoL7hhZeCM2Rjc6WZFLOQLbjquEerfeFKhUZk2MU053vZ8lJvn001OhhI0JtXOg4E7vw0OjtNE4xJTZC5XdC0CYuHIPHutfEuwljR0Lg6aKjVK44wIYOZOnsxVeTBQJye7wlGIx1hPI7Cl6cqfUmGq1mpsA8bZLoTyFAagPL4cwTGUJDp/mH6AxGuxeeKraG4CiElByuxgKEMJ6ZPxgDS+gUnJ8uXDASoNJuH2QJ5FDR3OT1JMOsLGwkwKwgqlhhoMooLa6AzfI6ahhOqFQG7DBwkjINs1VUyywjLeNJQm53B5rqPsD52Khp16xMrnXwAkqbnCTlHNrRlt2FkKNYph0WqNq1cOWg9LpvLB43mNgg5zQ8aLgMmFJrXWhqf7NB22vi3YFc/WiHq8vHE02eHK09nUQYLQi8lbDkkJaMh4Ip+Dp0oAVxGNucfCGVrBkGsnZULDsNaopDgp85oVXhJlwUDHG4v/ogRYYABfqSoMxWw4WmuZAo50Bhd4gteqQWAltxihhtEwiBahlSpW5Zuz+g+DBWYEUBkjE9iQEeQfGDrNWgQWTTvKeWmY3u0EONRp420MtiZSPZB7TOIQUOBBkppB1Nw+VGs1y2hJCjm0BBGYJoDht2DE+MGdE6PzZ1bPBAyfbv8WgyXC0dBS+RA+KJBwSfDGkHM1Rq0cqDR6+JHxJgrb30Pk2huLWLbaqUmzPAz9XpEdpjqUPrA19R4ZLqywwqU6U9O9lDDGGYFQTHaqqeNjwQ6GUTLiJGkjEz8CyUkTRxKknEi2nRNAN949ejieJkkFyyCdycnLaA7SElPLeaoUGm7WFQ2qQxM0xYZDnA8V4QG26jAPJht+KPJmgNSwQqqGLQY3Na+DmQrFQ9lpim2+gr3Ebbk8WOxhyIqmTsETmVbQjIHp0mDCGYtlVC8cSShVqAcg4UUBR7s/MIUmyY5rgzcxioyTacGzZmcgOrwZ+lkIkr4wROwoZAIGTvSwszxwbtNfGuQLvucTibQzIcAJqboyVdHiK82PDAk2DWF0KONo/bNBClMk4KYic4gJNEquJAzQserbQg0KJAikloXRRlZKCub/IbZnJlN+xKcA5STSB/oGZgcioVhehEcAZKmon4VkmxDMmryiBFuNxIoJFUhpSXgRAaewGyaTVoue0yOBN2mUo5/eRhGeEUS1BUeGX30yQQLnW2QB7mn/kNEiznBAIVbrsNJiOb3meg0fzkDIJd5uBhWZM3wog5K5XOCFrFatIqsFIZjmUroz3l9zGFHvaSM0lkq8lYlkCXwEWWUU57e028MxALOr6+tOschU4711vGWHvJoqZGe3njsLUHvrH40ioOm7LbKAtDFU7VjzwtLKiXZSMh7QTSNm/DeQjK68MuBzSB2lmg5gPg1Rn4uDDgzxfKEe6Zl2B7jFjD5QSKbGRJoZ1FQQ42tS/0SRZKADzVmiSJOWgsoLxhtCqhBZOIzwAROBO5mEGzlVWxkDVjyhWi/BIp1IjkJMsykTOUqNYMSjmWkXWNBruEoCe5QtjUxAT3aEHhrKWj8QxN5ugNSmvN5ProSA5XO3TOxEPUMeXDo6CHIC9RuyOLCQPKW/aJscYt1GLNe0384FCHYRAgaaFr3XlE5TYnlz5kS2JFbHuGFa1C0FHNE9QZ0JY3UjZ5sk8ypIgfLJWKNDaNMS3AIJjpieXoNUna87L3VqmgsLzqZsf7K/d4wiAGSgDKBRqiMEjo2SNfXU4c0eMmAahBOQHnafy22GWEBRKioKLVADFK8oKgWPCjNHt0eZM4cGhUCMCslj202RlOSJZfMhq5TGaowwUbzQkzHk3mSGujwmMlxRZFsg8TRM/ELgnRqmGPh36hJ9YlnGxJlOJBB0tlLmMKphlgtnPgqqbXizww8VAWTzVNWtiCLEUwC4BgmSl7TfzAgJXzuVWyj4HXVBa0lH0kIMlYF6pbCqJx86FtkkAcDiSpbOlKQpHpSTV1iSZkiZRtkCkkbeDUGaaEkNs50CKaND2sKwNRgswpDkSDE9P1V0OgBThWksjJMNsq3DPkVrMVBkumhclpsFFQN8wQcmYL2GuaVHTMhrqQ3rKXVyY2ZFrTkfZwno8JbsOCGSqHQa+MRTNCYNhghDTQyuVwjwjRiwCzhxOGfcdDfMCcIX8mN3h0RCQHQk4ycjZUGnM4mqmEvVquJF4dQJXuSRYBdJ0XghfQ1uY8pJYhiK1bYFuIlDzDctVABmU8cq/DpQ5nTa+JtwEdFCIWDkKsnZBL784nDVBnvF3t4QfCqA12yhkIwUZqZWTDzkKRC2FXC7S0IONQ6zyjakKxG5GCoyDlKJMhJkgE4I0FyX3MuIaTHTl5HZrvhSq+5VIDNVyOBd2vwQnEmNyEeVZo4XVRq+mZEuHWs6e9kYGaITDYRbIKtHwCkg6i5YGWqUuwjHkOBnSKpar5YwKG+4h1fpnSH5ZhWRoXpEFuRhwjtNOTJWS70JttSwoAGzxyl5m8dsHI5GKN29Hm7cJAMBlt1VDY65kpU0CIA6o9ogWiA4MSCK8S+i2biGrSSpHc3088JbjcvpCa92KAuKnRCtKARawrJigEhGD5HM2DR0KSspfdv4ygPGWPkInk6Ta5OBbY+rgC1HU2yGIjCPTILk+SgdQ9Iu2tnLDdcETsnY05umkmhIpW+UsFYPGUuDviF2Jfcg5UZQcshKqOTQ4dcLgmgDZcMvLIeji0VsMZSTAWdwoWKuENvjOgtVGgLJ0EAVoZYwY2ilDhbo0qZIDHlZTMZlBPT1JDUDtk9rSloKFdIfYCrcA0IigfHyab5ImFCx2Wy+8Ey1vMmJKMQYZRFc1GApn9+hqPNMbEXKYjMp2aCTYfsnQSQwLztT7DGrrO2pXzoSaFDTIrijYNbYjWa2IDrVicH4X2ZCVDbZwWbsSfPO206ESenfSGKXsHWs1wAno+eUKkpmykAbKKQtCYsZaDkBZqdNBC1TKbaAEK9LEdplF2NOnyTMJsy7hq9yBIdlTawkuLQ9IOUEhqJbEtzdqdZv25R3lzVOd3DVFyIEccBDm9LJFE9jJGeMnsBg5pZRlP7r2mW8eSLpdjQfkClFOv484QG/MUcqZAjVWhzeiGeyZpmVKDohCP4kHllFddWNJKMrla55qe+UCGABkRSgVaDqRMV7irTwFdO5BWdXjGsjH6QYAUo+TQw7GQkRlsQavCbY6E+3dNjGVCV4sIeLESsXymobHPxxiPOtjhJ5nPmTqDYeGWh5BtHWC0Ges2RhGzJmMXkbMy7XQhtMsHF8Anagm2xwRoIGx0YJuWsOpYzZ8Waslx55AaPfeacrNuEWIXHooKpr1Fy3g1SuKCYtWPmmeOS0Ghda4HwWkzp0mxVg4QGQLvRPwj/rDYKDsF8xNcE6nmjA0nlMwoTd6zogXkPCJUHZ6jhKUGpcGmkGNohXhcGvHIHXG24MvNEKAmTFKQGy3VTAvjUJqzRRdTtSuszd7hAYLtnokAb5TyzOg81iqPe2eO2JactDrERgkAZAbKxEYCDLwepcLLyWzMYFdG3B9ropfDa+GF8Ir4PIDuwwB47bzlkhFUc1mLTChW7mAXp1UBCnhwvJDJsS3lMCqmzVAEB3qsIEDQ817IMGYg904CZe0vCdYh+mzOVGDyPM5Yg6K8JlpuCXHqA002wIFllC1dxWHGlAcxcmDzghfN3soWaiqmQYv0skcshGAHrPHKaZ7JDA/KUdA1Mx+mLcMQJZfHiQXfMMMQ0kK+JDSR1g4PKrg3c7iklTneCkgvLDSqpaD5QNCD8BAtGexKSwKbpCfTi0NDBsZ+SR6GIyV23Bwik5hTvFCFEgy7YkyFD9CzF/baz/fwxHCZUw8344Hm2KE+RHReSaVisvtDTeQONysCYVgO9147e92LbD+8DFeG4WCnl0DCVDZ6Y9wKNEdtzcoWE4E678emLRfHKjk6giLI7Y2hM0NyhnyBWbsAwRYDRu6gTBC46ZxjkloxJ5NiC0MwYTNB81s/HkJeI1LFaReInDkQXGP7q8yRJx7hbTnByB6tR4dn4koGOLr2xQOZKc+gShHNA0llthDpKmbwBciKY1cTY+PVk485ZSFqOEBewDNkpZMQBCVsZ2tQlguCxx0LsQAxJ0M5W9i4Pj5eg5+g3I4oN1VMTv+dzuFjNOhpYSOVsKqtRnFCSaHSqyQ2Gz4rwkgGWxOs+YhEMjnYZDZ4x8cJtDKUIb+AMe+zNRELGouYh7bU2H0RLHv1KeAhYxwPGd0hMEW2kVMoAYBMZuMtpu0AzsX4I7k5ije8bkJHChUx24HKC0EH3Hbk8c0C1JDrBVrz/E/NIemCWojYHAstneY4iYx2mxPkzMlBdZI6ZLDnKLyi9EIyjM4ywZQa5po8CE2IGtHSGBahmO0bXgBFDcS0jYyOfDHGnlHo9g4RsbMmTwyaIXDJ01jAzb2zyeGcpAwZQASt5QteBBvQiqJW2WjXWTRBM0oAOLQI7NWxqZCJmZhc7bAQ9CZRAh6m2SRQ1m7CQ5fnKdjInJINEiw7lVra01KoiZSxnHG2CO7RIjpClDaSye1RKOYQYDLHfaYmaqcEr2mIG06UXAi61IbdS5Zr1BpTTGRCAAL8WsowDjR7NzxfFZ+qHBFiklRzysiDLY3MmlIyFRSC548NOls+CA9EkvJCTc+4YI5MaDwQbWlpQUKIEjQxjzDw61wXQgRT1Qcq16fZO1tIc2A50Di1BXYpQHQFFJHhuXdOZRmoq64sgGW4PHnLRrjUAm2UFTaZLWabk7fAdciJjaGZpGnDTqkHOB/TbLJAtryyka+xUhtgmS0epxrLFgOZYePqZraaDxuGDXxpNFrA4QNqqdvnAAcalIWqXGyYKOW29QGFVK1cCORwUmwBPFU84/Lrijz5Ghce4pOjW29HB8EuyzLKcG+uidiHuNvy/jS7BwxG7KgwHAwvENpcFMeaWWrItkuK46qc4PDwYPOrDw/UusRsgXByLEevxEqu631wDIQcV0NpPrIDFCpnE9gyixxMuQdjEcpeO2J7wqpYzY5I8L47NkZVY5dlg8aBQpTFpliEJNhl2aihaa/RpUOlUQFcMVjEpBuWrMIDGhVMhowPRwEcJxFgsdeHeGzvdAQ9Gc8KYIOHF0dyCLVcgvuweNoQcnckJkxwLxlSqGg9W4dHE63hJaqyQkuR6zh6XMhYMTroh4VCTVtRjqWmqLLAS1oy6UqNbbM48svuWMvZRbZGoOyuXPJ6zlarJWpK1jQxjO5jVzQaB9a9rSbWbhDaFTTsc+dlC2NSQg5gRWCEteFY8DJRzrVjm/WlqozbIDSrrLjxM8DUaAgKnmoJ5auKIK8PM5z2F3EiA5uUoVVI8HW6RGu7aHRbN3/DKVKyYdV2hwJje6rOu+P5MKf2J6YnGsNzAjGlZiZMPT6NWmHKNkqqoQGe306QRvbiONwm5hHBZHNsb/fdiAMB2KU5WGWjnBSbcNqkYhQagyuyo6QSyY99KWOmRe+TzbpzFkqkIPbkEDaWLm+cnxrafAeZAiXeEU7ECshtjqcXUJ4WTAWCbzWM3KOiRp5sB2cxW7VhWBxKuYRgygBLCBmL1nRs2Gt5AhbYWvLyqmXIveLvsdRSDGcqkLtB0QyvhUUI+dtqNOYOM4QRYUTrZwwZ1ErlGcksNKoPb8BnmExM4uHYa6BGRUeLh5CRz725GybzVteqNuQbpmSHmGxNEMmeIGgyzNbsb8FRBkM9XDtPZ+MjVHT0om9SyZ8hAoTwllFhbDKQdnud1m2qRvYE90iBPgThkoWBGcUl9EkP1XYAQvMd+yCrrXE9MeWI8CFWpuDYrQQGB0oVnphAftO+AEJwnAQqhyHZKiJjRCMnwFnh8DXzIVSqPBO65CUhvRQqw2DIPNka2UuwK73hslyLMOHVDOmyCntxcu/YNoy4jjKDPZbdIRwEryFXWG7mlJ9zgGQjbQHLbj3PYapoNc+6dfVkyNHV4SjyOTnqw3B23cP/H4t3wB0rRcpeAogQvCg4O62GBaTk8KFKVLWGNnkpWLYkuU50UnLJyktBbQRK5eqbk+S4VGwvZgJiWSC0E44jZXJlToGuMjqBVO8Xcwr2o6VgxS7TypZ7Z1q9VsqGmT29wcIZKFvz5gPtdotAf7NHDrFaqWiUFAlLlubwcNmY3nBRpKvGNZPaRkFyCMrpRTYqcxDgkqUCsXFlKoOZyhOy4NlCdXg5otdUWzvJGpeirmHWWaMZCxjkzFCBgNNODBo72DDtaQYnHEE5HZUkhMxgP2QMRCMUEcb4IQqSfMUNRpihtEypEyvjUbJkBchp/1OZfGCaQD6iRAsXGgllrJblUkk8ZynB596huefURO6wpouNsm70COrR2jJhtmxMCM4JhWqxsx/OQiHWC75cNYbIXrFo2DcqHr5mrHuhwy65DhgtNBCRk1LO0KeC2qA10/COp5YzFwF8dGw8DWwCe2e2LtjYZsOWE5EsFybDXjJ7hbQuhtSJqM0WM8PaJGFjo+C+FsEq/c4gFR1krwZcGCsl2R0Fr79r7RqtEMcazKYLw4DZOSwYYXSsryJ7JYUstELwJXBuAvcIcIqGD0B2/sErmaJV2cOfOy4PQcFri15Dt66BnJyCtTFbzpxQoPoBJUPgTHTDQeRsY3qesHdHCT00GzHBIUF3JLS3c87ahM1/cMYoAkBZOoeQyITJiTYXyjLTNoSQc5JWaREzkFOliwPdA2oi58ZJhVwqm2ZvIQ27JFCQoj1Je7ktKgYCOEM2tQAEGh2ug9RWSXs5EwV4Dra7TYPaclU2tBUb5uRXqnSgp0FzqOE8N1K8C7BmIICp1utuV1LA6sRUgbIYEMGPnxXRKDGEBbqZn1PL+QD05v145WMrfgjyQqBNmWUjn2vbhJEWYqPai74Z16h5hpqZ0XrylRwNmZKYVpec18oq20LanTlkv6qQamPI5lSSbNEAXjGHAIPg+HHBTIfTos1H3yy36KrgAg6MpwdA/NCa/bKz8tgVqyTE5CGlgN5n0cDJlsZcbXQ1Z8JqY7AUnOYYBcT3KMHJDIbt7TSKwJWpgzK4FZJSxBYNrVTIzAEZLaTmVU6FO5D93VATPQ/NWxo3TMjgleNpp5sT9YHPXWKs5dgNIhLYrgyA5ZaJwDrJbGdCtcEWQI4o6xAt1fraksM4p2flCTfOsBhU5UK4qzBpmh4pGQh1LJszuJXAPi30OnU0Aco2ttmEogXHeVI8hV2CVXpq0USz2F5UbmWN2I1Dl8TenFITYxZ0HteaE2oFY3poaqDMFpmLYEtI6UJfgYCZDlE7hJtTySGaE/5AGCu5HibQ3O5F0w1k0NTVuZq8mAk1CFjbfI2SfoJyDk2I70A1jSyaVTNplZ1o5gmQWSW4jfBYlFJNVwwkOy3uBMppB6hVoOYAhSP6RrJGVR8EIALUV7bMYyRFMCdNzoMmyg4DPyQ1sYY0MB9MBU/FdORuF7xqbOS1hRnwsNHXgDS25tgLpFoHr8ZqQ2jMFanjlP2AOGzlcBIHcvk0H3ltAVwXaGEToJxRhoemWjmVzKtC1YQhgoLnY8V2hoyPyHY8HOKwI7kOhKRhRL3GGRuX2TNEsaTJjlQcFDXd/JzPQGtami0olc68pNEZLQAavByzYhWCrb3RoxGtRIOBmryczZ6a5sNNkRbSQPKeFzO4iRoutMhAjqMEWzLNMPPwJw2AC3A+RxkNhbArZos8VGhiD7UOh2ChVGBwNVaIXoGQZWRWSMrG3bRD8zMBnaNiqcFsaMERLMs8kFtGiRBIQ872nbEEo2zw/krgCZbZ2EDOA2eOFxaA6gwDxAEpZjVkIkJoXLqU7sqaWDOIsQ3MUla0WBrunWbccihL9x5yrrZIdWwJNGewe2bzsRQtnSFZHeqImTICELAhzhbK9voksLU5DESuOBB9Dm1UYLIIy6S1szUNbck1rvfUosge1BZvBN3RV2e5HcsCjeKbQD/sEqLSAQ2hJmZ1TAYkO3/w8YAqARtfkNZNDccYMgOO8qwgUbDPt9Ky0JYuJpSfURKAEixFErUajRoNltyU4FYuT6O8TU8U2bOiTCWsxYRgL2g+3yIQsu/vmmegCBwfFw0MVmMgx9YRKYIUcqSEJYXBrmkMFmz1flHeQ1CEhLbuKmQEKGietLd7ZBWtfDaayQcsIvsQhMtDaDNEVAarDoREq02MSn8YB4Jdzpz5ZbIzjNxkjMknbSgI6O/0mugzGIjJadwY3vNAm9eGtGg5S00xPWJWuAChle2yJew6kPFOWXrb+bQC5RpXbZgbL4xFKCMeYUGLXoLbcm0MBHj+wa5LwnnceLZMQgNRJ0cZYUGyqFYyoSkNXo4lS/EDzX5RwEOWGBTw5OHJYAgmUNaDLnlt9/N87aCHLsGcugaUhi4mrBCYTCZbQjwIezlELlpa1VcG2ahFNxhbhLGSNEWKQQoMgkzIBtkER7SYMHpoNZJtjJ44HXMYQkY8ilmwvZ6icqZs7QpLk8EWG0OCVzKNomWjOUgxYShbSQiXBDZtHgEWqnjIJRObmjPhEDOTRQzSmCvMGtpZx8Z1Z0vZsRR+1RIknTYGLLAPHoWY6XAJ6b0zauIwkuUaD42PeqOaYGPZZRuXE5CZpCE4inZOf9gZt0AI6oI8MRBULRjtaEWgaOa44AYaWm+efOxXUPJiEA/2olmlLD9bG6UankCAkfKKEUOY7bZedAASeHknp8bCRuPGsbKLETNqoOXkJyuajI4azrb0ml8gWa29hVLtioQeuhkMRuyOd8RqzQptCYTDvWKtHWZJcA2j5BBlJMdjNXLkFBcIi+V4KJa91CYt20jADYiBUrA1mKVWCDoxvZhlt2IC85hph0k6KN6LSCUnUAJgLvfoVATm1MBD8nY3ZXf+qHcwikmnLP4bZFS9d0JkMN2Tj7gU1NWgg8CmOsITIMShSqssAkTOMOdJ1Z2ZwdA9meQaq8EdromZbmxIq0IdVxh40cIlFbBQbcxKOpvM4J0fCLZIwS4xoQm2OCpDjEHIFJXBowShCbcl7AmojMAeZVTQzavlNpSNlvLybKVY0zDHS2RbuJKQLO2mY9MSnZjl8n4VmS7ZKyGQvbwNjZQ6fDI7z0Y+W5xqVpMMhNeBJaQxIAtVupMpi+WCZbGG9cEDZDBxCIZ33Cuh4J7j1oKLw1j0ICtJ2IFWJivGskCLB5WPgWrpVUKaHVCCIu1CRyO2eoHcMtEkWb0sTWuMzdMPhXP/IGg324uL/pwkELJrxHgGi2xbVd52QYoMoLfFw9mMtqHETkGPXTbMl2UyNmfIKAemEVLsmgIdEvzUHRerkRY80IIGsk0OgcqEQXw/uN01MRIpZSWN5bZFAufhSUslxztW9tbYWNhL9Z4M+ymaQc3l3xa7PLow2NUDEEQJxXyKGyYQLhib191ypEvJbTehYCNQY4VXHcOFoOFhi5IgVXuo2GrpTPHVbVd0OWfPCnIQ0OU7TTE9pTA5msZbs4ICufbILVACgPmYz3CrsgMaVnuhdYOjmAFb1JIg1+CF3IwOxFjQHYVeCwJgIMN+INok10207QYsk3xtMFqugcJKiXNQL3hK6EXgzlZGIL1GTEZ+ilLbWbFtRvFRkyKmBcBRark+EBRVa+Wo4DvQMCFzBk1uEUOotjgUc608VhjrAJkJqRkuBLliZaQaMIrOzBSg6iiGbKIGrQpIl4fTWCa0IwIxDfHZymJbQMYhYeu6DZy+JnIMDzaeMYTUfXKwkUC+Ag2rRXbvI20lPUR4GxfEuAySZy/tSYPC5c7ZGhbYtoHsNswn1bQN04ucDcGWiesB5LpWrbrHowaNxsc1Ob4YrHkUDy1RKto8FWJWEoIgQAjZnfOMD9QSOBBrWBoFJ2frN/5h8sTGd99tGGz0gjTPIhFiwAWLM0eEkBaAfXlrtniIM1w24VC/YReCg0dzGtCYk2fmsku2p5jAEAifjbKQaXY0MppsJlqdAfbCSEEKXNwLCbVERgmQQtYoltF6TKqScpAgoPM8A1JjrIqNbmAy1vFyMSTJVCGj1Xv9ESugD5GMRkbT7GwmYlPhSIjrBSIMdrnlQGmKaWgCMTF5jArho2EiyoPYUiU12CV/wBiviTGeckEuUE5dZ6Mm5JmFmRID6Utjk4pm2SevkPANO0B7qRP27EJujXlswI+xMtDwoDWWVe8CjXnCDbALEHmIdgYoeHhQiW5j7+yy1RzLLVMtBRC8pvbKFy5NFVLQ5JVfU1IILRYEcrIbCAqpHXTOkKMfjJ62Y4f1sSAKNXVuwYkniaTVuAbGre+W83rzbZH4yBD7RR/tlDMwe2KQi4YkdYclgmcSc07EVCGNJycqlWTP36I1gItmu8iEVBJU8YcopWpXDJvrCwXFhiunbXKkpc8NMSbgoYSU2wk3PdrK0yY3ioNwfvqvbIWYmywQWy96a1zY9ZCDn+46DQAsCL35NEA9l32YlUME9j6FrMtijuWCk0RTXjPLfudgavS7H6Z8Hkb7IOkU07JiCkQQvBDeQ/PNUUNQaI6ihQiXzK5ZNbss2xlQrEcsIwSyRG6jIn/yig9Qbg4JwJx5v0ZV7cQo2JAz1gYuf+Rqsgb1UawQgCFllGPCO8A1xdC4NdZkiHwhCMhvI9pIIpf94TVBsu0UciU5QxE82+LQJouNbEwIX0SZZpsxNo2m5AWhRtlwKAvFtyA6yf4eDwXZPRl66VYrsPcQiqrhpMtbgslDl140Sg7wQFQ29OpMEzFQo3inTGCA5YRd4gacEHprD68Slp07jvyQZQcsGzT6/i4MhKMoiE+RpiYKD2W3wS0MXGHZw6TedqyG17/Y7MX0KIMgeCxYYmXkgtHppWSSEBWSO86x7Gs47pwh1DsTqIkP4Xjc25y0BcMC92R88EmCwn09WLbAx3Qml5eo/BJ82VBIewyn1hYKoGFuza0BjHWWpIFqHLMEnXK03hJsrBabHgTVXA16ZR+e+aHAmnYK0YtfM99wLLkjEjyiO+9ICLCGj01pMsRK1qCARbZNZq55Q7OdSweJI8nizvZEJcdAXCLl9E4DTFt5RFaXDR6ZvDjMYAFALFpbkum14riSW6Nhb9DslWVAkgdvqq3M1svu2GYIYKDBy/2ULKv3hWYJdGWe2BHRyNSDdqnwQPIyFsxxA6Z3Gai1haG+oE4nHuNDpCihXIo1gqAODe3okhleC4pqcgxtewh8WpYKhAyjvlWGzTvSLsggquM6WEzHxA5ajRC30rnaai3fxZga/bZeO8c8NN4wqGapfnw2aQ9AdZ97E97GzvVqo3Txh1sCxRwizA3fFrQwxMo2yWmXykyOkhJCBqIzOZh2REMMdikmFwZa8kCoSdbihFPdEOLWF5JltUBMElvxIfh1yoYMpfMczdvbGK75jg6NOf9hGYMadk8G0OWfTnRo5ZDILgQDsUGVqyYgJwMlWSUmwpvh+NBAEHgyJGAeC6/sgkUYYwc3LqlDpNIlNRYh85Rsb0ABpiSxlXTiOZVUtC2t8jQRuZ6SjVPIG2Ob2VoolZboA1Cx0V95UvBUy8hGecIOXWq87mkIQBRBLQg/6KeNkJOAQG5FpckWC7A7CVofd7Ya3QgCJGWIWGUymFlKWe56uCY2o3ISuQCU2Q0zq30eppg7TySHIWMaU+JeI34MrlZhIlBos/GmzGtpg8KDmyHuK4EFHgMx2/Re/ajFciAhNVsEyr4VLaGc6kxGi2xpC6MknUylIb8nbN2rlxlorJ1NypBKoKgQcvP6N8dxXh7SnDzJemRyyYYphoVQlaUhKhva2h2rbNgOiRVVe0FkHrYRHLJ33wei3VP3bMtir/YllnrDIuBZwQRyyAhOIXZBssPK6xCf6ZyPvU6tkySMAgc1QUaKzmwCjJ6AAgNNNnvRejMqf6WCxh1StnYUM9tYg17vbyOAbJmqYBoFq5kwFDTgm2OVTbSE7GNG02qJ5M1ogpdeKuTUS6sadPxjyUjSpijy3YCp0Yvw2rnB2ApK9jzRUkuL+hCm8wcHqQB+JQ4tSdg99C6IXCNx0MWZBxFC/RKJXEb06kwOLdO6jTsmOGARY3ClHCHyW/DdOL225J4CjqBK3nCAgwxgxPbDVp/NFWKmBTyUOXY8ATk4OYfQSBVDOSPGrWcIbxZr5PTKI8rhwUk+BIum1RxqegSM+Q6p+eFtwkNVHx457AXfFs8q4NgmiduAw9uVETM9wwy5sE0SdopiL5MHjflIJjwZG2UIeeJYQE17oFyOcJLKZn5llhCxsvvXqmGpu3iDTjHDIjJaZ6DBqpxA2dlCRi+hbAXPn4I7c8aNoaGTFzPh+SA1fdVTqMkUy6ci1XYXEgwJkWj3pY31JWx77SCaFNnRfjdDNZETqtl40tjz5jKjWYThZLIqgjl0yRiulMXlllqgTRhMtMWRWscbsNgYKE+oBqPyzHDiGki2aGOGkKnImjI54jfz0qEFiiBYoM3Z1E6MJbf6do9q9IpNIymisUk7G1ItNsNRCWOp7GBkObGZfcwfEDvCMVsZZJt0oQ2vOxEGr5EypuQrzdyg5XQoSIXOVhZZx5ZLBgmwZ+mH7mwU05JNkxBDK1VwAAdalBxpHJ6paM9ACLKlEcmTTFfSgmSvWhbunLBsbAEeBEsOVE62NpXcCjpwlE2WEBaJ7kyumdtilX64igyUHY90hFfzt32slKOTKzTLXofMw9Z7LVfYrTU3PYHxbHBQG5vlPQFToxc+WHOFrJnF9HL2bLSrQFgm9hNyrkXYla3lILB9FWwOuqLZwpaPQIkWQvUKtsnHp8c8dVnaKxWwf2glsUm3XQD3sTm/iVyQMRoeLQewV7pfMngCMY1WFSLQUYlWBqyixcYM469E6KUjvF7jyN8uuGChVdvDVGcw7cxCY6jWclAK7lPlBGwSaG+NamGkzUlsUWZMswCLjfJnpzyOoqERIELyzIFKFeFGE0VN2dohYBlUCDkWrCFkYC3XQI7+9MLE6BKHpW5yAhQ0es3HFqO1cGINuM6yDPxctCG/kkJmbHIgeNHKZg5UPsPJyLXNbLF0TqsYRjkg+zjv/BxpazSBVr7nYWr0m7hPbHZpcgewz1nOYByzKyR2Ho2WyTR66YwWiBVsLBC8vpYJrThAJkpSvjg1SgA84SKDHhYNBHC+jm0GbVtRQzmFV/vCI6oklpNOeGibSGhd0ceUJNHo3TebnORlT5Cm4YJsr1XxuLN1qqW3MNCsqgUgIArgXqTV5I3MAtaQn356rJzDxBAALXr+sCfs7rynCSxIZMvTia1j/PxBkgiyB1mWyG9vstBi87ghSWCgGBYY6+QyW0gKEUKOSNVR6BUoc9IgZHAFRpNH1rFxEsroZUdDOSKIVvAkGWsBVlE9XA1aMKd2rUY3JqMsSxvLJLsXFgIpDbMWHGCgxiIt+WTSND7VZgi2mfBegqnRb+i1c+ykbAZkq1wILzcs9QTbrqMFyRbabG4NXQvDQju2CBSa5Ruzj08jaFA0DdzU8M1KJ5cfoKDkHCUtNjLK5Lo4jRx9zIJW82zLIttcCm8Dcm4e10xCU6VRAaxrlc4uiQAE76wHHRwpOj+jzJSDRvSyA2EXMWj6zsRgVxLLA5qCCwS/MgvoPTcrE+NSjj5DvMIai2NqZwMyhebJFJnUGAgiDhQ7RegRrQ8KmWhxGkzx3e0YoskAlQkFRzupRzHch8WxsgAbXfb6LekiABRydIzrwxdCA/gdwrlBds40Gubwhh3It+zRxEUkM1vvmvRhPrKY4xhahDACDixLk2fgWFbadLLzslNNfnmLwyjL9zJMjX5d94lju1QH1aeXrCBwIVxEqKRQS58ZDCWQBSto2eSwBpgzVQ+KRn2yUq+ZsHGqZghaCu3RVZTPPLM9ChOKAGRPDLKStGo2g3FCJaC088SWO0iZ/QCrg7H2Is85WmQALMDI6gA1jw5lWQAI3FPvGlzpcF80SA4EmdnGmZUzpi3vECuXMRg9aBPCPt1FK0FUXeQQJA8cSzmlwV6dXIwdM/CBcceWWj4ZiEFAgBbRu0Z4xYxGRj/MJ7ls8VAsR0yOvcCQNvtyGVAR4mJHVy5v0cYEZ8Nmvny+amyOt1Jy0GF0tBt2P1SPKCb56Isn+IIF4LXLwzEhuhoiCY1hEO61mBo9/8HaDe2KF4hC9JQg2xxXY6NyLXI92OChQ0SXjaJ61RyedGJCQBtH2uz8wh3UOiSwD5P0wVBGt9CdBIjZVu0wgaa4Y2qPJQUnARrBLVSLRKoRK5XTK446NjVtqeFN2MJxxHGSONc1OvnNDK2Grk/2AzmBKDFACly9nKqPSGS2vVbPFsByqk5L0flpI2jBQ2TCnAQTZhSHEBONOdQkDVe18sRAQhhLt1g5yzJ0EmryMhhjMjJgUEqya3qOKxUyF2rc4iRsk20L5GFVbfL0JAcnBbTBsteKWyX0OpTNsjtPm2ozlgVrY0y1YQFMlggEGWFSbEfLPJXZqs9GEQiRybGch6/QyvdyTI1+zTURqDVqdjhWASgLjkRek0HTMQCRRpN0PhUakbLJNCJc+WkRyV6A2SamIZmW9pALw1g1bjMB9LFHmcRWDx3ImuJXHzFKDco+aI6i2jjCm0Konuc4AXDaskOloaWJEHIaSSN7sMfVm3PwcF4xG9jSFLKNtQ7k2wStkqAPngTvguGhxQxjyiKGMVwCMnjvSDOZipC8OMobJg+EkHpkGB+L+aWERT5YeDRt8TFNArqYQ+hhgRYrE7Yxy1is2pqqDANI0IGpZYwo0zLATmez2SgOmxxibAxcetijCaZEjxKjh5OtHoSNsHp9mF9zI81GOGWJCHV2MbnmQ3ZzX3JfxNToVx4UYiyEd977DAHWZt3HXHXkwjcIBgm2O9ydVURBqoNHRbIyoOXNnV8VJIGijxb1ZmLlYidZHTLVhQE15jaRofiAOYDnEL2QLqgtv7JJDFoRwtWcUoQIdtlmMwChLBUS4SZkEhJ0bQRMBtC299HZhcV5JGPf0ZvQtrDGN3igJ4ONlouTCZ2I6yo0CpTx8BAWaWKgZWpKKzPBuUl15iqRUDGRcMhCORqCHvCbD+J8ShhFI5q0kN2i4emRvMhmiw9iHaCkTcwhvAqEqwRCU4JclvZkc+tn3+DIamZlszHHjNawK+a5QTVKpsdDSOFSWE5OnQxWLZPWXoM1j/s+pkbPU03k/mq3J3acqo+WHcmhxRdPnltFAYYoUXhyAwqUjTKjdFqgHXtrzFna4y3YNWQTamJslC0MeCiDnAMG16nONghyyiIpXO7kgBhGYZB9fWpNJjiEAtlnEtLGFw1wFRi7r7Ggxawzm1GyhCoZiITK3KaOQcUHmRb5PZwJSqBDaqYYHtHhUJ3ftICH1hFsCwoCeVFJjZwRMAiGk8eEIdqtJIS6QZ7Oj8U8Q2fOcdF60Bo6sgE+OpqG6fbUuNCRxy7KshFOUqkgoHUeD21YbplOFRnZ0KlADuQhFA7+kAcQv3q4KhAs7T3lYKlrp2ECHkhbfLSEI9VQzDk4VUyJ0uBK0/0KU6PnVk1Eq1XwImKNeJZ7sbxwEqBKDLJhb70DSDnX2q27kDMQDXufxzRIz7EGmiw+Zo2ZHQXpMRkxaZQsyU1a1HsveOXolKFFlOyiZ1tJUqCcbUxJLrZhjT7gSQomD0+/7bTVDpaEL28a7PWgyYygjDV81KymjQTSkunyXaMDXv8huVRGoQ1F9pw5XBECTXyXcoCyLFaBVvCYVihkHgIW7W9qQwtMqpY8W+0CLJHcRtqSj87L1QzqgUxwa0DGppRBjsxJioNSixOjEug9mRhI8JJiY0gzSRMGS0LcYQjlG8iE8rSO8ObZQlVJbQcgwOWZW41ZCZZNuN9javRLD4r1AmqZKLBL2Ys7fhKw12EzoNJrCY1TbYxNl0QJOE7yuK0zD7BolwXmScJwftjlc8tZohnactmSJqkT+2XVkFSqBRIzkOo4B6hCNgwainbWQ9gCSGUbFNnKlcYxr1qAghRncMJhfaIPGpz+uS1slT+aVCsEEHEY3iEQ66DTSz28gwqc/iV87X6GRguwOJYCQcWyxkJIlV3AuxkqOtEoSvYQlQwwP0xq0dvGIZIXw6U6oKkX9laQx6LOQksrLRLpjQeRvQRxJieph+dAxdmiJ3hqQW5c5aOtdionkDMSZIpYG+UIb0dgavSLl+cxMLRC9Xyi03KAl4/8XEj01NSxldfHpgB7DWG7oyzYEldaBg5DpNFpIcWpmXwDPTS7PHN72JaqigDBqUwgNgyKxiI17Vd47PV52VrYDFNlr84todkGTfyJ/YVYSx1eSAoZZIcYKXOIBMlqKadUfnpzuMHovfPcZGXCXCjpkbQSArzSHOhpY0Gadz/Mcpw7WpSBWs3BR8FRNoFQf+RIR7PIAIVShKFATHqoDnyPC36SGGUp94staElw7ygam5nbRdlPMGpszC7WB6p3uezeZU8bRh/xYRGkAg6BLdZZqlOBTE3JZU6MJylmXSzeO/XiZIaODZga/QJeO+fqDCvoBg+tXS23j1/S5XLfLDF6MlNmi5OgfidiIklm4DGT6kyWh6/j+JGcdjjDGjJzFBDyfIXmK4FzQAvZBLRCnPEYVwJPUwhVFPhgM5ziopFPj/1Dy1EUKDofbCsWonxMYoI6JPflYYTdQjEziVv2yQsaTCkHv1xopA4rb2MJuVxozbQGQPAeEbn7AC2y2hUEW0QgMpAWSGppgeA3EOijBYRhFHHdmWzXGEcdjX6X0FbtWhEoeCaQJZhGfqMyrasPFUY5sAaVzgaW9iMdYCDQLUv6YgjJfrekXECJMDKBkgzGpm0By8DxuJQoQ8TEpAXanBDbE6zj/WJq9POXc82wdjxmWDxg4vDI7oPnp7jw+hlPSh0h0Cg3iAzR0Fsh6H2GheAr06Mr4XBch1HZp0hYtj+m4UBDMmw0OFXRLOMhNVxqgTqDi+w+aE0GCFbRtpywh9gQoJivnlejZCotU8bKYCMAe5zftkQKyXZAkIv5lQQQJbMxb8howQGGaQA63GxtVELA3uAU/DyUcKCjvIYxEMz500dhtJd0iTWleEyOW4hDo+kFJMXTYSUEbIFSp9b4s92QpEaH7viaP5uwuI+u2UH2JmTryThE7shM0a4cEZA/1r8INoITxwUP8SNzwnOokNqLcVbH7cfU6Ofw2rmWD53ffk6VxzvviWjXQSULLpHVEFB5/CSoCyY5eYTQ8qqQiyF6OMoCOQ4UzbFszc9AWIJmNQW09YcukSFQUYZcnk/Y0WkIqm3ptz1IBEVZHB4WgYIVETx/8ynYKD87DWeUAFCWbmMkycG8dJRxjNJoZmhFrjlAM0OQh23Z6M0QtHSNq+TUvkzIakkDGX3otDMazObFATDBN89RtFhgE22x2KJrFg0YmJJiYuLUDAEKGsUj1EBoQ3ZaGU2o37hWF0KRDYtIS7v0EGQsjCWRWJ0z1HE0aBTZtmoHVs0kQTF3AS6Eh7HjDmJq9DO4T/Qi1nKjtYomT7VQB6da2R1ki7tS7TIo6OykVKep0tFlYyi0qx/U4UT3fAxUQH8DRkTb67RgEo3CENwg1Bv/aCGLANEnMV0SKDetzdjCr9ExxMTV7ulNjgjUoAIzqPNwdkXy8FH1EBTggi+ThIUPCnqokXdQs2UHpmZFjp7uwgVIKtVCq2LjxFI1ykghd6RAUUbb23DKckGILx7LPUSPq0MqPdVZsAOCuLLU3tltVXDPtiGMDTo+IkC5jHh40I18CEBzcIOQgls4dW4GYmIOUR5sdeYEoEJWJAR7ocY+KoQcU/CwRGvHnYKp0XMeqKX3wdPK+kgAuoKG1eahguJjRl1tvQcnb0tGkvaEAJIlmqlKxUvFsoaMgWTxqVBnOezObINnEnKGkBN9uEIpFYLyBMQuFyTLbhkKS1tS00WmLGg4calM5gwioYGR65lDDPsihFFtAWpZar+GHQQ8tAdq1sczgZaskLkLOsoKcBMt4FQUpAKTgsP1TOBsZSSaCVQqylL5bppp0M30UoimhjlrAcsIQMBW9grhIHywpU12q4ajxp63HOtj4QiSFOKzzkZDLvcI1FABjzshW7AZLUOl0kJFRhM0Sdg0pizCIJAaOhpmNhTuQJ2MHXcRpkY/9UD2w4HIs7aOAY1upQ9etGnnYRYPTfiLkybKvjxwkjmPwKNrmhDhKVdrUJaOPOjjamkYFnnpmibVXVwhkCWEC8L4SQ9Qzh2UOHRO4vM7CBkZvXLRaxVQKqgxW1nYSPZ6hpcGoZjiOtDe4lBIpYxAyMqmuVjLNqcXaqHqXUaxFcOLyQnkQkGOzw00N3YyqpdqOfToU9OuNS5kiCHSwtZHMN/JGc4W2wFNaUwwTcnHXBkLmaLUJPJR4Z6JCYD71NJLSVFyQazqxl0QYZBFM8j3uiUnIOZYiAiESGPGIazjLsLU6CezJnrReWDYjx0GHxVCFpx50GibOJb0xdvYhi2ghkEdGtkiPHwyDSpkCdbILlnDeZ7DxARa8BDZqtvaqYHf5CcB9o23gRY0nEhq2tHDnBwBllDlo9ogBpW3QnhVedGUNgaSQY/JC9Vz4JZFKixBEZwqYXLITp4TiFhbBRIzIbzU5DIHFlcBc2xHX0kcTgsEq3zei4Jie3iBHKiskQewKoGTlA2wAI5dBuVkoOfE0NUwSmsBxopli6naUiEyOg5oBVJqeoJFW5rRArQrYVg0Coz1vnwLqhp3mI8xweu4azE1evZlsfo8GLn+cRTqRAEkQCxaCcT4IeST+YQrFLrG3sNG08T61IfqCxWAWrKurIhqeoJRIQasOtayT9yaWT29Y6shpCQziARpNSu0YQ4GGprr46mGQMHDREOUMSw5uq+WssNIi2U8PDFsuUdGDKrwSFtJHKKZ20616q9ohATXLNEHglVqRQYyucShRRdkbSVDwgTi8NkydGOZsw9hbFCLmhXNFnKS9GB0qQPH9rSgtdF5isEmFwqKbAnnT3tGNLQcPSxNTog8NMocHCC9MZbHFSK08nTcPZga/bjuEwEfj+FQ0Skj0By56MW0nJ6Q0PAcMjLDRgsMVZVoNAGKzyGf7mlxHyLsuqrLaxfgENkIG7kvlhLUksQ5OFVdYCJzPrYb4mdQzNBuCuyigeo8UGQLmSxZxgLHhZJbS6jpsjoMYZfyx41YU32KFl+sM017wfTqIi0aCakFjQS5IpUsQI1lMl2Zl3Kz5g5H5wxoEQsjBHqVx1lgtxFgFEKcx2nDk7RmCIB8dDJ6IOoeyF66iXZE9zG9lJlBchiTT4tkTylauWRVK4tFdCEkDakqM9s2c8c9BVOjZz2Q5xBPcRyZ9oTwURd4LPXSkl7fDfkAqyVLgjMwGyz22m4oW/CFEMBxLyZzJDMdGTU+DVpqXPE5dE6DLpqJVvCczeH07IAFkOo8wbEgVzQeXTQIhJgWAQg0NPzwasJ2OfnYQIJ707zIjjKZfR2dbAuD0ZIDpY2FSKoJSKHNHE+JtqJpGnRRJGCMHZexkhtZGMNnF0UlodwEZB/gaUNqtN7Z4YDKC9EZvHRFl1MPGG2y7N6yQoKFBwQxJcrqRlG0+wDJEl67EpBB4C4reRnd1Vg2xYTzLI3MHfc4TI2eifvE9krLE8IWbD6W8kmgiVp7BvtIMzDD7aJcmgiuZbTXEO5k4ehI5bf2nDad6Hh5CDEljygUh/ZSEzxfbU+He7bDAOpr0BwCiv0YtCZgu8l2Q4hR0NpSudVVrJzsBsJ4G47GGEuXBYJODV0Ed5yMRa+tpuSoYcXE8SckVMUHhlSSTPY0IDCVhGwE77KECdfAySHY1gSg2IoMHiLfzI2E8sPAmY8b3Xk+zmQLU6VsIQYqTrCihA0j2u6JGc7vdcZDCW3ZCIcXQlY3LLsM9elKxz0bU6NnXBYHHmBjIQ9kATKMOK46U6S27iREjFudpsFSF7IFn8R4OKo5KWnzcylkkpr5ZCU1FCdLnrKQPcdwVVp51cjlfbEqNi2KHSAjQ5hIKuQhNSk1FiEjpuGERIULzJayQS05lZiPzBDXFUXCAlsQmmtsgsBOrlg9PDJhEFJAS09auQi5m2hhpzdjARol2GYZLVQLtmQXsalRoEFLVFnbNak64qpNT4VnKnuhMlDzHFKpNY3GNkS+ks10C1DAow5QBgIysy0PTxJZbXEnw9COnRgd9yZMjX700rGj69MoLLY2cp1P1HRqRuWik63PPBhTl0tp60QEYpRogiC6LIN0inMLcuMnLIRauwA4Jyw2yg4Ljdk6P+CIcJmfOhp7h31vYLVt3SFD7XLJQbAAC27WagaCr3O6JsiJQQZHTIoWHKhsvrYh8gI20iVR89nA987aBYv8ZrNRfwomJPIb1UG0lJADESmLKBVtBY6vVSWkXKwaSOPqQcDoaVPWgxZF2cLJS6m9I80SmnRNgAY8lEo9EWdmkTGWjiZaFnSTOu59mBo9HfeJgE8yC+59aupID+cozwK5h16CCc15UOecMVyZQGUwQy3ksdMUyHirSQw5oEDraGLOUJpsdlUbcKCMtg9kdb6GI8S+1iKQKQvIQD0ToK/9relRjv7UwrDIGjAuuQxnQ1IIrd00adQdK5GgNyX0FPFwuFqmMgeyXlBjJt4XNEylVTUzwqPJDFLb99SAWhzKaiOtlGH+PmSporUlFtNmH0pxgqXMNFhvXWoBCiKZw/DGoogBlj1nI+Q2bbrGjAIEbOXvuDdjavRU3CdSGI5wnKA48BNvOemwxynbRLC15GsAIb6K8ooKb8tEn/Y6WcPj013PtBSqvphvk8gRWOZmqtKbSWpKhvfC16GjgHZ3AAsMySf84uNhZpCljIVL4lUHvseFI1tbKIpdOQPmJ2weI2BGDaHlc7+waaAYIuMiOroI4QytZks7YhVIiyS30tWYgwUUQbZAKwNQaTFZrWVa3eaRTa3hNAsaIYnB47kBDgkx0E670taxHlzOZgJk1WJpEWJaNmrDHZaO+xyyJuIAY8vDHWqhZHPQmUC+YkyYPIcspBEaZJ6XCpEtELcPOgsh8ryHrIockKtaGTwVnv1hMyH8bGnAw3VNVgp2eSzxPaRdACzFt4oHQ6pAKNZyZAhe5jctbAEaRfDQtoRKrcmTVzu9ZRcoe6rpCK9zZpQtY3lyUDKDqBympRCQ23wfCyP7cFHVZCI5Wj+JOlW2Na6b4MsGUBCHXGWIF54ZyDTiyxYWKA7MZgCT1IgTK9AI9kdXqbFDKXsOdFHJxrE2dtw3MTX6kUt53uMw81zRSaATQOdEPinbQpcIdWbYPpziEGSkZZwmzQ89FSeBkLtEwAktx3CVDX2d1p5PAqLTwhi5lZaqdJ/uaEh2TtFiMpINC6FmtkmvrRJiPjJAjnGdOVgE50ZnuGixHZ3fitI0YKVd5FCbsdKqHg+FlBwCmmaRzYr7o0xednRxc2QSTbGwasS00ZoyO7iWkXAIE0Ubo1iOJi0hDmrNljYvFCxJCDUDbZRVHFvQyjgIatGMVTrt4xAFiwGT9qi0Qit33NcxNfohv3b2NdPC51YCcp2FdoVqg4QiUPV5nC6G+ETMCgK13oo2BUhu2w0DoSlZqdMoQ4GqXRs4bjlmzQcP0TwTCmgtQ5O3rhPAdHS8zCBnqa1wNhluxOLgoVQIoUAbYSFa1195owTI0ZJp8yQBDURZjhioqYbhzTaQdvbjM5kM9IQhZ/JA7i9tDJOFBuIUApK0b2XQNMzTQ7RetzUZcuyVIwLFYetdsC4v5NSIki2grYGch/JpdrDj/oep0Q9eqnMCj/EvY/PeQWcJz0hfGGaBkK9qh3OUXRCkpMt8CPqgBnBaidGiQ4QrCxsN5zkEzfNRYsiwui0CYnmbBQV2euTyhWROc8azp2M8J1qFmE9jBhqORmtBDAmKqpbQuIQzWPBwaJOFlrI6WiyENWXBfWjuFEJRqudPBKn6ECKtVO+gDTXnyKCnKE8VdsuxRkAxlcEem2nLpRNLHEfZYpJ6ivK6HeMjY3O/HExILa1VjVQ4U7jK56erBMyRTbDAgTR06+q4f2Nq9AMPYN+ehT5LohVsHytbaeHJJHsx5Ut5Qxv8tBB5OtKL1l1awphqdWx8AcgdU9UFHLTksPMQ5sjNXWj2xUbSstxLkwUE/R/tqrk2WmDjIdSoH0AVj5wSRc0EIZHAEzOqaGZCtDWQo+IXc9NiYmUYhMqg3oN6SJeMcDb7bkvYxXXJY6ZmGg4xLdJC9XBSQlZLA/hpB1/Hx0RaSBTZHBqzDZp1Z2ty2m4nAMEbPOjIFEhIUoRnLC3Rd3S0mBp9f90nGj5vdOpEyZPmK6RQISDjhK4zzeSQSxLi0koLA3FboOvEaLiDPBg1E/Y5KwMCtlMM6uFSoK0tglKzCZRsMgcqY42u1o4ai5e0xgqvYGO4LNMqn+3jfChOyDYihszQ63BEuMlyGpDTPABMqo6d2PekejIeiKLn0D7/+d5NMlUQ2FF2CCyhWlAGGmUwLDAhMnsIWxjBdhDqjDIZsnRaBCxFnTkVa7fHtWVwlYQXQzmxjo5TYWr0fbhP1JkH8HzyCygqstPs06o5L32GRUPt1HUNSmUQKEuXJ9Xk2Fh/XM0YczlG/FNfb36YUGrr9VjWi5AJo+ggUvbJ/W3tQPtXt7QCY7GyegWkRAK0NOpuVGbBPntlsLe+zUOZA9jX0BoB0mB0Nulo+BHThrduAcgwciUztrwUqrggUNOgKqEwxpcSNGkaNvNkEvtCcKOEni1hFb3y1MLC7NMscwxpyfcqUcrjSPNA6Oi4fZgafY9eO7cn1gCfcxbTMRQOh8hIrVSfnXVSyoLOKhACSeNGWdA6eXs2Fw1STQBbjQKMCVIGC+41PBYeOejgzY4jSiBHrX0uR6ZRkMQJSI3AMtY9DvXISbWsnrw0zB/2WjpxtYNQaz4A5JACVqN1ZPGlttOw3wXFoN0+j55zIK/1Dr2S5zQis0fMcWHh7tQZQjEQsozgFgGBVM2AjKeZ5j+BOPkAH3r0MpY3BLWmM+34BDo6bjemRk+5hOdTnExqAZ5jzVnlkxhoT1P0cfJh08kKk43mDKcmHhpCZp29vgjTDkC1nSHyDjQTog8wWhzKpuW4w2zT5qIGCUxzpOVb8p6D96J2wfD8HSQm+yw3NnD0YBCO9bQhYjJweadoT6JnyHIgQoxYSaKXoEmSJWvNjRoeCo8dFBwbZDzksDGQO+VYyNgczuT21pQEZ6NgglqrXjHGu5ZBnfjPbaplKeZSKMrmzB0CWqetnQp78oZBBQo+jh0ddwKmRt99SZ6jE2eoRJ9wPgXDU0wgT03TDJ6dSlhMn8RxYcjOk9iyvMFs1LBkJrtY7CTT71mJY4IoBARGRWR6FWs5mvELabBobkiOzvkBeluVuodJY04AGi/mtJtZgUHzEKWyz2xWcg6u7wwXT02q5lu24HW2xxayKTAJZFmchHY0DjcyBIYwZ+ypp6G2EMa0ss+oWgSoAJ8JpMZHRm0IHtr3gCZpcHR2kWc4nTo67kxMjZ58SZzxgC8knnD51RmfcnHiufMpi0dz7k5cCeho0Vnr3peBPH640YluaegHAVIR4rKU2UPTk1TPnEJDYwtj3m+YHIEUowVOKQQt51CZQ7DRbMHyYJFUKvkWtHl5uVMb3+mjO1XLUorm0WvfDarNEYEkA9syDt5TGSeF3EdPwCLNkjwZqhUGVAhgGhoI2kdH0CKBXXI8hGGV9gxwTjU2dHTcdZgafSdeO7dnm089bLrA2OdZa0bI7NTkKWt+Aac4LwPzoTbhGT24TlEXIDtzIq4Zj4IHXIhqvjpjI2lqh1hbJEhMjp2m+aKtKDFpoeiHGk3VdyhQw5uyBTycJCYMwTyRhqefcsHs3Vcgej5/iGyLQb6N7Vo5S34dOuAou0DXatQ6xcqI4HxlN8KYBCMS5nxsx47guRMKnRE2eElPi1Uvr08JroMICIxwIA8KIbKjxvauo+OuxdToSXjtfJoz0npdxoD7gSZAtcWnewVSkOhwunRyTwgBmTwTBxbJNPMNWwr0NhO2hS0doVoMux9y2RSXqIyxGsKwL1KBwZkSOBQ9AQmEjEhFW7nSidaJqcrUTh6gK8vHYK59VNsON8wf7XgqIPaomRI4QSmmdtwaWyXBBJDZchm55S4PQjRCzq0sFpwtjpSNGQgL7JQ9hJB9R8eHElOjJ148nIg+WSFTs1GXCm0w6gU1NJnFyZZxOtdJ1vntF8uAczoJXbrNgSZihANUc9wwWpUAY9wsaBTb7K45O51D2DbZB6O68EKEZFpNyfnJcGOn5NZV13Zla4WiaeO9lL2A9w4PrdVUu1/ys4U+fucIo72EJNjRxzjpG8atUQT09KgLuwXL7mssTwxqcw/uvto4UuVv5iktOs4nxJgAXeIFR2MFWrmj427D1OgJF8dliROSZ7bOV2zxmaxlnd8UpNroLs770ImqhgYJ+XJvqCPKRmg4gCyNbvD2pMk5kD2NzEObBbRJq0uLguo4p6T7I6YVGJFDG1Gb5IoMzqmxINLIrhlLtGEmKZtMOSE/MSm0c8gQWCKlvJRruSDLxKaJHSYJBR0W3O8I+9bMe2dO3awpLRz89iLU6U0jRTFBPm8xg2nO7BHFB9rjhT5F0RRi0I6Hd8EZCuPMjo57AKZG334x+7pOcO6G4LNZCAEdzmwpaKTJlVda8qoXR9lMY5u+6BXli5MtrKZltqQHqJqfszUDTYiShpI04c0WAIGydHirlNsIr2fu5wYam7Eoh8Sm5sNeRkiDUKGyoLEHoJBMDgfZhcbrCchFA+ySyNENJj1icnT0kB0ic3QaOFSIUimMq5SzDZv0yMwmLRIBuKp0SnenE6mlxYPwcOXq6LhHYmr0raqJgC+AqE2Gzt9QfTbnBen7DpkDEEomfK1u4Bgk50XORpcWi8LE3ZzHos96NeKnjK4m6RmydULYUqY/Y9jInkrIaEGJvXOqcqlaBcEkGQ3PnMycABQHpiPUaNPItknihPYQ7a6N9RRK5sRytkAlZ4XSNGA5xQqodaxhFZ0/PKEFga59nljtplqSm1WqPITGZV9G6x0d92hMjb7lIp7WPIPzZC+hldmYRq38IQRLChvoEJKEHhs56tKcyZEz36mkUQ+70h8oPl4RMxW2LEBxYY+3Q4jAStdaNCJUGKTJnGQgWFlW8GCs7JEjLVLcEBZKBYrc0DnzCAdgquLiEWUxgp8SmkgFmmQATST0QHLR6v8ckmT28OYf0ikjFIcjEHAs54BNOdF6AesJI2Jr2pLZyStbR8e9DlOjb754OH15MtdFAjWugDjpfaKTI822IOtyCgFtXTAJyzQ3TDbJVE9wQI2Imfg2x+MCbIuas4prGzbnMVkuA2JY7MqR1BDMgy3/ShwbjWtCGDWG5bE5225q2g0a5SXGo3ibGeaE11kcwHa2VlHUcLMGQYFmIX/csEutCXNBZOUodVtHRkxpQqWWgnxsPZDE6HwsJmYIWLCrjB0d91pMjb7pIp7KdQ1AwfnNl06Sx059KRuvFjYuXrq0jfAW2gJXl1ySqGqsuOrkETcvV7UVGExIGRicaGQUk/Z6pxLNhOC6YFiNfmwsw3ZLplGcqJ4FsT152ms+SXKfGuERg5WBFgCPMkxbliiImRlkPyVg04NwX4FoSRiftp1GCJqMFTYORGs0XmkdHfcZTI2+4eKxkxtbne1spdvoemcvLe29A0C2G8IEMh2QbVzk5TLVjYWpp73z2I6ZqR0zektrfdPM1NSH7Z552Ozmi3dsJsEQF/j3o8tvPbH69uMrq6NN4GB77Bnb6A5CjsiGxqe++/jOmSmX/Kdfttu0o8vrP3vNSdk2fflZ2x+zZ2tN5t+PLf/X3Mrb51bXkH/nZmyP3Qsv8ZaTKy+9dTEHAjRQBuLxYTu2fMPZOwaLJvCLN8wdXFkPItsMEZ5z4W4Yf/S6kzu09w/dMfPl+7bTMZp62ZGFf5tfFWvTc86fffHBxXcvryg84TSRb/Q1+3acMzP13EMLsjYLkvLnz2795J1bf+PIws2r69unpjbHoRxhwQ9MTz9ky8zH7twspkMiL5pfO7pwcC2e/b533459PFLE6+aXX7ewYvlLZrc9YhuP19UrazC+a3nt5GiEnB+xdeZTd8YCGk89OGfh0i0z37FXO7tp028dW7xyZc3yc87chfanD88vj0bbp6fqJNgyNXXG9NTDtm5+9PbmxBjHc48uHM2pGpdvxRw2e26FV80tv2kxZv65u7Z+3PYtlgv/vrjy+oWVK1fWj62PLtg8ffmWmW/PqRb+4uTSGxdXD6+tY2IXbp7+nJ1bPlp53ra0+vsnlrZi/TZt+s69O87bPL2wPvqZI/PUN236yG2bv3wWZ+ymnzw8f3x9tDoa7Zie+qkD3GXgXxZX3ra09vZlnN2bHrZ1Bjv7yTvG5vajh+Zw7LYruTE7PXXuzPRjtm++cPOMLb94ZP7kaNMO/fKygfkj1YO3BqGjBWriRbrd8Ekfpz3bsliVifUFxmm043yIdeK1FlUBMi2EI29wABvDhY7ec/7mfbcsj53Hxteev+M3H75312YdWYU8/X+O//SVUcsKX3jWtmdctvtj9m1t7puUWXN45D/f+p8no7K85GF7v/68HZjeFfMrD3rTQRv/+GH7vurs7Zr/1NOuPP6c6+KKLXzRGVufcfHuj57d8mcHF7/yf46G9VR48rk7fvXSPZSwApwCp/K4/zn2f48uyX8KjB5zzvpofebNt1rdPT117Ucc2IddHm165k0nn/2+uJBGjzjrSdedfEHUu1PjLy/e+4At049475HQN+CF585++74dX3/j8d8/fur5fMmurb9x9uy5HF1HLfFnJ5e+8n0nLH/v3u2/fNYshJProw+75sj1KkAP3TLzlov2QcCV/7NHFxaHUOIzd2z5rbNnH7AlLsipK2Llv2PP9t88m6mAb735xG+fiFmNLj8T7WffcOzVWXAn8HWz2150zuw2HOsNuPTqw1evnuJc+sKdW/703D2osFYfdM3hK/BEJXzuzi1/c/5ey8Yfn1j6mptjfwsXzEy/+oI9H7Y1auvXv+/E75+cXMYn793+q2fNvmd57cHXxlH4w3N2f83ubS87ufTluYCP3b759Rdyrc6/6tBNeOLNKAg/dHDu549OHmIclGfs3/koPQ1gzXdfecj2CaBKPu/MWT/HPO6m4/93btn2Fp+yffMLzp59aO5ChzG9CU/GOBC440PhwFnBy1anMC2qa/zY1xYRINOod99kzsAMx4Oy3LZYhVA0AG0Y01VzOA3+4MaFL/y3w2Iy7Xe//djGggi84talr37rkREuTqTF3nnayOw9bfDN7zx2AlcCjON2Yn3Td7376MaCCLz88PLXsBRq2u8fLoXc1KFZO8V1OwbvXeLE+uirrjoe8/eTkDFepE4NDvp+oUm1aXD/dl7e9AF/Obf8u8d9IzyW6itmt33FrrjX+7Vji29f4nPMTx2ed0HEFfbic2ZRbp5xeP7Hj0wWROA1CyufdsMxXM+hJ3wndXuAC/78Zp4oRrgRC+U0mJ3adGET8or5lecei3LzhoWVKojAa+ZXrl+NW1QAd8RVEFF8v3n3tn2qpDesrX9t1rVnHppzQcSTwdP37/i0vJXD4vzmsYUHNbdjuNlE+zfzQ4X6x8V4hnZBBPCcgfY7bjmxsSACOChfu6FAG3uzxANY9qfceoqro8XrF1c/6fpjoXQk9AEujgU2lCR0vvb49V3L8vJClQwFMouLVJYbEejKcF7AzbWtIOakKi83R8nI4cxB2fInA4HLdsxc82lnv+ZjzvisM/niAnjd4eX/d3gJ5L+7ZfHXro2C9Q3n7XjXJ559zSed/fRL4y7jqoW1p13h8wZ5AQ+h0RtgPz7/v47QbpbBndr0qoNLv35TnJHfdPaOdz/6wNWPPutpF8Yrmvcurj3tqhMP3T7zYxfu8nbR1uF6+7ELdnn7xN1bYn89OjZIzVijR58zetQ5o0efPXqUt3NEHcPfnVj+8ZvmIkNhfdMn7tjyY2ft9BbGTZtwY/hjZ8KyA+1D8Pzf7O+vnD07euiZo4ecFduDz/z2PdvHcm7atHT5mTdeesaRS8/Ynfdcr54/9a0Z7h/P1BWIC/pJt578n+XVX8gLGK+m8doThRKvW215xNaZPzpn9z9fuBc1xRbcuz3j0OTzzSlv9E6JhQeeecOlZ7z74v2hc56nuA9qgTp+3aVn3PSAMy7fEkfq/+Vd5++eWLTgYoc9aivsy05GZlSc3zt392+fs/vHzogFf8fy2jV6gf8XeRf2+NmtP3lg1z9csPcBejWDdL+rVI/Ol+quiS8avzF/8fHF/8jKCHzKji0vn1v6reR86+5t77l4/1WX7P/hfTts+Z+VtWdtWL3v2bv96GUHcE/90zk9xL92w7Igzxsu2IvKbvXQ+ghV23KHgSOHA6fLFRtKkgVegXUxW8jKZdgFuK6hkEWZ0+ZL0YKZvvZ8JwhdzVBPKUimi13h4m1bPuOMHb/5YcNrmb/WW3ivPTScVb/8kL0P3jlz8faZn3zgns89EAf77w8tZ1psLvE5sQZvOLbyY1ee4F4M4B699uhwMv3ypbsftG3LJdtmfuqS2c/ONxP/4djKR+zc8qwLdz/rwllsFzf3As+6gJZnnT/71Wds0w5qT7HvUfGHHXzTyWVtK2xPrFy5sMoZDvcogWfdPHfD0voikhRGo6/bt/1ZZ+/yFkbWxJlnnbXrWWfuetZZO3HP0o519crqm+ZX3rSw/KZ5b7gIdeyarMZSY7ksK8gEzpyZRlm0/IbF1c+64bivaQz6bF2Tr5hftgX3dHgp+tW7t6FQoqbgpZ/MJFgotC/htt6O+niyOVUuy1fi7x/nbp6ud9l8A4tVxUtjGTZVsfvdpmadOxMzObY+wsvbb7z5BErnNZfsR/XBU8glGvdA3qD9xJGFT7j+6I8emnsRnoEuP3P98jPfqNfFn5F3jihnNzQ3ocbvnVj6t6Uo0FidfTPT/5BPRVCfd9bs5VtncGR/5sxddQf6D6d5GwHI+RIblwV58GyKyn6J34PatOmvTvWy+v4MvHZWSeK1gbXUR7S8LH21wOIi5bKl68cbHmjbUgh+bVBx3C20LqRiiMpreBWufAN/DCxnl+7cfG7eiL1nbg22d87F8+ojd28+Y7NLOWLXH7ozToKr8cTrsWpfPG7mryvwJ66du3W5OU11pb0zT8pH79q8b9pvFHCVHpqf81y9uCaml862BCx+HcQJoPVaicx5Dufsx7/ryMe/+4jaox//niO/duv8KbIJH3vFkRP52opgQrTaKQgtOI53WU9yiV86svjx1x7jdp226496T1t81LVH9l156MKrD59I1xeOfx7Son0FPfGqGfI7lmJJz52Z5juSiY/KJw+8XB2r8uPYMkz8FHjktUc+/rqjj7pueDP3808/T+MNiyvffPOJT77+aH0K9BGayZ/NLZ3ULD5u2+Zv2xMf1qBy/Xt+5PLFs9uqouHl7UtOLH3zLScvuebIZ99w7FB+evO/9w+36v+8uPpTRxY+88bjB648VHfKn7UzMgCogBY+MT8aeu3Cyh+l8TPEfGeekI/evnlX84r4IVnjNr5J+oq5ZZTjy64+/MOH4k3nx+3a6pJ9Sjw0D8R78rOsDkMnKy4hXqsuWL7YdEXZQllctHTZLtnntC0hiEZNMlZ78JbdNLS+dJXHLgiwtIgRN+1F4RNu5sU2um4hDuQ5OOogrKlsrU/V66+DK0oVA2XbJD9/23Chfs7bmw8iRLtuKajn8EYJsbCy35bn50GclKB4Fk1awWM14ypa80G8lVPBTI8lPHF/fLh5w+r66+aaWwMzOcRAJsJowfoHgLcur+FuyM82uD968dmzqAjSTo16BW34VbPl92W9yON2ChzFVE+D918T/3N57U16ExO4bPP0y87d/UlZtk4HlODfObGEW1qr2Lun666wbgm/YNfW2empT808fs1rvOaCvb985q6PbF4KAK9eWEENOqbd/MJdW9960b7/Nbtttpn24fXR9x2c+4GDfFPvM5uS/ZOHo2b9TH64DLw+J/bpmsB1WfLOad4DBYbTL5e38N7VdZTjqzLwK2e3/sW5uy2fEn6jALiZn2l3DJjmlYPNlxAuMMtUUbCySOF2Axc/a5Hf8ku+XUMlHXdR1vUJUFUqGquVF2nNpFcluAVzwjhcPfzmx/oIr5StvgevAXk3pI9KNo2O5Jvle/kSAmXLBcITG6sguABe9lC+rgEmb8FGo4uzYr4HxTdmy+1InkDMb7tn3iSIvWAIRvTzjcLJbHmb3vKg/W+5fH+0l+///jN3tDMEPmHHli+cjcvpf9qbWWdDXjSNmeA8NSWgSfbD+3a85aJ97RaOBs87c9eleU+3e3rqkeNfWNkIvIJ+YvOVFL9qNi7MPIueSaJ9Ye77xx15ndcdHPDGhagR7bdMCr94YNf+vKTPmJm+zXkCKIKod9i+bnYbCtx/X7wfk79+de01Oejc+uh3ji/irLCKF9TtbSzK/X9dvP8dF+/7uQM7H7Utzr13r6z7/UHg4ds2/8G5u0888MyXn7fnO/cMa/LyfGV6cS7IXGZFHf+YDTP3V20uzrcsJm7ijuSU2o9TjMdu3/x9zbE4a3p66v2+//DGrML72hfbHfkZCy6huh/UZeYr06WE57Q4gMpTGGWI8GGzxV4JLnnm28srVpUiwtnTgs0hthiUR6+6Zfnd83FyfNRufmnuMbvj+fy9i2v/dHRJ4aOr59f++JZ4v/yjQcD8AQ6hGlFjCbtnpr70jG2PPzCcRgFN4DH5xP6epbXfu3WBxnWMtfInhzL/LuRXQk+7Bd++1HoC2FnLnEMaE4/YvvkRO/hdOQrbt/CtLiYML4DT9eUX7j1jwwUQ2ZhwPMDwKONjXbB5mgM1WzgaPGXfDn8LBHjPyvrX3XxioakLp0R771RfbQE+Nl8Y4mX1q7IuHF3DzVos4Ccl4cPy/qtujoDrU/6wU736+/79mGfcZP370irmafn94HN3bn3tBXux/d65u+s7lS86FpMBfuboAl4U/2VO9eD6yO947r/y0MwVB6euOIjX3Q/buvkH9+98dfNNnWtX1//q5NJ2EbC9cm4Z94zPP3v2mfvj85DaqXor0Phk7f5X52cdhY/VjfZj8ui8Y3ntT/MrPu9cXv2TvHstQgFPDL901uwX5ov0Xz+++Iv5FciNuGJ5bYlnDnF7nlHuV9B9IhaH6+N65AqiashV82WPXtezBRrTjg3w1U7V1UebL1omNr9SMUBeWcIoiXnoNt63tPaEdx794rcc/rK3HbYFrx2+8Zwd4D/+rKGWPfY/D3/bu45+3xXHP/kth47nHR8JUY61LzErjSjM8l3CqT958L4Lm4+MA8h/xpD/G648/u1XHv++a44/9r+Pnsga8fh925Uz97cF10p2lnEJsSy5CIkvuuoYt6uPftHVaq85diVeEoJZkPzS8/UlxxYewtla/unxwuOLX3TjsXb74fyydAu8hPzqvDN9+/La0zZ8vnk78XW7t9fL6s+76fjn3XjsSbecvOjqw+/LA/SEvKn5tB0x3I1r6w+8+vBjrz968dWH/QYl8OmneaPwa3dv/6ysMv9vcfUn8gXpB4T2BfJG/IG8X7t7m6eC192/dWzxqpW1H2/G+vSdWx43uy3uuDZt+tkj829ZWn3z4uo/5V1YlcJ6VW58/W7u/nePf/G73i58fPOWxf9634nvuOXE99568tNvOFbTbQktntHcqv/gofnDG15iP+GWk19+0/EvuOnYLXkg+PWDjgb+9ECXK16BonVV8ob15PcQ88ILwW0VzXT5sgecjTRKWQ5UH4OAIUxTHpJThcyowPz6phfeOP/yQ0sLDsSLu8v3XI4n0tGmB2/f/ILLh0rxf9638Nwb5q/Pb3o/77Ld33Y2XodqbuhiJho3wXesOPToxZeNVxxFPXT7zG9cPLwd86KDC8+9ZeHGfGH+KxfOfotvMJl4bM4EWMPu13o2K5B4xcllbSspLMcXPwdwhp+xc+sP531HwEfBG785cNtAgXvF/Eq73dDcl7Vor6vnHlvc+NbV7QFehL74HNzSB141v/KC44v+NAPAq1cUTcs/ccbOh2UtuHJ1HdWk7q3On5n+yWYyE3hm4/qxD7wmvm5+uT6p+KNzdl91Cb/vgu17s069Ym4Z+/7sM3ZelC97v+PWk5ddc+RX8u7y63dvw50jhF/S/7QB/nFx9VHXHX3M9Udfm6+pvyl3c+I+8Yv18dTmqamHN29TfnJ+gveR2zb/SuZEcf2t40vPO7Z4c1axXz9r19efppB9zPYt37YnyiXYT9rwFUU8Nb5sbhmv+q1iZ3Fja7nD0MH2/RSu22hVO7houph5JesK9DKWCx2bvDgrajAq3F5s7jAWX1pWW+EVNXaF43zBi9xHz27+6rO2/8ejDjzh/J0V8oTzdr7u4fs/f//YEf2E3Vt+7dLdTzl3F+cDRGYJDBwvH7J/5u5tP3F+vGAklByTeeJZO1/74H2fx//nN+ATd215/oWz330mplE5/XQShMCwGl5AVUaTJpgT4FsTDYOBXP+fOTD72HyxeVcD1/l3Nfcv33HLbXz193TAxfZPF+79kl1b68vSs1P8QvKLz57Fq1dbgJ3TU2+8cO+X7tpa7/oDYOJ29V8v2tu+Hp/AJ+3YUre0AO5DQ7p9qJtEjPUVs1sfsIXfd8H2Vfl6FsXo908s7Z+ZfsMFe5+4Z3s7PezRD+3b8ZJz4lnzKft2/Om5ux+1bXhnDilQ7P7xgr2Pz2yXbJnZme7Lt0yfk3X2K5s7vk9q/k/hd+/b8Zrz93xO84E1gFfcLzhr15P2jj9BjuNZzVPFS08uT3z1B6PumOJn7ljwV523x/8HqaPF1OhxF+RVqpIx/Ce8vHpZR9LEHgT/TIsuezoVMqXL3mQ0uJj5X9kElIaACc7jLlWPa6/dbFxKlFM2EqhZCeLNy6vH1jatro8esHVmJz/mBDnvtpAWo8fcPEoOETMRDwROkoxI2nhvXl4/tr6OM+sBm2d24LSnreFUqjBKpNEnfTPuRKB3zTtOVXao3GvrQvb3auCG6+qVtUds24w7ozCdCjetcqkPzEyfNf556z0EeCl67er6g1HdTl+p8eIaPtTW0D9ovE9rgtOTp9/px+24szA1+uLz86rDcvvyVutrNVzobEeTR4W2up5tDHYa1QKtoDRRDmgf/y/JaH3N4F7JdhI0H3LoaSCd9pwSFNdiWuBAi9qNq0vBNg4u9YMlQVEzIUd2iqYVUaoplu2w13bWYimcgJ5CwodmIn+tdlo6OjruJkyNvihr4sSFDdS1SlkPciSTi06SCcMV7r65vMEcC7SEBvnlZZKyeMS0ANFPqBZydJCHzDbKIc2iWimhpowRqeqdU8NGarV35lhEoNEaFYKK3IKz8o6oVS9+TVttdh0dHXc7/P1EX7G6aN3aCIlCqrZgM4eC3ymDqAykqaXFbxdmrGUAKiTbHcJs8pGT4Xyt0NDoUp7wlqBYh9vCKaGF0XUn/x8LQzzJjB0CsQhycbN9U7xFyLf2ENJGafO4TJsbjUyWSSTAwvdqMxaTGZjKENSOjo57Cvy+m75yyGu1vfL9PUQpbLIKVLEIsmpf1QjRJSeNgWnEBhLTYlBXKMj6QngYHQumkzvEgpOYZlVt0JzBc9DoTILZovUoGTtEYctR0Hq/HCtJuwCCbv0iSe4yQAGtsjmQVRiSXLb7tpCxHjTJNJLY0dFxT4NeMPLy1tWOR1z2EBuBVUBXM3tcz+mirFahYwTKVm1xq/+Ex4QQZMRj8OquirLKkyeANqqwOE5Iu1ywQLcKIWgpR5lTabKEhoUpE3qDbG/1tkPiiB40K6MJFCSXkbMdnwn3JfmydXR03PPhCuXa4YvflzFadLy+Jbs86cIOb9rJUjmwUKnYYlPgkASVRVngAiCycPhmyhyKEnD7GZHcPHQZPBxosNgVsiqXjbTX63d9B2gouJ4AWrkrT5Q/tc7jcG7pgsz9hay9Do5IZsJiI2BLR0fHvQe+T9TGC3i8ornY+crn5lrga97lwExXCmSzUVt45UIxGpKYKS9LmDgsWE7uDJBVXjmrHIit03pTZoabXEa/lvdksvxZxUYZ9sxG1bFiwuiN9mozQ9HCgkcZi0BbR0fHvRdTo886L67kup5xzbNY6Z7Larwvpos/PqXFQwUiXLQRFGQMi7pIVRmSNshpoZ4WdDUHm6ja2MRSaJgRmwLbNA0ukb2bFJoW3UQqtPEZtACBlpQ7OjruW/Dnznmpc9MVH0bUAtUItKGKEy9CrZrTeHHnBTIkWCxTcAY+BiYgg/gQilxTasb1TR+ZNurWkkkyG72WJTCJxuIcxOSOQBaHbg0Xm4fAZmZlsOzAzKMxQ+jo6LhvQZ87x+Yq4KqBG6GqJi402uBCQQRg5Cti87NYkInOXhgzqjbTkAQ+NdGicyw2qjDkoHhwFEUVP9rkeCzbncECi6CTVwYb8yP1GE5GRpmplmYRIhU7bR0dHfdl6P1EbC5wuOarCrhGtJVoKBbYVDSxBT+/YTPQkoqeRVMaAGEoQC5S9WZibY6S3LbeIFOFpNYuPIJQgozhVQkmPCgMrs5oVb4Z5UC1HqKjo+P+h/rcOWtBVQQWiNxCVR0BswiuKdiIKiveWprsa1mhSFMqqIxrooLgTQk3WiDggbbqNTa/jsZ8hntGe9FKqI18cJQKusNdIr11dHTcjzE1euy5rAQoEwArAh4Tcus1XJhs8Q2jMBjRolMUdITXJzP2FoKs1p3LpT/W8LjS2dJfOdVD5UADRXb7xLSXAh5OghalUaN0dHR0jMPfxdG7iigULhmWabesugOBG4S0YyPs0r1YcLSFKwPh9RZqO4qNSkuOQihnUSNBr80pw4ubO3Nkp8D45uU/G90eWs4X5rRAgFGjd3R0dGwAqiEKhKoM6kSUKlQNVRM8yh4lydUwaw0trR2vYa1WiOpU0HKDjAeTYMvCF0xqIsjCbA7J/PSqXJpMu5IwxJayK4TMfIHc0dHRcVvwd3FUZVw4UM6AkFVrWG5cv0pQjfNt11D+ZAfId6mSy6mYzYIz5P/Y481dVkw8zMTDCcOiFhZvzkCXNsdyDlB92yiLo/hQ2e3o6Oi4HVBNBFxcLEeVkWTBluJQlsp7MZU/6FFAJfv/MsOCzrWJbYW7fsnOQVQcI9yENDrEd3x4WChaG0JvMaV2dHR0fODQ76260LCmqLSxtVFFh8XLFccFyMYsl+holGCayxktaqMCKjzyoNW9W3wJRq+jXUyL5qI5pJVsgQkVCJdHcWBHR0fHBw19uuLbOpYbtS5q2KIMWdX3+CAFTUYw8OIXEpj+iCOi1Do8VFfSsivQXg6nzLTbkjk1QkS1OaF4Av1lcUdHx50KvHZuy1zJKj2uPqxEqmiUZaQl7aCgja/OqGahg4BHBdpYAmUVM6gR1YQ7P2QTvNnb0dHRcRdD94muhi5zfj3LSqR6F6pKIQWoskcIBJUzf2DirUqYQyCZA9CbVRIbKmlVQ2RwIFrANEd1dHR0fKigz1hclbzhEbJKnlU8WKR8c2cC2qaKufyFXRwYq/yFpThO6/z2qg2XW0sdHR0dH1L4//a5hLkqudLJAtU3gLbQiLZKIdkKURvhRUZyhVj2tslVUn9sgEzfXdJhd0dHR8fdC33uHDVO5Sk+b5Ec5QwtileWQttNZoGzUZtVSEVjVPuyOl1i8SGxo6Oj4x4Cv5/YlkVVrrFKBxXFK/83SLlc7PCgtyl2sHMzU1SSFUK/OB0dHR33SPhzZwiqawb6tqhBBlwBbeGWAl8Fuwi6kqa3iqC3jo6OjnsDfJ/oEuYf2vL9oMoYWtiho96pjwJKAa1fFKP1rWLZRe3fHOzo6LgXAjXR93fYfIvnSpc1Dg/fIfor1jKI39RBmTs6OjruA9B9YtVEdFXj/FrY5c/eUHMD2Frq6OjouC9gmq+XUddc76Lq+V3CZvOtIjbAbUdHR8d9EfkbENzyVTMKIlB1EJseHR0dHfd56DvbLH/r8WLZ1a/XwY6OjvslcJ+on+rCTWJHR0fH/R7TvRp2dHR0FHCf2NHR0dER6DWxo6OjY0CviR0dHR0Dek3s6OjoGNBrYkdHR8eAXhM7Ojo6BvSa2NHR0TGg18SOjo6OAb0mdnR0dAzoNbGjo6NjQK+JHR0dHQN6Tezo6OgY0GtiR0dHx4BeEzs6OjoG9JrY0dHRMaDXxI6Ojo4BvSZ2dHR0DOg1saOjo2NAr4kdHR0dA3pN7Ojo6BjQa2JHR0fHgF4TOzo6Ogb0mtjR0dExoNfEjo6OjgG9JnZ0dHQM6DWxo6OjY0CviR0dHR0Dek3s6OjoGNBrYkdHR8eAXhM7Ojo6BvSa2NHR0TFganT5mSHeRzF1xcGQNm166bm7v3J222fecOx1Cyu2jDZtWtMKbL7i4JpNmzb9ztmz37hneyh3Kv7P8cVvu+VkKJs2rT/wwNTUVCgdHR33ANy/7hOPr6MGbrp2dQ3lz9v6pk1H1tYX10dVEIHLt8yE1NHRcT/Dfb8mPnDzsI+uibeusS0cXBu9a6UtiZsuG6+Jx9dROW8b80q+ESea8E/ZvuWFZ83WdrqbxDakcGztdk2jo6Pjg8F9vyZevnUocLglXB6Njo4Xr1vW1t89XhPPUxn9u/nlL7rx2PYrDu698vDWKw4++JrDP3tk3oT3LK+deeWhA1ceOuPKQ/uuPPS2pdXPvuHYN9x84im3niz7JVcffv7RhQdcfXjPlYcffu2RV80tI/D1iyv/++BcbaPR6DZDgF8/tnAWBrrq8COvPfKHJxY//JojF111+LyrDsH4j/kmQEdHx52C+0FNbG76Dq+Prl2ZvNu6cW396vGaCLzs5NLn3Hj8FfMrS1JReN6zsv4jh+a/9KbjMmw6tD5CtiPro2Pro+++9eSrF1Y2656v7Desrj/54Nw1qxzu7ctr35pvIx4fjWqz5f2HvHt57btunTuoOv6fy2tfe/PJd66sXb+2/r61EYybmaCjo+NOw/2rJh5aW79udbL83bS6jmIUyqZNl+om8aePLFgFvmH3tpA2bfrLueW3Lq2Gknj9Ii2bN429EEYN+5rZraGo8qK6hXIanDLkTYvDneCeqamvnR0mA7gQd3R03Fm4f9XE61fXr23Kn4GbxJuat+oeuGVmfTR6cxa+M6enfvec3Y/aNiR552lK20R1+rjtm19yzu5QhCO39b7kKUMONu9+PnzbzO+fu3vi3vAvTi5NXXHQW5g6OjruKO4PNXHYR5Sz9pbQuHJ1vf3UBTX0RPOGoz+WadF6C2fPTN6w7ZmetJzuQ5jCKUPWePsY2N6/uNPRcRfj/nWfeHh9hBekls+fiX2/cmXttc0nFQ/cMg3G181u8/b42W0vOrb4H0vv72Xv9+zdfvOlB37v3LFbvI31b/Il9wbcgZCOjo47F/f9mrh5/N7qprxP/Pjt8RoUNdGCcemWmf0z0yhwP3fmrku3TKNcfvutw7esT4lvO9UXvFc3VLiNlgncgRDgwVtmnrF/h7cwdXR03FHc92sicF7zwvZf9HkI8Ok7t1iYG687D9jM+8oXHls8/+rDzz6ycMPa+sTr1fUN93N7N7zmBVbzY+XCbXzCcodCgA/ftvknDuzyFqaOjo47ivtFTXxg8/IZNc7CY7dHTZwA7g3R/tjhOavAGy/cu6spessb7t12nrImRv8B4Pa8uF4bjfoL6o6Ouw73u5pYuHzLzIc3X+cunDEzvbg+el9+6nLm9NTHbd9yTr75CCxvuJub/NRGuD0veydwypDZpuBes7L2N/P9S9odHXch7qc18eLN09unpx6xdfIrz7OqP4sbqt7+pibentezwJ11n/jhzSTfu7r+Rfml8cLbllZ/9NCctzB1dHTcUdxPauLkbl6kL2ZvvE909dw3M9ybHVwfPf/owq0f+P813vjm4G3ilCGP3bHl8c0XuR+0YV+uWFn7qSML3sLU0dFxR3E/qYmTte9c3fdtrImXJhM3khaAJx+cu37DtxpvE7fzdrLF6UL+5Nw9r7tg76+dues15+959yVnnHWqty87OjruFNxPa+KDZPnwbZOvnf0f+4BfO2u2Pq3+mtmtj2j+H8uHGL98dGHqioOfesMxlOanHpp/0+LKrc13v/dN3y+OYEfHhwz3/d+U/WDw9qXVczdPnzkzvd68qp3WFx43WowJ++loLd5/yFuWVh913dHQx/F5O7e88vy9oXR0dNwZ6DXxXoBfP7bw9EPzEz9x9tWzW1989u7t/XV0R8edil4T7zV4w8LKwbX1VX44PvVx2zfvaz4K7+jouLPQa2JHR0fHgH6v0dHR0TGg18SOjo6OAb0mdnR0dAzoNbGjo6NjwMyzztgZ4geHpx2ae9Pi6r8urrxR29uX125eW3/QqX5k4a7G0w/NvWZ+5TN3Dv8fbiPeurT63KMLj9m2+fZ/l+WfFlZecGzxM3Zs2fgHSP/i5NJLji+9/xGfqv8M84gN3xIH4FocbXrwXbNWSH54bbTx2+l3Oo6trX+gXwz605NLv39b69bR8SHGnXafiKLwo4fnf/BQbE+49eQX3HT8sqsPX7vhT+JtxGOuO/rbxxdDuUP4pptPfN6Nxyz/3fzKK+bjr4CeDletrP3M0YX2T53cJt61zJAjp/r7Af+1tApXKKcBCKiqoYwDro1/9+rOApK/5baSr49Gj7r2yO+fuOOH4HtuPfkJ18f63378x+Jtr1tHx4cYd+Zr5wdtmR5dfmZtf3zO7qtW11Ecw30a3LK6/ual1W0f3FeP/2Z+uf6YyYvPnv2j8b/0dFfje/fteNtF+0I5PRaa/6DSArFP2Xd3/kT2TWvrb1le2zL5V7Y+ALx+YWXjH5Pp6Lg34i58P/Grdm/77B1b2puUf15Y+c1jCy88tvjvzd/nfOlJ/gnlNyysvqS5VXzl3PJPH56vP/pu4CXq6miEl+cvOLbwBycWb8y/SorAufXRdavrznD16jo2u4B3Lq/+yYmlnz0y/4cnFm/zpnU0GmEUCK+ZX37O4fl/a+ZZeOPCys8fmf/bZm43r42N+Nr55V89uvC7xxdx/xgmYWHEv3yA2zHM8x2NC7HIAAFT9ejefcxB/gFYN8zqH2QH8+Rp/ujV+1bXX3x88a+UagLvXl77veOLWEAsY5g2bfqzk0z4jwsr7SH46zmuQLubBg7B38wt/9KRhTo6XP/RaOf0VBv+psWVXzm68Mcnlib+tAOAgbA+uDU+3ZNER8fdiLv2MxbUqa357tvTDs19wg3HXnBs8UXHFz/6+mPfpT/o/val1Z/Xq6dXzi//+jFeUW9YWHnktUfwuvufF1c+76bjn3D90Xcts3wsrI++7H0nvu2Wkx97/bE/P7n8jTef/Mhrj7rgPuPw/PJo0w2r6xCg/vjh+afmLwk++/D8h1979McOzyPka28++dBrj7w5//bAKfHqhRWM8vj3HX/Gofl3LK998vXHvvZ9J8InfOstJz/xhmN/enL5c286/sU3xk8Z/tGJpfpZwyfccvLTbzyOkvQLRxcecd3Rpze/aYgX7Bddffj5Rxe/8ZaTH3Hd0VdkTUEsqjaEl55YwuiffcMx7P5fzC1/1o3H60/sA4+76TjW7R8WVj7jRi4LmH93qrcI8JRz3tWHn3d04YcPzT/smiNhFX7hyPxDrj3ym8cXf+/EEpbx+3QLjzX8JR2CV8wvv1BFDTd9H3XtkS+86fgbF1ewm590/dEr8s+3YvUOXHn4ibee/Ov5ZRydL9D7FVj2q1fWQdaBYJn7pptPfPz1x1BVsf6Yw+9krVwajR5z3VHs1P+dW/6o645iX2zv6Ljn4E6uiWujkbeDa+u4OP97Ze1xu/gOOm43nnNk4Sl7t7/l4v3/etG+vz9/z68fX/yNYwsfsW3zv1zIV52/eOauN+nl5zMPz2+Z4v+uefn5e9ceeGB2asqVznjtwsp/X7zv1Rfs/e+L96MO4l4MxmsecAZeen/Wji0QTDNwL4Zszzpj5/9cwkH/6YK9uFP7k1PdPU1gBre0F+37/XN3v+HCvX94cuknmwnsn+bckO2H9+14+fwybnnCIeCeCGXlD8/Z/R8X73/bxfvB+ekjC0fytxevXFnHHJD5yKVnnDk9hZs12yfw0ds3rz/wwL9dtO/nDuz8y7nlt6nuP/XgHNbwzRdy3+H1b53V803hdfPLT7j15Hft3f6fF+/HXrc/HIk8P3ho/pfO3PWGC/f9vwv3/fOFe597bBG324/ctvkNF/CHJH5FLgjPPDS3S7uJQ7D8wAMY5Rn6Swwn1te/5KbjXzG7Fev89xfs/Zvz9rxyfuX/HF+EevHm6U/X+oOMyvu7J5Zeed6ev71g7zsu2f9ls1uffOtJ/wDlV73vxJZNm664ZD/24h0X77vxA/8Fto6Ouxp3Zk18z8r65vce8nbWVYdxcX7Hnu2/etYsXLgveOTWmedJBj5959aHbJn5Q90cGf7o8b+XV1H1vmb3Nmn8VZgv3rUVN2XH86/Ff/3ubQ/V704/aOvMw7fN+CUnsG1qaseG97MeunXm787f82P5wTqu20s33643vb5pd/wdvsds34L68qrmdux78o2/75NwXb5+N/zHqv785BJutSD8zJm7UFnqN7rx9PDxO/hHYPbNTH/Rrq2nqwhYNH+0/bn6QNbVBPfR379/x6P0twbhRWGCsPHg/cfS2nkzU7+W6/x/z9uzJ+um36N48t7YtY/bvuWhW2ZeqlfNBp6K0KJ0/uPi6tfMxiGAEYfgj08uz6+P3rq0dv3aev2Rws/dtfU3z5r1r65h/etvT78EN6HbNn+enguB5545Ozfa5JviV88vf/ve7V6Qh23d/LH5pxM7Ou45uDNr4oO2TP/ymbtwu/F/zp597fl7bnzAGb95dlyc71pZO2v8Nwu+dNfWt+crsoIt339wbuqKg96++yDvUN6ZzPa3YQ5MT9efn2+vycLM1BQ4P3Dw5OffeOyyqw9fcs2Rq27fjcmHN7+WiGn/R/Pe39m5F/4j9xN///7AzPQLz5p9zcLKp95w7LyrDn37LSf+vqmnlzV3bftnpjb+NX1g59SmS/LXHs/UWCfXR3hB+tbltRoa+MTT/IGtty6vYg6hqHqemb8C+Tat4db3Hqq1/Z+VNTwJ2VvwIXhKcwi+Lw7BKpJDeHjztZ7v2Lv9saryuE/fofVfH40w0L8srVb4OVcfhh1H8D3La/OjTWc0z0pfnpW3o+Oeg+H6uVPwvft2fPe+Hd+yZ/un7tx6XvNT1bPTUxNvfS2d6v11Xy4vUUmt7XUX7MVNjTyb2r+gMto0Wswcp/zY+j+XVh99/dFfOrr4+Tu3/uG5u09eduABzZTeDzY3n8DiNu2CJgo1q8XShp3AfdCtl57xp+fuxp3gy+eWP/PG46gFduGVe4uNsUDtUQEWvCDFTVdbfzf+xRhj59TUyrin1XCn3C4stt86e/IDeu/fH5wzeQhwy2zXSjM0bh4t4DnJtdecr5vd1oZjw2rs1Cr2dxA77uG4k2vi6fDwrTMTnz/iJuXRuuNo/vZy/JkUFAuUVG+4wfxh3acYy801jhJTpQHXpIUWf3pyCQPg1euT9+1AVcU1ffXq+sSvEJ4S/9J8JvtfS6sff5qbMmDiL6i89MTS59xw7Ja19a+Y3fbCs3e/Wj/4+udz8RZB+zHrajP5FhvvY036mO2b2683vvY0n048Ytvma1fXDuZbCjesrl2Tt8YoiJgY7vJqbX/+6MJzjvCt0s3N6m08BO9YjkNwkf7ydf2BbOCSqw9/wvX8vdt6TsKdKTLMjUYV/uCtMz96eB6zumDzDG6u/faocdd9K7Oj4w7jQ1QTn7R3+3Wr699xywncdh1aW3/Wobm/mV/5/uZLeVeurr9ybvljt2/58l1bf+LI/Ovml/GaEe0PHpz3q7ONQImpuypckgif+MRj7/QUrjl/QoKK/BU38RPk031/pQWu4b+dWz6xvv4LR+bfsLj6Lfn+5m0ClevvFlYw0JsXV69eWftVfYryGTn/9hvimvxtz6TwtP07X72w8oxDc29fWn3yrSd/sHmeaIHbsfNmpp90y0ncnB5dW//fB+eqyP7Avh0oxD9wcA5LcePq2nfdcvKV8yvP2D/8L6b3rqz99dzyJ+7Y8iW7tj778DxKMNbqtTgEh+Y+fSd34Qt2bf30HVuefngON+DXrqx9w80nDq6PvmcvDyLW/+/nV/5Z6//M/Tv/Ym75l48uYAKogBjx6pX1r9NbtN+1d8ezjyy85Pgibjb/6uTS8/RNA+P5Rxe+6MZjbzxNre/o+JDhQ1QTz98884rz9rx+YeXsqw6fedXhPzi59Ifn7P58vQ1/1sz0l+3a+n0H577iffzeyW+dPYsL79NuPL77ykNoP3XHlp87c5dyTGJhfVQ/0/+Vs1vfubL2KTcca//A3g/t3/l5O7c84/D8GVceesg1Rz5u+2YM9Bd51/Z+8Mhtmz/3puN7rjz8OyeW8EIeNzvhuC08YMvM88/aheLymOuPXnrNkb+aW/7ts2c/+lS3majmp3ztfDp83q6tv3v27J+eXH74dUf/dXH1pw9wTSZeyAPnbZ7+k3N3X7u6/uBrj+y/6vCBmeHPtVyyZeZvzt/z5qXVB15z5IKrj7zw+OJT9+9ABYQLUY/btfUpB+e+Rl88etHZs5+0Y8tjbziGQ/DpNx7/rJ1bPRzwO2fPnjMz/cjrjl5yzZFXzS0/58DOr9ITxuNmt92wtv4JNxzDbfW37d3+S2fuwq0lJvCR1x19y9La/8m3lZ95xs5v27Ptew/ObX3voSfp83HbgePro1f0P13dcQ8Av3IR4ocEt6yub57i35UPPXFsbX1maqr+vjvuI3A7g0vanzMY4OwdV9GWZW00wuvi9hMG432r67gpqz/Id3x9fc/p/67T380vf86Nx9960b6HbZ25amX98jv0f5BHo9H1q+t4OX/27Xv7ssXGfbT6spNLH71t80VbZubX+e1o1KPPu+n4f1y0D+XbzAnctLq+f3rqlP8B+fDa+rH1EZ6K2r+mD0wcguXR6KqVNaz/xiXFGh5ZG9VnQQU8IbWfpF2/uoZceMkceoMrltfu2Np2dNzV+FDXxHs4qia2n67eE/AlNx1/5/Lqmy/at3t6GiXpM244fnBt/arx72N2dHR88PiAb2Q67hb83IFdeK2Nl/Mfce2RvVcevmVtHS+Ew9fR0XHnod8nTgK3Yw9rvvFzj8Lfzi0fXh89atvMQ+6pM+zouLej18SOjo6OAf21c0dHR8eAD/Y+8eQ6f1zrrctrh9fWL9sy8/jZbXfLb2vf0/C6+eW/nV/5gf07zpyZfuppvksIPOdUXzNaGY1++sjClL65EqZT4TXzy38/v3LKDKfDq+aWX79w2yGY8KO2b/7K/h/vOu6X+KBq4h+fWHrSrSePro8+YuvM/umpf9L/cHj+Wbu+U9/jvWN48+Lqp95w9F2X7D//VN/huD34ghuPPWTLzC/l7yDcLfiNYwvfeevcOy7e98AtM9vfeyisG3D8sjN2b/hikGMhvOfi/e/nCyvPO7rwvQfnlh94wL/dcHvwC0f4E+i3ecSnrjj41P076juJG7Gun/z6/v07/E3s24+/PLn0pe870d+u6bgn446/dsYdx9fcfOLDts7g0n3bxfv/8cJ9116y/xt3b/uuW+dee6rf9buduGZ17eQH8mXmjXjl/MruU30v727BNv3umbc/1q9//945s2XZWBCBFx1f/Ex9lfr5p/kxsTuMJ++9Xb8HDo5/9ed0uMO/y33F7fhDFB0ddy/u+N+oesqtc1evrL3y/D31Gejemekv2LX12UcWbllb/9q8g/j7+eVXzi2/c3kNV/85+R3mf1lc+bfF1Qdtmfnr+eU/P7m8Nho9QF8Aftfy6p+cXP63pdVLNs8sj+IXYt6xtPoPCytgXru6tn9mqv3GNV6w/9+55X9aXFkdbbpI5JccX/zLueX9M9PLo9FHjX/H8M/1Y1ntl4p/5/ji3ukp/3QV6jjm+XbN89yc5z8trPzz4upHNHlwa/yQrTMzU1N/Q/Lq+mjTb59YRG24ePwLzP++tPrX8yvftXd7O9w7ltf+bG75y2a3fuTpv/z4dv7m48JPHti1vmnTH5xYesaGo/OmxZWXnljeOjWF+sL/7XfGTkzmL04u3aD/1/xXc8tvXVo9a2YK1RapqC6vnjsz7SeJG1fX37Wy7j+GhZC1EX8h/FXzy/+6yL/9UN8w/9el1a2bQr1VK/w3+q+WddP64uNLr5pfOXtm+tDaOhYZqSC/dmHl7+aXL9o847FeN7/8irnlNy+t3rw28ohY4b+YW/7vlbXLNvPr4v7e/j8vrLxyfvk/OJdRvTK4cmXtdQsr581Mv/D44sEM7+j40OCOv3be8d6DX7Zr2x+cO/nDKkujUf0iwxNuOYnT+mO3bV4cjf5ree3p+3fgaof9u289+YJji4/ZthlXyAM2T8P1v2aZ6lePLvzC0YVrV9cv3jz95bu24vXvsw7N/fiRhYdumcFVhEKza2rT/7twn4vdH51Y+l+4Ud0yg+v5P5fXfnDfjmcf2Pnga444HISJn5jFS8LHz279k3Pja31+Hff2i/Z9+LbNT7715POPLX70ts0roxFS/dC+HT+rN90wz187tlhL9J9Lq4+87uirz9/zmTu3foV+hhqF+9D66BFbZ95y8X5zjHrt3H6t509OLH31zSdwn/h+XnJ+760nn3ds8cRlB3Ab/oU3Hf+ts2a/rfkPcJ9xwzE8l2yf2nTr+uiRGHR5za+dH3IN/xvffy2tYkn/cXF159Sml5yz+yved+Izdmz5+4UVFLjrHnAGalz72nnzFQc/d+cWFO5HbZu5emX98Prod8+e/Qb9NmK9dn7z4upj9BMPX7hzyyvmVzDin5635/j66EtuOu5Fvmjz9Bsu3OeF9U8xvujs2Y/fvhnH/Q2Lq5+1YwuqNow4lH923h4sJsqrA5H/iXt3PO3Q3HOOLGD1sAt4IvzOPdufr/8F6Hl+6o4tqIxQ7/Ap2tFxBzDcxXxAuHZlbXHEH0wMvUEVRNyFoSD+9Xl73nTRvv+8eP+fn7v7p44s1B8JWd206Utntx697ABcz9y/4w9PLiHnd+/b8ZwDvDP6twv3oSDiIkdB/Mkzdv73Jfv/7aJ9rz1/z1z+UDZKBgril+3a+s5L9qMe/eyBnT9/dOG9K2uug0/cs32iIAJP2LMd1239F+nfPr504cw0CiJqKwriX567+18v2odUf3Xu7p87uvDS/L3b93OLctPaCKVz7rIDuODD9EEDBREFCE8AuOOenRp7+fxNN5+4bnXtnZfsu+WyA9dcsv/k+E9I4H4W5f71F+57y0X75kebUBD/4JzZ11yw9z8u2odahXvn4DVAQcTL+TdftP/QZQc+btvmlzQ/8Wtg9Edv24yS9PLz9x669AyU4Gcfnt/4u9wAbqjfdOHeay/Z/617tv8E/47N6snLDvzdBXsR+1WzW/98jr8K/KtnzX6Lai4ODQri6X563QkB1Hc8NyBt6B0dHxLcwZron716/2/b4bXq43Zt9Q89AF82u+0zd2xpf9j5O/IXm/0Xfjf+ZdGHbp352/P3PD1fP166ZQZVzEPilu3M6ak/z2L0Q/t3vuTs2XqH65T/z/cH9/M9sj/VBI6urb98fvmpZ9DyspNLn7Nzy+PyY9Yvnt2GquTKi3u8naf/BAO3YygBO6enHrjhf/7eMfjVvQsH8A27t+OmtX72FS9Ov23P9gv1AhMv1T9h/KclcLf1hVrqR2zbjNvqL9619X/pbhQlbM/UVP16WAvspn/BAQDNr75b7JqewtPSk245ecPqGl7qHr/sjN9p/iBi+9nOl89u+9jtW/z2xY/u34lqiFi7/EPcG3GbP72O0wPPDUgbekfHhwR3sCY+ZOvmA9NT78hfSz0l9NvaYwXl7Jnpt+UVfv7M9L58r82/BT1x4wPgltM/lP15Nx679OrDD7jmyPV5bb8NNbF5qw74+j3b62tAp7wKUbk+Eq/+VHd+T9fe16sicJ7jn3XUPDGBXY1nojpedieVwoL/VtS33HJy/5WHsOHeGepPH6bxyNo6XnW2P8eAe+SQhPqRC2DLVPwMorF1im9ohNLAf8XBwCGY+M1w4Bn7d+LZ4gXHFy+8+sin3XDsuUeHn/aawKXNlwQ+Ytvmq1bWvvHmEx9z3dG97z30k0eGW7/2bdTb/On1S071KqSj467GHT/tPmb75lPWxIdec+Siq/hz87NTU8vjVxmuzCorG3890H/yrf27S34/65eO4uXk1j/WD2XjPjF84z/4DIxSfT+F6ol7t+N13C2r6y89ufSVs1v9sS9uRtqfqgWWRlH+tk8h2zCfiRE33Nd+UMCs3ri4+hFb+R3P2j5x++bfV2X0L/t7iYyJwdt7PKztYlPgcFN/yt8l881+YeMROXvz9CvO3/ufF+37qTP4A4rPPDz/LTeP/RXDwnoznSffevKbbjmJIvuj+3e8+aJ9v3cO7wT9s5XbpoYTTss+honC3Z4JHR0fMtzxmohXZ/+6tPo83doU/uzkEp7/v3UP778evm3zu8eL5ntW1uq3rSYuyEL7VwRQuVCVRpef+V37duA1FDy4TzyqUoQXj1evrtefeL52ZW36vYd+RN+ORsjp8CR9cfIn9UuxvkkEHr51c/15AOPdOc/9M9Pt3et7V8ZeXZ5uF+4Ynqu30n7vnN2/efZsbX4d/YJjC9unpx62Zab+Lg3wX+NzXmiK4CJ/xDtkYGE0VkwLbd2EtPFe8stuOv7EW05+1LbNTzuDL4e/bnab/xJ0+7vcRv1ZBQz0G8cWn7l/xy+eOfvFs9su3zrjc2BVBNx319F5Pz+93tFxN+KO18Qn7t3xuTu3fO/Buacfmnv9wspbl1Z/9sj8k245+dHbNv9v/Xrzk/duf9PS6tMOzeF1H26CnnLryatW1uvvxk38cZIJvPjEIhLunZ7Ctf0z+n38966sPV4/Ousi9b37th+Ymfq+g3O36secv+bmE7g9rHfiblxd3/gH441v37Pt144tnjU99UW7oiZiSm9ZXvvBg3OH19aR7ftvPYmhn6Lq+Yhtm4+tjzCBq1fWXj639ET9QeTCnVsTn3Nk4cO3zmDE0AXv0U/r5ecT9m7HzH/92MK8fv66/QurQDsZ1Mf2zvcUbyUK7QLpDzmEXMC93m8eX3zpiSWszD8trLx+ceUz9YPbhn+X23INgZu7PdNT/7i4Cu/qaPSyk0vP1uTncnoY6HeOL960un6bP73e0XG34I7XROCvz9vznXu2//qxxU+94dhHXXf0Rw7NP27X1pedtxsvi+D96O1bfv+c2RcdWzzjqsPnXH347+dX/uK83Y851Vvm7RtZH71ty4dtmUGqHzk099T9Oz5n55anHpo/cOUhvCT/pB1bcHOKywy0PdPTf37unrcvrZ2tH3M+vj763XNm/UW2b9i9/WePLnxW/kH6CXzHnu0YzV86MXA/+yfn7P6DE4sHrjqMbK/iPPd8gr41/Sk7tvzIvh2YwKXXHPnWm0++YPz/xiycrth84PhbFZdvbWZVwN0ZasfbUab37fjf+3b8+OH5XVceevz7TtRfVTXaO64P6K8aGCij8UZvg+efNftFO7d+1c0nsDKfcsMx3DtjkWGf+F3uCfzCmbveuLBy+TVHtrz30HOPLvyVvq31Mt1gftz2zfunp775lpN4mnk/P73e0XE34s75XZzrV9fm1zc9YMv0Kd8DwivcvdPT9UEkwBuZ0eiUPyhtvG91ff/MlL/WAxkXub/UPRqNToxG7de2cZeBCjDxi9bIj1eC9RlOi9fNL3/ajcf/86L4kmML3LzMTm/a+H9L5tZHb1te9d8ORFrPauJXvj+U+J/l1fbjEWBiMhvVndNTE/8FcIJzYn0dz0yn3B3cxB061RenkcG/yw1hIhDZbl0bHZiest1/nruO2g2ra+fMTNcL8FP+9PrGnB0dHxrcOTXxXoErltfO3Tz9UdcewY3hX9553yjs6Oi4L+F+9FT81ENzu688dHBt9ON39L8zdnR03OdxP7pPfNfy6juW1z5jx5b+oqyjo+N0uB/VxI6Ojo7bRL9j6ujo6BjQa2JHR0fHgF4TOzo6Ogb0mtjR0dExoNfEjo6OjgG9JnZ0dHQM6DWxo6OjY0CviR0dHR0Dek3s6OjoGNBrYkdHR8eAXhM7Ojo6BvSa2NHR0TGg18SOjo6OAb0mdnR0dAzoNbGjo6NjQK+JHR0dHQN6Tezo6OgY0GtiR0dHx4BeEzs6OjoG9JrY0dHRMaDXxI6Ojo4BvSZ2dHR0DOg1saOjo2NAr4kdHR0dA3pN7Ojo6BjQa2JHR0fHgF4TOzo6OhKbNv1/Oryz0fTVz6kAAAAASUVORK5CYII=') - Write-AlertMessage -message "Potential Phishing page detected. Detected Information: $($request.headers | ConvertTo-Json -Depth 5)" -sev 'Alert' -tenant $Request.query.TenantId + + $AlertMessage = If ($Request.headers.referer) { + "Potential Phishing page detected. Detected Information: Hosted at $($Request.headers.referer). Access by IP $($request.headers.'x-forwarded-for')" + } else { + "Potential Phishing page detected. Detected Information: Access by IP $($request.headers.'x-forwarded-for')" + } + Write-AlertMessage -message $AlertMessage -sev 'Alert' -tenant $Request.query.TenantId } # Associate values to output bindings by calling 'Push-OutputBinding'. diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 deleted file mode 100644 index af03360c1973..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 +++ /dev/null @@ -1,19 +0,0 @@ - -function Push-CIPPAlertAdminPassword { - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [pscustomobject]$QueueItem, - $TriggerMetadata - ) - 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 deleted file mode 100644 index 07571db760aa..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -function Push-CIPPAlertApnCertExpiry { - [CmdletBinding()] - Param ( - [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata - ) - $LastRunTable = Get-CIPPTable -Table AlertLastRun - - 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 deleted file mode 100644 index 581c56ebd962..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -function Push-CIPPAlertAppSecretExpiry { - [CmdletBinding()] - Param ( - [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata - ) - $LastRunTable = Get-CIPPTable -Table AlertLastRun - - Write-Host "Checking app expire for $($QueueItem.tenant)" - 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 $_) { - Write-Host "checking $($App.displayName)" - 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-Host ("Application '{0}' has secrets expiring on {1}" -f $App.displayName, $Credential.endDateTime) - 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 { - - } -} - diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 deleted file mode 100644 index b69ed1b50ab2..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 +++ /dev/null @@ -1,16 +0,0 @@ - -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 deleted file mode 100644 index e9d4e06adae0..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderStatus.ps1 +++ /dev/null @@ -1,16 +0,0 @@ - -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-CIPPAlertMFAAlertUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 deleted file mode 100644 index 3537616d02ed..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -function Push-CIPPAlertMFAAlertUsers { - [CmdletBinding()] - Param ( - [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata - ) - try { - - $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$filter=isMfaRegistered eq false and userType eq ''member''&$select=userPrincipalName,lastUpdatedDateTime,isMfaRegistered' -tenantid $($QueueItem.tenant) - if ($users) { - Write-AlertMessage -tenant $QueueItem.tenant -message "The following $($users.Count) users do not have MFA registered: $($users.UserPrincipalName -join ', ')" - } - - } catch { - Write-LogMessage -message "Failed to check MFA status for all users: $($_.exception.message)" -API 'MFA Alerts - Informational' -tenant $QueueItem.tenant -sev Info - } - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 deleted file mode 100644 index 17b5363a1c54..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -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 "Conditional Access Config Alert: 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 deleted file mode 100644 index 1e5f94064479..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -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 { - if ($_.StorageUsedInBytes -eq 0) { continue } - $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 "Mailbox Quota Alert Error occurred: $(Get-NormalizedError -message $_.Exception.message)" - } -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 deleted file mode 100644 index 03848da177c9..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -function Push-CIPPStandard { - param ( - $QueueItem, $TriggerMetadata - ) - - Write-Host "Received queue item for $($QueueItem.Tenant) and standard $($QueueItem.Standard)." - $Tenant = $QueueItem.Tenant - $Standard = $QueueItem.Standard - $FunctionName = 'Invoke-CIPPStandard{0}' -f $Standard - Write-Host "We'll be running $FunctionName" - & $FunctionName -Tenant $Tenant -Settings $QueueItem.Settings -} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ListGraphRequestQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ListGraphRequestQueue.ps1 deleted file mode 100644 index 82f12e1df665..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ListGraphRequestQueue.ps1 +++ /dev/null @@ -1,67 +0,0 @@ -function Push-ListGraphRequestQueue { - <# - .FUNCTIONALITY - Entrypoint - #> - # Input bindings are passed in via param block. - param($QueueItem, $TriggerMetadata) - - # Write out the queue message and metadata to the information log. - Write-Host "PowerShell queue trigger function processed work item: $($QueueItem.Endpoint) - $($QueueItem.TenantFilter)" - - $TenantQueueName = '{0} - {1}' -f $QueueItem.QueueName, $QueueItem.TenantFilter - Update-CippQueueEntry -RowKey $QueueItem.QueueId -Status 'Processing' -Name $TenantQueueName - - $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) - foreach ($Item in ($QueueItem.Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { - $ParamCollection.Add($Item.Key, $Item.Value) - } - - $PartitionKey = $QueueItem.PartitionKey - - $TableName = ('cache{0}' -f ($QueueItem.Endpoint -replace '[^A-Za-z0-9]'))[0..62] -join '' - Write-Host $TableName - $Table = Get-CIPPTable -TableName $TableName - - $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $QueueItem.TenantFilter - Write-Host $Filter - Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey | Remove-AzDataTableEntity @Table - - $GraphRequestParams = @{ - TenantFilter = $QueueItem.TenantFilter - Endpoint = $QueueItem.Endpoint - Parameters = $QueueItem.Parameters - NoPagination = $QueueItem.NoPagination - ReverseTenantLookupProperty = $QueueItem.ReverseTenantLookupProperty - ReverseTenantLookup = $QueueItem.ReverseTenantLookup - SkipCache = $true - } - - $RawGraphRequest = try { - Get-GraphRequestList @GraphRequestParams - } catch { - [PSCustomObject]@{ - Tenant = $QueueItem.Tenant - CippStatus = "Could not connect to tenant. $($_.Exception.message)" - } - } - - $GraphResults = foreach ($Request in $RawGraphRequest) { - $Json = ConvertTo-Json -Depth 5 -Compress -InputObject $Request - [PSCustomObject]@{ - TenantFilter = [string]$QueueItem.TenantFilter - QueueId = [string]$QueueItem.QueueId - QueueType = [string]$QueueItem.QueueType - RowKey = [string](New-Guid) - PartitionKey = [string]$PartitionKey - Data = [string]$Json - } - } - try { - Add-CIPPAzDataTableEntity @Table -Entity $GraphResults -Force | Out-Null - Update-CippQueueEntry -RowKey $QueueItem.QueueId -Status 'Completed' - } catch { - Write-Host "Queue Error: $($_.Exception.Message)" - Update-CippQueueEntry -RowKey $QueueItem.QueueId -Status 'Failed' - } -} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 deleted file mode 100644 index d9800f40a34a..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -function Push-SchedulerAlert { - param ( - $QueueItem, $TriggerMetadata - ) - $Tenant = $QueueItem - try { - $Table = Get-CIPPTable -Table SchedulerConfig - if ($Tenant.tag -eq 'AllTenants') { - $Filter = "RowKey eq 'AllTenants' and PartitionKey eq 'Alert'" - } else { - $Filter = "RowKey eq '{0}' and PartitionKey eq 'Alert'" -f $Tenant.tenantid - } - $Alerts = Get-CIPPAzDataTableEntity @Table -Filter $Filter - - - $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 - tenantid = $tenant.tenantid - FunctionName = "CIPPAlert$($Task)" - } - Push-OutputBinding -Name QueueItemOut -Value $QueueItem - } - - $Table = Get-CIPPTable - $PartitionKey = Get-Date -UFormat '%Y%m%d' - $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 -Entity $_ | Out-Null - } - - } catch { - $Message = 'Exception on line {0} - {1}' -f $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -message $Message -API 'Alerts' -tenant $tenant.tenant -sev Error - } -} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 index 97a499bdfc82..9b1885d5c465 100644 --- a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 @@ -2,23 +2,35 @@ function Get-CIPPAuthentication { [CmdletBinding()] param ( - $APIName = "Get Keyvault Authentication" + $APIName = 'Get Keyvault Authentication' ) + $Variables = @('ApplicationId', 'ApplicationSecret', 'TenantId', 'RefreshToken') try { - Connect-AzAccount -Identity - - @('ApplicationId','ApplicationSecret','TenantId','RefreshToken') | Foreach-Object { - Set-Item -path ENV:$_ -value (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name $_ -AsPlainText -ErrorAction Stop) -Force + if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { + $Table = Get-CIPPTable -tablename 'DevSecrets' + $Secret = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'Secret' and RowKey eq 'Secret'" + if (!$Secret) { + throw 'Development variables not set' + } + foreach ($Var in $Variables) { + if ($Secret.$Var) { + Set-Item -Path ENV:$Var -Value $Secret.$Var -Force -ErrorAction Stop + } + } + } else { + Connect-AzAccount -Identity + + $Variables | ForEach-Object { + Set-Item -Path ENV:$_ -Value (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name $_ -AsPlainText -ErrorAction Stop) -Force + } } - $ENV:SetFromProfile = $true - Write-LogMessage -message "Reloaded authentication data from KeyVault" -Sev 'debug' -API "CIPP Authentication" + Write-LogMessage -message 'Reloaded authentication data from KeyVault' -Sev 'debug' -API 'CIPP Authentication' - return $true - } - catch { - Write-LogMessage -message "Could not retrieve keys from Keyvault: $($_.Exception.Message)" -Sev 'CRITICAL' -API "CIPP Authentication" + return $true + } catch { + Write-LogMessage -message 'Could not retrieve keys from Keyvault' -Sev 'CRITICAL' -API 'CIPP Authentication' -LogData (Get-CippException -Exception $_) return $false } } diff --git a/Modules/CIPPCore/Public/Get-CIPPBitlockerKey.ps1 b/Modules/CIPPCore/Public/Get-CIPPBitlockerKey.ps1 index 7d886ea2912f..a80a5d3b002e 100644 --- a/Modules/CIPPCore/Public/Get-CIPPBitlockerKey.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPBitlockerKey.ps1 @@ -4,18 +4,17 @@ function Get-CIPPBitlockerKey { param ( $device, $TenantFilter, - $APIName = "Get Bitlocker key", + $APIName = 'Get Bitlocker key', $ExecutingUser ) try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/informationProtection/bitlocker/recoveryKeys?`$filter=deviceId eq '$($device)'" -tenantid $TenantFilter | ForEach-Object { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/informationProtection/bitlocker/recoveryKeys?`$filter=deviceId eq '$($device)'" -tenantid $TenantFilter | ForEach-Object { (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/informationProtection/bitlocker/recoveryKeys/$($_.id)?`$select=key" -tenantid $TenantFilter).key } return $GraphRequest - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not add OOO for $($userid)" -Sev "Error" -tenant $TenantFilter + } catch { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not add OOO for $($userid)" -Sev 'Error' -tenant $TenantFilter -LogData (Get-CippException -Exception $_) return "Could not add out of office message for $($userid). Error: $($_.Exception.Message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomainAnalyser.ps1 b/Modules/CIPPCore/Public/Get-CIPPDomainAnalyser.ps1 similarity index 50% rename from Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomainAnalyser.ps1 rename to Modules/CIPPCore/Public/Get-CIPPDomainAnalyser.ps1 index ef10d6a3505d..fc4ad53915a1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomainAnalyser.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPDomainAnalyser.ps1 @@ -1,25 +1,18 @@ - -using namespace System.Net - -Function Invoke-ListDomainAnalyser { - <# - .FUNCTIONALITY - Entrypoint - #> +function Get-CIPPDomainAnalyser { [CmdletBinding()] - param($Request, $TriggerMetadata) + Param($TenantFilter) $DomainTable = Get-CIPPTable -Table 'Domains' # Get all the things - if ($Request.Query.tenantFilter -ne 'AllTenants') { - $DomainTable.Filter = "TenantId eq '{0}'" -f $Request.Query.tenantFilter + if ($TenantFilter -ne 'AllTenants') { + $DomainTable.Filter = "TenantGUID eq '{0}'" -f $TenantFilter } try { # Extract json from table results $Results = foreach ($DomainAnalyserResult in (Get-CIPPAzDataTableEntity @DomainTable).DomainAnalyser) { - try { + try { if (![string]::IsNullOrEmpty($DomainAnalyserResult)) { $Object = $DomainAnalyserResult | ConvertFrom-Json -ErrorAction SilentlyContinue $Object @@ -29,11 +22,5 @@ Function Invoke-ListDomainAnalyser { } catch { $Results = @() } - - - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($Results) - }) + return $Results } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 b/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 index 698d30653774..9668cd51b50e 100644 --- a/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 @@ -3,19 +3,17 @@ function Get-CIPPLicenseOverview { [CmdletBinding()] param ( $TenantFilter, - $APIName = "Get License Overview", + $APIName = 'Get License Overview', $ExecutingUser ) $LicRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $TenantFilter - $LicOverviewRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/directory/subscriptions' -tenantid $TenantFilter | Where-Object -Property nextLifecycleDateTime -GT (Get-Date) | Select-Object *, - @{Name = 'consumedUnits'; Expression = { ($LicRequest | Where-Object -Property skuid -EQ $_.skuId).consumedUnits } }, - @{Name = 'prepaidUnits'; Expression = { ($LicRequest | Where-Object -Property skuid -EQ $_.skuId).prepaidUnits } } + $SkuIDs = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/directory/subscriptions' -tenantid $TenantFilter $RawGraphRequest = [PSCustomObject]@{ Tenant = $TenantFilter - Licenses = $LicOverviewRequest + Licenses = $LicRequest } Set-Location (Get-Item $PSScriptRoot).FullName $ConvertTable = Import-Csv Conversiontable.csv @@ -27,33 +25,42 @@ function Get-CIPPLicenseOverview { 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 } - $diff = $sku.nextLifecycleDateTime - $sku.createdDateTime + # Initialize $Term with the default value - $Term = "Term unknown or non-NCE license" - if ($diff.Days -ge 360 -and $diff.Days -le 1089) { - $Term = "Yearly" + $TermInfo = foreach ($Subscription in $sku.subscriptionIds) { + $SubInfo = $SkuIDs | Where-Object { $_.id -eq $Subscription } + $diff = $SubInfo.nextLifecycleDateTime - $SubInfo.createdDateTime + $Term = 'Term unknown or non-NCE license' + if ($diff.Days -ge 360 -and $diff.Days -le 1089) { + $Term = 'Yearly' + } elseif ($diff.Days -ge 1090 -and $diff.Days -le 1100) { + $Term = '3 Year' + } elseif ($diff.Days -ge 25 -and $diff.Days -le 35) { + $Term = 'Monthly' + } + $TimeUntilRenew = ($subinfo.nextLifecycleDateTime - (Get-Date)).days + [PSCustomObject]@{ + Status = $SubInfo.status + Term = $Term + TotalLicenses = $SubInfo.totalLicenses + DaysUntilRenew = $TimeUntilRenew + NextLifecycle = $SubInfo.nextLifecycleDateTime + IsTrial = $SubInfo.isTrial + SubscriptionId = $subinfo.id + CSPSubscriptionId = $SubInfo.commerceSubscriptionId + OCPSubscriptionId = $SubInfo.ocpSubscriptionId + } } - elseif ($diff.Days -ge 1090 -and $diff.Days -le 1100) { - $Term = "3 Year" - } - elseif ($diff.Days -ge 25 -and $diff.Days -le 35) { - $Term = "Monthly" - } - $TimeUntilRenew = $sku.nextLifecycleDateTime - (Get-Date) [pscustomobject]@{ Tenant = [string]$singlereq.Tenant License = [string]$PrettyName CountUsed = [string]"$($sku.consumedUnits)" CountAvailable = [string]$sku.prepaidUnits.enabled - $sku.consumedUnits - TotalLicenses = [string]"$($sku.TotalLicenses)" + TotalLicenses = [string]"$($sku.prepaidUnits.enabled)" skuId = [string]$sku.skuId skuPartNumber = [string]$PrettyName availableUnits = [string]$sku.prepaidUnits.enabled - $sku.consumedUnits - EstTerm = [string]$Term - TimeUntilRenew = [string]"$($TimeUntilRenew.Days)" - Trial = [bool]$sku.isTrial - dateCreated = [string]$sku.createdDateTime - dateExpires = [string]$sku.nextLifecycleDateTime + TermInfo = [string]($TermInfo | ConvertTo-Json -Depth 10 -Compress) 'PartitionKey' = 'License' 'RowKey' = "$($singlereq.Tenant) - $($sku.skuid)" } diff --git a/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 b/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 index 73a2295b3be1..40eb80366161 100644 --- a/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 @@ -3,7 +3,7 @@ function Get-CIPPMFAState { [CmdletBinding()] param ( $TenantFilter, - $APIName = "Get MFA Status", + $APIName = 'Get MFA Status', $ExecutingUser ) @@ -23,8 +23,7 @@ function Get-CIPPMFAState { Try { $MFARegistration = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails' -tenantid $TenantFilter) - } - catch { + } catch { $CAState.Add('Not Licensed for Conditional Access') | Out-Null $MFARegistration = $null } @@ -51,8 +50,7 @@ function Get-CIPPMFAState { } } } - } - catch { + } catch { } } @@ -68,12 +66,10 @@ function Get-CIPPMFAState { 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*') { + } 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 } - } - else { + } else { Write-Host 'Adding to CA' $UserCAState.Add($CA) | Out-Null } @@ -81,7 +77,8 @@ function Get-CIPPMFAState { $PerUser = if ($_.StrongAuthenticationRequirements.StrongAuthenticationRequirement.state -ne $null) { $_.StrongAuthenticationRequirements.StrongAuthenticationRequirement.state } else { 'Disabled' } - $MFARegUser = if (($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).IsMFARegistered -eq $null) { $false } else { ($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).IsMFARegistered } + $MFARegUser = if (($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).IsMFARegistered -eq $null) { $false } else { ($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName) } + [PSCustomObject]@{ Tenant = $TenantFilter ID = $_.ObjectId @@ -90,7 +87,8 @@ function Get-CIPPMFAState { AccountEnabled = $_.accountEnabled PerUser = $PerUser isLicensed = $_.isLicensed - MFARegistration = $MFARegUser + MFARegistration = $MFARegUser.IsMFARegistered + MFAMethods = $($MFARegUser.authMethods -join ', ') CoveredByCA = ($UserCAState -join ', ') CoveredBySD = $SecureDefaultsState RowKey = [string]($_.UserPrincipalName).replace('#', '') diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-CippException.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-CippException.ps1 new file mode 100644 index 000000000000..92c4d936dd22 --- /dev/null +++ b/Modules/CIPPCore/Public/GraphHelper/Get-CippException.ps1 @@ -0,0 +1,14 @@ +function Get-CippException { + Param( + $Exception + ) + + [PSCustomObject]@{ + Message = $Exception.Exception.Message + NormalizedError = Get-NormalizedError -message $Exception.Exception.Message + Position = $Exception.InvocationInfo.PositionMessage + ScriptName = $Exception.InvocationInfo.ScriptName + LineNumber = $Exception.InvocationInfo.ScriptLineNumber + Category = $Exception.CategoryInfo.ToString() + } +} diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-ClassicAPIToken.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-ClassicAPIToken.ps1 index f887e8ce850e..ce7e48fa5247 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-ClassicAPIToken.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-ClassicAPIToken.ps1 @@ -5,10 +5,10 @@ function Get-ClassicAPIToken($tenantID, $Resource) { #> $TokenKey = '{0}-{1}' -f $TenantID, $Resource if ($script:classictoken.$TokenKey -and [int](Get-Date -UFormat %s -Millisecond 0) -lt $script:classictoken.$TokenKey.expires_on) { - Write-Host 'Classic: cached token' + #Write-Host 'Classic: cached token' return $script:classictoken.$TokenKey } else { - Write-Host 'Using classic' + #Write-Host 'Using classic' $uri = "https://login.microsoftonline.com/$($TenantID)/oauth2/token" $Body = @{ client_id = $env:ApplicationID diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-GraphToken.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-GraphToken.ps1 index 05b1b7f9c8fc..b0021973a701 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-GraphToken.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-GraphToken.ps1 @@ -36,10 +36,10 @@ function Get-GraphToken($tenantid, $scope, $AsApp, $AppID, $refreshToken, $Retur try { if ($script:AccessTokens.$TokenKey -and [int](Get-Date -UFormat %s -Millisecond 0) -lt $script:AccessTokens.$TokenKey.expires_on -and $SkipCache -ne $true) { - Write-Host 'Graph: cached token' + #Write-Host 'Graph: cached token' $AccessToken = $script:AccessTokens.$TokenKey } else { - Write-Host 'Graph: new token' + #Write-Host 'Graph: new token' $AccessToken = (Invoke-RestMethod -Method post -Uri "https://login.microsoftonline.com/$($tenantid)/oauth2/v2.0/token" -Body $Authbody -ErrorAction Stop) $ExpiresOn = [int](Get-Date -UFormat %s -Millisecond 0) + $AccessToken.expires_in Add-Member -InputObject $AccessToken -NotePropertyName 'expires_on' -NotePropertyValue $ExpiresOn diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 index e44eccbea5d9..2cb51e4d929a 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 @@ -8,7 +8,9 @@ function Get-Tenants { [switch]$SkipList, [Parameter( ParameterSetName = 'Standard')] [switch]$IncludeAll, - [switch]$IncludeErrors + [switch]$IncludeErrors, + [switch]$SkipDomains, + [switch]$TriggerRefresh ) $TenantsTable = Get-CippTable -tablename 'Tenants' @@ -28,49 +30,81 @@ function Get-Tenants { } $IncludedTenantsCache = Get-CIPPAzDataTableEntity @TenantsTable -Filter $Filter - if (($IncludedTenantsCache | Measure-Object).Count -gt 0) { - try { - $LastRefresh = ($IncludedTenantsCache | Where-Object { $_.customerId } | Sort-Object LastRefresh -Descending | Select-Object -First 1).LastRefresh | Get-Date -ErrorAction Stop - } catch { $LastRefresh = $false } - } else { - $LastRefresh = $false + if (($IncludedTenantsCache | Measure-Object).Count -eq 0) { + $BuildRequired = $true } - if (!$LastRefresh -or $LastRefresh -lt (Get-Date).Addhours(-24).ToUniversalTime()) { - try { - Write-Host "Renewing. Cache not hit. $LastRefresh" - $TenantList = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/tenants?`$top=999" -tenantid $env:TenantID ) | Select-Object id, @{l = 'customerId'; e = { $_.tenantId } }, @{l = 'DefaultdomainName'; e = { [string]($_.contract.defaultDomainName) } } , @{l = 'MigratedToNewTenantAPI'; e = { $true } }, DisplayName, domains, @{n = 'delegatedPrivilegeStatus'; exp = { $_.tenantStatusInformation.delegatedPrivilegeStatus } } | Where-Object { $_.defaultDomainName -NotIn $SkipListCache.defaultDomainName -and $_.defaultDomainName -ne $null } - } catch { - Write-Host "Get-Tenants - Lighthouse Error, using contract/delegatedAdminRelationship calls. Error: $($_.Exception.Message)" - [System.Collections.Generic.List[PSCustomObject]]$BulkRequests = @( - @{ - id = 'Contracts' - method = 'GET' - url = "/contracts?`$top=999" - }, - @{ - id = 'GDAPRelationships' - method = 'GET' - url = '/tenantRelationships/delegatedAdminRelationships' - } - ) + if ($BuildRequired -or $TriggerRefresh.IsPresent) { + #get the full list of tenants + $GDAPRelationships = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships?`$filter=status eq 'active' and not startsWith(displayName,'MLT_')&`$select=customer,autoExtendDuration,endDateTime" -NoAuthCheck:$true + $GDAPList = foreach ($Relationship in $GDAPRelationships) { + [PSCustomObject]@{ + customerId = $Relationship.customer.tenantId + displayName = $Relationship.customer.displayName + autoExtend = ($Relationship.autoExtendDuration -ne 'PT0S') + relationshipEnd = $Relationship.endDateTime + } + } + $ActiveRelationships = $GDAPList | Where-Object { $_.customerId -notin $SkipListCache.customerId } + $TenantList = $ActiveRelationships | Group-Object -Property customerId | ForEach-Object -Parallel { + Write-Host "Processing $($_.Name) to add to tenant list." + Import-Module CIPPCore + Import-Module AzBobbyTables + $ExistingTenantInfo = Get-CIPPAzDataTableEntity @using:TenantsTable -Filter "PartitionKey eq 'Tenants' and RowKey eq '$($_.Name)'" + if ($ExistingTenantInfo -and $ExistingInfo.RequiresRefresh -eq $false) { + Write-Host 'Existing tenant found. We already have it cached, skipping.' + $ExistingTenantInfo + continue + } + $LatestRelationship = $_.Group | Sort-Object -Property relationshipEnd | Select-Object -Last 1 + $AutoExtend = ($_.Group | Where-Object { $_.autoExtend -eq $true } | Measure-Object).Count -gt 0 - $BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantFilter -NoAuthCheck:$true - $Contracts = Get-GraphBulkResultByID -Results $BulkResults -ID 'Contracts' -Value - $GDAPRelationships = Get-GraphBulkResultByID -Results $BulkResults -ID 'GDAPRelationships' -Value + if (-not $SkipDomains.IsPresent) { + try { + Write-Host "Getting domains for $($_.Name)." + $Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $LatestRelationship.customerId -NoAuthCheck:$true -ErrorAction Stop + $defaultDomainName = ($Domains | Where-Object { $_.isDefault -eq $true }).id + $initialDomainName = ($Domains | Where-Object { $_.isInitial -eq $true }).id + } catch { + try { + #doing alternative method to temporarily get domains. Nightly refresh will fix this as it will be marked for renew. + $Domain = (New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/tenantRelationships/findTenantInformationByTenantId(tenantId='$($LatestRelationship.customerId)')" -NoAuthCheck:$true).defaultDomainName + $defaultDomainName = $Domain + $initialDomainName = $Domain + $RequiresRefresh = $true - $ContractList = $Contracts | Select-Object id, customerId, DefaultdomainName, DisplayName, domains, @{l = 'MigratedToNewTenantAPI'; e = { $true } }, @{ n = 'delegatedPrivilegeStatus'; exp = { $CustomerId = $_.customerId; if (($GDAPRelationships | Where-Object { $_.customer.tenantId -EQ $CustomerId -and $_.status -EQ 'active' } | Measure-Object).Count -gt 0) { 'delegatedAndGranularDelegetedAdminPrivileges' } else { 'delegatedAdminPrivileges' } } } | Where-Object -Property defaultDomainName -NotIn $SkipListCache.defaultDomainName + } catch { + Write-LogMessage -API 'Get-Tenants' -message "Tried adding $($LatestRelationship.customerId) to tenant list but failed to get domains - $($_.Exception.Message)" -level 'Critical' - $GDAPOnlyList = $GDAPRelationships | Where-Object { $_.status -eq 'active' -and $Contracts.customerId -notcontains $_.customer.tenantId } | Select-Object id, @{l = 'customerId'; e = { $($_.customer.tenantId) } }, @{l = 'defaultDomainName'; e = { (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$($_.customer.tenantId)')" -noauthcheck $true -asApp:$true -tenant $env:TenantId).defaultDomainName } }, @{l = 'MigratedToNewTenantAPI'; e = { $true } }, @{n = 'displayName'; exp = { $_.customer.displayName } }, domains, @{n = 'delegatedPrivilegeStatus'; exp = { 'granularDelegatedAdminPrivileges' } } | Where-Object { $_.defaultDomainName -NotIn $SkipListCache.defaultDomainName -and $_.defaultDomainName -ne $null } | Sort-Object -Property customerId -Unique + } + } - $TenantList = @($ContractList) + @($GDAPOnlyList) + [PSCustomObject]@{ + PartitionKey = 'Tenants' + RowKey = $_.Name + customerId = $_.Name + displayName = $LatestRelationship.displayName + relationshipEnd = $LatestRelationship.relationshipEnd + relationshipCount = $_.Count + defaultDomainName = $defaultDomainName + initialDomainName = $initialDomainName + hasAutoExtend = $AutoExtend + delegatedPrivilegeStatus = 'granularDelegatedAdminPrivileges' + domains = '' + Excluded = $false + ExcludeUser = '' + ExcludeDate = '' + GraphErrorCount = 0 + LastGraphError = '' + RequiresRefresh = [bool]$RequiresRefresh + LastRefresh = (Get-Date).ToUniversalTime() + } + } } - <#if (!$TenantList.customerId) { - $TenantList = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/contracts?`$top=999" -tenantid $env:TenantID ) | Select-Object id, customerId, DefaultdomainName, DisplayName, domains | Where-Object -Property defaultDomainName -NotIn $SkipListCache.defaultDomainName - }#> - $IncludedTenantsCache = [system.collections.generic.list[hashtable]]::new() + $IncludedTenantsCache = [system.collections.generic.list[object]]::new() if ($env:PartnerTenantAvailable) { - $IncludedTenantsCache.Add(@{ + # Add partner tenant if env is set + $IncludedTenantsCache.Add([PSCustomObject]@{ RowKey = $env:TenantID PartitionKey = 'Tenants' customerId = $env:TenantID @@ -87,28 +121,16 @@ function Get-Tenants { } foreach ($Tenant in $TenantList) { if ($Tenant.defaultDomainName -eq 'Invalid' -or !$Tenant.defaultDomainName) { continue } - $IncludedTenantsCache.Add(@{ - RowKey = [string]$Tenant.customerId - PartitionKey = 'Tenants' - customerId = [string]$Tenant.customerId - defaultDomainName = [string]$Tenant.defaultDomainName - displayName = [string]$Tenant.DisplayName - delegatedPrivilegeStatus = [string]$Tenant.delegatedPrivilegeStatus - domains = '' - Excluded = $false - ExcludeUser = '' - ExcludeDate = '' - GraphErrorCount = 0 - LastGraphError = '' - LastRefresh = (Get-Date).ToUniversalTime() - }) | Out-Null + $IncludedTenantsCache.Add($Tenant) | Out-Null } + } - if ($IncludedTenantsCache) { - $TenantsTable.Force = $true - Add-CIPPAzDataTableEntity @TenantsTable -Entity $IncludedTenantsCache + if ($IncludedTenantsCache) { + Add-CIPPAzDataTableEntity @TenantsTable -Entity $IncludedTenantsCache -Force + $CurrentTenants = Get-CIPPAzDataTableEntity @TenantsTable -Filter "PartitionKey eq 'Tenants' and Excluded eq false" + $CurrentTenants | Where-Object { $_.customerId -notin $IncludedTenantsCache.customerId } | ForEach-Object { + Remove-AzDataTableEntity @TenantsTable -Entity $_ } } - return ($IncludedTenantsCache | Where-Object -Property defaultDomainName -NE $null | Sort-Object -Property displayName) - + return ($IncludedTenantsCache | Where-Object { $null -ne $_.defaultDomainName -and ($_.defaultDomainName -notmatch 'Domain Error' -or $IncludeAll.IsPresent) } | Sort-Object -Property displayName) } diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 index cfe78a4af8c6..4a05cf778fe7 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 @@ -5,7 +5,8 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc #> if ((Get-AuthorisedRequest -TenantID $tenantid) -or $NoAuthCheck -eq $True) { $token = Get-ClassicAPIToken -resource 'https://outlook.office365.com' -Tenantid $tenantid - $tenant = (get-tenants -IncludeErrors | Where-Object { $_.defaultDomainName -eq $tenantid -or $_.customerId -eq $tenantid }).customerId + $Tenant = Get-Tenants -IncludeErrors | Where-Object { $_.defaultDomainName -eq $tenantid -or $_.customerId -eq $tenantid } + if ($cmdParams) { $Params = $cmdParams } else { @@ -23,11 +24,12 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc if ($cmdparams.User) { $Anchor = $cmdparams.User } if (!$Anchor -or $useSystemMailbox) { - $OnMicrosoft = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains?$top=999' -tenantid $tenantid -NoAuthCheck $NoAuthCheck | Where-Object -Property isInitial -EQ $true).id - + if (!$Tenant.initialDomainName) { + $OnMicrosoft = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains?$top=999' -tenantid $tenantid -NoAuthCheck $NoAuthCheck | Where-Object -Property isInitial -EQ $true).id + } else { + $OnMicrosoft = $Tenant.initialDomainName + } $anchor = "UPN:SystemMailbox{8cc370d3-822a-4ab8-a926-bb94bd0641a9}@$($OnMicrosoft)" - - } } Write-Host "Using $Anchor" @@ -40,9 +42,9 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc } try { if ($Select) { $Select = "`$select=$Select" } - $URL = "https://outlook.office365.com/adminapi/beta/$($tenant)/InvokeCommand?$Select" - - $ReturnedData = + $URL = "https://outlook.office365.com/adminapi/beta/$($tenant.customerId)/InvokeCommand?$Select" + + $ReturnedData = do { $Return = Invoke-RestMethod $URL -Method POST -Body $ExoBody -Headers $Headers -ContentType 'application/json; charset=utf-8' $URL = $Return.'@odata.nextLink' diff --git a/Modules/CIPPCore/Public/GraphHelper/New-GraphPOSTRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-GraphPOSTRequest.ps1 index 9236b7559fcf..315881e1048b 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-GraphPOSTRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-GraphPOSTRequest.ps1 @@ -1,5 +1,5 @@ -function New-GraphPOSTRequest ($uri, $tenantid, $body, $type, $scope, $AsApp, $NoAuthCheck, $skipTokenCache, $AddedHeaders, $contentType) { +function New-GraphPOSTRequest ($uri, $tenantid, $body, $type, $scope, $AsApp, $NoAuthCheck, $skipTokenCache, $AddedHeaders, $contentType, $IgnoreErrors) { <# .FUNCTIONALITY Internal @@ -20,7 +20,7 @@ function New-GraphPOSTRequest ($uri, $tenantid, $body, $type, $scope, $AsApp, $N $contentType = 'application/json; charset=utf-8' } try { - $ReturnedData = (Invoke-RestMethod -Uri $($uri) -Method $TYPE -Body $body -Headers $headers -ContentType $contentType) + $ReturnedData = (Invoke-RestMethod -Uri $($uri) -Method $TYPE -Body $body -Headers $headers -ContentType $contentType -SkipHttpErrorCheck:$IgnoreErrors) } catch { $Message = if ($_.ErrorDetails.Message) { Get-NormalizedError -Message $_.ErrorDetails.Message diff --git a/Modules/CIPPCore/Public/GraphHelper/New-passwordString.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-passwordString.ps1 index 6793c5cc17b3..5869c370108e 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-passwordString.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-passwordString.ps1 @@ -13,6 +13,23 @@ function New-passwordString { $Words = Get-Content .\words.txt (Get-Random -InputObject $words -Count 4) -join '-' } else { - -join ('abcdefghkmnrstuvwxyzABCDEFGHKLMNPRSTUVWXYZ23456789$%&*#'.ToCharArray() | Get-Random -Count $count) + # Generate a complex password with a maximum of 100 tries + $maxTries = 100 + $tryCount = 0 + + do { + $Password = -join ('abcdefghkmnrstuvwxyzABCDEFGHKLMNPRSTUVWXYZ23456789$%&*#'.ToCharArray() | Get-Random -Count $count) + + $containsUppercase = $Password -cmatch '[A-Z]' + $containsLowercase = $Password -cmatch '[a-z]' + $containsDigit = $Password -cmatch '\d' + $containsSpecialChar = $Password -cmatch "[$%&*#]" + + $isComplex = $containsUppercase -and $containsLowercase -and $containsDigit -and $containsSpecialChar + + $tryCount++ + } while (!$isComplex -and ($tryCount -lt $maxTries)) + + $Password } } diff --git a/Modules/CIPPCore/Public/GraphHelper/Write-AlertMessage.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-AlertMessage.ps1 index 567f1c05cb77..4d9fd8095059 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Write-AlertMessage.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Write-AlertMessage.ps1 @@ -3,14 +3,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() + #Do duplicate detection, if no duplicate, write. + $Table = Get-CIPPTable -tablename CippLogs + $PartitionKey = Get-Date -UFormat '%Y%m%d' + $Filter = "PartitionKey eq '{0}' and Message eq '{1}' and Tenant eq '{2}'" -f $PartitionKey, $message.Replace("'", "''"), $tenant + $ExistingMessage = Get-CIPPAzDataTableEntity @Table -Filter $Filter + if (!$ExistingMessage) { + Write-Host 'No duplicate message found, writing to log' + Write-LogMessage -message $message -tenant $tenant -sev 'Alert' -tenantId $tenantId -user 'CIPP' -API 'Alerts' + } else { + Write-Host 'Alerts: Duplicate entry found, not writing to log' + } - $Table.Entity = $TableRow - Add-CIPPAzDataTableEntity @Table | Out-Null } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 new file mode 100644 index 000000000000..91d7ce2cd4cc --- /dev/null +++ b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 @@ -0,0 +1,42 @@ +function Write-CippFunctionStats { + <# + .FUNCTIONALITY + Internal + #> + Param( + [string]$FunctionType, + $Entity, + [DateTime]$Start, + [DateTime]$End, + [string]$ErrorMsg = '' + ) + try { + $Table = Get-CIPPTable -tablename CippFunctionStats + $RowKey = [string](New-Guid).Guid + $TimeSpan = New-TimeSpan -Start $Start -End $End + $Duration = [int]$TimeSpan.TotalSeconds + $DurationMS = [int]$TimeSpan.TotalMilliseconds + + $StatEntity = @{} + # Flatten data to json string + $StatEntity.PartitionKey = $FunctionType + $StatEntity.RowKey = $RowKey + $StatEntity.Start = $Start + $StatEntity.End = $End + $StatEntity.Duration = $Duration + $StatEntity.DurationMS = $DurationMS + $StatEntity.ErrorMsg = $ErrorMsg + $Entity = [PSCustomObject]$Entity + foreach ($Property in $Entity.PSObject.Properties.Name) { + if ($Entity.$Property.GetType().Name -in ('Hashtable', 'PSCustomObject')) { + $StatEntity.$Property = [string]($Entity.$Property | ConvertTo-Json -Compress) + } elseif ($Property -notin ('ETag', 'RowKey', 'PartitionKey', 'Timestamp', 'LastRefresh')) { + $StatEntity.$Property = $Entity.$Property + } + } + $StatEntity = [PSCustomObject]$StatEntity + Add-CIPPAzDataTableEntity @Table -Entity $StatEntity -Force + } catch { + Write-Host "Exception logging stats $($_.Exception.Message)" + } +} diff --git a/Modules/CIPPCore/Public/GraphHelper/Write-LogMessage.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-LogMessage.ps1 index fbef7fae41bf..8dec84538272 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Write-LogMessage.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Write-LogMessage.ps1 @@ -1,14 +1,25 @@ -function Write-LogMessage ($message, $tenant = 'None', $API = 'None', $tenantId = $null, $user, $sev) { +function Write-LogMessage { <# .FUNCTIONALITY Internal #> + Param( + $message, + $tenant = 'None', + $API = 'None', + $tenantId = $null, + $user, + $sev, + $LogData = '' + ) try { $username = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($user)) | ConvertFrom-Json).userDetails } catch { $username = $user } + if ($LogData) { $LogData = ConvertTo-Json -InputObject $LogData -Depth 10 -Compress } + $Table = Get-CIPPTable -tablename CippLogs if (!$tenant) { $tenant = 'None' } @@ -27,13 +38,14 @@ function Write-LogMessage ($message, $tenant = 'None', $API = 'None', $tenantId 'SentAsAlert' = $false 'PartitionKey' = $PartitionKey 'RowKey' = ([guid]::NewGuid()).ToString() + 'LogData' = [string]$LogData } if ($tenantId) { $TableRow.Add('TenantID', [string]$tenantId) } - + $Table.Entity = $TableRow Add-CIPPAzDataTableEntity @Table | Out-Null } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 index 3b3b4660a0bf..fa9490364e83 100644 --- a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 +++ b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 @@ -68,6 +68,7 @@ function Get-GraphRequestList { $TableName = ('cache{0}' -f ($Endpoint -replace '[^A-Za-z0-9]'))[0..62] -join '' Write-Host "Table: $TableName" + $Endpoint = $Endpoint -replace '^/', '' $DisplayName = ($Endpoint -split '/')[0] if ($QueueNameOverride) { @@ -110,7 +111,18 @@ function Get-GraphRequestList { Write-Host "Cached: $(($Rows | Measure-Object).Count) rows (Type: $($Type))" $QueueReference = '{0}-{1}' -f $TenantFilter, $PartitionKey - $RunningQueue = Get-CippQueue | Where-Object { $_.Reference -eq $QueueReference -and $_.Status -ne 'Completed' -and $_.Status -ne 'Failed' } + $RunningQueue = Invoke-ListCippQueue | Where-Object { $_.Reference -eq $QueueReference -and $_.Status -ne 'Completed' -and $_.Status -ne 'Failed' } + + if ($TenantFilter -ne 'AllTenants' -and $Endpoint -match '%tenantid%') { + $TenantId = (Get-Tenants -IncludeErrors | Where-Object { $_.defaultDomainName -eq $TenantFilter -or $_.customerId -eq $TenantFilter }).customerId + $Endpoint = $Endpoint -replace '%tenantid%', $TenantId + $GraphQuery = [System.UriBuilder]('https://graph.microsoft.com/{0}/{1}' -f $Version, $Endpoint) + $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) + foreach ($Item in ($Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { + $ParamCollection.Add($Item.Key, $Item.Value) + } + $GraphQuery.Query = $ParamCollection.ToString() + } if (!$Rows) { switch ($TenantFilter) { @@ -158,9 +170,9 @@ function Get-GraphRequestList { } Write-Host 'Pushing output bindings' try { - Get-Tenants -IncludeErrors | ForEach-Object { + $Batch = Get-Tenants -IncludeErrors | ForEach-Object { $TenantFilter = $_.defaultDomainName - $QueueTenant = [PSCustomObject]@{ + [PSCustomObject]@{ FunctionName = 'ListGraphRequestQueue' TenantFilter = $TenantFilter Endpoint = $Endpoint @@ -175,8 +187,15 @@ function Get-GraphRequestList { ReverseTenantLookup = $ReverseTenantLookup.IsPresent } - Push-OutputBinding -Name QueueItem -Value $QueueTenant + #Push-OutputBinding -Name QueueItem -Value $QueueTenant } + + $InputObject = @{ + OrchestratorName = 'GraphRequestOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json -Depth 5) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) } catch { Write-Host "QUEUE ERROR: $($_.Exception.Message)" } @@ -234,7 +253,13 @@ function Get-GraphRequestList { ReverseTenantLookup = $ReverseTenantLookup.IsPresent } - Push-OutputBinding -Name QueueItem -Value $QueueTenant + $InputObject = @{ + OrchestratorName = 'GraphRequestOrchestrator' + Batch = @($QueueTenant) + } + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + + #Push-OutputBinding -Name QueueItem -Value $QueueTenant [PSCustomObject]@{ QueueMessage = ('Loading {0} rows for {1}. Please check back after the job completes' -f $Count, $TenantFilter) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPPartnerWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPPartnerWebhookProcessing.ps1 new file mode 100644 index 000000000000..5a16c69d530d --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-CIPPPartnerWebhookProcessing.ps1 @@ -0,0 +1,75 @@ +function Invoke-CippPartnerWebhookProcessing { + [CmdletBinding()] + param ( + $Data + ) + + try { + if ($Data.AuditUri) { + $AuditLog = New-GraphGetRequest -uri $Data.AuditUri -tenantid $env:TenantID -NoAuthCheck $true -scope 'https://api.partnercenter.microsoft.com/.default' + } + + Switch ($Data.EventName) { + 'test-created' { + Write-LogMessage -API 'Webhooks' -message 'Partner Center webhook test received' -Sev 'Info' + } + default { + if ($Data.EventName -eq 'granular-admin-relationship-approved') { + if ($AuditLog.resourceNewValue) { + $AuditObj = $AuditLog.resourceNewValue | ConvertFrom-Json + Write-LogMessage -API 'Webhooks' -message "Partner Webhook: GDAP Relationship for $($AuditObj.customer.organizationDisplayName) was approved, starting onboarding" -LogData $AuditObj -Sev 'Alert' + $Id = $AuditObj.Id + $OnboardingSteps = [PSCustomObject]@{ + 'Step1' = @{ + 'Status' = 'pending' + 'Title' = 'Step 1: GDAP Invite' + 'Message' = 'Waiting for onboarding job to start' + } + 'Step2' = @{ + 'Status' = 'pending' + 'Title' = 'Step 2: GDAP Role Test' + 'Message' = 'Waiting for Step 1' + } + 'Step3' = @{ + 'Status' = 'pending' + 'Title' = 'Step 3: GDAP Group Mapping' + 'Message' = 'Waiting for Step 2' + } + 'Step4' = @{ + 'Status' = 'pending' + 'Title' = 'Step 4: CPV Refresh' + 'Message' = 'Waiting for Step 3' + } + 'Step5' = @{ + 'Status' = 'pending' + 'Title' = 'Step 5: Graph API Test' + 'Message' = 'Waiting for Step 4' + } + } + $TenantOnboarding = [PSCustomObject]@{ + PartitionKey = 'Onboarding' + RowKey = [string]$Id + CustomerId = '' + Status = 'queued' + OnboardingSteps = [string](ConvertTo-Json -InputObject $OnboardingSteps -Compress) + Relationship = '' + Logs = '' + Exception = '' + } + $OnboardTable = Get-CIPPTable -TableName 'TenantOnboarding' + Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop + Push-ExecOnboardTenantQueue -Item @{ Id = $Id } + } else { + if ($AuditLog) { + Write-LogMessage -API 'Webhooks' -message "Partner Center $($Data.EventName) audit log webhook received" -LogData $AuditObj -Sev 'Alert' + } else { + Write-LogMessage -API 'Webhooks' -message "Partner Center $($Data.EventName) webhook received" -LogData $Data -Sev 'Alert' + } + } + } + } + } + } catch { + Write-LogMessage -API 'Webhooks' -message 'Error processing Partner Center webhook' -LogData (Get-CippException -Exception $_) -Sev 'Error' + } +} diff --git a/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 index 353551cdaadf..8dd8055c6c93 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 @@ -1,4 +1,4 @@ - + function Invoke-CIPPStandardsRun { [CmdletBinding()] param( @@ -20,7 +20,7 @@ function Invoke-CIPPStandardsRun { $OldStd = $_ $OldStd.standards.psobject.properties.name | ForEach-Object { if ($_ -eq 'MailContacts') { - $OldStd.Standards.$_ = [pscustomobject]@{ + $OldStd.Standards.$_ = [pscustomobject]@{ GeneralContact = $OldStd.Standards.MailContacts.GeneralContact.Mail SecurityContact = $OldStd.Standards.MailContacts.SecurityContact.Mail MarketingContact = $OldStd.Standards.MailContacts.MarketingContact.Mail @@ -28,16 +28,16 @@ function Invoke-CIPPStandardsRun { remediate = $true } } else { - if ($OldStd.Standards.$_ -eq $true -and $_ -ne 'v2.1') { - $OldStd.Standards.$_ = @{ remediate = $true } - } else { - $OldStd.Standards.$_ | Add-Member -NotePropertyName 'remediate' -NotePropertyValue $true -Force + if ($OldStd.Standards.$_ -eq $true -and $_ -ne 'v2.1') { + $OldStd.Standards.$_ = @{ remediate = $true } + } else { + $OldStd.Standards.$_ | Add-Member -NotePropertyName 'remediate' -NotePropertyValue $true -Force } - + } } $OldStd | Add-Member -NotePropertyName 'v2.1' -NotePropertyValue $true -PassThru -Force - $Entity = @{ + $Entity = @{ PartitionKey = 'standards' RowKey = "$($OldStd.Tenant)" JSON = "$($OldStd | ConvertTo-Json -Depth 10)" @@ -76,15 +76,23 @@ function Invoke-CIPPStandardsRun { } } - #For each item in our object, run the queue. + #For each item in our object, run the queue. - foreach ($task in $object | Where-Object -Property Standard -NotLike 'v2*') { - $QueueItem = [pscustomobject]@{ + $Batch = foreach ($task in $object | Where-Object { $_.Standard -NotLike 'v2*' -and ($_.Settings.remediate -eq $true -or $_.Settings.alert -eq $true -or $_.Settings.report -eq $true) }) { + [PSCustomObject]@{ Tenant = $task.Tenant Standard = $task.Standard Settings = $task.Settings FunctionName = 'CIPPStandard' } - Push-OutputBinding -Name QueueItem -Value $QueueItem } + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'Standards' + Batch = @($Batch) + } + + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started orchestration with ID = '$InstanceId'" + #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 index fee8af4c24be..86e7e8a41c3f 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1 @@ -43,6 +43,18 @@ function Invoke-CippWebhookProcessing { $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' } + $IP = $data.ClientIP + $LocationInfo = @{ + RowKey = [string]$data.clientip + PartitionKey = [string]$data.UserId + Tenant = [string]$TenantFilter + CountryOrRegion = "$Country" + City = "$City" + Proxy = "$Proxy" + Hosting = "$hosting" + ASName = "$ASName" + } + $null = Add-CIPPAzDataTableEntity @LocationTable -Entity $LocationInfo -Force } } $TableObj = [PSCustomObject]::new() @@ -55,6 +67,7 @@ function Invoke-CippWebhookProcessing { 'OAuth2:Token' 'SAS:EndAuth' 'SAS:ProcessAuth' + 'Login:reprocess' ) if ($TableObj.RequestType -in $ExtendedPropertiesIgnoreList) { Write-Host 'No need to process this operation.' @@ -69,7 +82,7 @@ function Invoke-CippWebhookProcessing { { 'UserLoggedIn' -eq $data.operation -and $hosting -eq $true -and !$TrustedIps } { $data.operation = 'HostedIP' } { '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' + $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 } @@ -117,7 +130,7 @@ function Invoke-CippWebhookProcessing { $key = $parts[0] $operator = $parts[1] $value = $parts[2] - if (!$value) { + if (!$value) { Write-Host 'blank value, skip' continue } @@ -128,15 +141,16 @@ function Invoke-CippWebhookProcessing { $dynamicIf = "`$data.$key -$operator '$value'" } if (Invoke-Expression $dynamicIf) { + Write-Host "Condition met: $dynamicIf" $ConditionMet = $true } else { + Write-Host "Condition not met: $dynamicIf" $ConditionMet = $false } } if ($ConditionMet) { #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) { @@ -151,9 +165,9 @@ function Invoke-CippWebhookProcessing { $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" + "Disabled Inbox Rule $($_.Identity) for $username" $RuleDisabled ++ - } + } if ($RuleDisabled) { "Disabled $RuleDisabled Inbox Rules for $username" } else { @@ -197,7 +211,7 @@ function Invoke-CippWebhookProcessing { } } Write-Host 'Going to create the content' - foreach ($action in $dos) { + foreach ($action in $dos) { switch ($action.execute) { 'generatemail' { Write-Host 'Going to create the email' @@ -206,12 +220,13 @@ function Invoke-CippWebhookProcessing { Send-CIPPAlert -Type 'email' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter Write-Host 'email should be sent' - } + } 'generatePSA' { - $GenerateEmail = New-CIPPAlertTemplate -format 'html'-data $Data -LocationInfo $Location -ActionResults $ActionResults + $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -LocationInfo $Location -ActionResults $ActionResults Send-CIPPAlert -Type 'psa' -Title $GenerateEmail.title -HTMLContent $GenerateEmail.htmlcontent -TenantFilter $TenantFilter } 'generateWebhook' { + Write-Host 'Generating the webhook content' $GenerateJSON = New-CIPPAlertTemplate -format 'json' -data $Data -ActionResults $ActionResults $JsonContent = @{ Title = $GenerateJSON.Title @@ -225,28 +240,12 @@ function Invoke-CippWebhookProcessing { PotentialASName = $ASName ActionsTaken = [string]($ActionResults | ConvertTo-Json -Depth 15 -Compress) } | ConvertTo-Json -Depth 15 -Compress + Write-Host 'Sending Webhook Content' + Send-CIPPAlert -Type 'webhook' -Title $GenerateJSON.Title -JSONContent $JsonContent -TenantFilter $TenantFilter } } } } } - - 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 - } } diff --git a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 index 5fd635238470..78f3173c2aee 100644 --- a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 @@ -6,6 +6,7 @@ function New-CIPPCAPolicy { $TenantFilter, $State, $Overwrite, + $ReplacePattern = 'none', $APIName = 'Create CA Policy', $ExecutingUser ) @@ -46,7 +47,7 @@ function New-CIPPCAPolicy { } #If Grant Controls contains authenticationstrength, create these and then replace the id - if ($JSONobj.GrantControls.authenticationStrength.policyType -eq 'custom') { + if ($JSONobj.GrantControls.authenticationStrength.policyType -eq 'custom' -or $JSONobj.GrantControls.authenticationStrength.policyType -eq 'BuiltIn') { $ExistingStrength = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/authenticationStrength/policies/' -tenantid $TenantFilter | Where-Object -Property displayName -EQ $JSONobj.GrantControls.authenticationStrength.displayName if ($ExistingStrength) { $JSONObj.GrantControls.authenticationStrength = @{ id = $ExistingStrength.id } @@ -101,19 +102,42 @@ function New-CIPPCAPolicy { $index = [array]::IndexOf($JSONObj.conditions.locations.excludeLocations, $location) $JSONObj.conditions.locations.excludeLocations[$index] = $lookup.id } - + switch ($ReplacePattern) { + 'none' { + Write-Host 'Replacement pattern for inclusions and exclusions is none' + break + } + 'AllUsers' { + Write-Host 'Replacement pattern for inclusions and exclusions is All users. This policy will now apply to everyone.' + if ($JSONObj.conditions.users.includeUsers -ne 'All') { $JSONObj.conditions.users.includeUsers = @('All') } + if ($JSONObj.conditions.users.excludeUsers) { $JSONObj.conditions.users.excludeUsers = @() } + if ($JSONObj.conditions.users.includeGroups) { $JSONObj.conditions.users.includeGroups = @() } + if ($JSONObj.conditions.users.excludeGroups) { $JSONObj.conditions.users.excludeGroups = @() } + } + 'displayName' { + Write-Host 'Replacement pattern for inclusions and exclusions is displayName.' + $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/users?$select=id,displayName' -tenantid $TenantFilter + $Groups = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/groups?$select=id,displayName' -tenantid $TenantFilter + + if ($JSONObj.conditions.users.includeUsers -notin 'All', 'None', 'GuestOrExternalUsers') { $JSONObj.conditions.users.includeUsers = @(($users | Where-Object -Property displayName -In $JSONObj.conditions.users.includeUsers).id) } + if ($JSONObj.conditions.users.excludeUsers) { $JSONObj.conditions.users.excludeUsers = @(($users | Where-Object -Property displayName -In $JSONObj.conditions.users.excludeUsers).id) } + if ($JSONObj.conditions.users.includeGroups) { $JSONObj.conditions.users.includeGroups = @(($groups | Where-Object -Property displayName -In $JSONObj.conditions.users.includeGroups).id) } + if ($JSONObj.conditions.users.excludeGroups) { $JSONObj.conditions.users.excludeGroups = @(($groups | Where-Object -Property displayName -In $JSONObj.conditions.users.excludeGroups).id) } + } + + } $JsonObj.PSObject.Properties.Remove('LocationInfo') $RawJSON = $JSONObj | ConvertTo-Json -Depth 10 Write-Host $RawJSON try { Write-Host 'Checking' - $CheckExististing = New-GraphGETRequest -uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies' -tenantid $TenantFilter - if ($displayname -in $CheckExististing.displayName) { + $CheckExististing = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies' -tenantid $TenantFilter | Where-Object -Property displayName -EQ $displayname + if ($CheckExististing) { if ($Overwrite -ne $true) { Throw "Conditional Access Policy with Display Name $($Displayname) Already exists" return $false } else { - Write-Host 'overwriting' + Write-Host "overwriting $($CheckExististing.id)" $PatchRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExististing.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Updated Conditional Access Policy $($JSONObj.Displayname) to the template standard." -Sev 'Info' return "Updated policy $displayname for $tenantfilter" diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 6eac507448ec..e744232e9e60 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -11,7 +11,8 @@ function New-CIPPGraphSubscription { $EventType, $APIName = 'Create Webhook', $ExecutingUser, - [Switch]$Recreate + [Switch]$Recreate, + [switch]$PartnerCenter ) $CIPPID = (New-Guid).GUID $WebhookTable = Get-CIPPTable -TableName webhookTable @@ -76,13 +77,73 @@ function New-CIPPGraphSubscription { } } } + } elseif ($PartnerCenter.IsPresent) { + $WebhookFilter = "PartitionKey eq '$($env:TenantId)'" + $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter + $CIPPID = $env:TenantId + $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq 'PartnerCenter' -and $_.RowKey -eq $CIPPID } + + # Required event types + $EventList = [System.Collections.Generic.List[string]]@('test-created', 'granular-admin-relationship-approved') + if (($EventType | Measure-Object).count -gt 0) { + foreach ($Event in $EventType) { + if ($EventList -notcontains $Event) { + $EventList.Add($Event) + } + } + } + + $Body = [PSCustomObject]@{ + WebhookUrl = "https://$BaseURL/API/PublicWebhooks?CIPPID=$($CIPPID)&Type=PartnerCenter" + WebhookEvents = @($EventList) + } + $EventCompare = Compare-Object $EventList ($MatchedWebhook.EventType | ConvertFrom-Json) + try { + $Uri = 'https://api.partnercenter.microsoft.com/webhooks/v1/registration' + try { + $Existing = New-GraphGetRequest -NoAuthCheck $true -uri $Uri -tenantid $env:TenantId -scope 'https://api.partnercenter.microsoft.com/.default' + } catch {} + if ($Existing.webhookUrl -ne $MatchedWebhook.WebhookNotificationUrl -or $EventCompare) { + if ($Existing.WebhookUrl) { + $Action = 'Updated' + $Method = 'PUT' + Write-Host 'updating webhook' + } else { + $Action = 'Created' + $Method = 'POST' + Write-Host 'creating webhook' + } + + $Uri = 'https://api.partnercenter.microsoft.com/webhooks/v1/registration' + $GraphRequest = New-GraphPOSTRequest -uri $Uri -type $Method -tenantid $env:TenantId -scope 'https://api.partnercenter.microsoft.com/.default' -body ($Body | ConvertTo-Json) -NoAuthCheck $true + + $WebhookRow = @{ + PartitionKey = [string]$CIPPID + RowKey = [string]$CIPPID + EventType = [string](ConvertTo-Json -InputObject $EventList) + Resource = [string]'PartnerCenter' + SubscriptionID = [string]$GraphRequest.SubscriberId + Expiration = 'Does Not Expire' + WebhookNotificationUrl = [string]$Body.WebhookUrl + } + $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow -Force + Write-LogMessage -user $ExecutingUser -API $APIName -message "$Action Partner Center Webhook subscription" -Sev 'Info' -tenant 'PartnerTenant' + return "$Action Partner Center Webhook subscription" + } else { + Write-LogMessage -user $ExecutingUser -API $APIName -message 'Existing Partner Center Webhook subscription found' -Sev 'Info' -tenant 'PartnerTenant' + return 'Existing Partner Center Webhook subscription found' + } + } catch { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to create Partner Center Webhook Subscription: $($_.Exception.Message)" -Sev 'Error' -tenant 'PartnerTenant' + return "Failed to create Partner Webhook Subscription: $($_.Exception.Message)" + } + } else { # First check if there is an exsiting Webhook in place $WebhookFilter = "PartitionKey eq '$($TenantFilter)'" $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } - if (($MatchedWebhook | Measure-Object).count -eq 0 -or $Recreate) { - + if (($MatchedWebhook | Measure-Object).count -eq 0 -or $Recreate.IsPresent) { $expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $params = @{ changeType = $TypeofSubscription @@ -90,10 +151,10 @@ function New-CIPPGraphSubscription { resource = $Resource expirationDateTime = $expiredate } | ConvertTo-Json - + $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. + #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... $WebhookRow = @{ @@ -108,7 +169,7 @@ 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. + #add refresh webhook function based on table. 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 @@ -117,8 +178,6 @@ function New-CIPPGraphSubscription { 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 - Return "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)" + Return "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)" } - } - diff --git a/Modules/CIPPCore/Public/Remove-CIPPGroupMember.ps1 b/Modules/CIPPCore/Public/Remove-CIPPGroupMember.ps1 index 9567039fe8e4..54c6a33e1a9d 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPGroupMember.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPGroupMember.ps1 @@ -16,7 +16,7 @@ function Remove-CIPPGroupMember( } else { New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($GroupId)/members/$($Member)/`$ref" -tenantid $TenantFilter -type DELETE -body '{}' -Verbose } - $Message = "Successfully removed user $($Member) from $GroupId." + $Message = "Successfully removed user $($Member) from $($GroupId)." Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message $Message -Sev 'Info' return $message } catch { diff --git a/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 b/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 index a253311fae29..55d7e9db9803 100644 --- a/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 +++ b/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 @@ -18,7 +18,7 @@ function Send-CIPPAlert { Write-Host "Trying to send email" try { if ($Config.email -like '*@*') { - $Recipients = $Config.email.split(",").trim() | ForEach-Object { if ($_ -like '*@*') { [pscustomobject]@{EmailAddress = @{Address = $_ } } } } + $Recipients = $Config.email.split($(if ($Config.email -like "*,*") { ',' } else { ';' })).trim() | ForEach-Object { if ($_ -like '*@*') { [pscustomobject]@{EmailAddress = @{Address = $_ } } } } $PowerShellBody = [PSCustomObject]@{ message = @{ subject = $Title diff --git a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 index 35a90d7f9e90..fafaf83a482a 100644 --- a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 @@ -1,23 +1,25 @@ function Set-CIPPCPVConsent { [CmdletBinding()] param( - $Tenantfilter, + $TenantFilter, $APIName = 'CPV Consent', $ExecutingUser, [bool]$ResetSP = $false ) $Results = [System.Collections.Generic.List[string]]::new() - $Tenant = Get-Tenants -IncludeAll -IncludeErrors | Where-Object -Property defaultDomainName -EQ $Tenantfilter - $TenantName = $Tenant.defaultDomainName - $TenantFilter = $Tenant.customerId + $Tenant = Get-Tenants -IncludeAll | Where-Object -Property customerId -EQ $TenantFilter | Select-Object -First 1 + $TenantName = $Tenant.displayName - if ($Tenantfilter -eq $env:TenantID) { + if ($TenantFilter -eq $env:TenantID) { return @('Cannot modify CPV consent on partner tenant') } + if ($Tenant.customerId -ne $TenantFilter) { + return @('Not a valid tenant') + } if ($ResetSP) { 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 + $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 { $Results.add("Error deleting SP - $($_.Exception.Message)") @@ -51,7 +53,7 @@ function Set-CIPPCPVConsent { } Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force $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 + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Added our Service Principal to $($TenantName)" -Sev 'Info' -tenant $Tenant.defaultDomainName -tenantId $TenantFilter } catch { $ErrorMessage = Get-NormalizedError -message $_.Exception.Message @@ -68,7 +70,7 @@ function Set-CIPPCPVConsent { Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force 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 + 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 $Tenant.defaultDomainName -tenantId $TenantFilter return @("Could not add our Service Principal to the client tenant $($TenantName): $ErrorMessage") } return $Results diff --git a/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 b/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 index 407eccddc796..a4c87ab9eb5c 100644 --- a/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 @@ -6,8 +6,10 @@ function Set-CIPPGDAPInviteGroups { $Invite = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$($Relationship.id)'" $APINAME = 'GDAPInvites' $RoleMappings = $Invite.RoleMappings | ConvertFrom-Json - - foreach ($role in $RoleMappings) { + $AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$($Relationship.id)/accessAssignments" + foreach ($Role in $RoleMappings) { + # Skip mapping if group is present in relationship + if ($AccessAssignments.id -and $AccessAssignments.accessContainer.accessContainerid -contains $Role.GroupId ) { continue } try { $Mappingbody = ConvertTo-Json -Depth 10 -InputObject @{ 'accessContainer' = @{ @@ -35,11 +37,22 @@ function Set-CIPPGDAPInviteGroups { if (($InviteList | Measure-Object).Count -gt 0) { $Activations = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships?`$filter=status eq 'active'" - foreach ($Activation in $Activations) { + $Batch = foreach ($Activation in $Activations) { if ($InviteList.RowKey -contains $Activation.id) { Write-Host "Mapping groups for GDAP relationship: $($Activation.customer.displayName) - $($Activation.id)" - Push-OutputBinding -Name gdapinvitequeue -Value $Activation + $Activation | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'ExecGDAPInviteQueue' + $Activation + } + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'GDAPInviteOrchestrator' + Batch = @($Batch) + SkipLog = $true } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started GDAP Invite orchestration with ID = '$InstanceId'" } } } diff --git a/Modules/CIPPCore/Public/Set-CIPPSignature.ps1 b/Modules/CIPPCore/Public/Set-CIPPSignature.ps1 new file mode 100644 index 000000000000..fa2de7dc415b --- /dev/null +++ b/Modules/CIPPCore/Public/Set-CIPPSignature.ps1 @@ -0,0 +1,27 @@ +function Set-CIPPOutOfOffice { + [CmdletBinding()] + param ( + $userid, + $InternalMessage, + $ExternalMessage, + $TenantFilter, + $State, + $APIName = 'Set Outlook Roaming Signature', + $ExecutingUser, + $StartTime, + $EndTime + ) + + try { + $SignatureProfile = @' +[{"name":"Roaming_New_Signature","itemClass":"","id":"","scope":"AdeleV@M365x42953883.OnMicrosoft.com","parentSetting":"","secondaryKey":"","type":"String","timestamp":638296273181532792,"metadata":"","value":"Kelvin","isFirstSync":"true","source":"UserOverride"}] +'@ + $GraphRequest = New-GraphPostRequest -uri 'https://substrate.office.com/ows/beta/outlookcloudsettings/settings/global' -tenantid $TenantFilter -type PATCH -contentType 'application/json' -verbose -scope 'https://outlook.office.com/.default' + Write-LogMessage -user $ExecutingUser -API $APIName -message "Set Out-of-office for $($userid) to $state" -Sev 'Info' -tenant $TenantFilter + return "Set Out-of-office for $($userid) to $state." + + } catch { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not add OOO for $($userid)" -Sev 'Error' -tenant $TenantFilter + return "Could not add out of office message for $($userid). Error: $($_.Exception.Message)" + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 index e61c9bd9e75e..ae2d6a6c094a 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 @@ -16,9 +16,12 @@ function Invoke-CIPPStandardActivityBasedTimeout { "definition":["{\"ActivityBasedTimeoutPolicy\":{\"Version\":1,\"ApplicationPolicies\":[{\"ApplicationId\":\"default\",\"WebSessionIdleTimeout\":\"01:00:00\"}]}}"] } '@ - (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/activityBasedTimeoutPolicies' -Type POST -Body $body -ContentType 'application/json') + + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/activityBasedTimeoutPolicies' -Type POST -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled Activity Based Timeout of one hour' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Activity Based Timeout is already enabled' -sev Info } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled Activity Based Timeout of one hour' -sev Info } catch { Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable Activity Based Timeout $($_.exception.message)" -sev Error } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 index 9b6eb9012710..4a980913b0f6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 @@ -13,32 +13,37 @@ function Invoke-CIPPStandardAddDKIM { $SetDomains = $DKIM | Where-Object { $AllDomains -contains $_.Domain -and $_.Enabled -eq $false } If ($Settings.remediate) { - $ErrorCounter = 0 - # New-domains - foreach ($Domain in $NewDomains) { - try { - (New-ExoRequest -tenantid $tenant -cmdlet 'New-DkimSigningConfig' -cmdparams @{ KeySize = 2048; DomainName = $Domain; Enabled = $true } -useSystemMailbox $true) - Write-LogMessage -API 'Standards' -tenant $tenant -message "Enabled DKIM for $Domain" -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable DKIM. Error: $($_.Exception.Message)" -sev Error - $ErrorCounter++ + + if ($null -eq $NewDomains -and $null -eq $SetDomains) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'DKIM is already enabled for all available domains.' -sev Info + } else { + $ErrorCounter = 0 + # New-domains + foreach ($Domain in $NewDomains) { + try { + (New-ExoRequest -tenantid $tenant -cmdlet 'New-DkimSigningConfig' -cmdparams @{ KeySize = 2048; DomainName = $Domain; Enabled = $true } -useSystemMailbox $true) + Write-LogMessage -API 'Standards' -tenant $tenant -message "Enabled DKIM for $Domain" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable DKIM. Error: $($_.Exception.Message)" -sev Error + $ErrorCounter++ + } } - } - - # Set-domains - foreach ($Domain in $SetDomains) { - try { - (New-ExoRequest -tenantid $tenant -cmdlet 'Set-DkimSigningConfig' -cmdparams @{ Identity = $Domain.Domain; Enabled = $true } -useSystemMailbox $true) - Write-LogMessage -API 'Standards' -tenant $tenant -message "Enabled DKIM for $($Domain.Domain)" -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable DKIM. Error: $($_.Exception.Message)" -sev Error - $ErrorCounter++ + + # Set-domains + foreach ($Domain in $SetDomains) { + try { + (New-ExoRequest -tenantid $tenant -cmdlet 'Set-DkimSigningConfig' -cmdparams @{ Identity = $Domain.Domain; Enabled = $true } -useSystemMailbox $true) + Write-LogMessage -API 'Standards' -tenant $tenant -message "Enabled DKIM for $($Domain.Domain)" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable DKIM. Error: $($_.Exception.Message)" -sev Error + $ErrorCounter++ + } + } + if ($ErrorCounter -eq 0) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled DKIM for all domains in tenant' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Failed to enable DKIM for all domains in tenant' -sev Error } - } - if ($ErrorCounter -eq 0) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled DKIM for all domains in tenant' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Failed to enable DKIM for all domains in tenant' -sev Error } } @@ -51,6 +56,7 @@ function Invoke-CIPPStandardAddDKIM { Write-LogMessage -API 'Standards' -tenant $tenant -message "DKIM is not enabled for: $NoDKIM" -sev Alert } } + if ($Settings.report) { if ($null -eq $NewDomains -and $null -eq $SetDomains) { $DKIMState = $true } else { $DKIMState = $false } Add-CIPPBPAField -FieldName 'DKIM' -FieldValue [bool]$DKIMState -StoreAs bool -Tenant $tenant diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 index b17bccf7290c..93f0183adf14 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 @@ -5,12 +5,18 @@ function Invoke-CIPPStandardAnonReportDisable { #> param($Tenant, $Settings) $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/reportSettings' -tenantid $Tenant -AsApp $true + If ($Settings.remediate) { - try { - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/reportSettings' -Type patch -Body '{"displayConcealedNames": false}' -ContentType 'application/json' -AsApp $true - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Anonymous Reports Disabled.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable anonymous reports. Error: $($_.exception.message)" -sev Error + + if ($CurrentInfo.displayConcealedNames -eq $false) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Anonymous Reports is already disabled.' -sev Info + } else { + try { + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/reportSettings' -Type patch -Body '{"displayConcealedNames": false}' -ContentType 'application/json' -AsApp $true + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Anonymous Reports Disabled.' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable anonymous reports. Error: $($_.exception.message)" -sev Error + } } } if ($Settings.alert) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAntiPhishPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAntiPhishPolicy.ps1 new file mode 100644 index 000000000000..0373f47819d3 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAntiPhishPolicy.ps1 @@ -0,0 +1,80 @@ +function Invoke-CIPPStandardAntiPhishPolicy { + <# + .FUNCTIONALITY + Internal + #> + + param($Tenant, $Settings) + $PolicyName = 'Default Anti-Phishing Policy' + $AntiPhishPolicyState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AntiPhishPolicy' | + Where-Object -Property Name -EQ $PolicyName | + Select-Object Name, Enabled, PhishThresholdLevel, EnableMailboxIntelligence, EnableMailboxIntelligenceProtection, EnableSpoofIntelligence, EnableFirstContactSafetyTips, EnableSimilarUsersSafetyTips, EnableSimilarDomainsSafetyTips, EnableUnusualCharactersSafetyTips, EnableUnauthenticatedSender, EnableViaTag, MailboxIntelligenceProtectionAction, MailboxIntelligenceQuarantineTag + + $StateIsCorrect = if ( + ($AntiPhishPolicyState.Name -eq $PolicyName) -and + ($AntiPhishPolicyState.Enabled -eq $true) -and + ($AntiPhishPolicyState.PhishThresholdLevel -eq $Settings.PhishThresholdLevel) -and + ($AntiPhishPolicyState.EnableMailboxIntelligence -eq $true) -and + ($AntiPhishPolicyState.EnableMailboxIntelligenceProtection -eq $true) -and + ($AntiPhishPolicyState.EnableSpoofIntelligence -eq $true) -and + ($AntiPhishPolicyState.EnableFirstContactSafetyTips -eq $Settings.EnableFirstContactSafetyTips) -and + ($AntiPhishPolicyState.EnableSimilarUsersSafetyTips -eq $Settings.EnableSimilarUsersSafetyTips) -and + ($AntiPhishPolicyState.EnableSimilarDomainsSafetyTips -eq $Settings.EnableSimilarDomainsSafetyTips) -and + ($AntiPhishPolicyState.EnableUnusualCharactersSafetyTips -eq $Settings.EnableUnusualCharactersSafetyTips) -and + ($AntiPhishPolicyState.EnableUnauthenticatedSender -eq $true) -and + ($AntiPhishPolicyState.EnableViaTag -eq $true) -and + ($AntiPhishPolicyState.MailboxIntelligenceProtectionAction -eq $Settings.MailboxIntelligenceProtectionAction) -and + ($AntiPhishPolicyState.MailboxIntelligenceQuarantineTag -eq $Settings.MailboxIntelligenceQuarantineTag) + ) { $true } else { $false } + + if ($Settings.remediate) { + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Anti-phishing Policy already exists.' -sev Info + } else { + $cmdparams = @{ + Enabled = $true + PhishThresholdLevel = $Settings.PhishThresholdLevel + EnableMailboxIntelligence = $true + EnableMailboxIntelligenceProtection = $true + EnableSpoofIntelligence = $true + EnableFirstContactSafetyTips = $Settings.EnableFirstContactSafetyTips + EnableSimilarUsersSafetyTips = $Settings.EnableSimilarUsersSafetyTips + EnableSimilarDomainsSafetyTips = $Settings.EnableSimilarDomainsSafetyTips + EnableUnusualCharactersSafetyTips = $Settings.EnableUnusualCharactersSafetyTips + EnableUnauthenticatedSender = $true + EnableViaTag = $true + MailboxIntelligenceProtectionAction = $Settings.MailboxIntelligenceProtectionAction + MailboxIntelligenceQuarantineTag = $Settings.MailboxIntelligenceQuarantineTag + } + + try { + if ($AntiPhishPolicyState.Name -eq $PolicyName) { + $cmdparams.Add('Identity', $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-AntiPhishPolicy' -cmdparams $cmdparams + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Updated Anti-phishing Policy' -sev Info + } else { + $cmdparams.Add('Name', $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'New-AntiPhishPolicy' -cmdparams $cmdparams + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Created Anti-phishing Policy' -sev Info + } + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create Anti-phishing Policy. Error: $($_.exception.message)" -sev Error + } + } + } + + + if ($Settings.alert) { + + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Anti-phishing Policy is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Anti-phishing Policy is not enabled' -sev Alert + } + } + + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'AntiPhishPolicy' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $tenant + } + +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAtpPolicyForO365.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAtpPolicyForO365.ps1 new file mode 100644 index 000000000000..75d78bc395b0 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAtpPolicyForO365.ps1 @@ -0,0 +1,49 @@ +function Invoke-CIPPStandardAtpPolicyForO365 { + <# + .FUNCTIONALITY + Internal + #> + + param($Tenant, $Settings) + $AtpPolicyForO365State = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AtpPolicyForO365' | + Select-Object EnableATPForSPOTeamsODB, EnableSafeDocs, AllowSafeDocsOpen + + $StateIsCorrect = if ( + ($AtpPolicyForO365State.EnableATPForSPOTeamsODB -eq $true) -and + ($AtpPolicyForO365State.EnableSafeDocs -eq $true) -and + ($AtpPolicyForO365State.AllowSafeDocsOpen -eq $Settings.AllowSafeDocsOpen) + ) { $true } else { $false } + + if ($Settings.remediate) { + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Atp Policy For O365 already set.' -sev Info + } else { + $cmdparams = @{ + EnableATPForSPOTeamsODB = $true + EnableSafeDocs = $true + AllowSafeDocsOpen = $Settings.AllowSafeDocsOpen + } + + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-AntiPhishPolicy' -cmdparams $cmdparams + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Updated Atp Policy For O365' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to set Atp Policy For O365. Error: $($_.exception.message)" -sev Error + } + } + } + + if ($Settings.alert) { + + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Atp Policy For O365 is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Atp Policy For O365 is not enabled' -sev Alert + } + } + + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'AtpPolicyForO365' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $tenant + } + +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuditLog.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuditLog.ps1 index 63bbdafe4269..e3682928503c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuditLog.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuditLog.ps1 @@ -4,24 +4,28 @@ function Invoke-CIPPStandardAuditLog { Internal #> param($Tenant, $Settings) - - $AuditLogEnabled = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AdminAuditLogConfig').UnifiedAuditLogIngestionEnabled + Write-Host ($Settings | ConvertTo-Json) + $AuditLogEnabled = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AdminAuditLogConfig' -Select UnifiedAuditLogIngestionEnabled).UnifiedAuditLogIngestionEnabled If ($Settings.remediate) { Write-Host 'Time to remediate' - $DehydratedTenant = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').IsDehydrated + + $DehydratedTenant = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' -Select IsDehydrated).IsDehydrated if ($DehydratedTenant) { - New-ExoRequest -tenantid $Tenant -cmdlet 'Enable-OrganizationCustomization' + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Enable-OrganizationCustomization' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Organization customization enabled.' -sev Info + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable organization customization. Error: $ErrorMessage" -sev Debug + } } try { if ($AuditLogEnabled) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Unified Audit Log already enabled.' -sev Info } else { - $AdminAuditLogParams = @{ - UnifiedAuditLogIngestionEnabled = $true - } - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-AdminAuditLogConfig' -cmdParams $AdminAuditLogParams + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-AdminAuditLogConfig' -cmdParams @{UnifiedAuditLogIngestionEnabled = $true } Write-LogMessage -API 'Standards' -tenant $tenant -message 'Unified Audit Log Enabled.' -sev Info } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 index 429466241b5c..0a12b3e92524 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 @@ -5,25 +5,30 @@ function Invoke-CIPPStandardAutoExpandArchive { #> param($Tenant, $Settings) $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AutoExpandingArchiveEnabled + If ($Settings.remediate) { - try { - if (!$currentstate) { + if ($CurrentState) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Auto Expanding Archive is already enabled.' -sev Info + } else { + try { New-ExoRequest -tenantid $Tenant -cmdlet 'Set-OrganizationConfig' -cmdParams @{AutoExpandingArchive = $true } Write-LogMessage -API 'Standards' -tenant $tenant -message 'Added Auto Expanding Archive.' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to apply Auto Expanding Archives. Error: $($_.exception.message)" -sev Error } - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to apply Auto Expanding Archives Error: $($_.exception.message)" -sev Error } } + if ($Settings.alert) { - if ($AuditLogEnabled) { + if ($CurrentState) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Auto Expanding Archives is enabled' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Auto Expanding Archives is not enabled' -sev Alert } } + if ($Settings.report) { - Add-CIPPBPAField -FieldName 'AutoExpandingArchive' -FieldValue [bool]$CurrentState.AutoExpandingArchiveEnabled -StoreAs bool -Tenant $tenant + Add-CIPPBPAField -FieldName 'AutoExpandingArchive' -FieldValue [bool]$CurrentState -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDelegateSentItems.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDelegateSentItems.ps1 index 4c219d329ba6..dfca3c7c4456 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDelegateSentItems.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDelegateSentItems.ps1 @@ -7,28 +7,34 @@ function Invoke-CIPPStandardDelegateSentItems { $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ RecipientTypeDetails = @('UserMailbox', 'SharedMailbox') } | Where-Object { $_.MessageCopyForSendOnBehalfEnabled -eq $false -or $_.MessageCopyForSentAsEnabled -eq $false } If ($Settings.remediate) { - try { - $Mailboxes | ForEach-Object { - try { - $username = $_.UserPrincipalName - New-ExoRequest -tenantid $Tenant -cmdlet 'set-mailbox' -cmdParams @{Identity = $_.GUID ; MessageCopyForSendOnBehalfEnabled = $True; MessageCopyForSentAsEnabled = $True } -anchor $username - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not enable delegate sent item style for $($username): $($_.Exception.message)" -sev Warn - } - } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Delegate Sent Items Style enabled.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to apply Delegate Sent Items Style. Error: $($_.exception.message)" -sev Error + + if ($Mailboxes) { + try { + $Mailboxes | ForEach-Object { + try { + $username = $_.UserPrincipalName + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $_.GUID ; MessageCopyForSendOnBehalfEnabled = $True; MessageCopyForSentAsEnabled = $True } -anchor $username + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not enable delegate sent item style for $($username): $($_.Exception.message)" -sev Warn + } + } + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Delegate Sent Items Style enabled.' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to apply Delegate Sent Items Style. Error: $($_.exception.message)" -sev Error + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Delegate Sent Items Style already enabled.' -sev Info + } } if ($Settings.alert) { - if ($Mailboxes) { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Delegate Sent Items Style is not enabled for $($mailboxes.count) users" -sev Alert + Write-LogMessage -API 'Standards' -tenant $tenant -message "Delegate Sent Items Style is not enabled for $($mailboxes.count) mailboxes" -sev Alert } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Delegate Sent Items Style is enabled' -sev Info } } + if ($Settings.report) { $Filtered = $Mailboxes | Select-Object -Property UserPrincipalName, MessageCopyForSendOnBehalfEnabled, MessageCopyForSentAsEnabled Add-CIPPBPAField -FieldName 'DelegateSentItems' -FieldValue $Filtered -StoreAs json -Tenant $tenant diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 index 975368668560..ffda26966410 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 @@ -5,30 +5,34 @@ function Invoke-CIPPStandardDeletedUserRentention { #> param($Tenant, $Settings) $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + $StateSetCorrectly = if ($CurrentInfo.deletedUserPersonalSiteRetentionPeriodInDays -eq 365) { $true } else { $false } If ($Settings.remediate) { - try { - $body = '{"deletedUserPersonalSiteRetentionPeriodInDays": 365}' - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type PATCH -Body $body -ContentType 'application/json' - - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Set deleted user rentention of OneDrive to 1 year' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set deleted user rentention of OneDrive to 1 year: $($_.exception.message)" -sev Error + if ($StateSetCorrectly -eq $false) { + try { + $body = '{"deletedUserPersonalSiteRetentionPeriodInDays": 365}' + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type PATCH -Body $body -ContentType 'application/json' + + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Set deleted user rentention of OneDrive to 1 year' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set deleted user rentention of OneDrive to 1 year: $($_.exception.message)" -sev Error + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Deleted user rentention of OneDrive is already set to 1 year' -sev Info + } } + if ($Settings.alert) { - if ($CurrentInfo.deletedUserPersonalSiteRetentionPeriodInDays -eq 365) { + if ($StateSetCorrectly) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Deleted user rentention of OneDrive is set to 1 year' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Deleted user rentention of OneDrive is not set to 1 year' -sev Alert } } + if ($Settings.report) { - if ($CurrentInfo.deletedUserPersonalSiteRetentionPeriodInDays -eq 365) { - $CurrentInfo.deletedUserPersonalSiteRetentionPeriodInDays = $true - } else { - $CurrentInfo.deletedUserPersonalSiteRetentionPeriodInDays = $false - } - Add-CIPPBPAField -FieldName 'DeletedUserRentention' -FieldValue [bool]$CurrentInfo.deletedUserPersonalSiteRetentionPeriodInDays -StoreAs bool -Tenant $tenant + + Add-CIPPBPAField -FieldName 'DeletedUserRentention' -FieldValue [bool]$StateSetCorrectly -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 index c90b9cb4c4f9..c0bab8bcb749 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 @@ -4,7 +4,6 @@ function Invoke-CIPPStandardDisableAdditionalStorageProviders { Internal #> param($Tenant, $Settings) - $AdditionalStorageProvidersState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OwaMailboxPolicy' -cmdParams @{Identity = 'OwaMailboxPolicy-Default' } if ($Settings.remediate) { @@ -23,8 +22,7 @@ function Invoke-CIPPStandardDisableAdditionalStorageProviders { } - if ($Settings.alert) { - + if ($Settings.alert) { if ($AdditionalStorageProvidersState.AdditionalStorageProvidersAvailable) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'OWA additional storage providers are enabled' -sev Alert } else { @@ -33,7 +31,6 @@ function Invoke-CIPPStandardDisableAdditionalStorageProviders { } if ($Settings.report) { - Add-CIPPBPAField -FieldName 'AdditionalStorageProvidersEnabled' -FieldValue [bool]$AdditionalStorageProvidersState.AdditionalStorageProvidersEnabled -StoreAs bool -Tenant $tenant } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 new file mode 100644 index 000000000000..0d1d2d2a0af6 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 @@ -0,0 +1,37 @@ +function Invoke-CIPPStandardDisableAppCreation { + <# + .FUNCTIONALITY + Internal + #> + param($Tenant, $Settings) + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=defaultUserRolePermissions' -tenantid $Tenant + + If ($Settings.remediate) { + if ($CurrentInfo.defaultUserRolePermissions.allowedToCreateApps -eq $false) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are already not allowed to create App registrations.' -sev Info + } else { + try { + $body = '{"defaultUserRolePermissions":{"allowedToCreateApps":false}}' + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled users from creating App registrations.' -sev Info + $CurrentInfo.defaultUserRolePermissions.allowedToCreateApps = $false + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable users from creating App registrations: $($_.exception.message)" -sev Error + } + } + } + + if ($Settings.alert) { + + if ($CurrentInfo.defaultUserRolePermissions.allowedToCreateApps -eq $false) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are not allowed to create App registrations.' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are allowed to create App registrations.' -sev Alert + } + } + + if ($Settings.report) { + $State = -not $CurrentInfo.defaultUserRolePermissions.allowedToCreateApps + Add-CIPPBPAField -FieldName 'UserAppCreationDisabled' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 index 470e5498a1dd..ea9158643d86 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 @@ -4,43 +4,54 @@ function Invoke-CIPPStandardDisableBasicAuthSMTP { Internal #> param($Tenant, $Settings) + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig' + $SMTPusers = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-CASMailbox' -cmdParams @{ ResultSize = 'Unlimited' } | Where-Object { ($_.SmtpClientAuthenticationDisabled -eq $false) } + If ($Settings.remediate) { - # Disable SMTP Basic Authentication for the tenant - try { - $Request = New-ExoRequest -tenantid $Tenant -cmdlet 'Set-TransportConfig' -cmdParams @{ SmtpClientAuthenticationDisabled = $true } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled SMTP Basic Authentication' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable SMTP Basic Authentication: $($_.exception.message)" -sev Error - } - - # Disable SMTP Basic Authentication for all users - $SMTPusers = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-CASMailbox' -cmdParams @{ ResultSize = 'Unlimited' } | Where-Object { ($null -ne $_.SmtpClientAuthenticationDisabled) } - $SMTPusers | ForEach-Object { + if ($CurrentInfo.SmtpClientAuthenticationDisabled -and $SMTPusers.Count -eq 0) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMTP Basic Authentication for tenant and all users is already disabled' -sev Info + } else { + # Disable SMTP Basic Authentication for the tenant try { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-CASMailbox' -cmdParams @{ Identity = $_.Identity; SmtpClientAuthenticationDisabled = $null } -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $tenant -message "Disabled SMTP Basic Authentication for $($_.DisplayName), $($_.PrimarySmtpAddress)" -sev Info + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-TransportConfig' -cmdParams @{ SmtpClientAuthenticationDisabled = $true } + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled SMTP Basic Authentication' -sev Info } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable SMTP Basic Authentication for $($_.DisplayName), $($_.PrimarySmtpAddress). Error: $($_.exception.message)" -sev Error - + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable SMTP Basic Authentication: $($_.exception.message)" -sev Error + } + + # Disable SMTP Basic Authentication for all users + $SMTPusers | ForEach-Object { + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-CASMailbox' -cmdParams @{ Identity = $_.Identity; SmtpClientAuthenticationDisabled = $null } -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $tenant -message "Disabled SMTP Basic Authentication for $($_.DisplayName), $($_.PrimarySmtpAddress)" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable SMTP Basic Authentication for $($_.DisplayName), $($_.PrimarySmtpAddress). 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-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig' - - if ($Settings.alert) { + if ($Settings.alert) { + if ($CurrentInfo.SmtpClientAuthenticationDisabled -and $SMTPusers.Count -eq 0) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMTP Basic Authentication for tenant and all users is disabled' -sev Info + } else { + if ($CurrentInfo.SmtpClientAuthenticationDisabled) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMTP Basic Authentication is disabled' -sev Info + $LogMessage = 'SMTP Basic Authentication for tenant is disabled. ' } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMTP Basic Authentication is not disabled' -sev Alert + $LogMessage = 'SMTP Basic Authentication for tenant is not disabled. ' } + if ($SMTPusers.Count -eq 0) { + $LogMessage += 'SMTP Basic Authentication for all users is disabled' + } else { + $LogMessage += "SMTP Basic Authentication for $($SMTPusers.Count) users is not disabled" + } + Write-LogMessage -API 'Standards' -tenant $tenant -message $LogMessage -sev Alert } - if ($Settings.report) { - Add-CIPPBPAField -FieldName 'DisableBasicAuthSMTP' -FieldValue [bool]$CurrentInfo.SmtpClientAuthenticationDisabled -StoreAs bool -Tenant $tenant - } + } + + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'DisableBasicAuthSMTP' -FieldValue [bool]$CurrentInfo.SmtpClientAuthenticationDisabled -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 index dcd995600259..1bc4d0161cb0 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 @@ -4,25 +4,26 @@ function Invoke-CIPPStandardDisableEmail { Internal #> param($Tenant, $Settings) + $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.remediate) { - Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'Email' -Enabled $false + if ($State) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'Email' -Enabled $false + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Email authentication method is already disabled.' -sev Info + } } - # 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 + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Email authentication method is enabled' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Email authentication method 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-CIPPStandardDisableExternalCalendarSharing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 index e5e9232ed9b4..a1c29b7724c9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 @@ -4,33 +4,35 @@ function Invoke-CIPPStandardDisableExternalCalendarSharing { Internal #> param($Tenant, $Settings) + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SharingPolicy' | Where-Object { $_.Default -eq $true } if ($Settings.remediate) { - New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SharingPolicy' | Where-Object { $_.Default -eq $true } | ForEach-Object { - try { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SharingPolicy' -cmdParams @{ Identity = $_.Id ; Enabled = $false } -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $tenant -message "Successfully disabled external calendar sharing for the policy $($_.Name)" -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable external calendar sharing for the policy $($_.Name). Error: $($_.exception.message)" -sev Error + if ($CurrentInfo.Enabled) { + $CurrentInfo | ForEach-Object { + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SharingPolicy' -cmdParams @{ Identity = $_.Id ; Enabled = $false } -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $tenant -message "Successfully disabled external calendar sharing for the policy $($_.Name)" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable external calendar sharing for the policy $($_.Name). Error: $($_.exception.message)" -sev Error + } } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'External calendar sharing is already disabled' -sev Info + } - } - # This is ugly but done to avoid a second call to the Graph API - if ($Settings.alert -or $Settings.report) { - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SharingPolicy' | Where-Object { $_.Default -eq $true } + } - if ($Settings.alert) { - if ($CurrentInfo.Enabled) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'External calendar sharing is enabled' -sev Alert - } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'External calendar sharing is not enabled' -sev Info - } - } - if ($Settings.report) { - Add-CIPPBPAField -FieldName 'ExternalCalendarSharingDisabled' -FieldValue [bool]$CurrentInfo.Enabled -StoreAs bool -Tenant $tenant + if ($Settings.alert) { + if ($CurrentInfo.Enabled) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'External calendar sharing is enabled' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'External calendar sharing is not enabled' -sev Info } } - + if ($Settings.report) { + $CurrentInfo.Enabled = -not $CurrentInfo.Enabled + Add-CIPPBPAField -FieldName 'ExternalCalendarSharingDisabled' -FieldValue [bool]$CurrentInfo.Enabled -StoreAs bool -Tenant $tenant + } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 index 9729b4b163a7..44d92753db8f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 @@ -8,14 +8,16 @@ function Invoke-CIPPStandardDisableGuestDirectory { If ($Settings.remediate) { - - try { - $body = '{guestUserRoleId: "2af84b1e-32c8-42b7-82bc-daa82404023b"}' - (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type patch -Body $body -ContentType 'application/json') - - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled Guest access to directory information.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Guest access to directory information.: $($_.exception.message)" -sev 'Error' + if ($CurrentInfo.guestUserRoleId -eq '2af84b1e-32c8-42b7-82bc-daa82404023b') { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Guest access to directory information is already disabled.' -sev Info + } else { + try { + $body = '{guestUserRoleId: "2af84b1e-32c8-42b7-82bc-daa82404023b"}' + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled Guest access to directory information.' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Guest access to directory information.: $($_.exception.message)" -sev 'Error' + } } } @@ -27,12 +29,9 @@ function Invoke-CIPPStandardDisableGuestDirectory { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Guest access to directory information is not disabled.' -sev Alert } } + if ($Settings.report) { - if ($CurrentInfo.guestUserRoleId -eq '2af84b1e-32c8-42b7-82bc-daa82404023b') { - $CurrentInfo.guestUserRoleId = $true - } else { - $CurrentInfo.guestUserRoleId = $false - } + if ($CurrentInfo.guestUserRoleId -eq '2af84b1e-32c8-42b7-82bc-daa82404023b') { $CurrentInfo.guestUserRoleId = $true } else { $CurrentInfo.guestUserRoleId = $false } Add-CIPPBPAField -FieldName 'DisableGuestDirectory' -FieldValue [bool]$CurrentInfo.guestUserRoleId -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 index e328f3d99afc..6ccac9c5f3c8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 @@ -4,19 +4,24 @@ function Invoke-CIPPStandardDisableGuests { Internal #> 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 } + $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 { + + if ($GraphRequest) { foreach ($guest in $GraphRequest) { - New-GraphPostRequest -type Patch -tenantid $tenant -uri "https://graph.microsoft.com/beta/users/$($guest.id)" -body '{"accountEnabled":"false"}' - Write-LogMessage -API 'Standards' -tenant $tenant -message "Disabling guest $($guest.UserPrincipalName) ($($guest.id))" -sev Info + try { + New-GraphPostRequest -type Patch -tenantid $tenant -uri "https://graph.microsoft.com/beta/users/$($guest.id)" -body '{"accountEnabled":"false"}' + Write-LogMessage -API 'Standards' -tenant $tenant -message "Disabling guest $($guest.UserPrincipalName) ($($guest.id))" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable guest $($guest.UserPrincipalName) ($($guest.id)): $($_.exception.message)" -sev Error + } } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled guests accounts with a login longer than 90 days ago.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable guests older than 90 days: $($_.exception.message)" -sev Error + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'No guests accounts with a login longer than 90 days ago.' -sev Info } + } if ($Settings.alert) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 index bbb62f9b0b92..de0c916d64b7 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 @@ -7,19 +7,23 @@ function Invoke-CIPPStandardDisableM365GroupUsers { $CurrentState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/settings' -tenantid $tenant) | Where-Object -Property displayname -EQ 'Group.unified' If ($Settings.remediate) { - try { - if (!$CurrentState) { - #if no current configuration is found, we set it to the default template supplied by MS. - $CurrentState = '{"id":"","displayName":"Group.Unified","templateId":"62375ab9-6b52-47ed-826b-58e47e0e304b","values":[{"name":"NewUnifiedGroupWritebackDefault","value":"true"},{"name":"EnableMIPLabels","value":"false"},{"name":"CustomBlockedWordsList","value":""},{"name":"EnableMSStandardBlockedWords","value":"false"},{"name":"ClassificationDescriptions","value":""},{"name":"DefaultClassification","value":""},{"name":"PrefixSuffixNamingRequirement","value":""},{"name":"AllowGuestsToBeGroupOwner","value":"false"},{"name":"AllowGuestsToAccessGroups","value":"true"},{"name":"GuestUsageGuidelinesUrl","value":""},{"name":"GroupCreationAllowedGroupId","value":""},{"name":"AllowToAddGuests","value":"true"},{"name":"UsageGuidelinesUrl","value":""},{"name":"ClassificationList","value":""},{"name":"EnableGroupCreation","value":"true"}]}' - New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/settings/$($CurrentState.id)" -AsApp $true -Type POST -Body $CurrentState -ContentType 'application/json' - $CurrentState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/settings' -tenantid $tenant) | Where-Object -Property displayname -EQ 'Group.unified' + if (($CurrentState.values | Where-Object { $_.name -eq 'EnableGroupCreation' }).value -eq 'false') { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are already disabled from creating M365 Groups.' -sev Info + } else { + try { + if (!$CurrentState) { + # If no current configuration is found, we set it to the default template supplied by MS. + $CurrentState = '{"id":"","displayName":"Group.Unified","templateId":"62375ab9-6b52-47ed-826b-58e47e0e304b","values":[{"name":"NewUnifiedGroupWritebackDefault","value":"true"},{"name":"EnableMIPLabels","value":"false"},{"name":"CustomBlockedWordsList","value":""},{"name":"EnableMSStandardBlockedWords","value":"false"},{"name":"ClassificationDescriptions","value":""},{"name":"DefaultClassification","value":""},{"name":"PrefixSuffixNamingRequirement","value":""},{"name":"AllowGuestsToBeGroupOwner","value":"false"},{"name":"AllowGuestsToAccessGroups","value":"true"},{"name":"GuestUsageGuidelinesUrl","value":""},{"name":"GroupCreationAllowedGroupId","value":""},{"name":"AllowToAddGuests","value":"true"},{"name":"UsageGuidelinesUrl","value":""},{"name":"ClassificationList","value":""},{"name":"EnableGroupCreation","value":"true"}]}' + New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/settings/$($CurrentState.id)" -AsApp $true -Type POST -Body $CurrentState -ContentType 'application/json' + $CurrentState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/settings' -tenantid $tenant) | Where-Object -Property displayname -EQ 'Group.unified' + } + ($CurrentState.values | Where-Object { $_.name -eq 'EnableGroupCreation' }).value = 'false' + $body = "{values : $($CurrentState.values | ConvertTo-Json -Compress)}" + $null = New-GraphPostRequest -tenantid $tenant -asApp $true -Uri "https://graph.microsoft.com/beta/settings/$($CurrentState.id)" -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled users from creating M365 Groups.' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable users from creating M365 Groups: $($_.exception.message)" -sev 'Error' } - ($CurrentState.values | Where-Object { $_.name -eq 'EnableGroupCreation' }).value = 'false' - $body = "{values : $($CurrentState.values | ConvertTo-Json -Compress)}" - New-GraphPostRequest -tenantid $tenant -asApp $true -Uri "https://graph.microsoft.com/beta/settings/$($CurrentState.id)" -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Standards API: Disabled users from creating M365 Groups.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable users from creating M365 Groups: $($_.exception.message)" -sev 'Error' } } if ($Settings.alert) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 index 2f438e417605..6f6c0b9f7f7a 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 @@ -4,27 +4,31 @@ function Invoke-CIPPStandardDisableReshare { Internal #> param($Tenant, $Settings) + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + If ($Settings.remediate) { - - try { - $body = '{"isResharingByExternalUsersEnabled": "False"}' - $Request = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' - Write-Host ($Request | ConvertTo-Json) - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled guests from resharing files' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable guests from resharing files: $($_.exception.message)" -sev Error + if ($CurrentInfo.isResharingByExternalUsersEnabled) { + try { + $body = '{"isResharingByExternalUsersEnabled": "False"}' + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled guests from resharing files' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable guests from resharing files: $($_.exception.message)" -sev Error + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Guests are already disabled from resharing files' -sev Info } } if ($Settings.alert) { - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true - if ($CurrentInfo.isResharingByExternalUsersEnabled -eq $false) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Guests are not allowed to reshare files' -sev Info - } else { + if ($CurrentInfo.isResharingByExternalUsersEnabled) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Guests are allowed to reshare files' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Guests are not allowed to reshare files' -sev Info } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'DisableReshare' -FieldValue [bool]$CurrentInfo.isResharingByExternalUsersEnabled -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 index d133c34deef5..337dffde4b20 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 @@ -4,25 +4,26 @@ function Invoke-CIPPStandardDisableSMS { Internal #> param($Tenant, $Settings) + $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.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 ($State) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'SMS' -Enabled $false + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMS authentication method is already disabled.' -sev Info } - if ($Settings.report) { - Add-CIPPBPAField -FieldName 'DisableSMS' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + } + + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMS authentication method is enabled' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMS authentication method 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-CIPPStandardDisableSecurityGroupUsers.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 index 75facbab7757..56510a1eb758 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 @@ -4,28 +4,32 @@ function Invoke-CIPPStandardDisableSecurityGroupUsers { Internal #> param($Tenant, $Settings) + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + If ($Settings.remediate) { - - - try { - $body = '{"defaultUserRolePermissions":{"allowedToCreateSecurityGroups":false}}' - (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type patch -Body $body -ContentType 'application/json') - - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Standards API: Disabled users from creating Security Groups.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable users from creating Security Groups: $($_.exception.message)" -sev 'Error' + if ($CurrentInfo.defaultUserRolePermissions.allowedToCreateSecurityGroups -eq $false) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are already not allowed to create Security Groups.' -sev Info + } else { + try { + $body = '{"defaultUserRolePermissions":{"allowedToCreateSecurityGroups":false}}' + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled users from creating Security Groups.' -sev Info + $CurrentInfo.defaultUserRolePermissions.allowedToCreateSecurityGroups = $false + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable users from creating Security Groups: $($_.exception.message)" -sev 'Error' + } } } if ($Settings.alert) { - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant if ($CurrentInfo.defaultUserRolePermissions.allowedToCreateSecurityGroups -eq $false) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are not allowed to create Security Groups.' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are allowed to create Security Groups.' -sev Alert } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'DisableSecurityGroupUsers' -FieldValue [bool]$CurrentInfo.defaultUserRolePermissions.allowedToCreateSecurityGroups -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 index 77e7295aaca8..45b637dcc905 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 @@ -5,24 +5,31 @@ function Invoke-CIPPStandardDisableSharedMailbox { #> param($Tenant, $Settings) $SharedMailboxList = (New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($Tenant)/Mailbox?`$filter=ExchangeUserAccountControl ne 'accountdisabled'" -Tenantid $tenant -scope ExchangeOnline | Where-Object { $_.RecipientTypeDetails -EQ 'SharedMailbox' -or $_.RecipientTypeDetails -eq 'SchedulingMailbox' }) + If ($Settings.remediate) { - try { + if ($SharedMailboxList) { $SharedMailboxList | ForEach-Object { - New-GraphPOSTRequest -uri "https://graph.microsoft.com/v1.0/users/$($_.ObjectKey)" -type 'PATCH' -body '{"accountEnabled":"false"}' -tenantid $tenant + try { + New-GraphPOSTRequest -uri "https://graph.microsoft.com/v1.0/users/$($_.ObjectKey)" -type PATCH -body '{"accountEnabled":"false"}' -tenantid $tenant + Write-LogMessage -API 'Standards' -tenant $tenant -message "AAD account for shared mailbox $($_.DisplayName) disabled." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable AAD account for shared mailbox. Error: $($_.exception.message)" -sev Error + } } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'AAD Accounts for shared mailboxes disabled.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable AAD accounts for shared mailboxes. Error: $($_.exception.message)" -sev Error + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'All AAD accounts for shared mailboxes are already disabled.' -sev Info } } + if ($Settings.alert) { if ($SharedMailboxList) { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Shared mailboxes with enabled accounts: $($SharedMailboxList.count)" -sev Alert + Write-LogMessage -API 'Standards' -tenant $tenant -message "Shared mailboxes with enabled accounts: $($SharedMailboxList.Count)" -sev Alert } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'No AAD accounts enables for shared mailboxes.' -sev Info + Write-LogMessage -API 'Standards' -tenant $tenant -message 'All AAD accounts for shared mailboxes are disabled.' -sev Info } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'DisableSharedMailbox' -FieldValue $SharedMailboxList -StoreAs json -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 index 55cde0260efc..d7a57014edcd 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 @@ -4,25 +4,33 @@ function Invoke-CIPPStandardDisableTenantCreation { Internal #> param($Tenant, $Settings) + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + $State = $CurrentInfo.defaultUserRolePermissions.allowedToCreateTenants + If ($Settings.remediate) { - try { - $body = '{"defaultUserRolePermissions":{"allowedToCreateTenants":false}}' - (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type patch -Body $body -ContentType 'application/json') - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Standards API: Disabled users from creating tenants.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable users from creating tenants: $($_.exception.message)" -sev 'Error' + + if ($State) { + try { + $body = '{"defaultUserRolePermissions":{"allowedToCreateTenants":false}}' + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled users from creating tenants.' -sev Info + $State = $false + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable users from creating tenants: $($_.exception.message)" -sev 'Error' + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are already disabled from creating tenants.' -sev Info } } if ($Settings.alert) { - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant - if ($CurrentInfo.defaultUserRolePermissions.allowedToCreateTenants -eq $false) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are not allowed to create tenants.' -sev Info - } else { + if ($State) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are allowed to create tenants.' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Users are not allowed to create tenants.' -sev Info } } if ($Settings.report) { - Add-CIPPBPAField -FieldName 'DisableTenantCreation' -FieldValue [bool]$CurrentInfo.defaultUserRolePermissions.allowedToCreateTenants -StoreAs bool -Tenant $tenant + Add-CIPPBPAField -FieldName 'DisableTenantCreation' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 index 3943cbc38d43..da501dac7956 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 @@ -4,24 +4,33 @@ function Invoke-CIPPStandardDisableUserSiteCreate { Internal #> param($Tenant, $Settings) + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + If ($Settings.remediate) { - try { - $body = '{"isSiteCreationEnabled": false}' - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled standard users from creating sites' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable standard users from creating sites: $($_.exception.message)" -sev Error + + if ($CurrentInfo.isSiteCreationEnabled) { + try { + $body = '{"isSiteCreationEnabled": false}' + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled standard users from creating sites' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable standard users from creating sites: $($_.exception.message)" -sev Error + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Standard users are already disabled from creating sites' -sev Info } + } + if ($Settings.alert) { - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true if ($CurrentInfo.isSiteCreationEnabled -eq $false) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Standard users are not allowed to create sites' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Standard users are allowed to create sites' -sev Alert } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'DisableUserSiteCreate' -FieldValue [bool]$CurrentInfo.isSiteCreationEnabled -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableViva.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableViva.ps1 index 3c7332d68d7c..788e9204328f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableViva.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableViva.ps1 @@ -4,26 +4,34 @@ function Invoke-CIPPStandardDisableViva { Internal #> param($Tenant, $Settings) - $currentsetting = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$tenant/settings/peopleInsights" -tenantid $Tenant -AsApp $true + $CurrentSetting = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$tenant/settings/peopleInsights" -tenantid $Tenant -AsApp $true + If ($Settings.remediate) { - try { - New-GraphPOSTRequest -Uri "https://graph.microsoft.com/beta/organization/$tenant/settings/peopleInsights" -tenantid $Tenant -AsApp $true -Type PATCH -Body '{"isEnabledInOrganization": false}' -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled Viva insights' -sev Info - - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Viva for all users Error: $($_.exception.message)" -sev Error + + if ($CurrentSetting.isEnabledInOrganization -eq $false) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Viva is already disabled.' -sev Info + } else { + try { + # TODO This does not work. Throws an "EXCEPTION: Tenant admin role is required" error. Cant figure out how to fix -Bobby + New-GraphPOSTRequest -Uri "https://graph.microsoft.com/beta/organization/$tenant/settings/peopleInsights" -tenantid $Tenant -AsApp $true -Type PATCH -Body '{"isEnabledInOrganization": false}' -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled Viva insights' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Viva for all users. Error: $($_.exception.message)" -sev Error + } } } + if ($Settings.alert) { - if ($currentsetting.isEnabled -eq $false) { + if ($CurrentSetting.isEnabledInOrganization -eq $false) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Viva is disabled' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Viva is not disabled' -sev Alert } } + if ($Settings.report) { - Add-CIPPBPAField -FieldName 'DisableViva' -FieldValue [bool]$currentsetting.isEnabled -StoreAs bool -Tenant $tenant + Add-CIPPBPAField -FieldName 'DisableViva' -FieldValue [bool]$CurrentSetting.isEnabledInOrganization -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 index 50d18e51c43f..96c52b33e37c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 @@ -4,25 +4,26 @@ function Invoke-CIPPStandardDisableVoice { Internal #> param($Tenant, $Settings) + $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.remediate) { - Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'Voice' -Enabled $false + if ($State) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'Voice' -Enabled $false + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Voice authentication method is already disabled.' -sev Info + } } - # 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 + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Voice authentication method is enabled' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Voice authentication method 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 index 94a36d9b9125..c2da2547c1a5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 @@ -4,25 +4,27 @@ function Invoke-CIPPStandardDisablex509Certificate { Internal #> param($Tenant, $Settings) + $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.remediate) { - Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'x509Certificate' -Enabled $false + if ($State) { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'x509Certificate' -Enabled $false + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'x509Certificate authentication method is already disabled.' -sev Info + } } - # 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 + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'x509Certificate authentication method is enabled' -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'x509Certificate authentication method 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 ff80b7f264f8..8fb8ff1a0544 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 @@ -4,24 +4,30 @@ function Invoke-CIPPStandardEnableFIDO2 { Internal #> param($Tenant, $Settings) - + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -tenantid $Tenant + $State = if ($CurrentInfo.state -eq 'enabled') { $true } else { $false } + If ($Settings.remediate) { - Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'Fido2' -Enabled $true + + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'FIDO2 Support is already enabled.' -sev Info + } else { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'Fido2' -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/Fido2' -tenantid $Tenant - 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.alert) { + + if ($State) { + 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]$State -StoreAs bool -Tenant $tenant + } + } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 index 605a5188a47d..53dc0eef1798 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 @@ -4,24 +4,29 @@ function Invoke-CIPPStandardEnableHardwareOAuth { Internal #> param($Tenant, $Settings) - + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/HardwareOath' -tenantid $Tenant + $State = if ($CurrentInfo.state -eq 'enabled') { $true } else { $false } + 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 ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'HardwareOAuth Support is already enabled.' -sev Info + } else { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'HardwareOath' -Enabled $true } - if ($Settings.report) { - Add-CIPPBPAField -FieldName 'EnableHardwareOAuth' -FieldValue [bool]$CurrentInfo.state -StoreAs bool -Tenant $tenant + } + + if ($Settings.alert) { + + if ($State) { + 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]$State -StoreAs bool -Tenant $tenant + } } + diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 index 51991bf57f9e..47eb5a54118f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 @@ -5,38 +5,35 @@ function Invoke-CIPPStandardEnableMailTips { #> param($Tenant, $Settings) + $MailTipsState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold + $StateIsCorrect = if ($MailTipsState.MailTipsAllTipsEnabled -and $MailTipsState.MailTipsExternalRecipientsTipsEnabled -and $MailTipsState.MailTipsGroupMetricsEnabled -and $MailTipsState.MailTipsLargeAudienceThreshold -eq $Settings.MailTipsLargeAudienceThreshold) { $true } else { $false } if ($Settings.remediate) { - - try { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-OrganizationConfig' -cmdparams @{ MailTipsAllTipsEnabled = $true; MailTipsExternalRecipientsTipsEnabled = $true; MailTipsGroupMetricsEnabled = $true; MailTipsLargeAudienceThreshold = $Settings.MailTipsLargeAudienceThreshold } - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Enabled all MailTips' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to enable all MailTips: $($_.exception.message)" -sev Error + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'All MailTips are already enabled.' -sev Info + } else { + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-OrganizationConfig' -cmdparams @{ MailTipsAllTipsEnabled = $true; MailTipsExternalRecipientsTipsEnabled = $true; MailTipsGroupMetricsEnabled = $true; MailTipsLargeAudienceThreshold = $Settings.MailTipsLargeAudienceThreshold } + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Enabled all MailTips' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to enable all MailTips. Error: $($_.exception.message)" -sev Error + } } } + if ($Settings.alert) { - if ($Settings.alert -or $Settings.report) { - $MailTipsState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold - - if ($Settings.alert) { - if ($MailTipsState.MailTipsAllTipsEnabled -and $MailTipsState.MailTipsExternalRecipientsTipsEnabled -and $MailTipsState.MailTipsGroupMetricsEnabled -and $MailTipsState.MailTipsLargeAudienceThreshold -eq $Settings.MailTipsLargeAudienceThreshold) { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'All MailTips are enabled' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Not all MailTips are enabled' -sev Alert - } + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'All MailTips are enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Not all MailTips are enabled' -sev Alert } + } - if ($Settings.report) { + if ($Settings.report) { - if ($MailTipsState.MailTipsAllTipsEnabled -and $MailTipsState.MailTipsExternalRecipientsTipsEnabled -and $MailTipsState.MailTipsGroupMetricsEnabled -and $MailTipsState.MailTipsLargeAudienceThreshold -eq $Settings.MailTipsLargeAudienceThreshold) { - $MailTipsState = $true - } else { - $MailTipsState = $false - } - Add-CIPPBPAField -FieldName 'MailTipsEnabled' -FieldValue [bool]$MailTipsState -StoreAs bool -Tenant $tenant - } + Add-CIPPBPAField -FieldName 'MailTipsEnabled' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $tenant } + } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 index 3bde7b01121f..6d959f03d8d9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 @@ -4,14 +4,15 @@ function Invoke-CIPPStandardEnableMailboxAuditing { Internal #> param($Tenant, $Settings) - $AuditState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AuditDisabled - if ( $Settings.remediate) { + + if ($Settings.remediate) { if ($AuditState) { # Enable tenant level mailbox audit try { New-ExoRequest -tenantid $Tenant -cmdlet 'Set-OrganizationConfig' -cmdParams @{AuditDisabled = $false } -useSystemMailbox $true Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Tenant level mailbox audit enabled' -sev Info + $LogMessage = 'Tenant level mailbox audit enabled. ' } catch { Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to enable tenant level mailbox audit. Error: $($_.exception.message)" -sev Error } @@ -20,7 +21,7 @@ function Invoke-CIPPStandardEnableMailboxAuditing { } # Check for mailbox audit on all mailboxes. Enable for all that it's not enabled for - $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ResultSize = 'Unlimited' } | Where-Object { $_.AuditEnabled -ne $true } + $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{filter = "auditenabled -eq 'False'" } -useSystemMailbox $true -Select 'AuditEnabled,UserPrincipalName' $Mailboxes | ForEach-Object { try { New-ExoRequest -tenantid $Tenant -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $_.UserPrincipalName; AuditEnabled = $true } -Anchor $_.UserPrincipalName @@ -31,7 +32,8 @@ function Invoke-CIPPStandardEnableMailboxAuditing { } # Disable audit bypass for all mailboxes that have it enabled - $BypassMailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxAuditBypassAssociation' -cmdParams @{ResultSize = 'Unlimited' } | Where-Object { $_.AuditBypassEnabled -eq $true } + + $BypassMailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxAuditBypassAssociation' -select 'GUID, AuditBypassEnabled, Name' -useSystemMailbox $true | Where-Object { $_.AuditBypassEnabled -eq $true } $BypassMailboxes | ForEach-Object { try { New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MailboxAuditBypassAssociation' -cmdParams @{Identity = $_.Guid; AuditBypassEnabled = $false } -UseSystemMailbox $true @@ -41,15 +43,15 @@ function Invoke-CIPPStandardEnableMailboxAuditing { } } - if ($Mailboxes.Count -eq 0 -and $BypassMailboxes.Count -eq 0) { + $LogMessage = if ($Mailboxes.Count -eq 0 -and $BypassMailboxes.Count -eq 0) { # Make log message smaller if both are already in the desired state - $LogMessage += 'User level mailbox audit already enabled and mailbox audit bypass already disabled for all mailboxes' + 'User level mailbox audit already enabled and mailbox audit bypass already disabled for all mailboxes' } else { if ($Mailboxes.Count -eq 0) { - $LogMessage += 'User level mailbox audit already enabled for all mailboxes. ' + 'User level mailbox audit already enabled for all mailboxes. ' } if ($BypassMailboxes.Count -eq 0) { - $LogMessage += 'Mailbox audit bypass already disabled for all mailboxes' + 'Mailbox audit bypass already disabled for all mailboxes' } } @@ -63,7 +65,9 @@ function Invoke-CIPPStandardEnableMailboxAuditing { Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Tenant level mailbox audit is enabled' -sev Info } } + if ($Settings.report) { + $AuditState = -not $AuditState Add-CIPPBPAField -FieldName 'MailboxAuditingEnabled' -FieldValue [bool]$AuditState -StoreAs bool -Tenant $Tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 index 7d52d6c4eb74..902fe7301a6f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 @@ -4,27 +4,49 @@ function Invoke-CIPPStandardExcludedfileExt { Internal #> param($Tenant, $Settings) - $Exts = $Settings.ext -split ',' + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + $Exts = ($Settings.ext -replace ' ', '') -split ',' + # Add a wildcard to the extensions since thats what the SP admin center does + $Exts = $Exts | ForEach-Object { if ($_ -notlike '*.*') { "*.$_" } else { $_ } } + + + $MissingExclutions = foreach ($Exclusion in $Exts) { + if ($Exclusion -notin $CurrentInfo.excludedFileExtensionsForSyncApp) { + $Exclusion + } + } + + Write-Host "MissingExclutions: $($MissingExclutions)" + + If ($Settings.remediate) { - - - try { - $body = ConvertTo-Json -InputObject @{ excludedFileExtensionsForSyncApp = @($Exts) } - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message "Added $($Settings.ext) to excluded synced files" -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to add $($Settings.ext) to excluded synced files: $($_.exception.message)" -sev Error + + # If the number of extensions in the settings does not match the number of extensions in the current settings, we need to update the settings + $MissingExclutions = if ($Exts.Count -ne $CurrentInfo.excludedFileExtensionsForSyncApp.Count) { $true } else { $MissingExclutions } + if ($MissingExclutions) { + Write-Host "CurrentInfo.excludedFileExtensionsForSyncApp: $($CurrentInfo.excludedFileExtensionsForSyncApp)" + Write-Host "Exts: $($Exts)" + try { + $body = ConvertTo-Json -InputObject @{ excludedFileExtensionsForSyncApp = @($Exts) } + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message "Added $($Settings.ext) to excluded synced files" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to add $($Settings.ext) to excluded synced files: $($_.exception.message)" -sev Error + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Excluded synced files already contains $($Settings.ext)" -sev Info } } + if ($Settings.alert) { - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true - if ($CurrentInfo.excludedFileExtensionsForSyncApp -contains $Exts) { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Excluded synced files contains $($Settings.ext)" -sev Info + if ($MissingExclutions) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Excluded synced files does not contain $($MissingExclutions -join ',')" -sev Alert } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Excluded synced files does not contain $($Settings.ext)" -sev Alert + Write-LogMessage -API 'Standards' -tenant $tenant -message "Excluded synced files contains $($Settings.ext)" -sev Info } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'ExcludedfileExt' -FieldValue $CurrentInfo.excludedFileExtensionsForSyncApp -StoreAs json -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 new file mode 100644 index 000000000000..3b9df5cfb779 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 @@ -0,0 +1,44 @@ +function Invoke-CIPPStandardExternalMFATrusted { + <# + .FUNCTIONALITY + Internal + #> + param($Tenant, $Settings) + + $ExternalMFATrusted = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default?$select=inboundTrust' -tenantid $Tenant) + $WantedState = if ($Settings.state -eq 'true') { $true } else { $false } + $StateMessage = if ($WantedState) { 'enabled' } else { 'disabled' } + + if ($Settings.remediate) { + + Write-Host 'Remediate External MFA Trusted' + if ($ExternalMFATrusted.inboundTrust.isMfaAccepted -eq $WantedState ) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "External MFA Trusted is already $StateMessage." -sev Info + } else { + try { + $NewBody = $ExternalMFATrusted + $NewBody.inboundTrust.isMfaAccepted = $WantedState + $NewBody = ConvertTo-Json -Depth 10 -InputObject $NewBody -Compress + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default' -Type patch -Body $NewBody -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message "Set External MFA Trusted to $StateMessage." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set External MFA Trusted to $StateMessage. Error: $($_.exception.message)" -sev Error + } + } + } + + if ($Settings.alert) { + + if ($ExternalMFATrusted.inboundTrust.isMfaAccepted -eq $WantedState) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "External MFA Trusted is $StateMessage." -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message "External MFA Trusted is not $StateMessage." -sev Alert + } + + } + + if ($Settings.report) { + + Add-CIPPBPAField -FieldName 'ExternalMFATrusted' -FieldValue [bool]$ExternalMFATrusted.inboundTrust.isMfaAccepted -StoreAs bool -Tenant $tenant + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGroupTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGroupTemplate.ps1 index f07e54320d72..e2809c6f4bd9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGroupTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGroupTemplate.ps1 @@ -32,15 +32,24 @@ function Invoke-CIPPStandardGroupTemplate { } $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/groups' -tenantid $tenant -type POST -body (ConvertTo-Json -InputObject $BodyToship -Depth 10) -verbose } else { - $Params = @{ - Name = $groupobj.Displayname - Alias = $groupobj.username - Description = $groupobj.Description - PrimarySmtpAddress = $email - Type = $groupobj.groupType - RequireSenderAuthenticationEnabled = [bool]!$groupobj.AllowExternal + if ($groupobj.groupType -eq 'dynamicdistribution') { + $Params = @{ + Name = $groupobj.Displayname + RecipientFilter = $groupobj.membershipRules + PrimarySmtpAddress = $email + } + $GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DynamicDistributionGroup' -cmdParams $params + } else { + $Params = @{ + Name = $groupobj.Displayname + Alias = $groupobj.username + Description = $groupobj.Description + PrimarySmtpAddress = $email + Type = $groupobj.groupType + RequireSenderAuthenticationEnabled = [bool]!$groupobj.AllowExternal + } + $GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DistributionGroup' -cmdParams $params } - $GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DistributionGroup' -cmdParams $params } Write-LogMessage -user $request.headers.'x-ms-client-principal' -API 'Standards' -tenant $tenant -message "Created group $($groupobj.displayname) with id $($GraphRequest.id) " -Sev 'Info' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 index d6676fc437ec..9b17f9dd3b6b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 @@ -20,6 +20,31 @@ function Invoke-CIPPStandardIntuneTemplate { $RawJSON = $Request.body.RawJSON switch ($Request.body.Type) { + 'AppProtection' { + $TemplateType = ($RawJSON | ConvertFrom-Json).'@odata.type' -replace '#microsoft.graph.', '' + $TemplateTypeURL = "$($TemplateType)s" + $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/$TemplateTypeURL" -tenantid $tenant + if ($displayname -in $CheckExististing.displayName) { + $ExistingID = $CheckExististing | Where-Object -Property displayName -EQ $PolicyName + $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/$TemplateTypeURL/$($ExistingID.Id)" -tenantid $tenant -type PATCH -body $RawJSON + } else { + $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/$TemplateTypeURL" -tenantid $tenant -type POST -body $RawJSON + } + } + 'deviceCompliancePolicies' { + $TemplateTypeURL = 'deviceCompliancePolicies' + $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$TemplateTypeURL" -tenantid $tenant + + $JSON = $RawJSON | ConvertFrom-Json | Select-Object * -ExcludeProperty id, createdDateTime, lastModifiedDateTime, version, 'scheduledActionsForRule@odata.context', '@odata.context' + $JSON.scheduledActionsForRule = @($JSON.scheduledActionsForRule | Select-Object * -ExcludeProperty 'scheduledActionConfigurations@odata.context') + $RawJSON = ConvertTo-Json -InputObject $JSON -Depth 20 -Compress + Write-Host $RawJSON + if ($displayname -in $CheckExististing.displayName) { + $ExistingID = $CheckExististing | Where-Object -Property displayName -EQ $PolicyName + $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$TemplateTypeURL/$($ExistingID.Id)" -tenantid $tenant -type PATCH -body $RawJSON + } + $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$TemplateTypeURL" -tenantid $tenant -type POST -body $RawJson + } 'Admin' { $TemplateTypeURL = 'groupPolicyConfigurations' $CreateBody = '{"description":"' + $description + '","displayName":"' + $displayname + '","roleScopeTagIds":["0"]}' @@ -75,6 +100,7 @@ function Invoke-CIPPStandardIntuneTemplate { if ($Settings.AssignTo) { Write-Host "Assigning Policy to $($Settings.AssignTo) the create ID is $($CreateRequest)" + if ($Settings.AssignTo -eq 'customGroup') { $Settings.AssignTo = $Settings.customGroup } Set-CIPPAssignedPolicy -PolicyId $CreateRequest.id -TenantFilter $tenant -GroupName $Settings.AssignTo -Type $TemplateTypeURL } Write-LogMessage -API 'Standards' -tenant $tenant -message "Successfully added Intune Template policy for $($Tenant)" -sev 'Info' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 index fc2b34dda7b1..09b58fe0671a 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 @@ -5,28 +5,37 @@ function Invoke-CIPPStandardMailContacts { #> param($Tenant, $Settings) $TenantID = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/organization' -tenantid $tenant) + $CurrentInfo = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$($TenantID.id)" -tenantid $Tenant + $contacts = $settings + $TechAndSecurityContacts = @($Contacts.SecurityContact, $Contacts.TechContact) If ($Settings.remediate) { - $contacts = $settings - try { - $Body = [pscustomobject]@{} - switch ($Contacts) { - { $Contacts.MarketingContact } { $body | Add-Member -NotePropertyName marketingNotificationEmails -NotePropertyValue @($Contacts.MarketingContact) } - { $Contacts.SecurityContact } { $body | Add-Member -NotePropertyName technicalNotificationMails -NotePropertyValue @($Contacts.SecurityContact) } - { $Contacts.TechContact } { $body | Add-Member -NotePropertyName technicalNotificationMails -NotePropertyValue @($Contacts.TechContact) } - { $Contacts.GeneralContact } { $body | Add-Member -NotePropertyName privacyProfile -NotePropertyValue @{contactEmail = $Contacts.GeneralContact } } + # TODO: Make this smaller if possible + if ($CurrentInfo.marketingNotificationEmails -eq $Contacts.MarketingContact -and ` + ($CurrentInfo.securityComplianceNotificationMails -in $TechAndSecurityContacts -or + $CurrentInfo.technicalNotificationMails -in $TechAndSecurityContacts) -and ` + $CurrentInfo.privacyProfile.contactEmail -eq $Contacts.GeneralContact) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Contact emails are already set.' -sev Info + } else { + try { + $Body = [pscustomobject]@{} + switch ($Contacts) { + { $Contacts.MarketingContact } { $body | Add-Member -NotePropertyName marketingNotificationEmails -NotePropertyValue @($Contacts.MarketingContact) } + { $Contacts.SecurityContact } { $body | Add-Member -NotePropertyName technicalNotificationMails -NotePropertyValue @($Contacts.SecurityContact) } + { $Contacts.TechContact } { $body | Add-Member -NotePropertyName technicalNotificationMails -NotePropertyValue @($Contacts.TechContact) -ErrorAction SilentlyContinue } + { $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/v1.0/organization/$($TenantID.id)" -asApp $true -Type patch -Body (ConvertTo-Json -InputObject $body) -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Contact emails set.' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set contact emails: $($_.exception.message)" -sev Error } - Write-Host (ConvertTo-Json -InputObject $body) - New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/v1.0/organization/$($TenantID.id)" -asApp $true -Type patch -Body (ConvertTo-Json -InputObject $body) -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Contact emails set.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set contact emails: $($_.exception.message)" -sev Error } } if ($Settings.alert) { - $CurrentInfo = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$($TenantID.id)" -tenantid $Tenant if ($CurrentInfo.marketingNotificationEmails -eq $Contacts.MarketingContact) { Write-LogMessage -API 'Standards' -tenant $tenant -message "Marketing contact email is set to $($Contacts.MarketingContact)" -sev Info } else { @@ -47,6 +56,7 @@ function Invoke-CIPPStandardMailContacts { } else { Write-LogMessage -API 'Standards' -tenant $tenant -message "General contact email is not set to $($Contacts.GeneralContact)" -sev Alert } + } if ($Settings.report) { Add-CIPPBPAField -FieldName 'MailContacts' -FieldValue $CurrentInfo -StoreAs json -Tenant $tenant diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 new file mode 100644 index 000000000000..40ed853dc1d2 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 @@ -0,0 +1,70 @@ +function Invoke-CIPPStandardMalwareFilterPolicy { + <# + .FUNCTIONALITY + Internal + #> + + param($Tenant, $Settings) + $MalwareFilterState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MalwareFilterPolicy' | + Where-Object -Property Name -eq $PolicyName | + Select-Object Name, EnableFileFilter, FileTypeAction, ZapEnabled, QuarantineTag, EnableInternalSenderAdminNotifications, InternalSenderAdminAddress, EnableExternalSenderAdminNotifications, ExternalSenderAdminAddress + + $PolicyName = "Default Malware Policy" + $StateIsCorrect = if ( + ($MalwareFilterState.Name -eq $PolicyName) -and + ($MalwareFilterState.EnableFileFilter -eq $true) -and + ($MalwareFilterState.FileTypeAction -eq $Settings.FileTypeAction) -and + ($MalwareFilterState.ZapEnabled -eq $true) -and + ($MalwareFilterState.QuarantineTag -eq $Settings.QuarantineTag) -and + ($MalwareFilterState.EnableInternalSenderAdminNotifications -eq $Settings.EnableInternalSenderAdminNotifications) -and + ($MalwareFilterState.InternalSenderAdminAddress -eq $Settings.InternalSenderAdminAddress) -and + ($MalwareFilterState.EnableExternalSenderAdminNotifications -eq $Settings.EnableExternalSenderAdminNotifications) -and + ($MalwareFilterState.ExternalSenderAdminAddress -eq $Settings.ExternalSenderAdminAddress) + ) { $true } else { $false } + + if ($Settings.remediate) { + + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Malware Filter Policy already exists.' -sev Info + } else { + $cmdparams = @{ + EnableFileFilter = $true + FileTypeAction = $Settings.FileTypeAction + ZapEnabled = $true + QuarantineTag = $Settings.QuarantineTag + EnableInternalSenderAdminNotifications = $Settings.EnableInternalSenderAdminNotifications + InternalSenderAdminAddress = $Settings.InternalSenderAdminAddress + EnableExternalSenderAdminNotifications = $Settings.EnableExternalSenderAdminNotifications + ExternalSenderAdminAddress = $Settings.ExternalSenderAdminAddress + } + + try { + if ($MalwareFilterState.Name -eq $PolicyName) { + $cmdparams.Add("Identity", $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MalwareFilterPolicy' -cmdparams $cmdparams + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Updated Malware Filter Policy' -sev Info + } else { + $cmdparams.Add("Name", $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'New-MalwareFilterPolicy' -cmdparams $cmdparams + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Created Malware Filter Policy' -sev Info + } + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create Malware Filter Policy. Error: $($_.exception.message)" -sev Error + } + } + } + + if ($Settings.alert) { + + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Malware Filter Policy is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Malware Filter Policy is not enabled' -sev Alert + } + } + + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'MalwareFilterPolicy' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $tenant + } + +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 new file mode 100644 index 000000000000..294729a36583 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 @@ -0,0 +1,34 @@ +function Invoke-CIPPStandardMessageExpiration { + <# + .FUNCTIONALITY + Internal + #> + param($Tenant, $Settings) + + $MessageExpiration = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig').messageExpiration + + If ($Settings.remediate) { + Write-Host 'Time to remediate' + if ($MessageExpiration -ne '12:00:00') { + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-TransportConfig' -cmdParams @{MessageExpiration = '12:00:00' } + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Set transport configuration message expiration to 12 hours' -sev Info + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set transport configuration message expiration to 12 hours. Error: $ErrorMessage" -sev Debug + } + } + + } + if ($Settings.alert) { + if ($MessageExpiration -ne '12:00:00') { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Transport configuration message expiration is set to 12 hours' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Transport configuration message expiration is not set to 12 hours' -sev Alert + } + } + if ($Settings.report) { + if ($MessageExpiration -ne '12:00:00') { $MessageExpiration = $false } else { $MessageExpiration = $true } + Add-CIPPBPAField -FieldName 'messageExpiration' -FieldValue [bool]$MessageExpiration -StoreAs bool -Tenant $tenant + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardNudgeMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardNudgeMFA.ps1 index f9a6a0710d44..a022bea6f0f6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardNudgeMFA.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardNudgeMFA.ps1 @@ -5,33 +5,39 @@ function Invoke-CIPPStandardNudgeMFA { #> param($Tenant, $Settings) $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy' -tenantid $Tenant + $State = if ($CurrentInfo.registrationEnforcement.authenticationMethodsRegistrationCampaign.state -eq 'enabled') { $true } else { $false } If ($Settings.remediate) { - $status = if ($Settings.enable -and $Settings.disable) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'You cannot both enable and disable the Nudge MFA setting' -sev Error - Exit - } elseif ($Settings.enable) { 'enabled' } else { 'disabled' } - Write-Output $status - try { - $Body = $CurrentInfo - $body.registrationEnforcement.authenticationMethodsRegistrationCampaign.state = $status - $body = ConvertTo-Json -Depth 10 -InputObject ($body | Select-Object registrationEnforcement) - New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy" -Type patch -Body $body -ContentType "application/json" - Write-LogMessage -API "Standards" -tenant $tenant -message "$status Authenticator App Nudge" -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to $status Authenticator App Nudge: $($_.exception.message)" -sev Error + + if ($Settings.state -ne $CurrentInfo.registrationEnforcement.authenticationMethodsRegistrationCampaign.state -or $Settings.snoozeDurationInDays -ne $CurrentInfo.registrationEnforcement.authenticationMethodsRegistrationCampaign.snoozeDurationInDays) { + try { + $Body = $CurrentInfo + $body.registrationEnforcement.authenticationMethodsRegistrationCampaign.state = $Settings.state + $body.registrationEnforcement.authenticationMethodsRegistrationCampaign.snoozeDurationInDays = $Settings.snoozeDurationInDays + + $body = ConvertTo-Json -Depth 10 -InputObject ($body | Select-Object registrationEnforcement) + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy' -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message "$($Settings.state) Authenticator App Nudge with a snooze duration of $($Settings.snoozeDurationInDays)" -sev Info + $CurrentInfo.registrationEnforcement.authenticationMethodsRegistrationCampaign.state = $Settings.state + $CurrentInfo.registrationEnforcement.authenticationMethodsRegistrationCampaign.snoozeDurationInDays = $Settings.snoozeDurationInDays + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to $($Settings.state) Authenticator App Nudge: $($_.exception.message)" -sev Error + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Authenticator App Nudge is already set to $($Settings.state) with a snooze duration of $($Settings.snoozeDurationInDays)" -sev Info } } + if ($Settings.alert) { - if ($CurrentInfo.registrationEnforcement.authenticationMethodsRegistrationCampaign.state -eq 'enabled') { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Authenticator App Nudge is enabled' -sev Info + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Authenticator App Nudge is enabled with a snooze duration of $($CurrentInfo.registrationEnforcement.authenticationMethodsRegistrationCampaign.snoozeDurationInDays)" -sev Info } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Authenticator App Nudge is not enabled' -sev Alert + Write-LogMessage -API 'Standards' -tenant $tenant -message "Authenticator App Nudge is not enabled with a snooze duration of $($CurrentInfo.registrationEnforcement.authenticationMethodsRegistrationCampaign.snoozeDurationInDays)" -sev Alert } } + if ($Settings.report) { - if ($CurrentInfo.registrationEnforcement.authenticationMethodsRegistrationCampaign.state -eq 'enabled') { $actualstate = $true } else { $actualstate = $false } - Add-CIPPBPAField -FieldName 'NudgeMFA' -FieldValue [bool]$actualstate -StoreAs bool -Tenant $tenant + Add-CIPPBPAField -FieldName 'NudgeMFA' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 index 1816a15ca998..9758a8c40b76 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 @@ -4,24 +4,32 @@ function Invoke-CIPPStandardOutBoundSpamAlert { Internal #> param($Tenant, $Settings) + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -useSystemMailbox $true + If ($Settings.remediate) { - $Contacts = $settings.OutboundSpamContact - try { - New-ExoRequest -tenantid $tenant -cmdlet 'Set-HostedOutboundSpamFilterPolicy' -cmdparams @{ Identity = 'Default'; NotifyOutboundSpam = $true; NotifyOutboundSpamRecipients = $Contacts } -useSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $tenant -message "Set outbound spam filter alert to $($Contacts)" -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set outbound spam contact to $($Contacts). $($_.exception.message)" -sev Error + + if ($CurrentInfo.NotifyOutboundSpam -ne $true -or $CurrentInfo.NotifyOutboundSpamRecipients -ne $settings.OutboundSpamContact) { + $Contacts = $settings.OutboundSpamContact + try { + New-ExoRequest -tenantid $tenant -cmdlet 'Set-HostedOutboundSpamFilterPolicy' -cmdparams @{ Identity = 'Default'; NotifyOutboundSpam = $true; NotifyOutboundSpamRecipients = $Contacts } -useSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $tenant -message "Set outbound spam filter alert to $($Contacts)" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set outbound spam contact to $($Contacts). $($_.exception.message)" -sev Error + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Outbound spam filter alert is already set to $($CurrentInfo.NotifyOutboundSpamRecipients)" -sev Info } } + if ($Settings.alert) { - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -useSystemMailbox $true if ($CurrentInfo.NotifyOutboundSpam -eq $true) { Write-LogMessage -API 'Standards' -tenant $tenant -message "Outbound spam filter alert is set to $($CurrentInfo.NotifyOutboundSpamRecipients)" -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Outbound spam filter alert is not set' -sev Alert } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'OutboundSpamAlert' -FieldValue [bool]$CurrentInfo.NotifyOutboundSpam -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 index 2cd6cb851ba7..ae8dad7d31ea 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 @@ -5,42 +5,48 @@ function Invoke-CIPPStandardPWcompanionAppAllowedState { #> param($Tenant, $Settings) $authenticatorFeaturesState = (New-GraphGetRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -Type GET) + $authstate = if ($authenticatorFeaturesState.featureSettings.companionAppAllowedState.state -eq 'enabled') { $true } else { $false } + If ($Settings.remediate) { - $Setting = $Settings - try { - # Get current state of microsoftAuthenticator policy - # Remove number matching from featureSettings because this is now Microsoft enforced and shipping it returns an error - $authenticatorFeaturesState.featureSettings.PSObject.Properties.Remove('numberMatchingRequiredState') - # Define feature body - $featureBody = @{ - state = $Setting.state - includeTarget = [PSCustomObject]@{ - targetType = 'group' - id = 'all_users' - } - excludeTarget = [PSCustomObject]@{ - targetType = 'group' - id = '00000000-0000-0000-0000-000000000000' + + if ($authenticatorFeaturesState.featureSettings.companionAppAllowedState.state -eq $Settings.state) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "companionAppAllowedState is already set to the desired state of $($Settings.state)." -sev Info + } else { + try { + # Remove number matching from featureSettings because this is now Microsoft enforced and shipping it returns an error + $authenticatorFeaturesState.featureSettings.PSObject.Properties.Remove('numberMatchingRequiredState') + # Define feature body + $featureBody = @{ + state = $Settings.state + includeTarget = [PSCustomObject]@{ + targetType = 'group' + id = 'all_users' + } + excludeTarget = [PSCustomObject]@{ + targetType = 'group' + id = '00000000-0000-0000-0000-000000000000' + } } + $authenticatorFeaturesState.featureSettings.companionAppAllowedState = $featureBody + $body = ConvertTo-Json -Depth 3 -Compress -InputObject $authenticatorFeaturesState + (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 "Set companionAppAllowedState to $($Settings.state)." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set companionAppAllowedState to $($Settings.state). Error: $($_.exception.message)" -sev Error } - $authenticatorFeaturesState.featureSettings.companionAppAllowedState = $featureBody - $body = $authenticatorFeaturesState | ConvertTo-Json -Depth 3 - (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 companionAppAllowedState.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable companionAppAllowedState. Error: $($_.exception.message)" -sev Error } } + if ($Settings.alert) { - if ($authenticatorFeaturesState.featureSettings.companionAppAllowedState.state -eq 'enabled') { + if ($authstate) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'companionAppAllowedState is enabled.' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'companionAppAllowedState is not enabled.' -sev Alert } } + if ($Settings.report) { - if ($authenticatorFeaturesState.featureSettings.companionAppAllowedState.state -eq 'enabled') { $authstate = $true } else { $authstate = $false } Add-CIPPBPAField -FieldName 'companionAppAllowedState' -FieldValue [bool]$authstate -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 index 4bf9ebcd3426..78d183b10e27 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 @@ -4,24 +4,26 @@ function Invoke-CIPPStandardPWdisplayAppInformationRequiredState { Internal #> param($Tenant, $Settings) + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + $State = if ($CurrentInfo.state -eq 'enabled') { $true } else { $false } If ($Settings.remediate) { - Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'MicrosoftAuthenticator' -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/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 ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Passwordless with Information and Number Matching is already enabled.' -sev Info + } else { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'MicrosoftAuthenticator' -Enabled $true } - if ($Settings.report) { - Add-CIPPBPAField -FieldName 'PWdisplayAppInformationRequiredState' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + } + + 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 + } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 index 3788a12d72b9..47cdc60712c8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 @@ -4,26 +4,41 @@ function Invoke-CIPPStandardPasswordExpireDisabled { Internal #> param($Tenant, $Settings) - $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $Tenant + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant + $DomainswithoutPassExpire = $GraphRequest | Where-Object -Property passwordValidityPeriodInDays -NE '2147483647' + If ($Settings.remediate) { - try { - $GraphRequest | Where-Object -Property passwordValidityPeriodInDays -NE '2147483647' | ForEach-Object { - New-GraphPostRequest -type Patch -tenantid $Tenant -uri "https://graph.microsoft.com/beta/domains/$($_.id)" -body '{"passwordValidityPeriodInDays": 2147483647 }' + + if ($DomainswithoutPassExpire) { + $DomainswithoutPassExpire | ForEach-Object { + try { + if ( $null -eq $_.passwordNotificationWindowInDays ) { + $Body = '{"passwordValidityPeriodInDays": 2147483647, "passwordNotificationWindowInDays": 14 }' + Write-Host "PasswordNotificationWindowInDays is null for $($_.id). Setting to the default of 14 days." + } else { + $Body = '{"passwordValidityPeriodInDays": 2147483647 }' + } + New-GraphPostRequest -type Patch -tenantid $Tenant -uri "https://graph.microsoft.com/v1.0/domains/$($_.id)" -body $Body + Write-LogMessage -API 'Standards' -tenant $tenant -message "Disabled Password Expiration for $($_.id)." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Password Expiration for $($_.id). Error: $($_.exception.message)" -sev Error + } } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled Password Expiration' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Password Expiration. Error: $($_.exception.message)" -sev Error + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Password Expiration is already disabled for all $($GraphRequest.Count) domains." -sev Info } + } - if ($Settings.alert) { - $GraphRequest | Where-Object -Property passwordValidityPeriodInDays -NE '2147483647' | ForEach-Object { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Password Expiration is not disabled for $($_.name)" -sev Alert + if ($Settings.alert) { + if ($DomainswithoutPassExpire) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Password Expiration is not disabled for the following $($DomainswithoutPassExpire.Count) domains: $($DomainswithoutPassExpire.id -join ', ')" -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Password Expiration is disabled for all $($GraphRequest.Count) domains." -sev Info } } + if ($Settings.report) { - $DomainswithoutPassExpire = $GraphRequest | Where-Object -Property passwordValidityPeriodInDays -NE '2147483647' Add-CIPPBPAField -FieldName 'PasswordExpireDisabled' -FieldValue $DomainswithoutPassExpire -StoreAs json -Tenant $tenant - } -} +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 index 5246d658f5de..17236da05fdf 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 @@ -8,22 +8,26 @@ function Invoke-CIPPStandardRotateDKIM { If ($Settings.remediate) { - $DKIM | ForEach-Object { - try { - (New-ExoRequest -tenantid $tenant -cmdlet 'Rotate-DkimSigningConfig' -cmdparams @{ KeySize = 2048; Identity = $_.Identity } -useSystemMailbox $true) - Write-LogMessage -API 'Standards' -tenant $tenant -message "Rotated DKIM for $($_.Identity)" -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to rotate DKIM Error: $($_.exception.message)" -sev Error + if ($DKIM) { + $DKIM | ForEach-Object { + try { + (New-ExoRequest -tenantid $tenant -cmdlet 'Rotate-DkimSigningConfig' -cmdparams @{ KeySize = 2048; Identity = $_.Identity } -useSystemMailbox $true) + Write-LogMessage -API 'Standards' -tenant $tenant -message "Rotated DKIM for $($_.Identity)" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to rotate DKIM Error: $($_.exception.message)" -sev Error + } } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'DKIM is already rotated for all domains' -sev Info } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Rotated DKIM' -sev Info + } if ($Settings.alert) { - if ($null -eq $DKIM) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'DKIM is rotated for all domains' -sev Info - } else { + if ($DKIM) { Write-LogMessage -API 'Standards' -tenant $tenant -message "DKIM is not rotated for $($DKIM.Identity -join ';')" -sev Alert + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'DKIM is rotated for all domains' -sev Info } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 new file mode 100644 index 000000000000..784e221b659f --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 @@ -0,0 +1,62 @@ +function Invoke-CIPPStandardSafeAttachmentPolicy { + <# + .FUNCTIONALITY + Internal + #> + + param($Tenant, $Settings) + $SafeAttachmentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentPolicy' | + Where-Object -Property Name -eq $PolicyName | + Select-Object Name, Enable, Action, QuarantineTag, Redirect, RedirectAddress + + $PolicyName = "Default Safe Attachment Policy" + $StateIsCorrect = if ( + ($SafeAttachmentState.Name -eq $PolicyName) -and + ($SafeAttachmentState.Enable -eq $true) -and + ($SafeAttachmentState.QuarantineTag -eq $Settings.QuarantineTag) -and + ($SafeAttachmentState.Redirect -eq $Settings.Redirect) -and + ($SafeAttachmentState.RedirectAddress -eq $Settings.RedirectAddress) + ) { $true } else { $false } + + if ($Settings.remediate) { + + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy already exists.' -sev Info + } else { + $cmdparams = @{ + Enable = $true + QuarantineTag = $Settings.QuarantineTag + Redirect = $Settings.Redirect + RedirectAddress = $Settings.RedirectAddress + } + + try { + if ($SafeAttachmentState.Name -eq $PolicyName) { + $cmdparams.Add("Identity", $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeAttachmentPolicy' -cmdparams $cmdparams + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Updated Safe Attachment Policy' -sev Info + } else { + $cmdparams.Add("Name", $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeAttachmentPolicy' -cmdparams $cmdparams + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Created Safe Attachment Policy' -sev Info + } + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create Safe Attachment Policy. Error: $($_.exception.message)" -sev Error + } + } + } + + if ($Settings.alert) { + + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy is not enabled' -sev Alert + } + } + + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'SafeAttachmentPolicy' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $tenant + } + +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 new file mode 100644 index 000000000000..7233a4e92fc9 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 @@ -0,0 +1,74 @@ +function Invoke-CIPPStandardSafeLinksPolicy { + <# + .FUNCTIONALITY + Internal + #> + + param($Tenant, $Settings) + $SafeLinkState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksPolicy' | + Where-Object -Property Name -eq $PolicyName | + Select-Object Name, EnableSafeLinksForEmail, EnableSafeLinksForTeams, EnableSafeLinksForOffice, TrackClicks, AllowClickThrough, ScanUrls, EnableForInternalSenders, DeliverMessageAfterScan, DisableUrlRewrite, EnableOrganizationBranding + + $PolicyName = "Default SafeLinks Policy" + $StateIsCorrect = if ( + ($SafeLinkState.Name -eq $PolicyName) -and + ($SafeLinkState.EnableSafeLinksForEmail -eq $true) -and + ($SafeLinkState.EnableSafeLinksForTeams -eq $true) -and + ($SafeLinkState.EnableSafeLinksForOffice -eq $true) -and + ($SafeLinkState.TrackClicks -eq $true) -and + ($SafeLinkState.ScanUrls -eq $true) -and + ($SafeLinkState.EnableForInternalSenders -eq $true) -and + ($SafeLinkState.DeliverMessageAfterScan -eq $true) -and + ($SafeLinkState.AllowClickThrough -eq $Settings.AllowClickThrough) -and + ($SafeLinkState.DisableUrlRewrite -eq $Settings.DisableUrlRewrite) -and + ($SafeLinkState.EnableOrganizationBranding -eq $Settings.EnableOrganizationBranding) + ) { $true } else { $false } + + if ($Settings.remediate) { + + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy already exists.' -sev Info + } else { + $cmdparams = @{ + EnableSafeLinksForEmail = $true + EnableSafeLinksForTeams = $true + EnableSafeLinksForOffice = $true + TrackClicks = $true + ScanUrls = $true + EnableForInternalSenders = $true + DeliverMessageAfterScan = $true + AllowClickThrough = $Settings.AllowClickThrough + DisableUrlRewrite = $Settings.DisableUrlRewrite + EnableOrganizationBranding = $Settings.EnableOrganizationBranding + } + + try { + if ($SafeLinkState.Name -eq $PolicyName) { + $cmdparams.Add("Identity", $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeLinksPolicy' -cmdparams $cmdparams + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Updated SafeLink Policy' -sev Info + } else { + $cmdparams.Add("Name", $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeLinksPolicy' -cmdparams $cmdparams + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Created SafeLink Policy' -sev Info + } + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create SafeLink Policy. Error: $($_.exception.message)" -sev Error + } + } + } + + if ($Settings.alert) { + + if ($StateIsCorrect) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy is not enabled' -sev Alert + } + } + + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'SafeLinksPolicy' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $tenant + } + +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeSendersDisable.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeSendersDisable.ps1 index a0cb61fa8e64..5563e3d5d4e4 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeSendersDisable.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeSendersDisable.ps1 @@ -4,6 +4,7 @@ function Invoke-CIPPStandardSafeSendersDisable { Internal #> param($Tenant, $Settings) + If ($Settings.remediate) { try { $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' | ForEach-Object { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 index 5d2c0c9c26f7..0c92a0829b60 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 @@ -5,26 +5,31 @@ function Invoke-CIPPStandardSecurityDefaults { #> param($Tenant, $Settings) $SecureDefaultsState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $tenant) + If ($Settings.remediate) { - try { - if ($SecureDefaultsState.IsEnabled -ne $true) { + if ($SecureDefaultsState.IsEnabled -ne $true) { + try { Write-Host "Secure Defaults is disabled. Enabling for $tenant" -ForegroundColor Yellow $body = '{ "isEnabled": true }' - (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -Type patch -Body $body -ContentType 'application/json') + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -Type patch -Body $body -ContentType 'application/json' + + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled Security Defaults.' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable Security Defaults. Error: $($_.exception.message)" -sev Error } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Standards API: Security Defaults Enabled.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable Security Defaults Error: $($_.exception.message)" -sev 'Error' + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Security Defaults is already enabled.' -sev Info } } - if ($Settings.alert) { + if ($Settings.alert) { if ($SecureDefaultsState.IsEnabled -eq $true) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Security Defaults is enabled.' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Security Defaults is not enabled.' -sev Alert } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'SecurityDefaults' -FieldValue [bool]$SecureDefaultsState.IsEnabled -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 index cd4972866185..6d465e0b51f8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 @@ -4,28 +4,31 @@ function Invoke-CIPPStandardSendFromAlias { Internal #> param($Tenant, $Settings) + $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').SendFromAliasEnabled + If ($Settings.remediate) { - try { - $AdminAuditLogParams = @{ - SendFromAliasEnabled = $true + if ($CurrentInfo -eq $false) { + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-OrganizationConfig' -cmdParams @{ SendFromAliasEnabled = $true } + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Send from alias enabled.' -sev Info + $CurrentInfo = $true + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable send from alias. Error: $($_.exception.message)" -sev Error } - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-OrganizationConfig' -cmdParams $AdminAuditLogParams - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Send from alias Enabled.' -sev Info - - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to apply Send from Alias Standard. Error: $($_.exception.message)" -sev Error + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Send from alias is already enabled.' -sev Info } } - if ($Settings.alert) { - $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig') - if ($CurrentInfo.SendFromAliasEnabled -eq $true) { + if ($Settings.alert) { + if ($CurrentInfo -eq $true) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Send from alias is enabled.' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Send from alias is not enabled.' -sev Alert } } + if ($Settings.report) { - Add-CIPPBPAField -FieldName 'SendFromAlias' -FieldValue [bool]$CurrentInfo.SendFromAliasEnabled -StoreAs bool -Tenant $tenant + Add-CIPPBPAField -FieldName 'SendFromAlias' -FieldValue [bool]$CurrentInfo -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 index 640cea2d18f0..e430e96a0f93 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 @@ -5,31 +5,45 @@ function Invoke-CIPPStandardSendReceiveLimitTenant { #> param($Tenant, $Settings) $AllMailBoxPlans = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxPlan' | Select-Object DisplayName, MaxSendSize, MaxReceiveSize, GUID + $MaxSendSize = [int64]"$($Settings.SendLimit)MB" + $MaxReceiveSize = [int64]"$($Settings.ReceiveLimit)MB" + + $NotSetCorrectly = foreach ($MailboxPlan in $AllMailBoxPlans) { + $PlanMaxSendSize = [int64]($MailboxPlan.MaxSendSize -replace '.*\(([\d,]+).*', '$1' -replace ',', '') + $PlanMaxReceiveSize = [int64]($MailboxPlan.MaxReceiveSize -replace '.*\(([\d,]+).*', '$1' -replace ',', '') + if ($PlanMaxSendSize -ne $MaxSendSize -or $PlanMaxReceiveSize -ne $MaxReceiveSize) { + $MailboxPlan + } + } + If ($Settings.remediate) { Write-Host "Time to remediate. Our Settings are $($Settings.SendLimit)MB and $($Settings.ReceiveLimit)MB" - $MaxReceiveSize = [int64]"$($Settings.SendLimit)MB" - $MaxSendSize = [int64]"$($Settings.ReceiveLimit)MB" - try { - foreach ($MailboxPlan in $AllMailBoxPlans) { - if ($MailboxPlan.MaxSendSize -ne $MaxSendSize -and $MailboxPlan.MaxReceiveSize -ne $MaxReceiveSize) { + if ($NotSetCorrectly.Count -gt 0) { + Write-Host "Found $($NotSetCorrectly.Count) Mailbox Plans that are not set correctly. Setting them to $($Settings.SendLimit)MB and $($Settings.ReceiveLimit)MB" + try { + foreach ($MailboxPlan in $NotSetCorrectly) { New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MailboxPlan' -cmdParams @{Identity = $MailboxPlan.GUID; MaxSendSize = $MaxSendSize; MaxReceiveSize = $MaxReceiveSize } -useSystemMailbox $true } + Write-LogMessage -API 'Standards' -tenant $tenant -message "Successfully set the tenant send($($Settings.SendLimit)MB) and receive($($Settings.ReceiveLimit)MB) limits" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set the tenant send and receive limits. Error: $($_.exception.message)" -sev Error } - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Successfully set the tenant send and receive limits ' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set the tenant send and receive limits. Error: $($_.exception.message)" -sev Error + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message "The tenant send($($Settings.SendLimit)MB) and receive($($Settings.ReceiveLimit)MB) limits are already set correctly" -sev Info } } + if ($Settings.alert) { - foreach ($MailboxPlan in $AllMailBoxPlans) { - if ($MailboxPlan.MaxSendSize -ne $MaxSendSize -and $MailboxPlan.MaxReceiveSize -ne $MaxReceiveSize) { - Write-LogMessage -API 'Standards' -tenant $tenant -message "The tenant send and receive limits are not set correctly for $($MailboxPlan.DisplayName)" -sev Alert - } + if ($NotSetCorrectly.Count -eq 0) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "The tenant send($($Settings.SendLimit)MB) and receive($($Settings.ReceiveLimit)MB) limits are set correctly" -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message "The tenant send($($Settings.SendLimit)MB) and receive($($Settings.ReceiveLimit)MB) limits are not set correctly" -sev Alert } } + if ($Settings.report) { - Add-CIPPBPAField -FieldName 'SendReceiveLimit' -FieldValue $AllMailBoxPlans -StoreAs json -Tenant $tenant + Add-CIPPBPAField -FieldName 'SendReceiveLimit' -FieldValue $NotSetCorrectly -StoreAs json -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 index 4b8d790f7dcf..a2613c4d0f3d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 @@ -4,28 +4,34 @@ function Invoke-CIPPStandardSpoofWarn { Internal #> param($Tenant, $Settings) + $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ExternalInOutlook') + If ($Settings.remediate) { $status = if ($Settings.enable -and $Settings.disable) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'You cannot both enable and disable the Spoof Warnings setting' -sev Error Exit - } elseif ($Settings.state -eq 'Enabled' -or $Settings.enable) { $true } else { $false } - try { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-ExternalInOutlook' -cmdParams @{ Enabled = $status; } - Write-LogMessage -API 'Standards' -tenant $tenant -message "Spoofing warnings set to $status." -sev Info + } elseif ($Settings.state -eq 'enabled' -or $Settings.enable) { $true } else { $false } - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set spoofing warnings to $status. Error: $($_.exception.message)" -sev Error + if ($CurrentInfo.Enabled -eq $status) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Outlook external spoof warnings are already set to $status." -sev Info + } else { + try { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-ExternalInOutlook' -cmdParams @{ Enabled = $status; } + Write-LogMessage -API 'Standards' -tenant $tenant -message "Outlook external spoof warnings set to $status." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set Outlook external spoof warnings to $status. Error: $($_.exception.message)" -sev Error + } } } - if ($Settings.alert) { - $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ExternalInOutlook') + if ($Settings.alert) { if ($CurrentInfo.Enabled -eq $true) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Spoofing warnings are enabled.' -sev Info + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Outlook external spoof warnings are enabled.' -sev Info } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Spoofing warnings are not enabled.' -sev Alert + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Outlook external spoof warnings are not enabled.' -sev Alert } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'SpoofingWarnings' -FieldValue [bool]$CurrentInfo.Enabled -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 index e98e7faddd01..178274286074 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 @@ -4,25 +4,26 @@ function Invoke-CIPPStandardTAP { Internal #> param($Tenant, $Settings) + $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.remediate) { - Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'TemporaryAccessPass' -Enabled $true -TAPisUsableOnce $Settings.config + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Temporary Access Passwords is already enabled.' -sev Info + } else { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'TemporaryAccessPass' -Enabled $true -TAPisUsableOnce $Settings.config + } } - # 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) { - Add-CIPPBPAField -FieldName 'TemporaryAccessPass' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + 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 } } -} \ No newline at end of file + + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'TemporaryAccessPass' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 index 1098903a7c74..5eae5226cd93 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 @@ -4,14 +4,35 @@ function Invoke-CIPPStandardUndoOauth { Internal #> param($Tenant, $Settings) + $CurrentState = New-GraphGetRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=permissionGrantPolicyIdsAssignedToDefaultUserRole' + $State = if ($CurrentState.permissionGrantPolicyIdsAssignedToDefaultUserRole -eq 'ManagePermissionGrantsForSelf.microsoft-user-default-legacy') { $true } else { $false } + $State + If ($Settings.remediate) { - try { - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type PATCH -Body '{"permissionGrantPolicyIdsAssignedToDefaultUserRole":["ManagePermissionGrantsForSelf.microsoft-user-default-legacy"]}' -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Application Consent Mode has been disabled.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set Application Consent Mode to disabled Error: $($_.exception.message)" -sev Error + if ($State) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Application Consent Mode is already disabled.' -sev Info + } else { + try { + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -Type PATCH -Body '{"permissionGrantPolicyIdsAssignedToDefaultUserRole":["ManagePermissionGrantsForSelf.microsoft-user-default-legacy"]}' -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Application Consent Mode has been disabled.' -sev Info + $CurrentState.permissionGrantPolicyIdsAssignedToDefaultUserRole = 'ManagePermissionGrantsForSelf.microsoft-user-default-legacy' + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set Application Consent Mode to disabled. Error: $($_.exception.message)" -sev Error + } + } + + } + + if ($Settings.alert) { + if ($State) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Application Consent Mode is disabled.' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Application Consent Mode is not disabled.' -sev Alert } } + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'UndoOauth' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant + } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 index f7a5ff7aa040..934f79995842 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 @@ -5,39 +5,43 @@ function Invoke-CIPPStandardUserSubmissions { #> param($Tenant, $Settings) $Policy = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionPolicy' + If ($Settings.remediate) { - if ($Settings.enable -and $Settings.disable) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'You cannot both enable and disable the User Submission policy' -sev Error - Exit - } elseif ($Settings.enable) { - $status = $true - try { - - if ($Policy.length -eq 0) { - New-ExoRequest -tenantid $Tenant -cmdlet 'New-ReportSubmissionPolicy' - Write-LogMessage -API 'Standards' -tenant $tenant -message "User Submission policy set to $status." -sev Info - } else { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-ReportSubmissionPolicy' -cmdParams @{ EnableReportToMicrosoft = $status; Identity = $($Policy.Identity); } - Write-LogMessage -API 'Standards' -tenant $tenant -message "User Submission policy set to $status." -sev Info + $Status = if ($Settings.state -eq 'enable') { $true } else { $false } + + # If policy is set correctly, log and skip setting the policy + if ($Policy.EnableReportToMicrosoft -eq $status) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "User Submission policy is already set to $status." -sev Info + } else { + if ($Settings.state -eq 'enable') { + # Policy is not set correctly, enable the policy. Create new policy if it does not exist + try { + if ($Policy.length -eq 0) { + New-ExoRequest -tenantid $Tenant -cmdlet 'New-ReportSubmissionPolicy' -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $tenant -message "User Submission policy set to $status." -sev Info + } else { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-ReportSubmissionPolicy' -cmdParams @{ EnableReportToMicrosoft = $status; Identity = $($Policy.Identity); } -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $tenant -message "User Submission policy set to $status." -sev Info + } + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set User Submission policy to $status. Error: $($_.exception.message)" -sev Error } - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set User Submission policy to $status. Error: $($_.exception.message)" -sev Error - } - } else { - $status = $false - try { - $Policy = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionPolicy' - if ($Policy.length -eq 0) { - Write-LogMessage -API 'Standards' -tenant $tenant -message "User Submission policy set to $status." -sev Info - } else { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-ReportSubmissionPolicy' -cmdParams @{ EnableReportToMicrosoft = $status; Identity = $($Policy.Identity); EnableThirdPartyAddress = $status; ReportJunkToCustomizedAddress = $status; ReportNotJunkToCustomizedAddress = $status; ReportPhishToCustomizedAddress = $status; } - Write-LogMessage -API 'Standards' -tenant $tenant -message "User Submission policy set to $status." -sev Info + } else { + # Policy is not set correctly, disable the policy. + try { + if ($Policy.length -eq 0) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "User Submission policy set to $status." -sev Info + } else { + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-ReportSubmissionPolicy' -cmdParams @{ EnableReportToMicrosoft = $status; Identity = $($Policy.Identity); EnableThirdPartyAddress = $status; ReportJunkToCustomizedAddress = $status; ReportNotJunkToCustomizedAddress = $status; ReportPhishToCustomizedAddress = $status; } -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $tenant -message "User Submission policy set to $status." -sev Info + } + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set User Submission policy to $status. Error: $($_.exception.message)" -sev Error } - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Could not set User Submission policy to $status. Error: $($_.exception.message)" -sev Error } } } + if ($Settings.alert) { if ($Policy.length -eq 0) { @@ -50,6 +54,7 @@ function Invoke-CIPPStandardUserSubmissions { } } } + if ($Settings.report) { if ($Policy.length -eq 0) { Add-CIPPBPAField -FieldName 'UserSubmissionPolicy' -FieldValue $false -StoreAs bool -Tenant $tenant @@ -57,4 +62,4 @@ function Invoke-CIPPStandardUserSubmissions { Add-CIPPBPAField -FieldName 'UserSubmissionPolicy' -FieldValue [bool]$Policy.EnableReportToMicrosoft -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 8455c93208f5..883c23c3ca5a 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 @@ -4,26 +4,27 @@ function Invoke-CIPPStandardallowOAuthTokens { Internal #> param($Tenant, $Settings) + $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.remediate) { - Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'softwareOath' -Enabled $true + if ($State) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Software OTP/oAuth tokens is already enabled.' -sev Info + } else { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'softwareOath' -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/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 ($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) { - Add-CIPPBPAField -FieldName 'softwareOath' -FieldValue [bool]$State -StoreAs bool -Tenant $tenant - } + if ($Settings.report) { + Add-CIPPBPAField -FieldName 'softwareOath' -FieldValue [bool]$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 cca5dc8ae8e2..8f3f6b456010 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 @@ -4,25 +4,26 @@ 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) { - Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'MicrosoftAuthenticator' -Enabled $true -MicrosoftAuthenticatorSoftwareOathEnabled $true + if ($CurrentInfo.isSoftwareOathEnabled) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'MS authenticator OTP/oAuth tokens is already enabled.' -sev Info + } else { + Set-CIPPAuthenticationPolicy -Tenant $tenant -APIName 'Standards' -AuthenticationMethodId 'MicrosoftAuthenticator' -Enabled $true -MicrosoftAuthenticatorSoftwareOathEnabled $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/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 diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 index 41cd85e43c53..b273dbcb9fdd 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 @@ -4,27 +4,34 @@ function Invoke-CIPPStandarddisableMacSync { Internal #> param($Tenant, $Settings) - If ($Settings.remediate) { - + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true - try { - $body = '{"isMacSyncAppEnabled": false}' - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled Mac OneDrive Sync' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Mac OneDrive Sync: $($_.exception.message)" -sev Error + If ($Settings.remediate) { + + if ($CurrentInfo.isMacSyncAppEnabled -eq $true) { + try { + $body = '{"isMacSyncAppEnabled": false}' + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled Mac OneDrive Sync' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Mac OneDrive Sync: $($_.exception.message)" -sev Error + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Mac OneDrive Sync is already disabled' -sev Info } } + if ($Settings.alert) { - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true if ($CurrentInfo.isMacSyncAppEnabled -eq $false) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Mac OneDrive Sync is disabled' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Mac OneDrive Sync is not disabled' -sev Alert } } + if ($Settings.report) { + $CurrentInfo.isMacSyncAppEnabled = -not $CurrentInfo.isMacSyncAppEnabled Add-CIPPBPAField -FieldName 'MacSync' -FieldValue [bool]$CurrentInfo.isMacSyncAppEnabled -StoreAs bool -Tenant $tenant } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 index 6d2ece082f77..713fe5e8ff41 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 @@ -7,13 +7,17 @@ function Invoke-CIPPStandardintuneDeviceReg { $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant If ($Settings.remediate) { - try { - $PreviousSetting.userDeviceQuota = $Settings.max - $Newbody = ConvertTo-Json -Compress -InputObject $PreviousSetting - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -Type PUT -Body $NewBody -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message "Set user device quota to $($Settings.max)" -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set user device quota to $($Settings.max) : $($_.exception.message)" -sev Error + if ($PreviousSetting.userDeviceQuota -eq $Settings.max) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "User device quota is already set to $($Settings.max)" -sev Info + } else { + try { + $PreviousSetting.userDeviceQuota = $Settings.max + $Newbody = ConvertTo-Json -Compress -InputObject $PreviousSetting + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -Type PUT -Body $NewBody -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message "Set user device quota to $($Settings.max)" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set user device quota to $($Settings.max) : $($_.exception.message)" -sev Error + } } } if ($Settings.alert) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 index 502be8862b12..8ab48cf6efb9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 @@ -4,27 +4,34 @@ function Invoke-CIPPStandardintuneDeviceRetirementDays { Internal #> param($Tenant, $Settings) + $CurrentInfo = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings' -tenantid $Tenant) + If ($Settings.remediate) { - try { - $body = @{ DeviceInactivityBeforeRetirementInDays = $Settings.days } | ConvertTo-Json - (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings' -Type PATCH -Body $body -ContentType 'application/json') - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled DeviceInactivityBeforeRetirementInDays.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable DeviceInactivityBeforeRetirementInDays. Error: $($_.exception.message)" -sev Error + if ($CurrentInfo.DeviceInactivityBeforeRetirementInDays -eq $Settings.days) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "DeviceInactivityBeforeRetirementInDays for $($Settings.days) days is already enabled." -sev Info + } else { + try { + $body = @{ DeviceInactivityBeforeRetirementInDays = $Settings.days } | ConvertTo-Json + (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings' -Type PATCH -Body $body -ContentType 'application/json') + Write-LogMessage -API 'Standards' -tenant $tenant -message "Enabled DeviceInactivityBeforeRetirementInDays for $($Settings.days) days." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable DeviceInactivityBeforeRetirementInDays. Error: $($_.exception.message)" -sev Error + } } } + if ($Settings.alert) { - $CurrentInfo = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings' -tenantid $Tenant) if ($CurrentInfo.DeviceInactivityBeforeRetirementInDays -eq $Settings.days) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'DeviceInactivityBeforeRetirementInDays is enabled.' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'DeviceInactivityBeforeRetirementInDays is not enabled.' -sev Alert } } + if ($Settings.report) { - if ($PreviousSetting.DeviceInactivityBeforeRetirementInDays -eq $Settings.days) { $UserQuota = $true } else { $UserQuota = $false } + $UserQuota = if ($PreviousSetting.DeviceInactivityBeforeRetirementInDays -eq $Settings.days) { $true } else { $false } Add-CIPPBPAField -FieldName 'intuneDeviceRetirementDays' -FieldValue [bool]$UserQuota -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 index 30a25866768e..f2cc643746e9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 @@ -7,26 +7,32 @@ function Invoke-CIPPStandardintuneRequireMFA { $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant If ($Settings.remediate) { - try { - $NewSetting = $PreviousSetting - $NewSetting.multiFactorAuthConfiguration = '1' - $Newbody = ConvertTo-Json -Compress -InputObject $NewSetting - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -Type PUT -Body $NewBody -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Set required to use MFA when joining Intune Devices' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set require to use MFA when joining Intune Devices: $($_.exception.message)" -sev Error + if ($PreviousSetting.multiFactorAuthConfiguration -eq 'required') { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Require to use MFA when joining/registering Entra Devices is already enabled.' -sev Info + } else { + try { + $NewSetting = $PreviousSetting + $NewSetting.multiFactorAuthConfiguration = '1' + $Newbody = ConvertTo-Json -Compress -InputObject $NewSetting + New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -Type PUT -Body $NewBody -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Set required to use MFA when joining/registering Entra Devices' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set require to use MFA when joining/registering Entra Devices: $($_.exception.message)" -sev Error + } } } + if ($Settings.alert) { if ($PreviousSetting.multiFactorAuthConfiguration -eq 'required') { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Require to use MFA when joining Intune Devices is enabled.' -sev Info + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Require to use MFA when joining/registering Entra Devices is enabled.' -sev Info } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Require to use MFA when joining Intune Devices is not enabled.' -sev Alert + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Require to use MFA when joining/registering Entra Devices is not enabled.' -sev Alert } } + if ($Settings.report) { - if ($PreviousSetting.multiFactorAuthConfiguration -eq 'required') { $UserQuota = $true } else { $UserQuota = $false } - Add-CIPPBPAField -FieldName 'intuneRequireMFA' -FieldValue [bool]$UserQuota -StoreAs bool -Tenant $tenant + $RequireMFA = if ($PreviousSetting.multiFactorAuthConfiguration -eq 'required') { $true } else { $false } + Add-CIPPBPAField -FieldName 'intuneRequireMFA' -FieldValue [bool]$RequireMFA -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 index 7ae60c772a41..5732995e01ca 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 @@ -7,24 +7,29 @@ function Invoke-CIPPStandardlaps { $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant If ($Settings.remediate) { - - try { - $previoussetting.localadminpassword.isEnabled = $true - $Newbody = ConvertTo-Json -Compress -InputObject $PreviousSetting - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -Type PUT -Body $newBody -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'LAPS has been enabled.' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set LAPS: $($_.exception.message)" -sev Error + if ($PreviousSetting.localadminpassword.isEnabled) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'LAPS is already enabled.' -sev Info + } else { + try { + $previoussetting.localadminpassword.isEnabled = $true + $Newbody = ConvertTo-Json -Compress -InputObject $PreviousSetting + New-GraphPostRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -Type PUT -Body $NewBody -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'LAPS has been enabled.' -sev Info + } catch { + $previoussetting.localadminpassword.isEnabled = $false + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to set LAPS: $($_.exception.message)" -sev Error + } } } if ($Settings.alert) { - if ($PreviousSetting.localadminpassword.isEnabled -eq $true) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'LAPS is enabled.' -sev Info + if ($PreviousSetting.localadminpassword.isEnabled) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'LAPS is enabled.' -sev Info } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'LAPS is not enabled.' -sev Alert + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'LAPS is not enabled.' -sev Alert } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'laps' -FieldValue [bool]$PreviousSetting.localadminpassword.isEnabled -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 index 0003bddc0be4..ee36e115f5b0 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 @@ -5,23 +5,35 @@ function Invoke-CIPPStandardsharingCapability { #> param($Tenant, $Settings) $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + # $CurrentInfo.sharingCapability.GetType() + $Settings.Level + $CurrentInfo.sharingCapability If ($Settings.remediate) { - try { - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body "{`"sharingCapability`":`"$($Settings.Level)`"}" -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message "Set sharing level to $($Settings.Level)" -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set sharing level to $($Settings.Level): $($_.exception.message)" -sev Error + + if ($CurrentInfo.sharingCapability -eq $Settings.Level) { + Write-Host "Sharing level is already set to $($Settings.Level)" + Write-LogMessage -API 'Standards' -tenant $tenant -message "Sharing level is already set to $($Settings.Level)" -sev Info + } else { + Write-Host "Setting sharing level to $($Settings.Level) from $($CurrentInfo.sharingCapability)" + try { + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body "{`"sharingCapability`":`"$($Settings.Level)`"}" -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message "Set sharing level to $($Settings.Level) from $($CurrentInfo.sharingCapability)" -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set sharing level to $($Settings.Level): $($_.exception.message)" -sev Error + } } } + if ($Settings.alert) { - if ($CurrentInfo.sharingCapability -eq $Settings.level) { + if ($CurrentInfo.sharingCapability -eq $Settings.Level) { Write-LogMessage -API 'Standards' -tenant $tenant -message "Sharing level is set to $($Settings.Level)" -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message "Sharing level is not set to $($Settings.Level)" -sev Alert } } + if ($Settings.report) { Add-CIPPBPAField -FieldName 'sharingCapability' -FieldValue $CurrentInfo.sharingCapability -StoreAs string -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 index 42261e2b2d86..185fd53610f0 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 @@ -4,30 +4,33 @@ function Invoke-CIPPStandardunmanagedSync { Internal #> param($Tenant, $Settings) + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + If ($Settings.remediate) { - try { - $body = '{"isUnmanagedSyncAppForTenantRestricted": false}' - New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Disabled Sync for unmanaged devices' -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Sync for unmanaged devices: $($_.exception.message)" -sev Error + + if ($CurrentInfo.isUnmanagedSyncAppForTenantRestricted -eq $false) { + try { + #$body = '{"isUnmanagedSyncAppForTenantRestricted": true}' + #$null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -AsApp $true -Type patch -Body $body -ContentType 'application/json' + Write-LogMessage -API 'Standards' -tenant $tenant -message 'The unmanaged Sync standard has been temporarily disabled.' -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Sync for unmanaged devices: $($_.exception.message)" -sev Error + } + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Sync for unmanaged devices is already disabled' -sev Info } } + if ($Settings.alert) { - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true - if ($CurrentInfo.isUnmanagedSyncAppForTenantRestricted -eq $false) { + if ($CurrentInfo.isUnmanagedSyncAppForTenantRestricted -eq $true) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Sync for unmanaged devices is disabled' -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Sync for unmanaged devices is not disabled' -sev Alert } } + if ($Settings.report) { - if ($CurrentInfo.isUnmanagedSyncAppForTenantRestricted -eq $false) { - $CurrentInfo.isUnmanagedSyncAppForTenantRestricted = $true - } else { - $CurrentInfo.isUnmanagedSyncAppForTenantRestricted = $false - } Add-CIPPBPAField -FieldName 'unmanagedSync' -FieldValue [bool]$CurrentInfo.isUnmanagedSyncAppForTenantRestricted -StoreAs bool -Tenant $tenant } } diff --git a/Modules/CIPPCore/Public/Test-CIPPAccessPermissions.ps1 b/Modules/CIPPCore/Public/Test-CIPPAccessPermissions.ps1 index 971753f68d40..ac2629135250 100644 --- a/Modules/CIPPCore/Public/Test-CIPPAccessPermissions.ps1 +++ b/Modules/CIPPCore/Public/Test-CIPPAccessPermissions.ps1 @@ -2,7 +2,7 @@ function Test-CIPPAccessPermissions { [CmdletBinding()] param ( $TenantFilter, - $APIName = "Access Check", + $APIName = 'Access Check', $ExecutingUser ) Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Started permissions check' -Sev 'Debug' @@ -20,13 +20,13 @@ function Test-CIPPAccessPermissions { TenantId = '' UserPrincipalName = '' } - Write-Host "Setting success to true by default." + Write-Host 'Setting success to true by default.' $Success = $true try { Set-Location (Get-Item $PSScriptRoot).FullName $ExpectedPermissions = Get-Content '.\SAMManifest.json' | ConvertFrom-Json - - $GraphToken = Get-GraphToken -returnRefresh $true + Get-CIPPAuthentication + $GraphToken = Get-GraphToken -returnRefresh $true -SkipCache $true if ($GraphToken) { $GraphPermissions = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/myorganization/applications?`$filter=appId eq '$env:ApplicationID'" -NoAuthCheck $true } @@ -38,7 +38,7 @@ function Test-CIPPAccessPermissions { $KV = $ENV:WEBSITE_DEPLOYMENT_ID $KeyVaultRefresh = Get-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -AsPlainText if ($ENV:RefreshToken -ne $KeyVaultRefresh) { - Write-Host "Setting success to false due to nonmaching token." + Write-Host 'Setting success to false due to nonmaching token.' $Success = $false $Messages.Add('Your refresh token does not match key vault, clear your cache or wait 30 minutes.') | Out-Null @@ -47,43 +47,38 @@ function Test-CIPPAccessPermissions { Href = 'https://docs.cipp.app/setup/installation/cleartokencache' } ) | Out-Null - } - else { + } else { $Messages.Add('Your refresh token matches key vault.') | Out-Null } - } - catch { + } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Key vault exception: $($_) " -Sev 'Error' } } try { $AccessTokenDetails = Read-JwtAccessDetails -Token $GraphToken.access_token -erroraction SilentlyContinue - } - catch { + } catch { $AccessTokenDetails = [PSCustomObject]@{ Name = '' AuthMethods = @() } Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Token exception: $($_) " -Sev 'Error' $Success = $false - Write-Host "Setting success to false due to not able to decode token." + Write-Host 'Setting success to false due to not able to decode token.' } if ($AccessTokenDetails.Name -eq '') { $Messages.Add('Your refresh token is invalid, check for line breaks or missing characters.') | Out-Null - Write-Host "Setting success to false invalid token." + Write-Host 'Setting success to false invalid token.' $Success = $false - } - else { + } else { if ($AccessTokenDetails.AuthMethods -contains 'mfa') { $Messages.Add('Your access token contains the MFA claim.') | Out-Null - } - else { + } else { $Messages.Add('Your access token does not contain the MFA claim, Refresh your SAM tokens.') | Out-Null - Write-Host "Setting success to False due to invalid list of claims." + Write-Host 'Setting success to False due to invalid list of claims.' $Success = $false $Links.Add([PSCustomObject]@{ @@ -107,16 +102,14 @@ function Test-CIPPAccessPermissions { Href = 'https://docs.cipp.app/setup/installation/permissions' } ) | Out-Null - } - else { + } else { $Messages.Add('Your Secure Application Model has all required permissions') | Out-Null } - } - catch { + } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Permissions check failed: $($_) " -Sev 'Error' $Messages.Add("We could not connect to the API to retrieve the permissions. There might be a problem with the secure application model configuration. The returned error is: $(Get-NormalizedError -message $_)") | Out-Null - Write-Host "Setting success to False due to not being able to connect." + Write-Host 'Setting success to False due to not being able to connect.' $Success = $false } diff --git a/Modules/CIPPCore/Public/Test-CIPPAccessTenant.ps1 b/Modules/CIPPCore/Public/Test-CIPPAccessTenant.ps1 index 6e9d85fd7a74..014218b3cc16 100644 --- a/Modules/CIPPCore/Public/Test-CIPPAccessTenant.ps1 +++ b/Modules/CIPPCore/Public/Test-CIPPAccessTenant.ps1 @@ -25,19 +25,11 @@ function Test-CIPPAccessTenant { $TenantIds = foreach ($Tenant in $Tenants) { ($TenantList | Where-Object { $_.defaultDomainName -eq $Tenant }).customerId } - try { - $MyRoles = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/myRoles?`$filter=tenantId in ('$($TenantIds -join "','")')" - } catch { - $MyRoles = @() - $AddedText = 'but could not retrieve GDAP roles from Lighthouse API' - } + $results = foreach ($tenant in $Tenants) { $AddedText = '' try { $TenantId = ($TenantList | Where-Object { $_.defaultDomainName -eq $tenant }).customerId - $Assignments = ($MyRoles | Where-Object { $_.tenantId -eq $TenantId }).assignments - $SAMUserRoles = $Assignments.roles - $BulkRequests = $ExpectedRoles | ForEach-Object { @( @{ id = "roleManagement_$($_.id)" @@ -49,10 +41,12 @@ function Test-CIPPAccessTenant { $GDAPRolesGraph = New-GraphBulkRequest -tenantid $tenant -Requests $BulkRequests $GDAPRoles = [System.Collections.Generic.List[object]]::new() $MissingRoles = [System.Collections.Generic.List[object]]::new() + + #Write-Host ($GDAPRolesGraph.body.value | ConvertTo-Json -Depth 10) foreach ($RoleId in $ExpectedRoles) { $GraphRole = $GDAPRolesGraph.body.value | Where-Object -Property roleDefinitionId -EQ $RoleId.Id $Role = $GraphRole.principal | Where-Object -Property organizationId -EQ $ENV:tenantid - $SAMRole = $SAMUserRoles | Where-Object -Property templateId -EQ $RoleId.Id + if (!$Role) { $MissingRoles.Add( [PSCustomObject]@{ @@ -62,16 +56,10 @@ function Test-CIPPAccessTenant { ) $AddedText = 'but missing GDAP roles' } else { - $GDAPRoles.Add([PSCustomObject]$RoleId) - } - if (!$SAMRole) { - $MissingRoles.Add( - [PSCustomObject]@{ - Name = $RoleId.Name - Type = 'SAM User' - } - ) - $AddedText = 'but missing GDAP roles' + $GDAPRoles.Add([PSCustomObject]@{ + Role = $RoleId.Name + Group = $Role.displayName + }) } } if (!($MissingRoles | Measure-Object).Count -gt 0) { @@ -82,7 +70,6 @@ function Test-CIPPAccessTenant { Status = "Successfully connected $($AddedText)" GDAPRoles = $GDAPRoles MissingRoles = $MissingRoles - SAMUserRoles = $SAMUserRoles } Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message 'Tenant access check executed successfully' -Sev 'Info' diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 7e376917303d..02651d9a2a3f 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -19,16 +19,108 @@ function Receive-CippHttpTrigger { function Receive-CippQueueTrigger { Param($QueueItem, $TriggerMetadata) + Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName + $Start = (Get-Date).ToUniversalTime() $APIName = $TriggerMetadata.FunctionName + Write-Host "#### Running $APINAME" Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName $FunctionName = 'Push-{0}' -f $APIName $QueueTrigger = @{ QueueItem = $QueueItem TriggerMetadata = $TriggerMetadata } + try { + & $FunctionName @QueueTrigger + } catch { + $ErrorMsg = $_.Exception.Message + } + + $End = (Get-Date).ToUniversalTime() + + $Stats = @{ + FunctionType = 'Queue' + Entity = $QueueItem + Start = $Start + End = $End + ErrorMsg = $ErrorMsg + } + Write-Information '####### Adding stats' + Write-CippFunctionStats @Stats +} + +function Receive-CippOrchestrationTrigger { + param($Context) + Write-Host 'Orchestrator started' + try { + if (Test-Json -Json $Context.Input) { + $OrchestratorInput = $Context.Input | ConvertFrom-Json + } else { + $OrchestratorInput = $Context.Input + } + + $DurableRetryOptions = @{ + FirstRetryInterval = (New-TimeSpan -Seconds 5) + MaxNumberOfAttempts = if ($OrchestratorInput.MaxAttempts) { $OrchestratorInput.MaxAttempts } else { 1 } + BackoffCoefficient = 2 + } + #Write-Host ($OrchestratorInput | ConvertTo-Json -Depth 10) + $RetryOptions = New-DurableRetryOptions @DurableRetryOptions + + if ($Context.IsReplaying -ne $true -and $OrchestratorInput.SkipLog -ne $true) { + Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $OrchestratorInput.TenantFilter -message "Started $($OrchestratorInput.OrchestratorName)" -sev info + } + + if (!$OrchestratorInput.Batch -or ($OrchestratorInput.Batch | Measure-Object).Count -eq 0) { + $Batch = (Invoke-ActivityFunction -FunctionName 'CIPPActivityFunction' -Input $OrchestratorInput.QueueFunction -ErrorAction Stop) + } else { + $Batch = $OrchestratorInput.Batch + } + + if (($Batch | Measure-Object).Count -gt 0) { + $Tasks = foreach ($Item in $Batch) { + Invoke-DurableActivity -FunctionName 'CIPPActivityFunction' -Input $Item -NoWait -RetryOptions $RetryOptions -ErrorAction Stop + } + $null = Wait-ActivityFunction -Task $Tasks + } + + if ($Context.IsReplaying -ne $true -and $OrchestratorInput.SkipLog -ne $true) { + Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $tenant -message "Finished $($OrchestratorInput.OrchestratorName)" -sev Info + } + } catch { + Write-Host "Orchestrator error $($_.Exception.Message)" + } +} + +function Receive-CippActivityTrigger { + Param($Item) + + $Start = (Get-Date).ToUniversalTime() + Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName + + if ($Item.FunctionName) { + $FunctionName = 'Push-{0}' -f $Item.FunctionName + try { + & $FunctionName -Item $Item + } catch { + $ErrorMsg = $_.Exception.Message + } + } else { + $ErrorMsg = 'Function not provided' + } + + $End = (Get-Date).ToUniversalTime() + + $Stats = @{ + FunctionType = 'Durable' + Entity = $Item + Start = $Start + End = $End + ErrorMsg = $ErrorMsg + } - & $FunctionName @QueueTrigger + #Write-Information '####### Adding stats' + Write-CippFunctionStats @Stats } -Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippQueueTrigger') +Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippQueueTrigger', 'Receive-CippOrchestrationTrigger', 'Receive-CippActivityTrigger') diff --git a/Modules/CippExtensions/CippExtensions.psd1 b/Modules/CippExtensions/CippExtensions.psd1 index 437cc32f8661..8a30d23c6ef1 100644 Binary files a/Modules/CippExtensions/CippExtensions.psd1 and b/Modules/CippExtensions/CippExtensions.psd1 differ diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneExtensionScheduler.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneExtensionScheduler.ps1 new file mode 100644 index 000000000000..02ac73202eb6 --- /dev/null +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneExtensionScheduler.ps1 @@ -0,0 +1,109 @@ +function Invoke-NinjaOneExtensionScheduler { + $Table = Get-CIPPTable -TableName NinjaOneSettings + $Settings = (Get-AzDataTableEntity @Table) + $TimeSetting = ($Settings | Where-Object { $_.RowKey -eq 'NinjaSyncTime' }).SettingValue + + + if (($TimeSetting | Measure-Object).count -ne 1) { + [int]$TimeSetting = Get-Random -Minimum 1 -Maximum 95 + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaSyncTime' + 'SettingValue' = $TimeSetting + } + Add-AzDataTableEntity @Table -Entity $AddObject -Force + } + + Write-Host "Ninja Time Setting: $TimeSetting" + + $LastRunTime = Get-Date(($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue) + + Write-Host "Last Run: $LastRunTime" + + $CurrentTime = Get-Date + $CurrentInterval = ($CurrentTime.Hour * 4) + [math]::Floor($CurrentTime.Minute / 15) + + Write-Host "Current Interval: $CurrentInterval" + + $CIPPMapping = Get-CIPPTable -TableName CippMapping + $Filter = "PartitionKey eq 'NinjaOrgsMapping'" + $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } + + if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $TimeSetting -eq $CurrentInterval) { + Write-Host 'Executing' + $Batch = foreach ($Tenant in $TenantsToProcess | Sort-Object lastEndTime) { + <#Push-OutputBinding -Name NinjaProcess -Value @{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + } + Start-Sleep -Seconds 1#> + [PSCustomObject]@{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + 'FunctionName' = 'NinjaOneQueue' + } + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } + + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaLastRunTime' + 'SettingValue' = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffK') + } + Add-AzDataTableEntity @Table -Entity $AddObject -Force + + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "NinjaOne Daily Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' + + } else { + if ($LastRunTime -lt (Get-Date).AddMinutes(-90)) { + $TenantsToProcess | ForEach-Object { + if ($Null -ne $_.lastEndTime -and $_.lastEndTime -ne '') { + $_.lastEndTime = (Get-Date($_.lastEndTime)) + } else { + $_ | Add-Member -NotePropertyName lastEndTime -NotePropertyValue $Null -Force + } + + if ($Null -ne $_.lastStartTime -and $_.lastStartTime -ne '') { + $_.lastStartTime = (Get-Date($_.lastStartTime)) + } else { + $_ | Add-Member -NotePropertyName lastStartTime -NotePropertyValue $Null -Force + } + } + $CatchupTenants = $TenantsToProcess | Where-Object { (((($_.lastEndTime -eq $Null) -or ($_.lastStartTime -gt $_.lastEndTime)) -and ($_.lastStartTime -lt (Get-Date).AddMinutes(-30)))) -or ($_.lastStartTime -lt $LastRunTime) } + $Batch = foreach ($Tenant in $CatchupTenants) { + #Push-OutputBinding -Name NinjaProcess -Value @{ + # 'NinjaAction' = 'SyncTenant' + # 'MappedTenant' = $Tenant + #} + [PSCustomObject]@{ + NinjaAction = 'SyncTenant' + MappedTenant = $Tenant + FunctionName = 'NinjaOneQueue' + } + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } + + if (($CatchupTenants | Measure-Object).count -gt 0) { + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "NinjaOne Synchronization Catchup Queued for $(($CatchupTenants | Measure-Object).count) Tenants" -Sev 'Info' + } + + } + + } +} \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 index 0590894a8fec..f351d43ee039 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 @@ -3,30 +3,30 @@ function Invoke-NinjaOneOrgMapping { [System.Collections.Generic.List[PSCustomObject]]$MatchedM365Tenants = @() [System.Collections.Generic.List[PSCustomObject]]$MatchedNinjaOrgs = @() - $ExcludeSerials = @("0", "SystemSerialNumber", "To Be Filled By O.E.M.", "System Serial Number", "0123456789", "123456789", "............") + $ExcludeSerials = @('0', 'SystemSerialNumber', 'To Be Filled By O.E.M.', 'System Serial Number', '0123456789', '123456789', '............') $CIPPMapping = Get-CIPPTable -TableName CippMapping - + #Get available mappings $Mappings = [pscustomobject]@{} $Filter = "PartitionKey eq 'NinjaOrgsMapping'" Get-AzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.NinjaOneName)"; value = "$($_.NinjaOne)" } } - + #Get Available Tenants $Tenants = Get-Tenants #Get available Ninja clients $Table = Get-CIPPTable -TableName Extensionsconfig $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json).NinjaOne - + $Token = Get-NinjaOneToken -configuration $Configuration - + # Fetch Ninja Orgs $After = 0 $PageSize = 1000 $NinjaOrgs = do { - $Result = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organizations?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + $Result = (Invoke-WebRequest -Uri "https://$($Configuration.Instance)/api/v2/organizations?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -Depth 100 $Result $ResultCount = ($Result.id | Measure-Object -Maximum) $After = $ResultCount.maximum @@ -51,11 +51,11 @@ function Invoke-NinjaOneOrgMapping { $After = 0 $PageSize = 1000 $NinjaDevicesRaw = do { - $Result = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/devices-detailed?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + $Result = (Invoke-WebRequest -Uri "https://$($Configuration.Instance)/api/v2/devices-detailed?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -Depth 100 $Result $ResultCount = ($Result.id | Measure-Object -Maximum) $After = $ResultCount.maximum - + } while ($ResultCount.count -eq $PageSize) @@ -71,12 +71,12 @@ function Invoke-NinjaOneOrgMapping { } # Remove any devices with duplicate serials - $ParsedNinjaDevices = $NinjaDevices | Where-Object { $_.Serial -in (($NinjaDevices | Group-Object Serial | where-object { $_.count -eq 1 }).name) } + $ParsedNinjaDevices = $NinjaDevices | Where-Object { $_.Serial -in (($NinjaDevices | Group-Object Serial | Where-Object { $_.count -eq 1 }).name) } # First lets match on Org names foreach ($Tenant in $Tenants | Where-Object { $_.customerId -notin $MatchedM365Tenants.customerId }) { - $MatchedOrg = $NinjaOrgs | where-object { $_.name -eq $Tenant.displayName } + $MatchedOrg = $NinjaOrgs | Where-Object { $_.name -eq $Tenant.displayName } if (($MatchedOrg | Measure-Object).count -eq 1) { $MatchedM365Tenants.add($Tenant) $MatchedNinjaOrgs.add($MatchedOrg) @@ -87,23 +87,34 @@ function Invoke-NinjaOneOrgMapping { 'NinjaOneName' = "$($MatchedOrg.name)" } Add-AzDataTableEntity @CIPPMapping -Entity $AddObject -Force - Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "Added mapping from Organization name match for $($Tenant.customerId). to $($($MatchedOrg.name))" -Sev 'Info' + Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "Added mapping from Organization name match for $($Tenant.customerId). to $($($MatchedOrg.name))" -Sev 'Info' } } # Now Let match on remaining Tenants - Foreach ($Tenant in $Tenants | Where-Object { $_.customerId -notin $MatchedM365Tenants.customerId }) { - - Push-OutputBinding -Name NinjaProcess -Value @{ + $Batch = Foreach ($Tenant in $Tenants | Where-Object { $_.customerId -notin $MatchedM365Tenants.customerId }) { + <#Push-OutputBinding -Name NinjaProcess -Value @{ + 'NinjaAction' = 'AutoMapTenant' + 'M365Tenant' = $Tenant + 'NinjaOrgs' = $NinjaOrgs | Where-Object { $_.id -notin $MatchedNinjaOrgs } + 'NinjaDevices' = $ParsedNinjaDevices + }#> + [PSCustomObject]@{ 'NinjaAction' = 'AutoMapTenant' 'M365Tenant' = $Tenant 'NinjaOrgs' = $NinjaOrgs | Where-Object { $_.id -notin $MatchedNinjaOrgs } 'NinjaDevices' = $ParsedNinjaDevices + 'FunctionName' = 'NinjaOneQueue' } - - Start-Sleep -Seconds 1 - + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" } } - \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneSync.ps1 index a168d291575b..df7d6f67111a 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneSync.ps1 @@ -7,12 +7,26 @@ function Invoke-NinjaOneSync { $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } - foreach ($Tenant in $TenantsToProcess) { - Push-OutputBinding -Name NinjaProcess -Value @{ + $Batch = foreach ($Tenant in $TenantsToProcess) { + <#Push-OutputBinding -Name NinjaProcess -Value @{ 'NinjaAction' = 'SyncTenant' 'MappedTenant' = $Tenant } - Start-Sleep -Seconds 1 + Start-Sleep -Seconds 1#> + [PSCustomObject]@{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + 'FunctionName' = 'NinjaOneQueue' + } + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" } $AddObject = @{ @@ -23,10 +37,9 @@ function Invoke-NinjaOneSync { Add-AzDataTableEntity @Table -Entity $AddObject -Force - Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' + Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' } catch { Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start NinjaOne Sync $($_.Exception.Message)" -sev Error } - + } - \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 05b4ecbcaaac..ebf491cec05a 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -7,11 +7,9 @@ function Invoke-NinjaOneTenantSync { $StartQueueTime = Get-Date Write-Host "$(Get-Date) - Starting NinjaOne Sync" - # Stagger start - Start-Sleep -Milliseconds (Get-Random -Minimum 0 -Maximum 5001) - + # Stagger start # Check Global Rate Limiting - $CurrentMap = Get-ExtensionRateLimit -ExtensionName 'NinjaOne' -ExtensionPartitionKey 'NinjaOrgsMapping' -RateLimit 5 -WaitTime 60 + $CurrentMap = Get-ExtensionRateLimit -ExtensionName 'NinjaOne' -ExtensionPartitionKey 'NinjaOrgsMapping' -RateLimit 5 -WaitTime 10 $StartTime = Get-Date @@ -653,7 +651,7 @@ function Invoke-NinjaOneTenantSync { M365ID = $device.id } $DeviceMap.Add($DeviceMapItem) - Add-CIPPAzDataTableEntity @DeviceMapTable -Entity $DeviceMapItem + Add-CIPPAzDataTableEntity @DeviceMapTable -Entity $DeviceMapItem -Force } elseif ($MappedDevice.NinjaOneID -ne $MatchedNinjaDevice.id) { $MappedDevice.NinjaOneID = $MatchedNinjaDevice.id @@ -742,7 +740,7 @@ function Invoke-NinjaOneTenantSync { PartitionKey = $Customer.CustomerId RowKey = $device.AzureADDeviceId RawDevice = "$($ParsedDevice | ConvertTo-Json -Depth 100 -Compress)" - } + } -Force $ParsedDevices.add($ParsedDevice) @@ -1264,7 +1262,7 @@ function Invoke-NinjaOneTenantSync { } - Add-CIPPAzDataTableEntity @UsersTable -Entity $ParsedUser + Add-CIPPAzDataTableEntity @UsersTable -Entity $ParsedUser -Force $ParsedUsers.add($ParsedUser) @@ -1330,7 +1328,7 @@ function Invoke-NinjaOneTenantSync { } | ConvertTo-Json -Depth 100)" } $NinjaUserUpdates.Add($UpdateObject) - Add-CIPPAzDataTableEntity @UsersUpdateTable -Entity $UpdateObject + Add-CIPPAzDataTableEntity @UsersUpdateTable -Entity $UpdateObject -Force } else { $CreateObject = [PSCustomObject]@{ @@ -1345,7 +1343,7 @@ function Invoke-NinjaOneTenantSync { } | ConvertTo-Json -Depth 100)" } $NinjaUserCreation.Add($CreateObject) - Add-CIPPAzDataTableEntity @UsersUpdateTable -Entity $CreateObject + Add-CIPPAzDataTableEntity @UsersUpdateTable -Entity $CreateObject -Force } @@ -1399,7 +1397,7 @@ function Invoke-NinjaOneTenantSync { M365ID = $Field.value } $UsersMap.Add($UserMapItem) - Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem -Force } elseif ($MappedUser.NinjaOneID -ne $UserDoc.documentId) { $MappedUser.NinjaOneID = $UserDoc.documentId @@ -1476,7 +1474,7 @@ function Invoke-NinjaOneTenantSync { M365ID = $Field.value } $UsersMap.Add($UserMapItem) - Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem -Force } elseif ($MappedUser.NinjaOneID -ne $UserDoc.documentId) { $MappedUser.NinjaOneID = $UserDoc.documentId @@ -2039,7 +2037,7 @@ function Invoke-NinjaOneTenantSync { ### Fetch BPA Data $Table = get-cipptable 'cachebpav2' - $BPAData = (Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq '$($Customer.customerId)' and RowKey eq 'CIPP Best Practices v1.0 - Table view'") + $BPAData = (Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq '$($Customer.customerId)'") if ($Null -ne $BPAData.Timestamp) { ## BPA Data Widgets @@ -2062,7 +2060,11 @@ function Invoke-NinjaOneTenantSync { # Unused Licenses $WidgetData.add([PSCustomObject]@{ Value = $( - $BPAUnusedLicenses = (($BpaData.Unusedlicenses | ConvertFrom-Json).availableUnits | Measure-Object -Sum).sum + try { + $BPAUnusedLicenses = (($BpaData.Unusedlicenses | ConvertFrom-Json -ErrorAction SilentlyContinue).availableUnits | Measure-Object -Sum).sum + } catch { + $BPAUnusedLicenses = 'Failed to retrieve unused licenses' + } if ($BPAUnusedLicenses -ne 0) { $ResultColour = '#D53948' } else { @@ -2072,7 +2074,7 @@ function Invoke-NinjaOneTenantSync { ) Description = 'Unused Licenses' Colour = $ResultColour - Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" + Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.5+-+Tenant+view&tenantFilter=$($Customer.customerId)" }) @@ -2103,7 +2105,7 @@ function Invoke-NinjaOneTenantSync { ) Description = 'Password Never Expires' Colour = $ResultColour - Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" + Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.5+-+Tenant+view&tenantFilter=$($Customer.customerId)" }) # oAuth App Consent @@ -2308,7 +2310,8 @@ function Invoke-NinjaOneTenantSync { Get-NormalizedError -Message $_.ErrorDetails.Message } else { $_.Exception.message - } Write-Error "Failed NinjaOne Processing for $($Customer.displayName) Linenumber: $($_.InvocationInfo.ScriptLineNumber) Error: $Message" + } + Write-Error "Failed NinjaOne Processing for $($Customer.displayName) Linenumber: $($_.InvocationInfo.ScriptLineNumber) Error: $Message" Write-LogMessage -API 'NinjaOneSync' -user 'NinjaOneSync' -message "Failed NinjaOne Processing for $($Customer.displayName) Linenumber: $($_.InvocationInfo.ScriptLineNumber) Error: $Message" -Sev 'Error' $CurrentItem | Add-Member -NotePropertyName lastEndTime -NotePropertyValue ([string]$((Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ'))) -Force $CurrentItem | Add-Member -NotePropertyName lastStatus -NotePropertyValue 'Failed' -Force diff --git a/Modules/CippExtensions/NinjaOne/Push-NinjaOneQueue.ps1 b/Modules/CippExtensions/NinjaOne/Push-NinjaOneQueue.ps1 new file mode 100644 index 000000000000..09fe668a97b4 --- /dev/null +++ b/Modules/CippExtensions/NinjaOne/Push-NinjaOneQueue.ps1 @@ -0,0 +1,15 @@ +function Push-NinjaOneQueue { + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell NinjaOne queue trigger function processed work item: $($Item.NinjaAction)" + + Switch ($Item.NinjaAction) { + 'StartAutoMapping' { Invoke-NinjaOneOrgMapping } + 'AutoMapTenant' { Invoke-NinjaOneOrgMappingTenant -QueueItem $Item } + 'SyncTenant' { Invoke-NinjaOneTenantSync -QueueItem $Item } + 'SyncTenants' { Invoke-NinjaOneSync } + } + +} \ No newline at end of file diff --git a/Modules/CippExtensions/Private/Get-HaloMapping.ps1 b/Modules/CippExtensions/Private/Get-HaloMapping.ps1 index ad4fc1e88111..8391252ae065 100644 --- a/Modules/CippExtensions/Private/Get-HaloMapping.ps1 +++ b/Modules/CippExtensions/Private/Get-HaloMapping.ps1 @@ -10,7 +10,7 @@ function Get-HaloMapping { Get-CIPPAzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.HaloPSAName)"; value = "$($_.HaloPSA)" } } - $Tenants = Get-Tenants -IncludeAll + $Tenants = Get-Tenants -IncludeErrors $Table = Get-CIPPTable -TableName Extensionsconfig try { $Configuration = ((Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -ea stop).HaloPSA @@ -30,9 +30,9 @@ function Get-HaloMapping { } else { $_.Exception.message } - + Write-LogMessage -Message "Could not get HaloPSA Clients, error: $Message " -Level Error -tenant 'CIPP' -API 'HaloMapping' - $RawHaloClients = @(@{name = "Could not get HaloPSA Clients, error: $Message" }) + $RawHaloClients = @(@{name = "Could not get HaloPSA Clients, error: $Message" }) } $HaloClients = $RawHaloClients | ForEach-Object { [PSCustomObject]@{ diff --git a/Modules/CippExtensions/Public/Get-ExtensionRateLimit.ps1 b/Modules/CippExtensions/Public/Get-ExtensionRateLimit.ps1 index 069dec362229..2a0e718402f4 100644 --- a/Modules/CippExtensions/Public/Get-ExtensionRateLimit.ps1 +++ b/Modules/CippExtensions/Public/Get-ExtensionRateLimit.ps1 @@ -3,14 +3,14 @@ function Get-ExtensionRateLimit($ExtensionName, $ExtensionPartitionKey, $RateLim $MappingTable = Get-CIPPTable -TableName CippMapping $CurrentMap = (Get-CIPPAzDataTableEntity @MappingTable -Filter "PartitionKey eq '$ExtensionPartitionKey'") $CurrentMap | ForEach-Object { - if ($Null -ne $_.lastEndTime -and $_.lastEndTime -ne ''){ - $_.lastEndTime = (Get-Date($_.lastEndTime)) + if ($Null -ne $_.lastEndTime -and $_.lastEndTime -ne '') { + $_.lastEndTime = (Get-Date($_.lastEndTime)) } else { $_ | Add-Member -NotePropertyName lastEndTime -NotePropertyValue $Null -Force } if ($Null -ne $_.lastStartTime -and $_.lastStartTime -ne '') { - $_.lastStartTime = (Get-Date($_.lastStartTime)) + $_.lastStartTime = (Get-Date($_.lastStartTime)) } else { $_ | Add-Member -NotePropertyName lastStartTime -NotePropertyValue $Null -Force } @@ -18,7 +18,7 @@ function Get-ExtensionRateLimit($ExtensionName, $ExtensionPartitionKey, $RateLim # Check Global Rate Limiting try { - $ActiveJobs = $CurrentMap | Where-Object { ($Null -ne $_.lastStartTime) -and ($_.lastStartTime -gt (Get-Date).AddMinutes(-10)) -and ($Null -eq $_.lastEndTime -or $_.lastStartTime -gt $_.lastEndTime) } + $ActiveJobs = $CurrentMap | Where-Object { ($Null -ne $_.lastStartTime) -and ($_.lastStartTime -gt (Get-Date).AddMinutes(-10)) -and ($Null -eq $_.lastEndTime -or $_.lastStartTime -gt $_.lastEndTime) } } catch { $ActiveJobs = 'FirstRun' } diff --git a/Modules/CippQueue/CippQueue.psm1 b/Modules/CippQueue/CippQueue.psm1 deleted file mode 100644 index e9b86a3bc74e..000000000000 --- a/Modules/CippQueue/CippQueue.psm1 +++ /dev/null @@ -1,104 +0,0 @@ -using namespace System.Net - -function New-CippQueueEntry { - Param( - $Name, - $Link, - $Reference - ) - - $CippQueue = Get-CippTable -TableName CippQueue - - $QueueEntry = @{ - PartitionKey = 'CippQueue' - RowKey = (New-Guid).Guid.ToString() - Name = $Name - Link = $Link - Reference = $Reference - Status = 'Queued' - } - $CippQueue.Entity = $QueueEntry - - Add-CIPPAzDataTableEntity @CippQueue - - $QueueEntry -} - -function Update-CippQueueEntry { - Param( - [Parameter(Mandatory = $true)] - $RowKey, - $Status, - $Name - ) - - $CippQueue = Get-CippTable -TableName CippQueue - - if ($RowKey) { - $QueueEntry = Get-CIPPAzDataTableEntity @CippQueue -Filter ("RowKey eq '{0}'" -f $RowKey) - - if ($QueueEntry) { - if ($Status) { - $QueueEntry.Status = $Status - } - if ($Name) { - $QueueEntry.Name = $Name - } - Update-AzDataTableEntity @CippQueue -Entity $QueueEntry - $QueueEntry - } - else { - return $false - } - } - else { - return $false - } -} - -function Get-CippQueue { - # Input bindings are passed in via param block. - param($Request = $null, $TriggerMetadata) - - if ($Request) { - $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - # Write to the Azure Functions log stream. - Write-Host 'PowerShell HTTP trigger function processed a request.' - } - - $CippQueue = Get-CippTable -TableName 'CippQueue' - $CippQueueData = Get-CIPPAzDataTableEntity @CippQueue | Where-Object { ($_.Timestamp.DateTime) -ge (Get-Date).ToUniversalTime().AddHours(-1) } | Sort-Object -Property Timestamp -Descending - if ($request) { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($CippQueueData) - }) - } - else { - return $CippQueueData - } -} - -function Remove-CippQueue { - # Input bindings are passed in via param block. - param($Request, $TriggerMetadata) - - $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - # Write to the Azure Functions log stream. - Write-Host 'PowerShell HTTP trigger function processed a request.' - - $CippQueue = Get-CippTable -TableName 'CippQueue' - Clear-AzDataTable @CippQueue - - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{Results = @('History cleared') } - }) -} - - -Export-ModuleMember -Function @('New-CippQueueEntry', 'Get-CippQueue', 'Update-CippQueueEntry', 'Remove-CippQueue') diff --git a/Modules/DNSHealth/1.0.7/DNSHealth.psm1 b/Modules/DNSHealth/1.0.7/DNSHealth.psm1 index b69e40274c16..749a158a5fa6 100644 --- a/Modules/DNSHealth/1.0.7/DNSHealth.psm1 +++ b/Modules/DNSHealth/1.0.7/DNSHealth.psm1 @@ -1179,7 +1179,7 @@ function Read-MXRecord { catch { Write-Verbose $_.Exception.Message } } - $ValidationPasses.Add('Mail exchanger records record(s) are present for this domain.') | Out-Null + $ValidationPasses.Add('Mail exchanger record(s) are present for this domain.') | Out-Null $MXRecords = $MXRecords | Sort-Object -Property Priority # Attempt to identify mail provider based on MX record diff --git a/PublicWebhooks/function.json b/PublicWebhooks/function.json deleted file mode 100644 index f59adac3eb84..000000000000 --- a/PublicWebhooks/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "QueueWebhook", - "queueName": "webhooksqueue" - } - ] -} diff --git a/PublicWebhooks/run.ps1 b/PublicWebhooks/run.ps1 deleted file mode 100644 index fc7ca248ea4c..000000000000 --- a/PublicWebhooks/run.ps1 +++ /dev/null @@ -1,37 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$WebhookTable = Get-CIPPTable -TableName webhookTable -$Webhooks = Get-CIPPAzDataTableEntity @WebhookTable -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' - if ($Webhooks.Resource -eq 'M365AuditLogs') { - Write-Host "Found M365AuditLogs - This is an old entry, we'll deny so Microsoft stops sending it." - $body = 'This webhook is not authorized.' - $StatusCode = [HttpStatusCode]::Forbidden - } - if ($Request.query.ValidationToken -or $Request.body.validationCode) { - Write-Host 'Validation token received' - $body = $request.query.ValidationToken - } else { - Push-OutputBinding -Name QueueWebhook -Value $Request - $Body = 'Webhook Recieved' - $StatusCode = [HttpStatusCode]::OK - } -} else { - $body = 'This webhook is not authorized.' - $StatusCode = [HttpStatusCode]::Forbidden -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $body - }) diff --git a/PublicWebhooksProcess/function.json b/PublicWebhooksProcess/function.json deleted file mode 100644 index d358059b9e50..000000000000 --- a/PublicWebhooksProcess/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "webhooksqueue" - } - ] -} diff --git a/PublicWebhooksProcess/run.ps1 b/PublicWebhooksProcess/run.ps1 deleted file mode 100644 index 50bbde87cfb2..000000000000 --- a/PublicWebhooksProcess/run.ps1 +++ /dev/null @@ -1,79 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($QueueItem, $TriggerMetadata) - -$Request = $QueueItem - -$WebhookTable = Get-CIPPTable -TableName webhookTable -$Webhooks = Get-AzDataTableEntity @WebhookTable -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' - $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID - - if ($Request.Query.Type -eq 'GraphSubscription') { - # Graph Subscriptions - [pscustomobject]$ReceivedItem = $Request.Body.value - Invoke-CippGraphWebhookProcessing -Data $ReceivedItem -CIPPID $request.Query.CIPPID -WebhookInfo $Webhookinfo - - } else { - # Auditlog Subscriptions - try { - foreach ($ReceivedItem In ($Request.body)) { - $ReceivedItem = [pscustomobject]$ReceivedItem - Write-Host "Received Item: $($ReceivedItem | ConvertTo-Json -Depth 15 -Compress))" - $TenantFilter = (Get-Tenants | Where-Object -Property customerId -EQ $ReceivedItem.TenantId).defaultDomainName - Write-Host "Webhook TenantFilter: $TenantFilter" - $ConfigTable = get-cipptable -TableName 'SchedulerConfig' - $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable | Where-Object { $_.Tenant -eq $TenantFilter -or $_.Tenant -eq 'AllTenants' } - $Operations = ($AlertConfig.if | ConvertFrom-Json -ErrorAction SilentlyContinue).selection + 'UserLoggedIn' - $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID - #Increased download efficiency: only download the data we need for processing. Todo: Change this to load from table or dynamic source. - $MappingTable = [pscustomobject]@{ - 'UserLoggedIn' = 'Audit.AzureActiveDirectory' - 'Add member to role.' = 'Audit.AzureActiveDirectory' - 'Disable account.' = 'Audit.AzureActiveDirectory' - 'Update StsRefreshTokenValidFrom Timestamp.' = 'Audit.AzureActiveDirectory' - 'Enable account.' = 'Audit.AzureActiveDirectory' - 'Disable Strong Authentication.' = 'Audit.AzureActiveDirectory' - 'Reset user password.' = 'Audit.AzureActiveDirectory' - 'Add service principal.' = 'Audit.AzureActiveDirectory' - 'HostedIP' = 'Audit.AzureActiveDirectory' - 'badRepIP' = 'Audit.AzureActiveDirectory' - 'UserLoggedInFromUnknownLocation' = 'Audit.AzureActiveDirectory' - 'customfield' = 'AnyLog' - 'anyAlert' = 'AnyLog' - 'New-InboxRule' = 'Audit.Exchange' - 'Set-InboxRule' = 'Audit.Exchange' - } - #Compare $Operations to $MappingTable. If there is a match, we make a new variable called $LogsToDownload - #Example: $Operations = 'UserLoggedIn', 'Set-InboxRule' makes : $LogsToDownload = @('Audit.AzureActiveDirectory',Audit.Exchange) - $LogsToDownload = $Operations | Where-Object { $MappingTable.$_ } | ForEach-Object { $MappingTable.$_ } - Write-Host "Our operations: $Operations" - Write-Host "Logs to download: $LogsToDownload" - if ($ReceivedItem.ContentType -in $LogsToDownload -or 'AnyLog' -in $LogsToDownload) { - $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' - } else { - Write-Host "No data to download for $($ReceivedItem.ContentType)" - continue - } - Write-Host "Data found: $($data.count) items" - $DataToProcess = if ('anylog' -NotIn $LogsToDownload) { $Data | Where-Object -Property Operation -In $Operations } else { $Data } - Write-Host "Data to process found: $($DataToProcess.count) items" - foreach ($Item in $DataToProcess) { - Write-Host "Processing $($item.operation)" - Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url - } - } - } catch { - Write-Host "Webhook Failed: $($_.Exception.Message). Line number $($_.InvocationInfo.ScriptLineNumber)" - } - } - -} else { - Write-Host 'Unauthorised Webhook' -} diff --git a/Scheduler_Extensions/function.json b/Scheduler_Extensions/function.json index f3e5317f409a..7474f0f13334 100644 --- a/Scheduler_Extensions/function.json +++ b/Scheduler_Extensions/function.json @@ -11,6 +11,11 @@ "direction": "out", "name": "NinjaProcess", "queueName": "NinjaOneQueue" + }, + { + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/Scheduler_Extensions/run.ps1 b/Scheduler_Extensions/run.ps1 index 66af8649ebf7..58e228ebbe1d 100644 --- a/Scheduler_Extensions/run.ps1 +++ b/Scheduler_Extensions/run.ps1 @@ -10,85 +10,5 @@ Write-Host 'Started Scheduler for Extensions' # NinjaOne Extension if ($Configuration.NinjaOne.Enabled -eq $True) { - - $Table = Get-CIPPTable -TableName NinjaOneSettings - $Settings = (Get-AzDataTableEntity @Table) - $TimeSetting = ($Settings | Where-Object { $_.RowKey -eq 'NinjaSyncTime' }).SettingValue - - - if (($TimeSetting | Measure-Object).count -ne 1) { - [int]$TimeSetting = Get-Random -Minimum 1 -Maximum 95 - $AddObject = @{ - PartitionKey = 'NinjaConfig' - RowKey = 'NinjaSyncTime' - 'SettingValue' = $TimeSetting - } - Add-AzDataTableEntity @Table -Entity $AddObject -Force - } - - Write-Host "Ninja Time Setting: $TimeSetting" - - $LastRunTime = Get-Date(($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue) - - Write-Host "Last Run: $LastRunTime" - - $CurrentTime = Get-Date - $CurrentInterval = ($CurrentTime.Hour * 4) + [math]::Floor($CurrentTime.Minute / 15) - - Write-Host "Current Interval: $CurrentInterval" - - $CIPPMapping = Get-CIPPTable -TableName CippMapping - $Filter = "PartitionKey eq 'NinjaOrgsMapping'" - $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } - - if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $TimeSetting -eq $CurrentInterval) { - Write-Host 'Executing' - foreach ($Tenant in $TenantsToProcess | Sort-Object lastEndTime) { - Push-OutputBinding -Name NinjaProcess -Value @{ - 'NinjaAction' = 'SyncTenant' - 'MappedTenant' = $Tenant - } - Start-Sleep -Seconds 1 - - } - - $AddObject = @{ - PartitionKey = 'NinjaConfig' - RowKey = 'NinjaLastRunTime' - 'SettingValue' = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffK') - } - Add-AzDataTableEntity @Table -Entity $AddObject -Force - - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "NinjaOne Daily Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' - - } else { - if ($LastRunTime -lt (Get-Date).AddMinutes(-90)) { - $TenantsToProcess | ForEach-Object { - if ($Null -ne $_.lastEndTime -and $_.lastEndTime -ne '') { - $_.lastEndTime = (Get-Date($_.lastEndTime)) - } else { - $_ | Add-Member -NotePropertyName lastEndTime -NotePropertyValue $Null -Force - } - - if ($Null -ne $_.lastStartTime -and $_.lastStartTime -ne '') { - $_.lastStartTime = (Get-Date($_.lastStartTime)) - } else { - $_ | Add-Member -NotePropertyName lastStartTime -NotePropertyValue $Null -Force - } - } - $CatchupTenants = $TenantsToProcess | Where-Object { (((($_.lastEndTime -eq $Null) -or ($_.lastStartTime -gt $_.lastEndTime)) -and ($_.lastStartTime -lt (Get-Date).AddMinutes(-30)))) -or ($_.lastStartTime -lt $LastRunTime) } - foreach ($Tenant in $CatchupTenants) { - Push-OutputBinding -Name NinjaProcess -Value @{ - 'NinjaAction' = 'SyncTenant' - 'MappedTenant' = $Tenant - } - Start-Sleep -Seconds 1 - } - if (($CatchupTenants | Measure-Object).count -gt 0) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "NinjaOne Synchronization Catchup Queued for $(($CatchupTenants | Measure-Object).count) Tenants" -Sev 'Info' - } - - } - - } + Invoke-NinjaOneExtensionScheduler } \ No newline at end of file diff --git a/Scheduler_GetQueue/function.json b/Scheduler_GetQueue/function.json index d0f59a682e3c..56e4cf0cfda1 100644 --- a/Scheduler_GetQueue/function.json +++ b/Scheduler_GetQueue/function.json @@ -7,10 +7,9 @@ "type": "timerTrigger" }, { - "type": "queue", - "direction": "out", - "name": "QueueItem", - "queueName": "CIPPGenericQueue" + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/Scheduler_GetQueue/run.ps1 b/Scheduler_GetQueue/run.ps1 index f14ccc274dd4..6d0553001e1e 100644 --- a/Scheduler_GetQueue/run.ps1 +++ b/Scheduler_GetQueue/run.ps1 @@ -5,7 +5,7 @@ $Tenants = Get-CIPPAzDataTableEntity @Table | Where-Object -Property PartitionKe $Tasks = foreach ($Tenant in $Tenants) { if ($Tenant.tenant -ne 'AllTenants') { - [pscustomobject]@{ + [pscustomobject]@{ Tenant = $Tenant.tenant Tag = 'SingleTenant' TenantID = $Tenant.tenantid @@ -15,7 +15,7 @@ $Tasks = foreach ($Tenant in $Tenants) { Write-Host 'All tenants, doing them all' $TenantList = Get-Tenants foreach ($t in $TenantList) { - [pscustomobject]@{ + [pscustomobject]@{ Tenant = $t.defaultDomainName Tag = 'AllTenants' TenantID = $t.customerId @@ -23,19 +23,22 @@ $Tasks = foreach ($Tenant in $Tenants) { } } } -} +} -foreach ($Task in $Tasks) { - $QueueItem = [pscustomobject]@{ +$Batch = foreach ($Task in $Tasks) { + [pscustomobject]@{ Tenant = $task.tenant Tenantid = $task.tenantid Tag = $task.tag Type = $task.type FunctionName = "Scheduler$($Task.Type)" } - try { - Push-OutputBinding -Name QueueItem -Value $QueueItem - } catch { - Write-Host "Could not launch queue item for $($Task.tenant): $($_.Exception.Message)" - } -} \ No newline at end of file +} +$InputObject = [PSCustomObject]@{ + OrchestratorName = 'SchedulerOrchestrator' + Batch = @($Batch) +} +#Write-Host ($InputObject | ConvertTo-Json) +$InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) +Write-Host "Started orchestration with ID = '$InstanceId'" +#$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId \ No newline at end of file diff --git a/Scheduler_GetWebhooks/function.json b/Scheduler_GetWebhooks/function.json new file mode 100644 index 000000000000..f30537d11b34 --- /dev/null +++ b/Scheduler_GetWebhooks/function.json @@ -0,0 +1,15 @@ +{ + "bindings": [ + { + "name": "Timer", + "schedule": "0 */15 * * * *", + "direction": "in", + "type": "timerTrigger" + }, + { + "name": "starter", + "type": "durableClient", + "direction": "in" + } + ] +} diff --git a/Scheduler_GetWebhooks/run.ps1 b/Scheduler_GetWebhooks/run.ps1 new file mode 100644 index 000000000000..b55b57d1f05c --- /dev/null +++ b/Scheduler_GetWebhooks/run.ps1 @@ -0,0 +1,26 @@ +param($Timer) + +try { + + $webhookTable = Get-CIPPTable -tablename webhookTable + $Webhooks = Get-CIPPAzDataTableEntity @webhookTable -Property RowKey + if (($Webhooks | Measure-Object).Count -eq 0) { + Write-Host 'No webhook subscriptions found. Exiting.' + return + } + Write-Host 'Processing webhooks' + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'WebhookOrchestrator' + QueueFunction = @{ + FunctionName = 'GetPendingWebhooks' + } + SkipLog = $true + } + Write-Host ($InputObject | ConvertTo-Json -Depth 5) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started orchestration with ID = '$InstanceId'" +} catch { + Write-LogMessage -API 'Webhooks' -message 'Error processing webhooks' -sev Error -LogData (Get-CippException -Exception $_) + Write-Host ( 'Webhook error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) +} diff --git a/Scheduler_Standards/function.json b/Scheduler_Standards/function.json index 35ec29f027f7..e071591357a0 100644 --- a/Scheduler_Standards/function.json +++ b/Scheduler_Standards/function.json @@ -7,10 +7,9 @@ "type": "timerTrigger" }, { - "type": "queue", - "direction": "out", - "name": "QueueItem", - "queueName": "CIPPGenericQueue" + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/Scheduler_UserTasks/function.json b/Scheduler_UserTasks/function.json index de2a7380d759..f7af84092121 100644 --- a/Scheduler_UserTasks/function.json +++ b/Scheduler_UserTasks/function.json @@ -7,10 +7,9 @@ "type": "timerTrigger" }, { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "scheduledcommandprocessor" + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/Scheduler_UserTasks/run.ps1 b/Scheduler_UserTasks/run.ps1 index 802d4624473f..2585ee499be6 100644 --- a/Scheduler_UserTasks/run.ps1 +++ b/Scheduler_UserTasks/run.ps1 @@ -3,12 +3,13 @@ param($Timer) $Table = Get-CippTable -tablename 'ScheduledTasks' $Filter = "TaskState eq 'Planned' or TaskState eq 'Failed - Planned'" $tasks = Get-CIPPAzDataTableEntity @Table -Filter $Filter +$Batch = [System.Collections.Generic.List[object]]::new() foreach ($task in $tasks) { $tenant = $task.Tenant $currentUnixTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds if ($currentUnixTime -ge $task.ScheduledTime) { try { - Update-AzDataTableEntity @Table -Entity @{ + $null = Update-AzDataTableEntity @Table -Entity @{ PartitionKey = $task.PartitionKey RowKey = $task.RowKey ExecutedTime = "$currentUnixTime" @@ -19,25 +20,32 @@ foreach ($task in $tasks) { if (!$task.Parameters) { $task.Parameters = @{} } $ScheduledCommand = [pscustomobject]@{ - Command = $task.Command - Parameters = $task.Parameters - TaskInfo = $task + Command = $task.Command + Parameters = $task.Parameters + TaskInfo = $task + FunctionName = 'ExecScheduledCommand' } if ($task.Tenant -eq 'AllTenants') { - $Results = Get-Tenants | ForEach-Object { - $ScheduledCommand.Parameters['TenantFilter'] = $_.defaultDomainName - Push-OutputBinding -Name Msg -Value $ScheduledCommand + $AllTenantCommands = foreach ($Tenant in Get-Tenants) { + $NewParams = $task.Parameters.Clone() + $NewParams.TenantFilter = $Tenant.defaultDomainName + [pscustomobject]@{ + Command = $task.Command + Parameters = $NewParams + TaskInfo = $task + FunctionName = 'ExecScheduledCommand' + } } + $Batch.AddRange($AllTenantCommands) } else { $ScheduledCommand.Parameters['TenantFilter'] = $task.Tenant - $Results = Push-OutputBinding -Name Msg -Value $ScheduledCommand + $Batch.Add($ScheduledCommand) } - } catch { $errorMessage = $_.Exception.Message - Update-AzDataTableEntity @Table -Entity @{ + $null = Update-AzDataTableEntity @Table -Entity @{ PartitionKey = $task.PartitionKey RowKey = $task.RowKey Results = "$errorMessage" @@ -47,4 +55,15 @@ foreach ($task in $tasks) { Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error } } +} +if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'UserTaskOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json -Depth 10) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 10) + + Write-Host "Started orchestration with ID = '$InstanceId'" } \ No newline at end of file diff --git a/Tools/Initialize-DevEnvironment.ps1 b/Tools/Initialize-DevEnvironment.ps1 index 4f4f8f55aa58..d712e396ad04 100644 --- a/Tools/Initialize-DevEnvironment.ps1 +++ b/Tools/Initialize-DevEnvironment.ps1 @@ -11,5 +11,5 @@ ForEach ($Key in $CIPPSettings.PSObject.Properties.Name) { Import-Module "$CippRoot\Modules\AzBobbyTables" Import-Module "$CippRoot\Modules\DNSHealth" -Import-Module "$CippRoot\Modules\CippQueue" Import-Module "$CippRoot\Modules\CippCore" +Get-CIPPAuthentication \ No newline at end of file diff --git a/UpdatePermissions/function.json b/UpdatePermissions/function.json index a7fdc6ca8da8..7e97fe568d29 100644 --- a/UpdatePermissions/function.json +++ b/UpdatePermissions/function.json @@ -7,10 +7,9 @@ "schedule": "0 0 0 * * *" }, { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "cpvqueue" + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/UpdatePermissions/run.ps1 b/UpdatePermissions/run.ps1 index e4e73f4be9ed..77290175242e 100644 --- a/UpdatePermissions/run.ps1 +++ b/UpdatePermissions/run.ps1 @@ -1,7 +1,16 @@ # Input bindings are passed in via param block. param($Timer) -$Tenants = get-tenants -IncludeAll | Where-Object { $_.customerId -ne $env:TenantId } -foreach ($Row in $Tenants) { - Push-OutputBinding -Name Msg -Value $row -} \ No newline at end of file +try { + $Tenants = Get-Tenants -IncludeAll -TriggerRefresh | Where-Object { $_.customerId -ne $env:TenantId -and $_.Excluded -eq $false } | ForEach-Object { $_ | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'UpdatePermissionsQueue'; $_ } + + if (($Tenants | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'UpdatePermissionsOrchestrator' + Batch = @($Tenants) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } +} catch {} \ No newline at end of file diff --git a/UpdatePermissionsQueue/function.json b/UpdatePermissionsQueue/function.json deleted file mode 100644 index 7fc4f12cef56..000000000000 --- a/UpdatePermissionsQueue/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "cpvqueue" - } - ] -} diff --git a/UpdatePermissionsQueue/run.ps1 b/UpdatePermissionsQueue/run.ps1 deleted file mode 100644 index 9b147478e274..000000000000 --- a/UpdatePermissionsQueue/run.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -# Input bindings are passed in via param block. -param($QueueItem, $TriggerMetadata) -Write-Host "Applying permissions for $($QueueItem.defaultDomainName)" -$Table = Get-CIPPTable -TableName cpvtenants -$CPVRows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Tenant -EQ $QueueItem.customerId -if (!$CPVRows -or $ENV:ApplicationID -notin $CPVRows.applicationId) { - Write-LogMessage -tenant $queueitem.defaultDomainName -tenantId $queueitem.customerId -message "A New tenant has been added, or a new CIPP-SAM Application is in use" -Sev "Warn" -API "NewTenant" - Write-Host "Adding CPV permissions" - Set-CIPPCPVConsent -Tenantfilter $QueueItem.defaultDomainName -} - -Add-CIPPApplicationPermission -RequiredResourceAccess "CippDefaults" -ApplicationId $ENV:ApplicationID -tenantfilter $QueueItem.defaultDomainName -Add-CIPPDelegatedPermission -RequiredResourceAccess "CippDefaults" -ApplicationId $ENV:ApplicationID -tenantfilter $QueueItem.defaultDomainName - -Write-LogMessage -tenant $QueueItem.defaultDomainName -tenantId $queueitem.customerId -message "Updated permissions for $($QueueItem.defaultDomainName)" -Sev "Info" -API "UpdatePermissionsQueue" \ No newline at end of file diff --git a/UpdateTokens/run.ps1 b/UpdateTokens/run.ps1 index 8f80a365bbb3..ed20d6a67fbf 100644 --- a/UpdateTokens/run.ps1 +++ b/UpdateTokens/run.ps1 @@ -6,18 +6,26 @@ $currentUTCtime = (Get-Date).ToUniversalTime() $Refreshtoken = (Get-GraphToken -ReturnRefresh $true).Refresh_token -if ($env:MSI_SECRET) { - Disable-AzContextAutosave -Scope Process | Out-Null - $AzSession = Connect-AzAccount -Identity -} - -$KV = $ENV:WEBSITE_DEPLOYMENT_ID - -if ($Refreshtoken) { - Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Refreshtoken -AsPlainText -Force) -} -else { - Write-LogMessage -message "Could not update refresh token. Will try again in 7 days." -sev "CRITICAL" +if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { + $Table = Get-CIPPTable -tablename 'DevSecrets' + $Secret = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'Secret' and RowKey eq 'Secret'" + if ($Secret) { + $Secret.RefreshToken = $Refreshtoken + Add-AzDataTableEntity @Table -Entity $Secret + } else { + Write-LogMessage -message 'Could not update refresh token. Will try again in 7 days.' -sev 'CRITICAL' + } +} else { + if ($env:MSI_SECRET) { + Disable-AzContextAutosave -Scope Process | Out-Null + $AzSession = Connect-AzAccount -Identity + } + $KV = $ENV:WEBSITE_DEPLOYMENT_ID + if ($Refreshtoken) { + Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Refreshtoken -AsPlainText -Force) + } else { + Write-LogMessage -message 'Could not update refresh token. Will try again in 7 days.' -sev 'CRITICAL' + } } # Write an information log with the current time. diff --git a/Z_CIPPHttpTrigger/function.json b/Z_CIPPHttpTrigger/function.json index 90c8dc9ea6af..a77f42a0ea97 100644 --- a/Z_CIPPHttpTrigger/function.json +++ b/Z_CIPPHttpTrigger/function.json @@ -27,36 +27,12 @@ "name": "Subscription", "queueName": "AlertSubscriptions" }, - { - "type": "queue", - "direction": "out", - "name": "LicenseQueue", - "queueName": "licqueue" - }, - { - "type": "queue", - "direction": "out", - "name": "mbxrulequeue", - "queueName": "mbxrulequeue" - }, - { - "type": "queue", - "direction": "out", - "name": "mfaqueue", - "queueName": "mfaqueue" - }, { "type": "queue", "direction": "out", "name": "mailboxstats", "queueName": "generalAllTenantQueue" }, - { - "type": "queue", - "direction": "out", - "name": "listusers", - "queueName": "generalAllTenantQueue" - }, { "type": "queue", "direction": "out", @@ -75,12 +51,6 @@ "name": "alertqueue", "queueName": "alertqueue" }, - { - "type": "queue", - "direction": "out", - "name": "gdapinvitequeue", - "queueName": "gdapinvitequeue" - }, { "type": "queue", "direction": "out", @@ -99,12 +69,6 @@ "name": "offboardingmailbox", "queueName": "offboardingmailbox" }, - { - "type": "queue", - "direction": "out", - "name": "QueueWebhook", - "queueName": "webhooksqueue" - }, { "name": "starter", "type": "durableClient", diff --git a/profile.ps1 b/profile.ps1 index f30159db12cb..ec6aa4e19b86 100644 --- a/profile.ps1 +++ b/profile.ps1 @@ -15,9 +15,10 @@ # Import modules @('CippCore', 'CippExtensions', 'Az.KeyVault', 'Az.Accounts') | ForEach-Object { try { + $Module = $_ Import-Module -Name $_ -ErrorAction Stop } catch { - Write-LogMessage -message "Failed to import module $($_): $_.Exception.Message" -Sev 'debug' + Write-LogMessage -message "Failed to import module - $Module" -LogData (Get-CippException -Exception $_) -Sev 'debug' $_.Exception.Message } } @@ -32,7 +33,7 @@ try { $Auth = Get-CIPPAuthentication } } catch { - Write-LogMessage -message "Could not retrieve keys from Keyvault: $($_.Exception.Message)" -Sev 'debug' + Write-LogMessage -message 'Could not retrieve keys from Keyvault' -LogData (Get-CippException -Exception $_) -Sev 'debug' } # Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell. diff --git a/version_latest.txt b/version_latest.txt index 1b47e8f3efe7..c7ba1e87f75e 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.1.2 \ No newline at end of file +5.5.0 \ No newline at end of file