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/run.ps1 b/DomainAnalyser_All/run.ps1 index c9f0b989c04e..981afa4b7bbc 100644 --- a/DomainAnalyser_All/run.ps1 +++ b/DomainAnalyser_All/run.ps1 @@ -36,6 +36,7 @@ try { $Result = [PSCustomObject]@{ Tenant = $Tenant.Tenant + TenantID = $Tenant.TenantGUID GUID = $($Domain.Replace('.', '')) LastRefresh = $(Get-Date (Get-Date).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z') Domain = $Domain @@ -116,8 +117,8 @@ try { $ScoreExplanation.Add('No SPF Record Found') | Out-Null } } catch { - $Message = 'SPF Exception: {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -sev Error + $Message = 'SPF Error' + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error throw $Message } @@ -179,8 +180,8 @@ try { } } } catch { - $Message = 'DMARC Exception: {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -sev Error + $Message = 'DMARC Error' + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error throw $Message } @@ -197,8 +198,8 @@ try { $ScoreExplanation.Add('DNSSEC Not Configured or Enabled') | Out-Null } } catch { - $Message = 'DNSSEC Exception: {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -sev Error + $Message = 'DNSSEC Error' + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error throw $Message } @@ -226,8 +227,8 @@ try { $ScoreExplanation.Add('DKIM Not Configured') | Out-Null } } catch { - $Message = 'DKIM Exception: {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -sev Error + $Message = 'DKIM Exception' + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.tenant -message $Message -LogData (Get-CippException -Exception $_) -sev Error throw $Message } # Final Score diff --git a/DomainAnalyser_GetTenantDomains/run.ps1 b/DomainAnalyser_GetTenantDomains/run.ps1 index 4e0fd71f2be8..6c4b8b0d8110 100644 --- a/DomainAnalyser_GetTenantDomains/run.ps1 +++ b/DomainAnalyser_GetTenantDomains/run.ps1 @@ -9,10 +9,13 @@ $TenantDomains = $Tenants | ForEach-Object -Parallel { $Tenant = $_ # Get Domains to Lookup try { - $Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant.defaultDomainName | Where-Object { ($_.id -notlike '*.microsoftonline.com' -and $_.id -NotLike '*.exclaimer.cloud' -and $_.id -NotLike '*.codetwo.online' -and $_.id -NotLike '*.call2teams.com' -and $_.isVerified) } + $Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant.defaultDomainName | Where-Object { ($_.id -notlike '*.microsoftonline.com' -and $_.id -NotLike '*.exclaimer.cloud' -and $_.id -Notlike '*.excl.cloud' -and $_.id -NotLike '*.codetwo.online' -and $_.id -NotLike '*.call2teams.com' -and $_.isVerified) } + foreach ($d in $domains) { [PSCustomObject]@{ Tenant = $Tenant.defaultDomainName + TenantGUID = $Tenant.customerId + InitialDomainName = $Tenant.initialDomainName Domain = $d.id AuthenticationType = $d.authenticationType IsAdminManaged = $d.isAdminManaged @@ -24,7 +27,7 @@ $TenantDomains = $Tenants | ForEach-Object -Parallel { } } } catch { - Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.defaultDomainName -message "DNS Analyser GraphGetRequest Exception: $($_.Exception.Message)" -sev Error + Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.defaultDomainName -message 'DNS Analyser GraphGetRequest' -LogData (Get-CippException -Exception $_) -sev Error } } | Sort-Object -Unique -Property Domain @@ -57,11 +60,12 @@ if ($TenantCount -gt 0) { $Filter = "PartitionKey eq 'TenantDomains' and RowKey eq '{0}'" -f $Tenant.Domain $Domain = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter - if (!$Domain) { + if (!$Domain -or $null -eq $Domain.TenantGUID) { $DomainObject = [pscustomobject]@{ DomainAnalyser = '' TenantDetails = $TenantDetails TenantId = $Tenant.Tenant + TenantGUID = $Tenant.TenantGUID DkimSelectors = '' MailProviders = '' RowKey = $Tenant.Domain @@ -87,6 +91,6 @@ if ($TenantCount -gt 0) { # Batch insert all tenant domains try { Add-CIPPAzDataTableEntity @DomainTable -Entity $TenantDomainObjects -Force - } catch { Write-LogMessage -API 'DomainAnalyser' -message "Domain Analyser GetTenantDomains Error $($_.Exception.Message)" -sev info } - } catch { Write-LogMessage -API 'DomainAnalyser' -message "GetTenantDomains loop exception: $($_.Exception.Message) line $($_.InvocationInfo.ScriptLineNumber)" -sev 'Error' } + } catch { Write-LogMessage -API 'DomainAnalyser' -message 'Domain Analyser GetTenantDomains error' -sev info -LogData (Get-CippException -Exception $_) } + } catch { Write-LogMessage -API 'DomainAnalyser' -message 'GetTenantDomains loop error' -sev 'Error' -LogData (Get-CippException -Exception $_) } } diff --git a/DomainAnalyser_Orchestration/run.ps1 b/DomainAnalyser_Orchestration/run.ps1 index 34848e03284d..e8d51585ffa7 100644 --- a/DomainAnalyser_Orchestration/run.ps1 +++ b/DomainAnalyser_Orchestration/run.ps1 @@ -33,7 +33,7 @@ try { Write-Host "Orchestrator exception UpdateDomains $($_.Exception.Message)" } } catch { - Write-LogMessage -API 'DomainAnalyser' -message "Domain Analyser Orchestrator Error $($_.Exception.Message)" -sev info + Write-LogMessage -API 'DomainAnalyser' -message 'Domain Analyser Orchestrator Error' -sev info -LogData (Get-CippException -Exception $_) #Write-Host $_.Exception | ConvertTo-Json } finally { Write-LogMessage -API 'DomainAnalyser' -message 'Domain Analyser has Finished' -sev Info 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/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 cc23c195820c..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) @@ -298,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 @@ -357,6 +359,7 @@ Function Push-ExecOnboardTenantQueue { } catch { $UserCount = 0 $ApiError = $_.Exception.Message + $ApiException = $_ } if ($UserCount -gt 0) { @@ -368,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' @@ -376,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 { @@ -385,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-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..d529d55b1f0f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecPartnerWebhook.ps1 @@ -0,0 +1,46 @@ +function Invoke-ExecPartnerWebhook { + Param($Request, $TriggerMetadata) + + switch ($Request.Query.Action) { + 'ListEventTypes' { + $Uri = 'https://api.partnercenter.microsoft.com/webhooks/v1/registration/events' + $Results = New-GraphGetRequest -uri $Uri -tenantid $env:TenantID -NoAuthCheck $true -scope 'https://api.partnercenter.microsoft.com/.default' + } + 'ListSubscription' { + $Uri = 'https://api.partnercenter.microsoft.com/webhooks/v1/registration' + $Results = New-GraphGetRequest -uri $Uri -tenantid $env:TenantID -NoAuthCheck $true -scope 'https://api.partnercenter.microsoft.com/.default' + } + 'CreateSubscription' { + $BaseURL = ([System.Uri]$request.headers.'x-ms-original-url').Host + $Webhook = @{ + TenantFilter = $env:TenantId + PartnerCenter = $true + BaseURL = $BaseURL + EventType = $Request.body.EventType + ExecutingUser = $Request.headers.'x-ms-client-principal' + } + $Results = New-CIPPGraphSubscription @Webhook + } + 'SendTest' { + $Results = New-GraphPOSTRequest -uri 'https://api.partnercenter.microsoft.com/webhooks/v1/registration/validationEvents' -tenantid $env:TenantID -NoAuthCheck $true -scope 'https://api.partnercenter.microsoft.com/.default' + } + 'ValidateTest' { + $Results = New-GraphGetRequest -uri "https://api.partnercenter.microsoft.com/webhooks/v1/registration/validationEvents/$($Request.Query.CorrelationId)" -tenantid $env:TenantID -NoAuthCheck $true -scope 'https://api.partnercenter.microsoft.com/.default' + } + default { + $Results = 'Invalid Action' + } + } + + $Body = [PSCustomObject]@{ + Results = $Results + Metadata = [PSCustomObject]@{ + Action = $Request.Query.Action + } + } + + Push-OutputBinding -Name Response -Value @{ + StatusCode = [System.Net.HttpStatusCode]::OK + Body = $Body + } +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/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/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 730cbba9d457..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 { 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..2cb51e4d929a 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 @@ -32,11 +32,11 @@ function Get-Tenants { if (($IncludedTenantsCache | Measure-Object).Count -eq 0) { $BuildRequired = $true - } + } 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 @@ -72,13 +72,13 @@ 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 @@ -129,7 +129,7 @@ function Get-Tenants { 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 + Remove-AzDataTableEntity @TenantsTable -Entity $_ } } 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..e744232e9e60 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -11,7 +11,8 @@ function New-CIPPGraphSubscription { $EventType, $APIName = 'Create Webhook', $ExecutingUser, - [Switch]$Recreate + [Switch]$Recreate, + [switch]$PartnerCenter ) $CIPPID = (New-Guid).GUID $WebhookTable = Get-CIPPTable -TableName webhookTable @@ -76,13 +77,73 @@ function New-CIPPGraphSubscription { } } } + } elseif ($PartnerCenter.IsPresent) { + $WebhookFilter = "PartitionKey eq '$($env:TenantId)'" + $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter + $CIPPID = $env:TenantId + $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq 'PartnerCenter' -and $_.RowKey -eq $CIPPID } + + # Required event types + $EventList = [System.Collections.Generic.List[string]]@('test-created', 'granular-admin-relationship-approved') + if (($EventType | Measure-Object).count -gt 0) { + foreach ($Event in $EventType) { + if ($EventList -notcontains $Event) { + $EventList.Add($Event) + } + } + } + + $Body = [PSCustomObject]@{ + WebhookUrl = "https://$BaseURL/API/PublicWebhooks?CIPPID=$($CIPPID)&Type=PartnerCenter" + WebhookEvents = @($EventList) + } + $EventCompare = Compare-Object $EventList ($MatchedWebhook.EventType | ConvertFrom-Json) + try { + $Uri = 'https://api.partnercenter.microsoft.com/webhooks/v1/registration' + try { + $Existing = New-GraphGetRequest -NoAuthCheck $true -uri $Uri -tenantid $env:TenantId -scope 'https://api.partnercenter.microsoft.com/.default' + } catch {} + if ($Existing.webhookUrl -ne $MatchedWebhook.WebhookNotificationUrl -or $EventCompare) { + if ($Existing.WebhookUrl) { + $Action = 'Updated' + $Method = 'PUT' + Write-Host 'updating webhook' + } else { + $Action = 'Created' + $Method = 'POST' + Write-Host 'creating webhook' + } + + $Uri = 'https://api.partnercenter.microsoft.com/webhooks/v1/registration' + $GraphRequest = New-GraphPOSTRequest -uri $Uri -type $Method -tenantid $env:TenantId -scope 'https://api.partnercenter.microsoft.com/.default' -body ($Body | ConvertTo-Json) -NoAuthCheck $true + + $WebhookRow = @{ + PartitionKey = [string]$CIPPID + RowKey = [string]$CIPPID + EventType = [string](ConvertTo-Json -InputObject $EventList) + Resource = [string]'PartnerCenter' + SubscriptionID = [string]$GraphRequest.SubscriberId + Expiration = 'Does Not Expire' + WebhookNotificationUrl = [string]$Body.WebhookUrl + } + $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow -Force + Write-LogMessage -user $ExecutingUser -API $APIName -message "$Action Partner Center Webhook subscription" -Sev 'Info' -tenant 'PartnerTenant' + return "$Action Partner Center Webhook subscription" + } else { + Write-LogMessage -user $ExecutingUser -API $APIName -message 'Existing Partner Center Webhook subscription found' -Sev 'Info' -tenant 'PartnerTenant' + return 'Existing Partner Center Webhook subscription found' + } + } catch { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to create Partner Center Webhook Subscription: $($_.Exception.Message)" -Sev 'Error' -tenant 'PartnerTenant' + return "Failed to create Partner Webhook Subscription: $($_.Exception.Message)" + } + } else { # First check if there is an exsiting Webhook in place $WebhookFilter = "PartitionKey eq '$($TenantFilter)'" $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } - if (($MatchedWebhook | Measure-Object).count -eq 0 -or $Recreate) { - + if (($MatchedWebhook | Measure-Object).count -eq 0 -or $Recreate.IsPresent) { $expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $params = @{ changeType = $TypeofSubscription @@ -90,10 +151,10 @@ function New-CIPPGraphSubscription { resource = $Resource expirationDateTime = $expiredate } | ConvertTo-Json - + $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/subscriptions' -tenantid $TenantFilter -type POST -body $params -verbose - #If creation is succesfull, we store the GUID in the storage table webhookTable to make sure we can check against this later on. + #If creation is succesfull, we store the GUID in the storage table webhookTable to make sure we can check against this later on. #We store the GUID as rowkey, the event type, the resource, and the expiration date as properties, we also add the Tenant name so we can easily find this later on. #We don't store the return, because Ms decided that a renewal or re-authenticate does not change the url, but does change the id... $WebhookRow = @{ @@ -108,7 +169,7 @@ function New-CIPPGraphSubscription { } $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow #todo: add remove webhook function, add check webhook function, add list webhooks function - #add refresh webhook function based on table. + #add refresh webhook function based on table. Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Graph Webhook subscription for $($TenantFilter)" -Sev 'Info' -tenant $TenantFilter } else { Write-LogMessage -user $ExecutingUser -API $APIName -message "Existing Graph Webhook subscription for $($TenantFilter) found" -Sev 'Info' -tenant $TenantFilter @@ -117,8 +178,6 @@ function New-CIPPGraphSubscription { return "Created Webhook subscription for $($TenantFilter)" } catch { Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to create Webhook Subscription: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter - Return "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)" + Return "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)" } - } - diff --git a/Modules/CIPPCore/Public/Set-CIPPSignature.ps1 b/Modules/CIPPCore/Public/Set-CIPPSignature.ps1 new file mode 100644 index 000000000000..fa2de7dc415b --- /dev/null +++ b/Modules/CIPPCore/Public/Set-CIPPSignature.ps1 @@ -0,0 +1,27 @@ +function Set-CIPPOutOfOffice { + [CmdletBinding()] + param ( + $userid, + $InternalMessage, + $ExternalMessage, + $TenantFilter, + $State, + $APIName = 'Set Outlook Roaming Signature', + $ExecutingUser, + $StartTime, + $EndTime + ) + + try { + $SignatureProfile = @' +[{"name":"Roaming_New_Signature","itemClass":"","id":"","scope":"AdeleV@M365x42953883.OnMicrosoft.com","parentSetting":"","secondaryKey":"","type":"String","timestamp":638296273181532792,"metadata":"","value":"Kelvin","isFirstSync":"true","source":"UserOverride"}] +'@ + $GraphRequest = New-GraphPostRequest -uri 'https://substrate.office.com/ows/beta/outlookcloudsettings/settings/global' -tenantid $TenantFilter -type PATCH -contentType 'application/json' -verbose -scope 'https://outlook.office.com/.default' + Write-LogMessage -user $ExecutingUser -API $APIName -message "Set Out-of-office for $($userid) to $state" -Sev 'Info' -tenant $TenantFilter + return "Set Out-of-office for $($userid) to $state." + + } catch { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not add OOO for $($userid)" -Sev 'Error' -tenant $TenantFilter + return "Could not add out of office message for $($userid). Error: $($_.Exception.Message)" + } +} diff --git a/Modules/CIPPCore/Public/Test-CIPPAccessPermissions.ps1 b/Modules/CIPPCore/Public/Test-CIPPAccessPermissions.ps1 index f82b937d9e57..ac2629135250 100644 --- a/Modules/CIPPCore/Public/Test-CIPPAccessPermissions.ps1 +++ b/Modules/CIPPCore/Public/Test-CIPPAccessPermissions.ps1 @@ -2,7 +2,7 @@ function Test-CIPPAccessPermissions { [CmdletBinding()] param ( $TenantFilter, - $APIName = "Access Check", + $APIName = 'Access Check', $ExecutingUser ) Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Started permissions check' -Sev 'Debug' @@ -20,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 - + 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..02651d9a2a3f 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -50,7 +50,7 @@ function Receive-CippQueueTrigger { function Receive-CippOrchestrationTrigger { param($Context) - + Write-Host 'Orchestrator started' try { if (Test-Json -Json $Context.Input) { $OrchestratorInput = $Context.Input | ConvertFrom-Json @@ -77,9 +77,10 @@ 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) { 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 3238344b3b0d..c7ba1e87f75e 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.4.4 \ No newline at end of file +5.5.0 \ No newline at end of file