Skip to content

Commit

Permalink
Merge pull request #1133 from JohnDuprey/dev
Browse files Browse the repository at this point in the history
Bugfixes and Audit Logs functions
  • Loading branch information
JohnDuprey authored Oct 3, 2024
2 parents b8b7981 + 125aaf1 commit 2e95af5
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 76 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
function Push-AuditLogTenant {
Param($Item)

$SchedulerConfig = Get-CippTable -TableName 'SchedulerConfig'
$ConfigTable = Get-CippTable -TableName 'WebhookRules'
#$Tenant = Get-Tenants -TenantFilter $Item.customerId -IncludeErrors
$TenantFilter = $Item.TenantFilter

Write-Information "Audit Logs: Processing $($TenantFilter)"
# Query CIPPURL for linking
$CIPPURL = Get-CIPPAzDataTableEntity @SchedulerConfig -Filter "PartitionKey eq 'webhookcreation'" | Select-Object -First 1 -ExpandProperty CIPPURL

# Get CIPP Url, cleanup legacy tasks
$SchedulerConfig = Get-CippTable -TableName 'SchedulerConfig'
$LegacyWebhookTasks = Get-CIPPAzDataTableEntity @SchedulerConfig -Filter "PartitionKey eq 'webhookcreation'"
$LegacyUrl = $LegacyWebhookTasks | Select-Object -First 1 -ExpandProperty CIPPURL
$CippConfigTable = Get-CippTable -tablename Config
$CippConfig = Get-CIPPAzDataTableEntity @CippConfigTable -Filter "PartitionKey eq 'InstanceProperties' and RowKey eq 'CIPPURL'"
if ($LegacyUrl) {
if (!$CippConfig) {
$Entity = @{
PartitionKey = 'InstanceProperties'
RowKey = 'CIPPURL'
Value = [string]([System.Uri]$LegacyUrl).Host
}
Add-CIPPAzDataTableEntity @CippConfigTable -Entity $Entity -Force
}
# remove legacy webhooks
foreach ($Task in $LegacyWebhookTasks) {
Remove-AzDataTableEntity @SchedulerConfig -Entity $Task
}
$CIPPURL = $LegacyUrl
} else {
$CIPPURL = 'https://{0}' -f $CippConfig.Value
}

# Get webhook rules
$ConfigEntries = Get-CIPPAzDataTableEntity @ConfigTable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ Function Invoke-AddUser {
Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug'

$UserObj = $Request.body
# Write to the Azure Functions log stream.
Write-Host 'PowerShell HTTP trigger function processed a request.'

if ($UserObj.Scheduled.Enabled) {
$TaskBody = [pscustomobject]@{
TenantFilter = 'AllTenants'
TenantFilter = $UserObj.tenantID
Name = "New user creation: $($UserObj.User)@$($UserObj.Domain)"
Command = @{
value = 'New-CIPPUserTask'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
function Invoke-ExecAuditLogSearch {
<#
.FUNCTIONALITY
Entrypoint
.ROLE
Tenant.Alert.ReadWrite
#>
[CmdletBinding()]
param($Request, $TriggerMetadata)

$Query = $Request.Body
if (!$Query.TenantFilter) {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::BadRequest
Body = 'TenantFilter is required'
})
return
}
if (!$Query.StartTime -or !$Query.EndTime) {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::BadRequest
Body = 'StartTime and EndTime are required'
})
return
}

$Command = Get-Command New-CippAuditLogSearch
$AvailableParameters = $Command.Parameters.Keys
$BadProps = foreach ($Prop in $Query.PSObject.Properties.Name) {
if ($AvailableParameters -notcontains $Prop) {
$Prop
}
}
if ($BadProps) {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::BadRequest
Body = "Invalid parameters: $($BadProps -join ', ')"
})
return
}

try {
$Results = New-CippAuditLogSearch @Query
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
Body = $Results
})
} catch {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::BadRequest
Body = $_.Exception.Message
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
function Invoke-ListAuditLogSearches {
<#
.FUNCTIONALITY
Entrypoint
.ROLE
Tenant.Alert.Read
#>
Param($Request, $TriggerMetadata)

if ($Request.Query.TenantFilter) {
switch ($Request.Query.Type) {
'Searches' {
$Results = Get-CippAuditLogSearches -TenantFilter $Request.Query.TenantFilter
$Body = @{
Results = @($Results)
Metadata = @{
TenantFilter = $Request.Query.TenantFilter
TotalSearches = $Results.Count
}
} | ConvertTo-Json -Depth 10 -Compress
}
'SearchResults' {
$Results = Get-CippAuditLogSearchResults -TenantFilter $Request.Query.TenantFilter -QueryId $Request.Query.SearchId
$Body = @{
Results = @($Results)
Metadata = @{
SearchId = $Request.Query.SearchId
TenantFilter = $Request.Query.TenantFilter
TotalResults = $Results.Count
}
} | ConvertTo-Json -Depth 10 -Compress
}
default {
if ($Request.Query.Days) {
$Days = $Request.Query.Days
} else {
$Days = 1
}
$StartTime = (Get-Date).AddDays(-$Days).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')

$Table = Get-CIPPTable -TableName 'AuditLogSearches'
$Results = Get-CIPPAzDataTableEntity @Table -Filter "StartTime ge datetime'$StartTime'" | ForEach-Object {
$Query = try { $_.Query | ConvertFrom-Json } catch { $_.Query }
$MatchedRules = try { $_.MatchedRules | ConvertFrom-Json } catch { $_.MatchedRules }
[PSCustomObject]@{
SearchId = $_.RowKey
StartTime = $_.StartTime.DateTime
EndTime = $_.EndTime.DateTime
Query = $Query
MatchedRules = $MatchedRules
TotalLogs = $_.TotalLogs
MatchedLogs = $_.MatchedLogs
CippStatus = $_.CippStatus
}
}

$Body = @{
Results = @($Results)
Metadata = @{
StartTime = $StartTime
TenantFilter = $Request.Query.TenantFilter
}
} | ConvertTo-Json -Depth 10 -Compress
}
}

Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
Body = $Body
})
} else {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::BadRequest
Body = 'TenantFilter is required'
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ function Invoke-ListAuditLogTest {

$AuditLogQuery = @{
TenantFilter = $Request.Query.TenantFilter
LogType = $Request.Query.LogType
ShowAll = $true
SearchId = $Request.Query.SearchId
}
try {
$TestResults = Test-CIPPAuditLogRules @AuditLogQuery
Expand All @@ -38,4 +37,4 @@ function Invoke-ListAuditLogTest {
Body = $Body
})

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ function Invoke-ListGraphRequest {
}
}

if ($Request.Query.AsApp) {
$GraphRequestParams.AsApp = $true
}

Write-Host ($GraphRequestParams | ConvertTo-Json)

$Metadata = $GraphRequestParams
Expand Down
4 changes: 2 additions & 2 deletions Modules/CIPPCore/Public/New-CIPPUserTask.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ function New-CIPPUserTask {

try {
if ($Userobj.AddedAliases) {
$AliasResults = Add-CIPPAlias -user $CreationResults.username -Aliases ($UserObj.AddedAliases -split '\s') -UserprincipalName $UserObj.UserprincipalName -TenantFilter $UserObj.tenantID -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal'
$AliasResults = Add-CIPPAlias -user $CreationResults.username -Aliases ($UserObj.AddedAliases -split '\s') -UserprincipalName $CreationResults.Username -TenantFilter $UserObj.tenantID -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal'
$results.add($AliasResults)
}
} catch {
Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($userobj.tenantID) -message "Failed to create the Aliases. Error:$($_.Exception.Message)" -Sev 'Error'
$body = $results.add("Failed to create the Aliases: $($_.Exception.Message)")
}
if ($userobj.CopyFrom -ne '') {
$CopyFrom = Set-CIPPCopyGroupMembers -ExecutingUser $request.headers.'x-ms-client-principal' -CopyFromId $userObj.CopyFrom -UserID $UserObj.UserprincipalName -TenantFilter $UserObj.tenantID
$CopyFrom = Set-CIPPCopyGroupMembers -ExecutingUser $request.headers.'x-ms-client-principal' -CopyFromId $userObj.CopyFrom -UserID $CreationResults.Username -TenantFilter $UserObj.tenantID
$CopyFrom.Success | ForEach-Object { $results.Add($_) }
$CopyFrom.Error | ForEach-Object { $results.Add($_) }
}
Expand Down
92 changes: 79 additions & 13 deletions Modules/CIPPCore/Public/Set-CIPPCopyGroupMembers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,92 @@ function Set-CIPPCopyGroupMembers {
[string]$UserId,
[string]$CopyFromId,
[string]$TenantFilter,
[string]$APIName = 'Copy User Groups'
[string]$APIName = 'Copy User Groups',
[switch]$ExchangeOnly
)
$MemberIDs = 'https://graph.microsoft.com/v1.0/directoryObjects/' + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$UserId" -tenantid $TenantFilter).id
$AddMemberBody = "{ `"[email protected]`": $(ConvertTo-Json @($MemberIDs)) }"

$Requests = @(
@{
id = 'User'
url = 'users/{0}' -f $UserId
method = 'GET'
}
@{
id = 'UserMembership'
url = 'users/{0}/memberOf' -f $UserId
method = 'GET'
}
@{
id = 'CopyFromMembership'
url = 'users/{0}/memberOf' -f $CopyFromId
method = 'GET'
}
)
$Results = New-GraphBulkRequest -Requests $Requests -tenantid $TenantFilter
$User = ($Results | Where-Object { $_.id -eq 'User' }).body
$CurrentMemberships = ($Results | Where-Object { $_.id -eq 'UserMembership' }).body.value
$CopyFromMemberships = ($Results | Where-Object { $_.id -eq 'CopyFromMembership' }).body.value

Write-Information ($Results | ConvertTo-Json -Depth 10)

$ODataBind = 'https://graph.microsoft.com/v1.0/directoryObjects/{0}' -f $User.id
$AddMemberBody = @{
'@odata.id' = $ODataBind
} | ConvertTo-Json -Compress

$Success = [System.Collections.Generic.List[string]]::new()
$Errors = [System.Collections.Generic.List[string]]::new()
(New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$CopyFromId/memberOf" -tenantid $TenantFilter) | Where-Object { $_.GroupTypes -notin 'herohero' } | ForEach-Object {
$Memberships = $CopyFromMemberships | Where-Object { $_.'@odata.type' -eq '#microsoft.graph.group' -and $_.groupTypes -notcontains 'DynamicMembership' -and $_.onPremisesSyncEnabled -ne $true -and $_.visibility -ne 'Public' -and $CurrentMemberships.id -notcontains $_.id }
$ScheduleExchangeGroupTask = $false
foreach ($MailGroup in $Memberships) {
try {
$MailGroup = $_
if ($PSCmdlet.ShouldProcess($_.displayName, "Add $UserId to group")) {
if ($MailGroup.MailEnabled -and $Mailgroup.ResourceProvisioningOptions -notin 'Team') {
$Params = @{ Identity = $MailGroup.mail; Member = $UserId; BypassSecurityGroupManagerCheck = $true }
$null = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Add-DistributionGroupMember' -cmdParams $params -UseSystemMailbox $true
} else {
$null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($_.id)" -tenantid $TenantFilter -type patch -body $AddMemberBody -Verbose
if ($PSCmdlet.ShouldProcess($MailGroup.displayName, "Add $UserId to group")) {
if ($MailGroup.MailEnabled -and $Mailgroup.ResourceProvisioningOptions -notcontains 'Team' -and $MailGroup.groupTypes -notcontains 'Unified') {
$Params = @{ Identity = $MailGroup.mailNickname; Member = $UserId; BypassSecurityGroupManagerCheck = $true }
try {
$null = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Add-DistributionGroupMember' -cmdParams $params -UseSystemMailbox $true
} catch {
if ($_.Exception.Message -match 'Ex94914C|Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException') {
if (($User.assignedLicenses | Measure-Object).Count -gt 0 -and !$ExchangeOnly.IsPresent) {
$ScheduleExchangeGroupTask = $true
} else {
throw $_
}
} else {
throw $_
}
}
} elseif (!$ExchangeOnly.IsPresent) {
$null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($MailGroup.id)/members/`$ref" -tenantid $TenantFilter -body $AddMemberBody -Verbose
}
}

if ($ScheduleExchangeGroupTask) {
$TaskBody = [PSCustomObject]@{
TenantFilter = $TenantFilter
Name = "Copy Exchange Group Membership: $UserId from $CopyFromId"
Command = @{
value = 'Set-CIPPCopyGroupMembers'
}
Parameters = [PSCustomObject]@{
UserId = $UserId
CopyFromId = $CopyFromId
TenantFilter = $TenantFilter
ExchangeOnly = $true
}
ScheduledTime = [int64](([datetime]::UtcNow).AddMinutes(5) - (Get-Date '1/1/1970')).TotalSeconds
PostExecution = @{
Webhook = $false
Email = $false
PSA = $false
}
}
Add-CIPPScheduledTask -Task $TaskBody -hidden $false
$Errors.Add("We've scheduled a task to add $UserId to the Exchange group $($MailGroup.displayName)") | Out-Null
} else {
Write-LogMessage -user $ExecutingUser -API $APIName -message "Added $UserId to group $($MailGroup.displayName)" -Sev 'Info' -tenant $TenantFilter
$Success.Add("Added user to group: $($MailGroup.displayName)") | Out-Null
}
Write-LogMessage -user $ExecutingUser -API $APIName -message "Added $UserId to group $($_.displayName)" -Sev 'Info' -tenant $TenantFilter
$Success.Add("Added group: $($MailGroup.displayName)") | Out-Null
} catch {
$ErrorMessage = Get-CippException -Exception $_
$Errors.Add("We've failed to add the group $($MailGroup.displayName): $($ErrorMessage.NormalizedError)") | Out-Null
Expand Down
34 changes: 1 addition & 33 deletions Modules/CippEntrypoints/CippEntrypoints.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -57,38 +57,6 @@ function Receive-CippHttpTrigger {
}
}

function Receive-CippQueueTrigger {
Param($QueueItem, $TriggerMetadata)

Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName
$Start = (Get-Date).ToUniversalTime()
$APIName = $TriggerMetadata.FunctionName
Write-Information "#### Running $APINAME"
Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName
$FunctionName = 'Push-{0}' -f $APIName
$QueueTrigger = @{
QueueItem = $QueueItem
TriggerMetadata = $TriggerMetadata
}
try {
& $FunctionName @QueueTrigger
} catch {
$ErrorMsg = $_.Exception.Message
}

$End = (Get-Date).ToUniversalTime()

$Stats = @{
FunctionType = 'Queue'
Entity = $QueueItem
Start = $Start
End = $End
ErrorMsg = $ErrorMsg
}
Write-Information '####### Adding stats'
Write-CippFunctionStats @Stats
}

function Receive-CippOrchestrationTrigger {
param($Context)

Expand Down Expand Up @@ -267,5 +235,5 @@ function Receive-CIPPTimerTrigger {
}
}

Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippQueueTrigger', 'Receive-CippOrchestrationTrigger', 'Receive-CippActivityTrigger', 'Receive-CIPPTimerTrigger')
Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippOrchestrationTrigger', 'Receive-CippActivityTrigger', 'Receive-CIPPTimerTrigger')

Loading

0 comments on commit 2e95af5

Please sign in to comment.