Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ yarn.lock
# Ignore all root PowerShell files except profile.ps1
/*.ps1
!/profile.ps1
.DS_Store
32 changes: 21 additions & 11 deletions Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
function Add-CIPPScheduledTask {
[CmdletBinding(DefaultParameterSetName = 'Default')]
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, ParameterSetName = 'Default')]
[Parameter(Mandatory = $false)]
[pscustomobject]$Task,

[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
[Parameter(Mandatory = $false)]
[bool]$Hidden,

[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
[Parameter(Mandatory = $false)]
$DisallowDuplicateName = $false,

[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
[Parameter(Mandatory = $false)]
[string]$SyncType = $null,

[Parameter(Mandatory = $false, ParameterSetName = 'RunNow')]
[Parameter(Mandatory = $false)]
[switch]$RunNow,

[Parameter(Mandatory = $true, ParameterSetName = 'RunNow')]
[string]$RowKey,
[Parameter(Mandatory = $false)]
[string]$RowKey = $null,

[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
[Parameter(Mandatory = $false)]
[string]$DesiredStartTime = $null,

[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
[Parameter(Mandatory = $false, ParameterSetName = 'RunNow')]
[Parameter(Mandatory = $false)]
$Headers
)

Expand All @@ -39,6 +38,9 @@ function Add-CIPPScheduledTask {
$ExistingTask.TaskState = 'Planned'
Add-CIPPAzDataTableEntity @Table -Entity $ExistingTask -Force
Write-LogMessage -headers $Headers -API 'RunNow' -message "Task $($ExistingTask.Name) scheduled to run now" -Sev 'Info' -Tenant $ExistingTask.Tenant
Add-CippQueueMessage -Cmdlet 'Start-UserTasksOrchestrator' -Parameters @{
TaskId = $RowKey
}
return "Task $($ExistingTask.Name) scheduled to run now"
} catch {
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
Expand Down Expand Up @@ -192,6 +194,7 @@ function Add-CIPPScheduledTask {
Hidden = [bool]$Hidden
Results = 'Planned'
AlertComment = [string]$task.AlertComment
CustomSubject = [string]$task.CustomSubject
}


Expand Down Expand Up @@ -299,6 +302,13 @@ function Add-CIPPScheduledTask {
default { 'less than a minute' }
}

if ($RunNow.IsPresent) {
Add-CippQueueMessage -Cmdlet 'Start-UserTasksOrchestrator' -Parameters @{
TaskId = $RowKey
}
return "Task $($entity.Name) scheduled to run now"
}

return "Successfully added task: $($entity.Name). It will run in $relativeTime."
}
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function Push-CIPPDBCacheData {
'ServicePrincipalRiskDetections'
'RiskDetections'
'RoleEligibilitySchedules'
'RoleAssignmentSchedules'
'RoleAssignmentScheduleInstances'
'RoleManagementPolicies'
)
foreach ($CacheFunction in $P2CacheFunctions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,22 @@ function Invoke-AddScheduledItem {
$HeaderProperties = @('x-ms-client-principal', 'x-ms-client-principal-id', 'x-ms-client-principal-name', 'x-forwarded-for')
$Headers = $Request.Headers | Select-Object -Property $HeaderProperties -ErrorAction SilentlyContinue

if ($Request.Body.RunNow -eq $true) {
try {
$Table = Get-CIPPTable -TableName 'ScheduledTasks'
$Filter = "PartitionKey eq 'ScheduledTask' and RowKey eq '$($Request.Body.RowKey)'"
$ExistingTask = (Get-CIPPAzDataTableEntity @Table -Filter $Filter)
$Table = Get-CIPPTable -TableName 'ScheduledTasks'

if ($ExistingTask) {
$RerunParams = @{
TenantFilter = $ExistingTask.Tenant
Type = 'ScheduledTask'
API = $Request.Body.RowKey
Clear = $true
}
$null = Test-CIPPRerun @RerunParams
$Result = Add-CIPPScheduledTask -RowKey $Request.Body.RowKey -RunNow -Headers $Headers
} else {
$Result = "Task with id $($Request.Body.RowKey) does not exist"
}
} catch {
Write-Warning "Error scheduling task: $($_.Exception.Message)"
Write-Information $_.InvocationInfo.PositionMessage
$Result = "Error scheduling task: $($_.Exception.Message)"
if ($Request.Body.RowKey) {
$Filter = "PartitionKey eq 'ScheduledTask' and RowKey eq '$($Request.Body.RowKey)'"
$ExistingTask = (Get-CIPPAzDataTableEntity @Table -Filter $Filter)
}

if ($ExistingTask -and $Request.Body.RunNow -eq $true) {
$RerunParams = @{
TenantFilter = $ExistingTask.Tenant
Type = 'ScheduledTask'
API = $Request.Body.RowKey
Clear = $true
}
$null = Test-CIPPRerun @RerunParams
$Result = Add-CIPPScheduledTask -RowKey $Request.Body.RowKey -RunNow -Headers $Headers
} else {
$ScheduledTask = @{
Task = $Request.Body
Expand All @@ -49,6 +42,9 @@ function Invoke-AddScheduledItem {
DisallowDuplicateName = $DisallowDuplicateName
DesiredStartTime = $Request.Body.DesiredStartTime
}
if ($Request.Body.RunNow -eq $true) {
$ScheduledTask.RunNow = $true
}
$Result = Add-CIPPScheduledTask @ScheduledTask
}
return ([HttpResponseContext]@{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ function Invoke-ListScheduledItems {
$ScheduledItemFilter.Add("PartitionKey eq 'ScheduledTask'")

$Id = $Request.Query.Id ?? $Request.Body.Id
$TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter

if ($Id) {
# Interact with query parameters.
$ScheduledItemFilter.Add("RowKey eq '$($Id)'")
Expand Down Expand Up @@ -48,6 +50,10 @@ function Invoke-ListScheduledItems {
$Tasks = $Tasks | Where-Object { $_.command -eq $Type }
}

if ($TenantFilter) {
$Tasks = $Tasks | Where-Object { $_.tenant -eq $TenantFilter -or $TenantFilter -eq 'AllTenants' }
}

if ($SearchTitle) {
$Tasks = $Tasks | Where-Object { $_.Name -like $SearchTitle }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,157 @@ function Invoke-ListTenantGroups {
param($Request, $TriggerMetadata)

$groupFilter = $Request.Query.groupId ?? $Request.Body.groupId
$includeUsage = $Request.Query.includeUsage ?? $Request.Body.includeUsage
$TenantGroups = (Get-TenantGroups -GroupId $groupFilter -SkipCache) ?? @()

if ($includeUsage -eq 'true') {
$UsageByGroup = @{}
foreach ($Group in $TenantGroups) {
$UsageByGroup[$Group.Id] = [System.Collections.Generic.List[PSCustomObject]]::new()
}

$AddGroupUsage = {
param($FilterArray, $UsedIn, $Name, $Type)
foreach ($Filter in $FilterArray) {
if ($Filter.type -eq 'Group' -and $Filter.value -and $UsageByGroup.ContainsKey($Filter.value)) {
$UsageByGroup[$Filter.value].Add([PSCustomObject]@{
UsedIn = $UsedIn
Name = $Name
Type = $Type
})
}
}
}

# Standards Templates
$TemplateTable = Get-CippTable -tablename 'templates'
$TemplateFilter = "PartitionKey eq 'StandardsTemplateV2'"
$Templates = Get-CIPPAzDataTableEntity @TemplateTable -Filter $TemplateFilter

foreach ($Template in $Templates) {
try {
$TemplateData = $Template.JSON | ConvertFrom-Json
$TemplateName = $TemplateData.templateName ?? $Template.RowKey
if ($TemplateData.tenantFilter) {
& $AddGroupUsage $TemplateData.tenantFilter 'Standards Template' $TemplateName 'Tenant Filter'
}
if ($TemplateData.excludedTenants) {
& $AddGroupUsage $TemplateData.excludedTenants 'Standards Template' $TemplateName 'Excluded Tenants'
}
} catch {
Write-Warning "Failed to parse standards template $($Template.RowKey): $($_.Exception.Message)"
}
}

# Scheduled Tasks
$TaskTable = Get-CippTable -tablename 'ScheduledTasks'
$TaskFilter = "PartitionKey eq 'ScheduledTask'"
$Tasks = Get-CIPPAzDataTableEntity @TaskTable -Filter $TaskFilter

foreach ($Task in $Tasks) {
if ($Task.TenantGroup) {
try {
$TenantGroupObject = $Task.TenantGroup | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($TenantGroupObject.value -and $UsageByGroup.ContainsKey($TenantGroupObject.value)) {
$UsageByGroup[$TenantGroupObject.value].Add([PSCustomObject]@{
UsedIn = 'Scheduled Task'
Name = $Task.Name ?? $Task.RowKey
Type = 'Tenant Filter'
})
}
} catch {
Write-Warning "Failed to parse tenant group for task $($Task.RowKey): $($_.Exception.Message)"
}
}
}

# Dynamic Group Rules referencing other groups
foreach ($Group in $TenantGroups) {
if ($Group.GroupType -eq 'dynamic' -and $Group.DynamicRules) {
foreach ($Rule in $Group.DynamicRules) {
if ($Rule.property -eq 'TenantGroup' -and $Rule.value -and $UsageByGroup.ContainsKey($Rule.value)) {
$UsageByGroup[$Rule.value].Add([PSCustomObject]@{
UsedIn = 'Dynamic Group Rule'
Name = $Group.Name
Type = 'Rule Reference'
})
}
}
}
}

# Webhook Rules
$WebhookTable = Get-CippTable -tablename 'WebhookRules'
$WebhookRules = Get-CIPPAzDataTableEntity @WebhookTable

foreach ($Rule in $WebhookRules) {
try {
$RuleName = $Rule.Name ?? $Rule.RowKey
if ($Rule.Tenants) {
$Tenants = $Rule.Tenants | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($Tenants) {
& $AddGroupUsage $Tenants 'Alert Rule' $RuleName 'Tenant Filter'
}
}
if ($Rule.excludedTenants) {
$ExclTenants = $Rule.excludedTenants | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($ExclTenants) {
& $AddGroupUsage $ExclTenants 'Alert Rule' $RuleName 'Excluded Tenants'
}
}
} catch {
Write-Warning "Failed to parse webhook rule $($Rule.RowKey): $($_.Exception.Message)"
}
}

# Custom Roles
$RolesTable = Get-CippTable -tablename 'CustomRoles'
$CustomRoles = Get-CIPPAzDataTableEntity @RolesTable

foreach ($Role in $CustomRoles) {
try {
$RoleName = $Role.Name ?? $Role.RowKey
if ($Role.AllowedTenants) {
$AllowedTenants = $Role.AllowedTenants | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($AllowedTenants) {
& $AddGroupUsage $AllowedTenants 'Custom Role' $RoleName 'Allowed Tenants'
}
}
if ($Role.BlockedTenants) {
$BlockedTenants = $Role.BlockedTenants | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($BlockedTenants) {
& $AddGroupUsage $BlockedTenants 'Custom Role' $RoleName 'Blocked Tenants'
}
}
} catch {
Write-Warning "Failed to parse custom role $($Role.RowKey): $($_.Exception.Message)"
}
}

# Custom Data Mappings
$MappingsTable = Get-CippTable -tablename 'CustomDataMappings'
$Mappings = Get-CIPPAzDataTableEntity @MappingsTable

foreach ($Mapping in $Mappings) {
try {
$MappingName = $Mapping.Name ?? $Mapping.RowKey
if ($Mapping.tenantFilter) {
$TenantFilters = $Mapping.tenantFilter | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($TenantFilters) {
if ($TenantFilters -isnot [System.Array]) { $TenantFilters = @($TenantFilters) }
& $AddGroupUsage $TenantFilters 'Data Mapping' $MappingName 'Tenant Filter'
}
}
} catch {
Write-Warning "Failed to parse custom data mapping $($Mapping.RowKey): $($_.Exception.Message)"
}
}

foreach ($Group in $TenantGroups) {
$Group | Add-Member -MemberType NoteProperty -Name 'Usage' -Value @($UsageByGroup[$Group.Id]) -Force
}
}

$Body = @{ Results = @($TenantGroups) }

return ([HttpResponseContext]@{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ function Invoke-CIPPOffboardingJob {
}
}

$InstanceId = Start-CIPPOrchestrator -InputObject $InputObject -CallerIsQueueTrigger
$InstanceId = Start-CIPPOrchestrator -InputObject $InputObject
Write-Information "Started offboarding job for $Username with ID = '$InstanceId'"
Write-LogMessage -API $APIName -tenant $TenantFilter -message "Started offboarding job for $Username with $($Batch.Count) tasks. Instance ID: $InstanceId" -sev Info

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,41 @@ function Invoke-ExecOffboardUser {
$AllUsers = $Request.Body.user.value
$TenantFilter = $request.Body.tenantFilter.value ? $request.Body.tenantFilter.value : $request.Body.tenantFilter
$OffboardingOptions = $Request.Body | Select-Object * -ExcludeProperty user, tenantFilter, Scheduled

$StatusCode = [HttpStatusCode]::OK
$Results = foreach ($username in $AllUsers) {
try {
$APIName = 'ExecOffboardUser'
$Headers = $Request.Headers


if ($Request.Body.Scheduled.enabled) {
$taskObject = [PSCustomObject]@{
TenantFilter = $TenantFilter
Name = "Offboarding: $Username"
Command = @{
value = 'Invoke-CIPPOffboardingJob'
}
Parameters = [pscustomobject]@{
Username = $Username
APIName = 'Scheduled Offboarding'
options = $OffboardingOptions
RunScheduled = $true
}
ScheduledTime = $Request.Body.Scheduled.date
PostExecution = @{
Webhook = [bool]$Request.Body.PostExecution.webhook
Email = [bool]$Request.Body.PostExecution.email
PSA = [bool]$Request.Body.PostExecution.psa
}
Reference = $Request.Body.reference
$taskObject = [PSCustomObject]@{
TenantFilter = $TenantFilter
Name = "Offboarding: $Username"
Command = @{
value = 'Invoke-CIPPOffboardingJob'
}
Parameters = [pscustomobject]@{
Username = $Username
APIName = 'Scheduled Offboarding'
options = $OffboardingOptions
RunScheduled = $true
}
Add-CIPPScheduledTask -Task $taskObject -hidden $false -Headers $Headers
PostExecution = @{
Webhook = [bool]$Request.Body.PostExecution.webhook
Email = [bool]$Request.Body.PostExecution.email
PSA = [bool]$Request.Body.PostExecution.psa
}
Reference = $Request.Body.reference
}
$Params = @{
Task = $taskObject
hidden = $false
Headers = $Headers
}
if ($Request.Body.Scheduled.enabled) {
$taskObject.ScheduledTime = $Request.Body.Scheduled.date
} else {
Invoke-CIPPOffboardingJob -Username $Username -TenantFilter $TenantFilter -Options $OffboardingOptions -APIName $APIName -Headers $Headers
$Params.RunNow = $true
}
$StatusCode = [HttpStatusCode]::OK

Add-CIPPScheduledTask @Params
} catch {
$StatusCode = [HttpStatusCode]::Forbidden
$_.Exception.message
Expand Down
Loading