Skip to content

Commit

Permalink
Merge branch 'KelvinTegelaar:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
jephens authored Dec 3, 2024
2 parents dc1d267 + a352c4c commit 0d56975
Show file tree
Hide file tree
Showing 29 changed files with 547 additions and 298 deletions.
8 changes: 8 additions & 0 deletions CIPPTimers.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,13 @@
"Priority": 15,
"RunOnProcessor": true,
"IsSystem": true
},
{
"Command": "Start-TableCleanup",
"Description": "Timer to cleanup tables",
"Cron": "0 0 23 * * *",
"Priority": 20,
"RunOnProcessor": true,
"IsSystem": true
}
]
2 changes: 1 addition & 1 deletion Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function Add-CIPPScheduledTask {
$Parameters = [System.Collections.Hashtable]@{}
foreach ($Key in $task.Parameters.PSObject.Properties.Name) {
$Param = $task.Parameters.$Key
if ($Param -is [System.Collections.IDictionary]) {
if ($Param -is [System.Collections.IDictionary] -or $Param.Key) {
$ht = @{}
foreach ($p in $Param.GetEnumerator()) {
$ht[$p.Key] = $p.Value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,21 @@ function Get-CippAuditLogSearchResults {
[string]$TenantFilter,
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
[Alias('id')]
[string]$QueryId
[string]$QueryId,
[switch]$CountOnly
)

process {
New-GraphGetRequest -uri ('https://graph.microsoft.com/beta/security/auditLog/queries/{0}/records?$top=999' -f $QueryId) -AsApp $true -tenantid $TenantFilter -ErrorAction Stop
$GraphRequest = @{
Uri = ('https://graph.microsoft.com/beta/security/auditLog/queries/{0}/records?$top=999&$count=true' -f $QueryId)
Method = 'GET'
AsApp = $true
tenantid = $TenantFilter
}
if ($CountOnly.IsPresent) {
$GraphRequest.CountOnly = $true
}

New-GraphGetRequest @GraphRequest -ErrorAction Stop
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ function Push-DomainAnalyserTenant {
'*.excl.cloud'
'*.codetwo.online'
'*.call2teams.com'
'*signature365.net'
'*.signature365.net'
'*.myteamsconnect.io'
'*.teams.dstny.com'
)
$Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $Tenant.customerId | Where-Object { $_.isVerified -eq $true } | ForEach-Object {
$Domain = $_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ function Push-AuditLogTenant {
# Get webhook rules
$ConfigEntries = Get-CIPPAzDataTableEntity @ConfigTable
$LogSearchesTable = Get-CippTable -TableName 'AuditLogSearches'

Write-Information ("Audit: Memory usage before processing $([System.GC]::GetTotalMemory($false))")
$SearchCount = 0
$Configuration = $ConfigEntries | Where-Object { ($_.Tenants -match $TenantFilter -or $_.Tenants -match 'AllTenants') }
if ($Configuration) {
try {
Expand Down Expand Up @@ -88,12 +89,17 @@ function Push-AuditLogTenant {
}
}
}
$SearchCount++
Write-Information "Audit: Memory usage after processing $SearchCount searches: $([System.GC]::GetTotalMemory($false))"
}
} catch {
Write-Information ( 'Audit Log search: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message)
}
}
} catch {
Write-Information ( 'Push-AuditLogTenant: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message)
} finally {
Write-Information "Audit Logs: Completed processing $($TenantFilter)"
Write-Information "Audit Logs: Memory usage after processing $([System.GC]::GetTotalMemory($false))"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Function Invoke-ExecRestoreBackup {
$APIName = $TriggerMetadata.FunctionName
Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug'
try {
foreach ($line in ($Request.body | ConvertFrom-Json | Select-Object * -ExcludeProperty ETag)) {
foreach ($line in ($Request.body | ConvertFrom-Json | Select-Object * -ExcludeProperty ETag, Timestamp)) {
Write-Host ($line)
$Table = Get-CippTable -tablename $line.table
$ht2 = @{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,19 @@ Function Invoke-ExecDeviceAction {
if ($Request.Query.Action -eq 'setDeviceName') {
$ActionBody = @{ deviceName = $Request.Body.input } | ConvertTo-Json -Compress
}
$ActionResult = New-CIPPDeviceAction -Action $Request.Query.Action -ActionBody $ActionBody -DeviceFilter $Request.Query.GUID -TenantFilter $Request.Query.TenantFilter -ExecutingUser $request.headers.'x-ms-client-principal' -APINAME $APINAME
else {
$ActionBody = $Request.Body | ConvertTo-Json -Compress
}

$cmdparams = @{
Action = $Request.Query.Action
ActionBody = $ActionBody
DeviceFilter = $Request.Query.GUID
TenantFilter = $Request.Query.TenantFilter
ExecutingUser = $request.headers.'x-ms-client-principal'
APINAME = $APINAME
}
$ActionResult = New-CIPPDeviceAction @cmdparams
$body = [pscustomobject]@{'Results' = "$ActionResult" }

} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ Function Invoke-AddGroup {
}
$GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DistributionGroup' -cmdParams $params
}
$GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DistributionGroup' -cmdParams $params
# At some point add logic to use AddOwner/AddMember for New-DistributionGroup, but idk how we're going to brr that - rvdwegen
}
"Successfully created group $($groupobj.displayname) for $($tenant)"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using namespace System.Net

function Invoke-ListPerUserMFA {
<#
.FUNCTIONALITY
Entrypoint
.ROLE
Identity.User.Read
#>
[CmdletBinding()]
param($Request, $TriggerMetadata)

$APIName = $TriggerMetadata.FunctionName
$User = $request.headers.'x-ms-client-principal'
Write-LogMessage -user $User -API $APINAME -message 'Accessed this API' -Sev 'Debug'

# Write to the Azure Functions log stream.
Write-Host 'PowerShell HTTP trigger function processed a request.'

# Parse query parameters
$Tenant = $Request.query.tenantFilter
try {
$AllUsers = [System.Convert]::ToBoolean($Request.query.allUsers)
} catch {
$AllUsers = $false
}
$UserId = $Request.query.userId

# Get the MFA state for the user/all users
try {
if ($AllUsers -eq $true) {
$Results = Get-CIPPPerUserMFA -TenantFilter $Tenant -AllUsers $true
} else {
$Results = Get-CIPPPerUserMFA -TenantFilter $Tenant -userId $UserId
}
$StatusCode = [HttpStatusCode]::OK
} catch {
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
$Results = "Failed to get MFA State for $UserId : $ErrorMessage"
$StatusCode = [HttpStatusCode]::Forbidden
}

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $StatusCode
Body = @($Results)
})


}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ function Invoke-PublicWebhooks {
}
Add-CIPPAzDataTableEntity @WebhookIncoming -Entity $Entity
} else {
return 'Not replying to this webhook or processing it'
$Body = 'This webhook is not authorized.'
$StatusCode = [HttpStatusCode]::Forbidden
}
$Body = 'Webhook Recieved'
$StatusCode = [HttpStatusCode]::OK
Expand All @@ -80,4 +81,4 @@ function Invoke-PublicWebhooks {
StatusCode = $StatusCode
Body = $Body
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,28 @@ function Start-AuditLogOrchestrator {
param()
try {
$AuditLogSearchesTable = Get-CIPPTable -TableName 'AuditLogSearches'
$AuditLogSearches = Get-CIPPAzDataTableEntity @AuditLogSearchesTable -Filter "CippStatus eq 'Pending'"
$15MinutesAgo = (Get-Date).AddMinutes(-15).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
$1DayAgo = (Get-Date).AddDays(-1).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
$AuditLogSearches = Get-CIPPAzDataTableEntity @AuditLogSearchesTable -Filter "(CippStatus eq 'Pending' or (CippStatus eq 'Processing' and Timestamp le datetime'$15MinutesAgo')) and Timestamp ge datetime'$1DayAgo'" -Property PartitionKey, RowKey, Tenant, CippStatus, Timestamp

$WebhookRulesTable = Get-CIPPTable -TableName 'WebhookRules'
$WebhookRules = Get-CIPPAzDataTableEntity @WebhookRulesTable

if (($AuditLogSearches | Measure-Object).Count -eq 0) {
Write-Information 'No audit log searches available'
} elseif (($WebhookRules | Measure-Object).Count -eq 0) {
Write-Information 'No webhook rules defined'
} else {
$Queue = New-CippQueueEntry -Name 'Audit Log Collection' -Reference 'AuditLogCollection' -TotalTasks ($AuditLogSearches).Count
$Batch = $AuditLogSearches | Sort-Object -Property Tenant -Unique | Select-Object @{Name = 'TenantFilter'; Expression = { $_.Tenant } }, @{Name = 'QueueId'; Expression = { $Queue.RowKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogTenant' } }

$InputObject = [PSCustomObject]@{
OrchestratorName = 'AuditLogs'
Batch = @($Batch)
SkipLog = $true
}
Write-Information "Audit Logs: Processing $($AuditLogSearches.Count) searches"
if ($PSCmdlet.ShouldProcess('Start-AuditLogOrchestrator', 'Starting Audit Log Polling')) {
$Queue = New-CippQueueEntry -Name 'Audit Log Collection' -Reference 'AuditLogCollection' -TotalTasks ($AuditLogSearches).Count
$Batch = $AuditLogSearches | Sort-Object -Property Tenant -Unique | Select-Object @{Name = 'TenantFilter'; Expression = { $_.Tenant } }, @{Name = 'QueueId'; Expression = { $Queue.RowKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogTenant' } }

$InputObject = [PSCustomObject]@{
OrchestratorName = 'AuditLogs'
Batch = @($Batch)
SkipLog = $true
}
Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function Start-UserTasksOrchestrator {
$currentUnixTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds
if ($currentUnixTime -ge $task.ScheduledTime) {
try {
$null = Update-AzDataTableEntity @Table -Entity @{
$null = Update-AzDataTableEntity -Force @Table -Entity @{
PartitionKey = $task.PartitionKey
RowKey = $task.RowKey
ExecutedTime = "$currentUnixTime"
Expand All @@ -36,7 +36,9 @@ function Start-UserTasksOrchestrator {
if ($task.Tenant -eq 'AllTenants') {
$AllTenantCommands = foreach ($Tenant in $TenantList) {
$NewParams = $task.Parameters.Clone()
$NewParams.TenantFilter = $Tenant.defaultDomainName
if ((Get-Command $task.Command).Parameters.TenantFilter) {
$NewParams.TenantFilter = $Tenant.defaultDomainName
}
[pscustomobject]@{
Command = $task.Command
Parameters = $NewParams
Expand All @@ -46,13 +48,15 @@ function Start-UserTasksOrchestrator {
}
$Batch.AddRange($AllTenantCommands)
} else {
$ScheduledCommand.Parameters['TenantFilter'] = $task.Tenant
if ((Get-Command $task.Command).Parameters.TenantFilter) {
$ScheduledCommand.Parameters['TenantFilter'] = $task.Tenant
}
$Batch.Add($ScheduledCommand)
}
} catch {
$errorMessage = $_.Exception.Message

$null = Update-AzDataTableEntity @Table -Entity @{
$null = Update-AzDataTableEntity -Force @Table -Entity @{
PartitionKey = $task.PartitionKey
RowKey = $task.RowKey
Results = "$errorMessage"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
function Start-TableCleanup {
<#
.SYNOPSIS
Start the Table Cleanup Timer
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param()

$CleanupRules = @(
@{
DataTableProps = @{
Context = (Get-CIPPTable -tablename 'webhookTable').Context
Property = @('PartitionKey', 'RowKey', 'ETag', 'Resource')
}
Where = "`$_.Resource -match '^Audit'"
}
@{
DataTableProps = @{
Context = (Get-CIPPTable -tablename 'AuditLogSearches').Context
Filter = "Timestamp lt datetime'$((Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))'"
First = 10000
Property = @('PartitionKey', 'RowKey', 'ETag')
}
}
@{
DataTableProps = @{
Context = (Get-CIPPTable -tablename 'CippFunctionStats').Context
Filter = "Timestamp lt datetime'$((Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))'"
First = 10000
Property = @('PartitionKey', 'RowKey', 'ETag')
}
}
@{
DataTableProps = @{
Context = (Get-CIPPTable -tablename 'CippQueue').Context
Filter = "Timestamp lt datetime'$((Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))'"
First = 10000
Property = @('PartitionKey', 'RowKey', 'ETag')
}
}
@{
DataTableProps = @{
Context = (Get-CIPPTable -tablename 'CippQueueTasks').Context
Filter = "Timestamp lt datetime'$((Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))'"
First = 10000
Property = @('PartitionKey', 'RowKey', 'ETag')
}
}
)

if ($PSCmdlet.ShouldProcess('Start-TableCleanup', 'Starting Table Cleanup')) {
Write-Information 'Starting table cleanup'
foreach ($Rule in $CleanupRules) {
if ($Rule.Where) {
$Where = [scriptblock]::Create($Rule.Where)
} else {
$Where = { $true }
}
$DataTableProps = $Rule.DataTableProps

$CleanupCompleted = $false
do {
$Entities = Get-AzDataTableEntity @DataTableProps | Where-Object $Where
if ($Entities) {
Write-Information "Removing $($Entities.Count) entities from $($Rule.DataTableProps.Context.TableName)"
try {
Remove-AzDataTableEntity -Context $DataTableProps.Context -Entity $Entities -Force
if ($DataTableProps.First -and $Entities.Count -lt $DataTableProps.First) {
$CleanupCompleted = $true
}
} catch {
Write-LogMessage -API 'TableCleanup' -message "Failed to remove entities from $($DataTableProps.Context.TableName)" -sev Error -LogData (Get-CippException -Exception $_)
$CleanupCompleted = $true
}
} else {
$CleanupCompleted = $true
}
} while (!$CleanupCompleted)
}
Write-Information 'Table cleanup complete'
}
}
4 changes: 2 additions & 2 deletions Modules/CIPPCore/Public/Get-CIPPMFAState.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ function Get-CIPPMFAState {
}
}

$PerUser = if ($PerUserMFAState -eq $null) { $null } else { ($PerUserMFAState | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).PerUserMFAState }
$PerUser = if ($null -eq $PerUserMFAState) { $null } else { ($PerUserMFAState | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).PerUserMFAState }

$MFARegUser = if (($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.userPrincipalName).isMFARegistered -eq $null) { $false } else { ($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.userPrincipalName) }
$MFARegUser = if ($null -eq ($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.userPrincipalName).isMFARegistered) { $false } else { ($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.userPrincipalName) }

[PSCustomObject]@{
Tenant = $TenantFilter
Expand Down
Loading

0 comments on commit 0d56975

Please sign in to comment.