Skip to content

Commit 91d3738

Browse files
New tenant checks in get standards then executing.
1 parent 73d5ab2 commit 91d3738

File tree

2 files changed

+104
-8
lines changed

2 files changed

+104
-8
lines changed

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ function Invoke-CIPPStandardsRun {
4949
Write-Information 'Classic Standards Run'
5050

5151
$GetStandardParams = @{
52-
TenantFilter = $TenantFilter
53-
runManually = $runManually
52+
TenantFilter = $TenantFilter
53+
runManually = $runManually
54+
LicenseChecks = $true
5455
}
5556

5657
if ($TemplateID) {

Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -415,11 +415,107 @@ function Get-CIPPStandards {
415415
}
416416
Write-Host "We're removing Intune templates as the correct license is not present for this standard. We do this to not run unneeded cycles. If you're reading this don't touch."
417417
foreach ($Key in $IntuneKeys) { [void]$ComputedStandards.Remove($Key) }
418-
#After the license check we're now going to do a compare for the remaining standards.
419-
#This compare works by bulk requesting the top=1 entries for intune policies inside of the tenant.
420-
#if the 'lastModified' timestamp is the same we skip processing this standard too, there's no need because the tenant hasn't changed anything.
421-
#However, we also check the timestamp of our standardTemplate, if that is newer than the cached lastModified timestamp we also process the standard again, because the template has changed.
422-
#If our cache for this tenant is blank, we also process the standard.
418+
} else {
419+
# Bulk check policy timestamps per type
420+
$TypeMap = @{ Device = 'deviceManagement/deviceConfigurations'; Catalog = 'deviceManagement/configurationPolicies'; Admin = 'deviceManagement/groupPolicyConfigurations'; deviceCompliancePolicies = 'deviceManagement/deviceCompliancePolicies'; AppProtection_Android = 'deviceAppManagement/androidManagedAppProtections'; AppProtection_iOS = 'deviceAppManagement/iosManagedAppProtections' }
421+
$BulkRequests = $TypeMap.GetEnumerator() | ForEach-Object {
422+
@{ id = $_.Key; url = "$($_.Value)?`$orderby=lastModifiedDateTime desc&`$select=id,lastModifiedDateTime&`$top=999"; method = 'GET' }
423+
}
424+
try {
425+
$TrackingTable = Get-CippTable -tablename 'IntunePolicyTypeTracking'
426+
$BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantName -NoPaginateIds @($BulkRequests.id)
427+
$PolicyTimestamps = @{}
428+
429+
foreach ($Result in $BulkResults) {
430+
$GraphTime = $Result.body.value[0].lastModifiedDateTime
431+
$GraphId = $Result.body.value[0].id
432+
$GraphCount = ($Result.body.value | Measure-Object).Count
433+
$Cached = Get-CIPPAzDataTableEntity @TrackingTable -Filter "PartitionKey eq '$TenantName' and RowKey eq '$($Result.id)'"
434+
435+
# Check if count changed (indicates addition/deletion)
436+
$CountChanged = $false
437+
if ($Cached -and $Cached.PolicyCount -ne $null) {
438+
$CountChanged = ($GraphCount -ne $Cached.PolicyCount)
439+
if ($CountChanged) {
440+
Write-Host "Policy count changed for $($Result.id): $($Cached.PolicyCount) -> $GraphCount"
441+
}
442+
}
443+
444+
# Check if ID changed (different policy is now most recent)
445+
$IdChanged = $false
446+
if ($GraphId -and $Cached -and $Cached.LatestPolicyId) {
447+
$IdChanged = ($GraphId -ne $Cached.LatestPolicyId)
448+
if ($IdChanged) {
449+
Write-Host "Policy ID changed for $($Result.id): $($Cached.LatestPolicyId) -> $GraphId"
450+
}
451+
}
452+
453+
# Convert both to UTC DateTime for consistent comparison
454+
if ($GraphTime) {
455+
$GraphTimeUtc = ([DateTime]$GraphTime).ToUniversalTime()
456+
if ($Cached -and $Cached.LatestPolicyModified -and -not $IdChanged -and -not $CountChanged) {
457+
$CachedTimeUtc = ([DateTimeOffset]$Cached.LatestPolicyModified).UtcDateTime
458+
$TimeDiff = [Math]::Abs(($GraphTimeUtc - $CachedTimeUtc).TotalSeconds)
459+
$Changed = ($TimeDiff -gt 60) # Changed if difference > 1 minute
460+
} else {
461+
$Changed = $true # No cache, ID changed, count changed, or treat as changed
462+
}
463+
Add-CIPPAzDataTableEntity @TrackingTable -Entity @{ PartitionKey = $TenantName; RowKey = $Result.id; LatestPolicyModified = $GraphTime; LatestPolicyId = $GraphId; PolicyCount = $GraphCount } -Force | Out-Null
464+
} else {
465+
$Changed = $true # No Graph data means policies deleted or not yet created - always treat as changed
466+
}
467+
468+
$PolicyTimestamps[$Result.id] = $Changed
469+
}
470+
# Remove templates whose specific policy type hasn't changed
471+
$TemplateTable = Get-CippTable -tablename 'templates'
472+
$StandardTemplateTable = Get-CippTable -tablename 'templates'
473+
$IntuneKeys = @($ComputedStandards.Keys | Where-Object { $_ -like '*IntuneTemplate*' })
474+
foreach ($Key in $IntuneKeys) {
475+
$Template = $ComputedStandards[$Key]
476+
$TemplateEntity = Get-CIPPAzDataTableEntity @TemplateTable -Filter "PartitionKey eq 'IntuneTemplate' and RowKey eq '$($Template.TemplateList.value)'"
477+
478+
if (-not $TemplateEntity) { continue }
479+
480+
# Parse JSON to get Type property
481+
$ParsedTemplate = $TemplateEntity.JSON | ConvertFrom-Json
482+
if (-not $ParsedTemplate.Type) { continue }
483+
484+
$PolicyType = $ParsedTemplate.Type
485+
$PolicyChanged = if ($PolicyType -eq 'AppProtection') {
486+
[bool]($PolicyTimestamps['AppProtection_Android'] -or $PolicyTimestamps['AppProtection_iOS'])
487+
} else {
488+
[bool]$PolicyTimestamps[$PolicyType]
489+
}
490+
491+
# Check if StandardTemplate changed
492+
$StandardTemplate = Get-CIPPAzDataTableEntity @StandardTemplateTable -Filter "PartitionKey eq 'StandardsTemplateV2' and RowKey eq '$($Template.TemplateId)'"
493+
$StandardTemplateChanged = $false
494+
if ($StandardTemplate) {
495+
$StandardTimeUtc = ([DateTimeOffset]$StandardTemplate.Timestamp).UtcDateTime
496+
$CachedStandardTemplate = Get-CIPPAzDataTableEntity @TrackingTable -Filter "PartitionKey eq '$TenantName' and RowKey eq 'StandardTemplate_$($Template.TemplateId)'"
497+
498+
if ($CachedStandardTemplate -and $CachedStandardTemplate.CachedTimestamp) {
499+
$CachedStandardTimeUtc = ([DateTimeOffset]$CachedStandardTemplate.CachedTimestamp).UtcDateTime
500+
$TimeDiff = [Math]::Abs(($StandardTimeUtc - $CachedStandardTimeUtc).TotalSeconds)
501+
$StandardTemplateChanged = ($TimeDiff -gt 60) # Changed if difference > 1 minute
502+
} else {
503+
$StandardTemplateChanged = $true # No cache, treat as changed
504+
}
505+
506+
Add-CIPPAzDataTableEntity @TrackingTable -Entity @{ PartitionKey = $TenantName; RowKey = "StandardTemplate_$($Template.TemplateId)"; CachedTimestamp = $StandardTemplate.Timestamp } -Force | Out-Null
507+
}
508+
509+
# Remove if BOTH policy unchanged AND StandardTemplate unchanged
510+
if (-not $PolicyChanged -and -not $StandardTemplateChanged) {
511+
[void]$ComputedStandards.Remove($Key)
512+
} else {
513+
514+
}
515+
}
516+
} catch {
517+
Write-Host "Timestamp check failed for $TenantName`: $($_.Exception.Message)"
518+
}
423519
}
424520
}
425521

@@ -442,4 +538,3 @@ function Get-CIPPStandards {
442538
}
443539
}
444540
}
445-

0 commit comments

Comments
 (0)