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/DomainAnalyser_All/function.json b/DomainAnalyser_All/function.json
deleted file mode 100644
index 50f47ad53465..000000000000
--- a/DomainAnalyser_All/function.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "bindings": [
- {
- "name": "DomainObject",
- "direction": "in",
- "type": "activityTrigger"
- }
- ]
-}
diff --git a/DomainAnalyser_All/run.ps1 b/DomainAnalyser_All/run.ps1
deleted file mode 100644
index c9f0b989c04e..000000000000
--- a/DomainAnalyser_All/run.ps1
+++ /dev/null
@@ -1,244 +0,0 @@
-param($DomainObject)
-
-Import-Module DNSHealth
-
-try {
- $ConfigTable = Get-CippTable -tablename Config
- $Filter = "PartitionKey eq 'Domains' and RowKey eq 'Domains'"
- $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter $Filter
-
- $ValidResolvers = @('Google', 'CloudFlare', 'Quad9')
- if ($ValidResolvers -contains $Config.Resolver) {
- $Resolver = $Config.Resolver
- } else {
- $Resolver = 'Google'
- $Config = @{
- PartitionKey = 'Domains'
- RowKey = 'Domains'
- Resolver = $Resolver
- }
- Add-CIPPAzDataTableEntity @ConfigTable -Entity $Config -Force
- }
-} catch {
- $Resolver = 'Google'
-}
-Set-DnsResolver -Resolver $Resolver
-
-$Domain = $DomainObject.rowKey
-
-try {
- $Tenant = $DomainObject.TenantDetails | ConvertFrom-Json -ErrorAction Stop
-} catch {
- $Tenant = @{Tenant = 'None' }
-}
-
-#Write-Host "$($DomainObject.TenantDetails)"
-
-$Result = [PSCustomObject]@{
- Tenant = $Tenant.Tenant
- 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 = ''
- ActualMXRecords = ''
- MXPassTest = ''
- DMARCPresent = ''
- DMARCFullPolicy = ''
- DMARCActionPolicy = ''
- DMARCReportingActive = ''
- DMARCPercentagePass = ''
- DNSSECPresent = ''
- MailProvider = ''
- DKIMEnabled = ''
- DKIMRecords = ''
- Score = ''
- MaximumScore = 160
- ScorePercentage = ''
- ScoreExplanation = ''
-}
-
-$Scores = [PSCustomObject]@{
- SPFPresent = 10
- SPFCorrectAll = 20
- MXRecommended = 10
- DMARCPresent = 10
- DMARCSetQuarantine = 20
- DMARCSetReject = 30
- DMARCReportingActive = 20
- DMARCPercentageGood = 20
- DNSSECPresent = 20
- DKIMActiveAndWorking = 20
-}
-
-$ScoreDomain = 0
-# Setup Score Explanation
-$ScoreExplanation = [System.Collections.Generic.List[string]]::new()
-
-# Check MX Record
-$MXRecord = Read-MXRecord -Domain $Domain -ErrorAction Stop
-
-$Result.ExpectedSPFRecord = $MXRecord.ExpectedInclude
-$Result.MXPassTest = $false
-$Result.ActualMXRecords = $MXRecord.Records
-
-# Check fail counts to ensure all tests pass
-#$MXWarnCount = $MXRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count
-$MXFailCount = $MXRecord.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
-
-if ($MXFailCount -eq 0) {
- $Result.MXPassTest = $true
- $ScoreDomain += $Scores.MXRecommended
-} else {
- $ScoreExplanation.Add('MX record did not pass validation') | Out-Null
-}
-
-if ([string]::IsNullOrEmpty($MXRecord.MailProvider)) {
- $Result.MailProvider = 'Unknown'
-} else {
- $Result.MailProvider = $MXRecord.MailProvider.Name
-}
-
-# Get SPF Record
-try {
- $SPFRecord = Read-SpfRecord -Domain $Domain -ErrorAction Stop
- if ($SPFRecord.RecordCount -gt 0) {
- $Result.ActualSPFRecord = $SPFRecord.Record
- if ($SPFRecord.RecordCount -eq 1) {
- $ScoreDomain += $Scores.SPFPresent
- } else {
- $ScoreExplanation.Add('Multiple SPF records detected') | Out-Null
- }
- } else {
- $Result.ActualSPFRecord = 'No SPF Record'
- $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
- throw $Message
-}
-
-# Check SPF Record
-$Result.SPFPassAll = $false
-
-# Check warning + fail counts to ensure all tests pass
-#$SPFWarnCount = $SPFRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count
-$SPFFailCount = $SPFRecord.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
-
-if ($SPFFailCount -eq 0) {
- $ScoreDomain += $Scores.SPFCorrectAll
- $Result.SPFPassAll = $true
-} else {
- $ScoreExplanation.Add('SPF record did not pass validation') | Out-Null
-}
-
-# Get DMARC Record
-try {
- $DMARCPolicy = Read-DmarcPolicy -Domain $Domain -ErrorAction Stop
-
- If ([string]::IsNullOrEmpty($DMARCPolicy.Record)) {
- $Result.DMARCPresent = $false
- $ScoreExplanation.Add('No DMARC Records Found') | Out-Null
- } else {
- $Result.DMARCPresent = $true
- $ScoreDomain += $Scores.DMARCPresent
-
- $Result.DMARCFullPolicy = $DMARCPolicy.Record
- if ($DMARCPolicy.Policy -eq 'reject' -and $DMARCPolicy.SubdomainPolicy -eq 'reject') {
- $Result.DMARCActionPolicy = 'Reject'
- $ScoreDomain += $Scores.DMARCSetReject
- }
- if ($DMARCPolicy.Policy -eq 'none') {
- $Result.DMARCActionPolicy = 'None'
- $ScoreExplanation.Add('DMARC is not being enforced') | Out-Null
- }
- if ($DMARCPolicy.Policy -eq 'quarantine') {
- $Result.DMARCActionPolicy = 'Quarantine'
- $ScoreDomain += $Scores.DMARCSetQuarantine
- $ScoreExplanation.Add('DMARC Partially Enforced with quarantine') | Out-Null
- }
-
- $ReportEmailCount = $DMARCPolicy.ReportingEmails | Measure-Object | Select-Object -ExpandProperty Count
- if ($ReportEmailCount -gt 0) {
- $Result.DMARCReportingActive = $true
- $ScoreDomain += $Scores.DMARCReportingActive
- } else {
- $Result.DMARCReportingActive = $False
- $ScoreExplanation.Add('DMARC Reporting not Configured') | Out-Null
- }
-
- if ($DMARCPolicy.Percent -eq 100) {
- $Result.DMARCPercentagePass = $true
- $ScoreDomain += $Scores.DMARCPercentageGood
- } else {
- $Result.DMARCPercentagePass = $false
- $ScoreExplanation.Add('DMARC Not Checking All Messages') | Out-Null
- }
- }
-} 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
- throw $Message
-}
-
-# DNS Sec Check
-try {
- $DNSSECResult = Test-DNSSEC -Domain $Domain -ErrorAction Stop
- $DNSSECFailCount = $DNSSECResult.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
- $DNSSECWarnCount = $DNSSECResult.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
- if (($DNSSECFailCount + $DNSSECWarnCount) -eq 0) {
- $Result.DNSSECPresent = $true
- $ScoreDomain += $Scores.DNSSECPresent
- } else {
- $Result.DNSSECPresent = $false
- $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
- throw $Message
-}
-
-# DKIM Check
-try {
- $DkimParams = @{
- Domain = $Domain
- FallbackToMicrosoftSelectors = $true
- }
- if (![string]::IsNullOrEmpty($DomainObject.DkimSelectors)) {
- $DkimParams.Selectors = $DomainObject.DkimSelectors | ConvertFrom-Json
- }
-
- $DkimRecord = Read-DkimRecord @DkimParams -ErrorAction Stop
-
- $DkimRecordCount = $DkimRecord.Records | Measure-Object | Select-Object -ExpandProperty Count
- $DkimFailCount = $DkimRecord.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
- #$DkimWarnCount = $DkimRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count
- if ($DkimRecordCount -gt 0 -and $DkimFailCount -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
- throw $Message
-}
-# Final Score
-$Result.Score = $ScoreDomain
-$Result.ScorePercentage = [int](($Result.Score / $Result.MaximumScore) * 100)
-$Result.ScoreExplanation = ($ScoreExplanation) -join ', '
-
-
-$DomainObject.DomainAnalyser = ($Result | ConvertTo-Json -Compress).ToString()
-
-# Final Write to Output
-Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message "DNS Analyser Finished For $Domain" -sev Info
-
-Write-Output $DomainObject
\ No newline at end of file
diff --git a/DomainAnalyser_GetTenantDomains/function.json b/DomainAnalyser_GetTenantDomains/function.json
deleted file mode 100644
index ce320a44c1a3..000000000000
--- a/DomainAnalyser_GetTenantDomains/function.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "bindings": [
- {
- "name": "name",
- "type": "activityTrigger",
- "direction": "in"
- }
- ]
-}
diff --git a/DomainAnalyser_GetTenantDomains/run.ps1 b/DomainAnalyser_GetTenantDomains/run.ps1
deleted file mode 100644
index 4e0fd71f2be8..000000000000
--- a/DomainAnalyser_GetTenantDomains/run.ps1
+++ /dev/null
@@ -1,92 +0,0 @@
-param($name)
-
-$Tenants = Get-Tenants
-$ExcludedTenants = Get-Tenants -SkipList
-$DomainTable = Get-CippTable -tablename 'Domains'
-
-$TenantDomains = $Tenants | ForEach-Object -Parallel {
- Import-Module CippCore
- $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) }
- foreach ($d in $domains) {
- [PSCustomObject]@{
- Tenant = $Tenant.defaultDomainName
- Domain = $d.id
- AuthenticationType = $d.authenticationType
- IsAdminManaged = $d.isAdminManaged
- IsDefault = $d.isDefault
- IsInitial = $d.isInitial
- IsRoot = $d.isRoot
- IsVerified = $d.isVerified
- SupportedServices = $d.supportedServices
- }
- }
- } catch {
- Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.defaultDomainName -message "DNS Analyser GraphGetRequest Exception: $($_.Exception.Message)" -sev Error
- }
-} | Sort-Object -Unique -Property Domain
-
-# Cleanup domains from tenants with errors, skip domains with manually set selectors or mail providers
-foreach ($Exclude in $ExcludedTenants) {
- $Filter = "PartitionKey eq 'TenantDomains' and TenantId eq '{0}'" -f $Exclude.defaultDomainName
- $CleanupRows = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter
- $CleanupCount = ($CleanupRows | Measure-Object).Count
- if ($CleanupCount -gt 0) {
- Write-LogMessage -API 'DomainAnalyser' -tenant $Exclude.defaultDomainName -message "Cleaning up $CleanupCount domain(s) for excluded tenant" -sev Info
- Remove-AzDataTableEntity @DomainTable -Entity $CleanupRows
- }
-}
-
-$TenantCount = ($TenantDomains | Measure-Object).Count
-if ($TenantCount -gt 0) {
- Write-Host "$TenantCount tenant Domains"
-
- # Process tenant domain results
- try {
- $TenantDomainObjects = foreach ($Tenant in $TenantDomains) {
- $TenantDetails = ($Tenant | ConvertTo-Json -Compress).ToString()
- $Filter = "PartitionKey eq '{0}' and RowKey eq '{1}'" -f $Tenant.Tenant, $Tenant.Domain
- $OldDomain = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter
-
- if ($OldDomain) {
- Remove-AzDataTableEntity @DomainTable -Entity $OldDomain | Out-Null
- }
-
- $Filter = "PartitionKey eq 'TenantDomains' and RowKey eq '{0}'" -f $Tenant.Domain
- $Domain = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter
-
- if (!$Domain) {
- $DomainObject = [pscustomobject]@{
- DomainAnalyser = ''
- TenantDetails = $TenantDetails
- TenantId = $Tenant.Tenant
- DkimSelectors = ''
- MailProviders = ''
- RowKey = $Tenant.Domain
- PartitionKey = 'TenantDomains'
- }
-
- if ($OldDomain) {
- $DomainObject.DkimSelectors = $OldDomain.DkimSelectors
- $DomainObject.MailProviders = $OldDomain.MailProviders
- }
- $Domain = $DomainObject
- } else {
- $Domain.TenantDetails = $TenantDetails
- if ($OldDomain) {
- $Domain.DkimSelectors = $OldDomain.DkimSelectors
- $Domain.MailProviders = $OldDomain.MailProviders
- }
- }
- # Return domain object to list
- $Domain
- }
-
- # 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' }
-}
diff --git a/DomainAnalyser_Orchestration/function.json b/DomainAnalyser_Orchestration/function.json
deleted file mode 100644
index 7326b39c184d..000000000000
--- a/DomainAnalyser_Orchestration/function.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "bindings": [
- {
- "name": "Context",
- "type": "orchestrationTrigger",
- "direction": "in"
- }
- ]
-}
\ No newline at end of file
diff --git a/DomainAnalyser_Orchestration/run.ps1 b/DomainAnalyser_Orchestration/run.ps1
deleted file mode 100644
index 34848e03284d..000000000000
--- a/DomainAnalyser_Orchestration/run.ps1
+++ /dev/null
@@ -1,40 +0,0 @@
-param($Context)
-
-try {
-
- $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)" }
-
- # 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
-
- 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
-}
\ No newline at end of file
diff --git a/DomainAnalyser_OrchestrationStarter/run.ps1 b/DomainAnalyser_OrchestrationStarter/run.ps1
index 7c85e87640cd..b2fd0769428c 100644
--- a/DomainAnalyser_OrchestrationStarter/run.ps1
+++ b/DomainAnalyser_OrchestrationStarter/run.ps1
@@ -1,19 +1,20 @@
using namespace System.Net
param($Request, $TriggerMetadata)
-if ($CurrentlyRunning) {
- $Results = [pscustomobject]@{'Results' = 'Already running. Please wait for the current instance to finish' }
- Write-LogMessage -API 'DomainAnalyser' -message 'Attempted to start domain analysis but an instance was already running.' -sev Info
-}
-else {
- $InstanceId = Start-NewOrchestration -FunctionName 'DomainAnalyser_Orchestration'
- Write-Host "Started orchestration with ID = '$InstanceId'"
- $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId
- Write-LogMessage -API 'DomainAnalyser' -message 'Started retrieving domain information' -sev Info
- $Results = [pscustomobject]@{'Results' = 'Started running analysis' }
-}
-Write-Host ($Orchestrator | ConvertTo-Json)
+$Results = [pscustomobject]@{'Results' = 'Domain Analyser started' }
+$InputObject = [PSCustomObject]@{
+ QueueFunction = [PSCustomObject]@{
+ FunctionName = 'GetTenants'
+ DurableName = 'DomainAnalyserTenant'
+ TenantParams = @{
+ IncludeAll = $true
+ }
+ }
+ OrchestratorName = 'DomainAnalyser_Tenants'
+ SkipLog = $true
+}
+Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Compress -Depth 5)
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
diff --git a/Domain_OrchestrationStarterTimer/run.ps1 b/Domain_OrchestrationStarterTimer/run.ps1
index 67e945736bb1..63d7cf8985bb 100644
--- a/Domain_OrchestrationStarterTimer/run.ps1
+++ b/Domain_OrchestrationStarterTimer/run.ps1
@@ -1,22 +1,33 @@
param($Timer)
-if ($env:DEV_SKIP_DOMAIN_TIMER) {
+if ($env:DEV_SKIP_DOMAIN_TIMER) {
Write-Host 'Skipping DomainAnalyser timer'
- exit 0
+ exit 0
}
try {
if ($CurrentlyRunning) {
$Results = [pscustomobject]@{'Results' = 'Already running. Please wait for the current instance to finish' }
Write-LogMessage -API 'DomainAnalyser' -message 'Attempted to start analysis but an instance was already running.' -sev Info
- }
- else {
- $InstanceId = Start-NewOrchestration -FunctionName 'DomainAnalyser_Orchestration'
+ } else {
+ #$InstanceId = Start-NewOrchestration -FunctionName 'DomainAnalyser_Orchestration'
Write-Host "Started orchestration with ID = '$InstanceId'"
- $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Timer -InstanceId $InstanceId
+ #Orchestrator = New-OrchestrationCheckStatusResponse -Request $Timer -InstanceId $InstanceId
Write-LogMessage -API 'DomainAnalyser' -message 'Starting Domain Analyser' -sev Info
$Results = [pscustomobject]@{'Results' = 'Starting Domain Analyser' }
+
+ $InputObject = [PSCustomObject]@{
+ QueueFunction = [PSCustomObject]@{
+ FunctionName = 'GetTenants'
+ DurableName = 'DomainAnalyserTenant'
+ TenantParams = @{
+ IncludeAll = $true
+ }
+ }
+ OrchestratorName = 'DomainAnalyser_Tenants'
+ SkipLog = $true
+ }
+ Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Compress -Depth 5)
}
Write-Host ($Orchestrator | ConvertTo-Json)
-}
-catch { Write-Host "Domain_OrchestratorStarterTimer Exception $($_.Exception.Message)" }
\ No newline at end of file
+} catch { Write-Host "Domain_OrchestratorStarterTimer Exception $($_.Exception.Message)" }
\ 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/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 a4c66a07cb7b..b29972bcce3a 100644
--- a/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1
+++ b/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1
@@ -1,18 +1,18 @@
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
}
@@ -21,9 +21,9 @@ function Add-CIPPGroupMember(
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/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserDomain.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserDomain.ps1
new file mode 100644
index 000000000000..c5aa1f420919
--- /dev/null
+++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserDomain.ps1
@@ -0,0 +1,252 @@
+function Push-DomainAnalyserDomain {
+ param($Item)
+ $DomainTable = Get-CippTable -tablename 'Domains'
+ $Filter = "PartitionKey eq 'TenantDomains' and RowKey eq '{0}'" -f $Item.RowKey
+ $DomainObject = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter
+
+ try {
+ $ConfigTable = Get-CippTable -tablename Config
+ $Filter = "PartitionKey eq 'Domains' and RowKey eq 'Domains'"
+ $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter $Filter
+
+ $ValidResolvers = @('Google', 'CloudFlare', 'Quad9')
+ if ($ValidResolvers -contains $Config.Resolver) {
+ $Resolver = $Config.Resolver
+ } else {
+ $Resolver = 'Google'
+ $Config = @{
+ PartitionKey = 'Domains'
+ RowKey = 'Domains'
+ Resolver = $Resolver
+ }
+ Add-CIPPAzDataTableEntity @ConfigTable -Entity $Config -Force
+ }
+ } catch {
+ $Resolver = 'Google'
+ }
+ Set-DnsResolver -Resolver $Resolver
+
+ $Domain = $DomainObject.rowKey
+
+ try {
+ $Tenant = $DomainObject.TenantDetails | ConvertFrom-Json -ErrorAction Stop
+ } catch {
+ $Tenant = @{Tenant = 'None' }
+ }
+
+ $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 = ''
+ ActualMXRecords = ''
+ MXPassTest = ''
+ DMARCPresent = ''
+ DMARCFullPolicy = ''
+ DMARCActionPolicy = ''
+ DMARCReportingActive = ''
+ DMARCPercentagePass = ''
+ DNSSECPresent = ''
+ MailProvider = ''
+ DKIMEnabled = ''
+ DKIMRecords = ''
+ Score = ''
+ MaximumScore = 160
+ ScorePercentage = ''
+ ScoreExplanation = ''
+ }
+
+ $Scores = [PSCustomObject]@{
+ SPFPresent = 10
+ SPFCorrectAll = 20
+ MXRecommended = 10
+ DMARCPresent = 10
+ DMARCSetQuarantine = 20
+ DMARCSetReject = 30
+ DMARCReportingActive = 20
+ DMARCPercentageGood = 20
+ DNSSECPresent = 20
+ DKIMActiveAndWorking = 20
+ }
+
+ $ScoreDomain = 0
+ # Setup Score Explanation
+ $ScoreExplanation = [System.Collections.Generic.List[string]]::new()
+
+ # Check MX Record
+ $MXRecord = Read-MXRecord -Domain $Domain -ErrorAction Stop
+
+ $Result.ExpectedSPFRecord = $MXRecord.ExpectedInclude
+ $Result.MXPassTest = $false
+ $Result.ActualMXRecords = $MXRecord.Records
+
+ # Check fail counts to ensure all tests pass
+ #$MXWarnCount = $MXRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count
+ $MXFailCount = $MXRecord.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
+
+ if ($MXFailCount -eq 0) {
+ $Result.MXPassTest = $true
+ $ScoreDomain += $Scores.MXRecommended
+ } else {
+ $ScoreExplanation.Add('MX record did not pass validation') | Out-Null
+ }
+
+ if ([string]::IsNullOrEmpty($MXRecord.MailProvider)) {
+ $Result.MailProvider = 'Unknown'
+ } else {
+ $Result.MailProvider = $MXRecord.MailProvider.Name
+ }
+
+ # Get SPF Record
+ try {
+ $SPFRecord = Read-SpfRecord -Domain $Domain -ErrorAction Stop
+ if ($SPFRecord.RecordCount -gt 0) {
+ $Result.ActualSPFRecord = $SPFRecord.Record
+ if ($SPFRecord.RecordCount -eq 1) {
+ $ScoreDomain += $Scores.SPFPresent
+ } else {
+ $ScoreExplanation.Add('Multiple SPF records detected') | Out-Null
+ }
+ } else {
+ $Result.ActualSPFRecord = 'No SPF Record'
+ $ScoreExplanation.Add('No SPF Record Found') | Out-Null
+ }
+ } catch {
+ $Message = 'SPF Error'
+ Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error
+ throw $Message
+ }
+
+ # Check SPF Record
+ $Result.SPFPassAll = $false
+
+ # Check warning + fail counts to ensure all tests pass
+ #$SPFWarnCount = $SPFRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count
+ $SPFFailCount = $SPFRecord.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
+
+ if ($SPFFailCount -eq 0) {
+ $ScoreDomain += $Scores.SPFCorrectAll
+ $Result.SPFPassAll = $true
+ } else {
+ $ScoreExplanation.Add('SPF record did not pass validation') | Out-Null
+ }
+
+ # Get DMARC Record
+ try {
+ $DMARCPolicy = Read-DmarcPolicy -Domain $Domain -ErrorAction Stop
+
+ If ([string]::IsNullOrEmpty($DMARCPolicy.Record)) {
+ $Result.DMARCPresent = $false
+ $ScoreExplanation.Add('No DMARC Records Found') | Out-Null
+ } else {
+ $Result.DMARCPresent = $true
+ $ScoreDomain += $Scores.DMARCPresent
+
+ $Result.DMARCFullPolicy = $DMARCPolicy.Record
+ if ($DMARCPolicy.Policy -eq 'reject' -and $DMARCPolicy.SubdomainPolicy -eq 'reject') {
+ $Result.DMARCActionPolicy = 'Reject'
+ $ScoreDomain += $Scores.DMARCSetReject
+ }
+ if ($DMARCPolicy.Policy -eq 'none') {
+ $Result.DMARCActionPolicy = 'None'
+ $ScoreExplanation.Add('DMARC is not being enforced') | Out-Null
+ }
+ if ($DMARCPolicy.Policy -eq 'quarantine') {
+ $Result.DMARCActionPolicy = 'Quarantine'
+ $ScoreDomain += $Scores.DMARCSetQuarantine
+ $ScoreExplanation.Add('DMARC Partially Enforced with quarantine') | Out-Null
+ }
+
+ $ReportEmailCount = $DMARCPolicy.ReportingEmails | Measure-Object | Select-Object -ExpandProperty Count
+ if ($ReportEmailCount -gt 0) {
+ $Result.DMARCReportingActive = $true
+ $ScoreDomain += $Scores.DMARCReportingActive
+ } else {
+ $Result.DMARCReportingActive = $False
+ $ScoreExplanation.Add('DMARC Reporting not Configured') | Out-Null
+ }
+
+ if ($DMARCPolicy.Percent -eq 100) {
+ $Result.DMARCPercentagePass = $true
+ $ScoreDomain += $Scores.DMARCPercentageGood
+ } else {
+ $Result.DMARCPercentagePass = $false
+ $ScoreExplanation.Add('DMARC Not Checking All Messages') | Out-Null
+ }
+ }
+ } catch {
+ $Message = 'DMARC Error'
+ Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error
+ throw $Message
+ }
+
+ # DNS Sec Check
+ try {
+ $DNSSECResult = Test-DNSSEC -Domain $Domain -ErrorAction Stop
+ $DNSSECFailCount = $DNSSECResult.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
+ $DNSSECWarnCount = $DNSSECResult.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
+ if (($DNSSECFailCount + $DNSSECWarnCount) -eq 0) {
+ $Result.DNSSECPresent = $true
+ $ScoreDomain += $Scores.DNSSECPresent
+ } else {
+ $Result.DNSSECPresent = $false
+ $ScoreExplanation.Add('DNSSEC Not Configured or Enabled') | Out-Null
+ }
+ } catch {
+ $Message = 'DNSSEC Error'
+ Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error
+ throw $Message
+ }
+
+ # DKIM Check
+ try {
+ $DkimParams = @{
+ Domain = $Domain
+ FallbackToMicrosoftSelectors = $true
+ }
+ if (![string]::IsNullOrEmpty($DomainObject.DkimSelectors)) {
+ $DkimParams.Selectors = $DomainObject.DkimSelectors | ConvertFrom-Json
+ }
+
+ $DkimRecord = Read-DkimRecord @DkimParams -ErrorAction Stop
+
+ $DkimRecordCount = $DkimRecord.Records | Measure-Object | Select-Object -ExpandProperty Count
+ $DkimFailCount = $DkimRecord.ValidationFails | Measure-Object | Select-Object -ExpandProperty Count
+ #$DkimWarnCount = $DkimRecord.ValidationWarns | Measure-Object | Select-Object -ExpandProperty Count
+ if ($DkimRecordCount -gt 0 -and $DkimFailCount -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'
+ Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error
+ throw $Message
+ }
+ # Final Score
+ $Result.Score = $ScoreDomain
+ $Result.ScorePercentage = [int](($Result.Score / $Result.MaximumScore) * 100)
+ $Result.ScoreExplanation = ($ScoreExplanation) -join ', '
+
+ $DomainObject.DomainAnalyser = ($Result | ConvertTo-Json -Compress).ToString()
+
+ try {
+ $DomainTable.Entity = $DomainObject
+ $DomainTable.Force = $true
+ Add-CIPPAzDataTableEntity @DomainTable -Entity $DomainObject -Force
+
+ # Final Write to Output
+ Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message "DNS Analyser Finished For $Domain" -sev Info
+ } catch {
+ Write-LogMessage -API -API 'DomainAnalyser' -tenant $tenant.tenant -message "Error saving domain $Domain to table " -sev Error -LogData (Get-CippException -Exception $_)
+ }
+ return $null
+}
\ No newline at end of file
diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserTenant.ps1
new file mode 100644
index 000000000000..b376215dca70
--- /dev/null
+++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserTenant.ps1
@@ -0,0 +1,107 @@
+function Push-DomainAnalyserTenant {
+ <#
+ .FUNCTIONALITY
+ Entrypoint
+ #>
+ param($Item)
+
+ $Tenant = Get-Tenants | Where-Object { $_.customerId -eq $Item.customerId }
+ $DomainTable = Get-CippTable -tablename 'Domains'
+
+ if ($Tenant.Excluded -eq $true) {
+ $Filter = "PartitionKey eq 'TenantDomains' and TenantId eq '{0}'" -f $Exclude.defaultDomainName
+ $CleanupRows = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter
+ $CleanupCount = ($CleanupRows | Measure-Object).Count
+ if ($CleanupCount -gt 0) {
+ Write-LogMessage -API 'DomainAnalyser' -tenant $Exclude.defaultDomainName -message "Cleaning up $CleanupCount domain(s) for excluded tenant" -sev Info
+ Remove-AzDataTableEntity @DomainTable -Entity $CleanupRows
+ }
+ } elseif ($Tenant.GraphErrorCount -gt 50) {
+ return
+ } else {
+ 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 '*.excl.cloud' -and $_.id -NotLike '*.codetwo.online' -and $_.id -NotLike '*.call2teams.com' -and $_.isVerified) }
+
+ $TenantDomains = foreach ($d in $domains) {
+ [PSCustomObject]@{
+ Tenant = $Tenant.defaultDomainName
+ TenantGUID = $Tenant.customerId
+ InitialDomainName = $Tenant.initialDomainName
+ Domain = $d.id
+ AuthenticationType = $d.authenticationType
+ IsAdminManaged = $d.isAdminManaged
+ IsDefault = $d.isDefault
+ IsInitial = $d.isInitial
+ IsRoot = $d.isRoot
+ IsVerified = $d.isVerified
+ SupportedServices = $d.supportedServices
+ }
+ }
+
+ $DomainCount = ($TenantDomains | Measure-Object).Count
+ if ($DomainCount -gt 0) {
+ Write-Host "$TenantCount tenant Domains"
+ try {
+ $TenantDomainObjects = foreach ($Tenant in $TenantDomains) {
+ $TenantDetails = ($Tenant | ConvertTo-Json -Compress).ToString()
+ $Filter = "PartitionKey eq '{0}' and RowKey eq '{1}'" -f $Tenant.Tenant, $Tenant.Domain
+ $OldDomain = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter
+
+ if ($OldDomain) {
+ Remove-AzDataTableEntity @DomainTable -Entity $OldDomain | Out-Null
+ }
+
+ $Filter = "PartitionKey eq 'TenantDomains' and RowKey eq '{0}'" -f $Tenant.Domain
+ $Domain = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter
+
+ if (!$Domain -or $null -eq $Domain.TenantGUID) {
+ $DomainObject = [pscustomobject]@{
+ DomainAnalyser = ''
+ TenantDetails = $TenantDetails
+ TenantId = $Tenant.Tenant
+ TenantGUID = $Tenant.TenantGUID
+ DkimSelectors = ''
+ MailProviders = ''
+ RowKey = $Tenant.Domain
+ PartitionKey = 'TenantDomains'
+ }
+
+ if ($OldDomain) {
+ $DomainObject.DkimSelectors = $OldDomain.DkimSelectors
+ $DomainObject.MailProviders = $OldDomain.MailProviders
+ }
+ $Domain = $DomainObject
+ } else {
+ $Domain.TenantDetails = $TenantDetails
+ if ($OldDomain) {
+ $Domain.DkimSelectors = $OldDomain.DkimSelectors
+ $Domain.MailProviders = $OldDomain.MailProviders
+ }
+ }
+ # Return domain object to list
+ $Domain
+ }
+
+ # Batch insert tenant domains
+ try {
+ Add-CIPPAzDataTableEntity @DomainTable -Entity $TenantDomainObjects -Force
+ $InputObject = [PSCustomObject]@{
+ Batch = $TenantDomainObjects | Select-Object RowKey, @{n = 'FunctionName'; exp = { 'DomainAnalyserDomain' } }
+ OrchestratorName = "DomainAnalyser_$($Tenant.defaultDomainName)"
+ SkipLog = $true
+ }
+ Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Compress -Depth 5)
+ } 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 $_)
+ }
+ }
+ } catch {
+ Write-Host (Get-CippException -Exception $_)
+ Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.defaultDomainName -message 'DNS Analyser GraphGetRequest' -LogData (Get-CippException -Exception $_) -sev Error
+ }
+ }
+ return $null
+}
\ 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
index e6401a7b117f..6af3ca798606 100644
--- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertMFAAlertUsers.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertMFAAlertUsers.ps1
@@ -6,7 +6,7 @@ function Push-CIPPAlertMFAAlertUsers {
)
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 $($Item.tenant)
+ $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 ', ')"
}
diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewAppApproval.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewAppApproval.ps1
index 438fd62739d6..f5317b9769a5 100644
--- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewAppApproval.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPAlertNewAppApproval.ps1
@@ -6,7 +6,7 @@ function Push-CIPPAlertNewAppApproval {
[pscustomobject]$Item
)
try {
- $Approvals = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identityGovernance/appConsent/appConsentRequests' -tenantid $item.tenant
+ $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."
}
diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1
index b6938cc1389c..221068716478 100644
--- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1
@@ -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 ($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)" })
- }
+ 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)
@@ -274,11 +275,7 @@ Function Push-ExecOnboardTenantQueue {
$Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Clearing tenant cache' })
$y = 0
do {
- try {
- Remove-CIPPCache -tenantsOnly $true
- } catch {}
-
- $Tenant = Get-Tenants -IncludeAll | Where-Object { $_.customerId -eq $Relationship.customer.tenantId } | Select-Object -First 1
+ $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)
@@ -302,6 +299,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 failed at CPV step for $($Relationship.customer.displayName)" -Sev 'Error'
return
}
$Refreshing = $true
@@ -327,10 +325,7 @@ Function Push-ExecOnboardTenantQueue {
$OnboardingSteps.Step4.Status = 'succeeded'
$OnboardingSteps.Step4.Message = 'CPV permissions refreshed'
if ($Tenant.defaultDomainName -match 'Domain Error') {
- try {
- Remove-CIPPCache -tenantsOnly $true
- } catch {}
- $Tenant = Get-Tenants -IncludeAll | Where-Object { $_.customerId -eq $Relationship.customer.tenantId } | Select-Object -First 1
+ $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' })
@@ -364,6 +359,7 @@ Function Push-ExecOnboardTenantQueue {
} catch {
$UserCount = 0
$ApiError = $_.Exception.Message
+ $ApiException = $_
}
if ($UserCount -gt 0) {
@@ -375,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'
@@ -383,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 {
@@ -392,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-GetTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-GetTenants.ps1
new file mode 100644
index 000000000000..4d9e274b7aaa
--- /dev/null
+++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-GetTenants.ps1
@@ -0,0 +1,10 @@
+function Push-GetTenants {
+ Param($Item)
+
+ $Params = $Item.TenantParams | ConvertTo-Json | ConvertFrom-Json -AsHashtable
+ try {
+ Get-Tenants @Params | Select-Object customerId, @{n = 'FunctionName'; e = { $Item.DurableName } }
+ } catch {
+ Write-Host "GetTenants Exception $($_.Exception.Message)"
+ }
+}
\ No newline at end of file
diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-PublicWebhookProcess.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-PublicWebhookProcess.ps1
index 4d321a825f4e..fa4872195b0e 100644
--- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-PublicWebhookProcess.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-PublicWebhookProcess.ps1
@@ -6,12 +6,14 @@ function Push-PublicWebhookProcess {
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
+ Remove-AzDataTableEntity @WebhookIncoming -Entity $Entity
}
}
\ No newline at end of file
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..1644f9a961dc
--- /dev/null
+++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecPartnerWebhook.ps1
@@ -0,0 +1,55 @@
+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' {
+ try {
+ $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'
+ } catch {}
+ if (!$Results) {
+ $Results = [PSCustomObject]@{
+ webhoookUrl = 'None'
+ lastModifiedTimestamp = 'Never'
+ webhookEvents = @()
+ }
+ }
+ }
+ '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/HTTP Functions/CIPP/Scheduler/Invoke-ExecScheduledCommand.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ExecScheduledCommand.ps1
deleted file mode 100644
index ce73476b5c97..000000000000
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/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/HTTP Functions/CIPP/Settings/Invoke-ExecDnsConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecDnsConfig.ps1
index 067da939c2ca..41768a704af5 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/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/HTTP Functions/CIPP/Settings/Invoke-ExecExcludeTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExcludeTenant.ps1
index ca63f9f20afa..ca3e85feed9a 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/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/Email-Exchange/Invoke-ExecSetOoO.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecSetOoO.ps1
index 128df28ca6a9..c0a004791254 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecSetOoO.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Invoke-ExecSetOoO.ps1
@@ -21,13 +21,22 @@ Function Invoke-ExecSetOoO {
}
$StartTime = $Request.body.StartTime
$EndTime = $Request.body.EndTime
-
+
+ $OutOfOffice = @{
+ userid = $Request.body.user
+ InternalMessage = $InternalMessage
+ ExternalMessage = $ExternalMessage
+ TenantFilter = $TenantFilter
+ State = $Request.Body.AutoReplyState
+ APIName = $APINAME
+ ExecutingUser = $request.headers.'x-ms-client-principal'
+ StartTime = $StartTime
+ EndTime = $EndTime
+ }
+ Write-Host ($OutOfOffice | ConvertTo-Json -Depth 10)
+
$Results = try {
- if ($Request.Body.AutoReplyState -ne 'Scheduled') {
- Set-CIPPOutOfOffice -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -InternalMessage $InternalMessage -ExternalMessage $ExternalMessage -State $Request.Body.AutoReplyState
- } else {
- Set-CIPPOutOfOffice -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -InternalMessage $InternalMessage -ExternalMessage $ExternalMessage -StartTime $StartTime -EndTime $EndTime -State $Request.Body.AutoReplyState
- }
+ Set-CIPPOutOfOffice @OutOfOffice
} catch {
"Could not add out of office message for $($username). Error: $($_.Exception.Message)"
}
diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSites.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSites.ps1
index 51f31ef28b70..48f3f8badb80 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSites.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSites.ps1
@@ -59,7 +59,7 @@ Function Invoke-ListSites {
# 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/HTTP Functions/Teams-Sharepoint/Invoke-ListTeams.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListTeams.ps1
index 5c83df245e5f..c5969ad1d8ae 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/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/HTTP Functions/Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1
index 38d6f00157bc..9f843768ab9b 100644
--- 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
@@ -54,6 +54,18 @@ function Invoke-PublicWebhooks {
## 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 {
@@ -90,6 +102,7 @@ function Invoke-PublicWebhooks {
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)"
diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenantDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenantDetails.ps1
index 85e3330926c0..9aa191e2e44f 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenantDetails.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenantDetails.ps1
@@ -14,13 +14,14 @@ Function Invoke-ListTenantDetails {
try {
$tenantfilter = $Request.Query.TenantFilter
- $org = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/organization' -tenantid $tenantfilter | Select-Object displayName, city, country, countryLetterCode, street, state, postalCode,
+ $org = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/organization' -tenantid $tenantfilter | Select-Object displayName, id, city, country, countryLetterCode, street, state, postalCode,
@{ Name = 'businessPhones'; Expression = { $_.businessPhones -join ', ' } },
@{ Name = 'technicalNotificationMails'; Expression = { $_.technicalNotificationMails -join ', ' } },
tenantType, createdDateTime, onPremisesLastPasswordSyncDateTime, onPremisesLastSyncDateTime, onPremisesSyncEnabled, assignedPlans
} catch {
$org = [PSCustomObject]@{
displayName = 'Error loading tenant'
+ id = ''
city = ''
country = ''
countryLetterCode = ''
diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenants.ps1
index 3d2967edb591..3c183a98af86 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenants.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Tenant/Invoke-ListTenants.ps1
@@ -21,15 +21,12 @@ 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
- }
+ Get-Tenants -IncludeAll -TriggerRefresh
+ }
+ if ($Request.query.TriggerRefresh) {
+ Get-Tenants -IncludeAll -TriggerRefresh
+ }
try {
$tenantfilter = $Request.Query.TenantFilter
$Tenants = Get-Tenants -IncludeErrors -SkipDomains
diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddCAPolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddCAPolicy.ps1
index ab6635459e4b..547828a337f4 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/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/HTTP Functions/Tenant/Conditional/Invoke-AddCATemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-AddCATemplate.ps1
index 7fc95e0d7af1..3f4b8d651650 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/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/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/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPInvite.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPInvite.ps1
index d6a5965f2055..4739df9c2df1 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/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/HTTP Functions/Tenant/Standards/Invoke-AddStandardsDeploy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-AddStandardsDeploy.ps1
index 567452b932c7..a97bb06cf6de 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/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) {
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/HTTP Functions/Tenant/Standards/Invoke-ListBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListBPA.ps1
index 7ff48e0ca1c9..0e632d4fba35 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListBPA.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListBPA.ps1
@@ -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/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/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1
index 6587c2c41822..f04c365c5ccf 100644
--- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1
@@ -33,7 +33,10 @@ Function Invoke-ExecGraphExplorerPreset {
}
$params = $Request.Body.preset | Select-Object endpoint, '$filter', '$select', '$count', '$expand', '$search', NoPagination, '$top', IsShared
- if ($params.'$select') { $params.'$select' = ($params.'$select').value -join ',' }
+
+ if ($params.'$select'.value) {
+ $params.'$select' = ($params.'$select').value -join ','
+ }
$Preset = [PSCustomObject]@{
PartitionKey = 'Preset'
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-ListGraphRequest.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphRequest.ps1
index 2cd1d9cd9bac..95866c41c395 100644
--- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphRequest.ps1
+++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphRequest.ps1
@@ -133,8 +133,13 @@ function Invoke-ListGraphRequest {
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-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/Get-CIPPAuthentication.ps1 b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1
index 0d3092fc54c3..9b1885d5c465 100644
--- a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1
+++ b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1
@@ -30,7 +30,7 @@ function Get-CIPPAuthentication {
return $true
} catch {
- Write-LogMessage -message "Could not retrieve keys from Keyvault: $($_.Exception.Message)" -Sev 'CRITICAL' -API 'CIPP Authentication'
+ 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/Get-CIPPDomainAnalyser.ps1 b/Modules/CIPPCore/Public/Get-CIPPDomainAnalyser.ps1
index e39e6d5953ba..fc4ad53915a1 100644
--- a/Modules/CIPPCore/Public/Get-CIPPDomainAnalyser.ps1
+++ b/Modules/CIPPCore/Public/Get-CIPPDomainAnalyser.ps1
@@ -6,7 +6,7 @@ function Get-CIPPDomainAnalyser {
# Get all the things
if ($TenantFilter -ne 'AllTenants') {
- $DomainTable.Filter = "TenantId eq '{0}'" -f $TenantFilter
+ $DomainTable.Filter = "TenantGUID eq '{0}'" -f $TenantFilter
}
try {
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-Tenants.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1
index bcb9b94ef9cc..02196d6f58e4 100644
--- a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1
+++ b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1
@@ -10,7 +10,8 @@ function Get-Tenants {
[switch]$IncludeAll,
[switch]$IncludeErrors,
[switch]$SkipDomains,
- [switch]$TriggerRefresh
+ [switch]$TriggerRefresh,
+ [switch]$CleanOld
)
$TenantsTable = Get-CippTable -tablename 'Tenants'
@@ -32,11 +33,27 @@ function Get-Tenants {
if (($IncludedTenantsCache | Measure-Object).Count -eq 0) {
$BuildRequired = $true
- }
+ }
+
+ if ($CleanOld) {
+ $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
+ }
+ }
+ $CurrentTenants = Get-CIPPAzDataTableEntity @TenantsTable -Filter "PartitionKey eq 'Tenants' and Excluded eq false"
+ $CurrentTenants | Where-Object { $_.customerId -notin $GDAPList.customerId } | ForEach-Object {
+ Remove-AzDataTableEntity @TenantsTable -Entity $_
+ }
+ }
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
+ $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
@@ -45,16 +62,15 @@ function Get-Tenants {
relationshipEnd = $Relationship.endDateTime
}
}
+
$ActiveRelationships = $GDAPList | Where-Object { $_.customerId -notin $SkipListCache.customerId }
- $TenantList = $ActiveRelationships | Group-Object -Property customerId | ForEach-Object -Parallel {
+ $TenantList = $ActiveRelationships | Group-Object -Property customerId | ForEach-Object {
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) {
+ $ExistingTenantInfo = Get-CIPPAzDataTableEntity @TenantsTable -Filter "PartitionKey eq 'Tenants' and RowKey eq '$($_.Name)'"
+ if ($ExistingTenantInfo -and $ExistingTenantInfo.RequiresRefresh -eq $false) {
Write-Host 'Existing tenant found. We already have it cached, skipping.'
$ExistingTenantInfo
- continue
+ return
}
$LatestRelationship = $_.Group | Sort-Object -Property relationshipEnd | Select-Object -Last 1
$AutoExtend = ($_.Group | Where-Object { $_.autoExtend -eq $true } | Measure-Object).Count -gt 0
@@ -72,13 +88,12 @@ function Get-Tenants {
$defaultDomainName = $Domain
$initialDomainName = $Domain
$RequiresRefresh = $true
-
+
} catch {
Write-LogMessage -API 'Get-Tenants' -message "Tried adding $($LatestRelationship.customerId) to tenant list but failed to get domains - $($_.Exception.Message)" -level 'Critical'
-
}
}
-
+
[PSCustomObject]@{
PartitionKey = 'Tenants'
RowKey = $_.Name
@@ -120,17 +135,17 @@ function Get-Tenants {
}) | Out-Null
}
foreach ($Tenant in $TenantList) {
- if ($Tenant.defaultDomainName -eq 'Invalid' -or !$Tenant.defaultDomainName) { continue }
+ if ($Tenant.defaultDomainName -eq 'Invalid' -or !$Tenant.defaultDomainName) {
+ Write-LogMessage -API 'Get-Tenants' -message "We're skipping $($Tenant.displayName) as it has an invalid default domain name. Something is up with this instance." -level 'Critical'
+ continue
+ }
$IncludedTenantsCache.Add($Tenant) | Out-Null
}
- }
-
- 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 -Context $TenantsTable -Entity $_ -Force
+ if ($IncludedTenantsCache) {
+ Add-CIPPAzDataTableEntity @TenantsTable -Entity $IncludedTenantsCache -Force | Out-Null
}
}
+
+
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-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/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/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-CIPPWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1
index db62dae8b160..86e7e8a41c3f 100644
--- a/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1
+++ b/Modules/CIPPCore/Public/Invoke-CIPPWebhookProcessing.ps1
@@ -82,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 }
@@ -130,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
}
@@ -165,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 {
@@ -211,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'
@@ -220,9 +220,9 @@ 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' {
diff --git a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1
index 1d749a69bf76..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
)
@@ -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/beta/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..378df5a1cb48 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,77 @@ 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)
+ }
+ try {
+ $EventCompare = Compare-Object $EventList ($MatchedWebhook.EventType | ConvertFrom-Json)
+ } catch {
+ $EventCompare = $false
+ }
+ 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 { $Existing = $false }
+ if (!$Existing -or $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 +155,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 +173,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 +182,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/Set-CIPPOutOfoffice.ps1 b/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1
index 79b6222f5c37..ee1266ca949d 100644
--- a/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1
+++ b/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1
@@ -6,7 +6,7 @@ function Set-CIPPOutOfOffice {
$ExternalMessage,
$TenantFilter,
$State,
- $APIName = "Set Out of Office",
+ $APIName = 'Set Out of Office',
$ExecutingUser,
$StartTime,
$EndTime
@@ -19,19 +19,17 @@ function Set-CIPPOutOfOffice {
if (-not $EndTime) {
$EndTime = (Get-Date $StartTime).AddDays(7)
}
- if ($State -ne "Scheduled") {
- $OutOfOffice = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-MailboxAutoReplyConfiguration" -cmdParams @{Identity = $userid; AutoReplyState = $State; InternalMessage = $InternalMessage; ExternalMessage = $ExternalMessage } -Anchor $userid
- Write-LogMessage -user $ExecutingUser -API $APIName -message "Set Out-of-office for $($userid) to $state" -Sev "Info" -tenant $TenantFilter
+ if ($State -ne 'Scheduled') {
+ $OutOfOffice = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-MailboxAutoReplyConfiguration' -cmdParams @{Identity = $userid; AutoReplyState = $State; InternalMessage = $InternalMessage; ExternalMessage = $ExternalMessage } -Anchor $userid
+ 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."
- }
- else {
- $OutOfOffice = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-MailboxAutoReplyConfiguration" -cmdParams @{Identity = $userid; AutoReplyState = $State; InternalMessage = $InternalMessage; ExternalMessage = $ExternalMessage; StartTime = $StartTime; EndTime = $EndTime } -Anchor $userid
- Write-LogMessage -user $ExecutingUser -API $APIName -message "Scheduled Out-of-office for $($userid) between $StartTime and $EndTime" -Sev "Info" -tenant $TenantFilter
+ } else {
+ $OutOfOffice = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-MailboxAutoReplyConfiguration' -cmdParams @{Identity = $userid; AutoReplyState = $State; InternalMessage = $InternalMessage; ExternalMessage = $ExternalMessage; StartTime = $StartTime; EndTime = $EndTime } -Anchor $userid
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Scheduled Out-of-office for $($userid) between $StartTime and $EndTime" -Sev 'Info' -tenant $TenantFilter
return "Scheduled Out-of-office for $($userid) between $($StartTime.toString()) and $($EndTime.toString())"
}
- }
- 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/Set-CIPPSignature.ps1 b/Modules/CIPPCore/Public/Set-CIPPSignature.ps1
new file mode 100644
index 000000000000..e3ad3c8dd83e
--- /dev/null
+++ b/Modules/CIPPCore/Public/Set-CIPPSignature.ps1
@@ -0,0 +1,27 @@
+function Set-CIPPSignature {
+ [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/Test-CIPPAccessPermissions.ps1 b/Modules/CIPPCore/Public/Test-CIPPAccessPermissions.ps1
index f82b937d9e57..110f2fde20e4 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,12 +20,12 @@ 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
-
+ $null = 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/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1
index f0bf7fcf2d1b..7c1b9b56833d 100644
--- a/Modules/CippEntrypoints/CippEntrypoints.psm1
+++ b/Modules/CippEntrypoints/CippEntrypoints.psm1
@@ -57,6 +57,7 @@ function Receive-CippOrchestrationTrigger {
} else {
$OrchestratorInput = $Context.Input
}
+ Write-Host "Orchestrator started $($OrchestratorInput.OrchestratorName)"
$DurableRetryOptions = @{
FirstRetryInterval = (New-TimeSpan -Seconds 5)
@@ -77,16 +78,17 @@ function Receive-CippOrchestrationTrigger {
}
if (($Batch | Measure-Object).Count -gt 0) {
- foreach ($Item in $Batch) {
- $null = Invoke-DurableActivity -FunctionName 'CIPPActivityFunction' -Input $Item -NoWait -RetryOptions $RetryOptions -ErrorAction Stop
+ $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)"
+ Write-Host "Orchestrator error $($_.Exception.Message) line $($_.InvocationInfo.ScriptLineNumber)"
}
}
diff --git a/Modules/CippExtensions/Private/New-HaloPSATicket.ps1 b/Modules/CippExtensions/Private/New-HaloPSATicket.ps1
index 5e14f3e1e80d..16b4cef697f8 100644
--- a/Modules/CippExtensions/Private/New-HaloPSATicket.ps1
+++ b/Modules/CippExtensions/Private/New-HaloPSATicket.ps1
@@ -18,6 +18,7 @@ function New-HaloPSATicket {
lookupdisplay = 'Enter Details Manually'
}
client_id = ($client | Select-Object -Last 1)
+ _forcereassign = $true
site_id = $null
user_name = $null
reportedby = $null
diff --git a/Scheduler_GetWebhooks/run.ps1 b/Scheduler_GetWebhooks/run.ps1
index b262f320738b..b55b57d1f05c 100644
--- a/Scheduler_GetWebhooks/run.ps1
+++ b/Scheduler_GetWebhooks/run.ps1
@@ -3,11 +3,12 @@ param($Timer)
try {
$webhookTable = Get-CIPPTable -tablename webhookTable
- $Webhooks = Get-CIPPAzDataTableEntity @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'
@@ -16,8 +17,10 @@ try {
}
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 - $($_.Exception.Message)" -sev Error
+ 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/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 6ffbe8ba8ebd..d2ff458a0121 100644
--- a/version_latest.txt
+++ b/version_latest.txt
@@ -1 +1 @@
-5.4.3
+5.5.3
\ No newline at end of file