From 2b79c1d9a6d5ea1c8c32dd5aaca482044f69cf41 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 17 Dec 2025 18:22:41 -0500 Subject: [PATCH 01/20] rollback workflows --- .github/workflows/dev_api.yml | 56 +----------------------- .github/workflows/publish_release.yml | 62 --------------------------- .github/workflows/upload_dev.yml | 60 -------------------------- 3 files changed, 1 insertion(+), 177 deletions(-) diff --git a/.github/workflows/dev_api.yml b/.github/workflows/dev_api.yml index 53bae27dbc81..deea847bd266 100644 --- a/.github/workflows/dev_api.yml +++ b/.github/workflows/dev_api.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v4 with: persist-credentials: false - + - name: Setup PowerShell module cache id: cacher uses: actions/cache@v3 @@ -33,60 +33,6 @@ jobs: path: "~/.local/share/powershell/Modules" key: ${{ runner.os }}-ModuleBuilder - - name: Install ModuleBuilder - if: steps.cacher.outputs.cache-hit != 'true' - shell: pwsh - run: | - Set-PSRepository PSGallery -InstallationPolicy Trusted - Install-Module ModuleBuilder -AllowClobber -Force - - - name: Build CIPPCore Module - shell: pwsh - run: | - $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CIPPCore" - $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" - - Write-Host "Building module from: $ModulePath" - Write-Host "Output directory: $OutputPath" - - # Generate function permissions before replacing the source module - $ToolsPath = Join-Path $env:GITHUB_WORKSPACE "Tools" - $ScriptPath = Join-Path $ToolsPath "Build-FunctionPermissions.ps1" - pwsh -File $ScriptPath -ModulePath $ModulePath - - # Build the module using ModuleBuilder - Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose - - # Replace the source module with the built module - Remove-Item -Path $ModulePath -Recurse -Force - Copy-Item -Path (Join-Path $OutputPath "CIPPCore") -Destination $ModulePath -Recurse -Force - - Write-Host "Module built and replaced successfully" - - # Clean up output directory - Remove-Item -Path $OutputPath -Recurse -Force - - - name: Build CippExtensions Module - shell: pwsh - run: | - $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CippExtensions" - $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" - - Write-Host "Building module from: $ModulePath" - Write-Host "Output directory: $OutputPath" - - # Build the module using ModuleBuilder - Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose - - # Replace the source module with the built module - Remove-Item -Path $ModulePath -Recurse -Force - Copy-Item -Path (Join-Path $OutputPath "CippExtensions") -Destination $ModulePath -Recurse -Force - - Write-Host "Module built and replaced successfully" - - # Clean up output directory - Remove-Item -Path $OutputPath -Recurse -Force - - name: Login to Azure uses: azure/login@v2 with: diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index 8230488fe999..81acfa86790f 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -70,68 +70,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Setup PowerShell module cache - id: cacher - uses: actions/cache@v3 - with: - path: "~/.local/share/powershell/Modules" - key: ${{ runner.os }}-ModuleBuilder - - - name: Install ModuleBuilder - if: steps.cacher.outputs.cache-hit != 'true' - shell: pwsh - run: | - Set-PSRepository PSGallery -InstallationPolicy Trusted - Install-Module ModuleBuilder -AllowClobber -Force - - - name: Build CIPPCore Module - shell: pwsh - run: | - $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CIPPCore" - $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" - - Write-Host "Building module from: $ModulePath" - Write-Host "Output directory: $OutputPath" - - # Generate function permissions before replacing the source module - $ToolsPath = Join-Path $env:GITHUB_WORKSPACE "Tools" - $ScriptPath = Join-Path $ToolsPath "Build-FunctionPermissions.ps1" - pwsh -File $ScriptPath -ModulePath $ModulePath - - # Build the module using ModuleBuilder - Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose - - # Replace the source module with the built module - Remove-Item -Path $ModulePath -Recurse -Force - Copy-Item -Path (Join-Path $OutputPath "CIPPCore") -Destination $ModulePath -Recurse -Force - - Write-Host "Module built and replaced successfully" - - # Clean up output directory - Remove-Item -Path $OutputPath -Recurse -Force - - - name: Build CippExtensions Module - shell: pwsh - run: | - $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CippExtensions" - $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" - - Write-Host "Building module from: $ModulePath" - Write-Host "Output directory: $OutputPath" - - # Build the module using ModuleBuilder - Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose - - # Replace the source module with the built module - Remove-Item -Path $ModulePath -Recurse -Force - Copy-Item -Path (Join-Path $OutputPath "CippExtensions") -Destination $ModulePath -Recurse -Force - - Write-Host "Module built and replaced successfully" - - # Clean up output directory - Remove-Item -Path $OutputPath -Recurse -Force - - # Create ZIP File in a New Source Directory - name: Prepare and Zip Release Files if: env.tag_exists == 'false' diff --git a/.github/workflows/upload_dev.yml b/.github/workflows/upload_dev.yml index 85048dc9fb61..37ceb8fcd3fb 100644 --- a/.github/workflows/upload_dev.yml +++ b/.github/workflows/upload_dev.yml @@ -17,66 +17,6 @@ jobs: uses: actions/checkout@v4 with: persist-credentials: false - - name: Setup PowerShell module cache - id: cacher - uses: actions/cache@v3 - with: - path: "~/.local/share/powershell/Modules" - key: ${{ runner.os }}-ModuleBuilder - - - name: Install ModuleBuilder - if: steps.cacher.outputs.cache-hit != 'true' - shell: pwsh - run: | - Set-PSRepository PSGallery -InstallationPolicy Trusted - Install-Module ModuleBuilder -AllowClobber -Force - - - name: Build CIPPCore Module - shell: pwsh - run: | - $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CIPPCore" - $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" - - Write-Host "Building module from: $ModulePath" - Write-Host "Output directory: $OutputPath" - - # Generate function permissions before replacing the source module - $ToolsPath = Join-Path $env:GITHUB_WORKSPACE "Tools" - $ScriptPath = Join-Path $ToolsPath "Build-FunctionPermissions.ps1" - pwsh -File $ScriptPath -ModulePath $ModulePath - - # Build the module using ModuleBuilder - Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose - - # Replace the source module with the built module - Remove-Item -Path $ModulePath -Recurse -Force - Copy-Item -Path (Join-Path $OutputPath "CIPPCore") -Destination $ModulePath -Recurse -Force - - Write-Host "Module built and replaced successfully" - - # Clean up output directory - Remove-Item -Path $OutputPath -Recurse -Force - - - name: Build CippExtensions Module - shell: pwsh - run: | - $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CippExtensions" - $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" - - Write-Host "Building module from: $ModulePath" - Write-Host "Output directory: $OutputPath" - - # Build the module using ModuleBuilder - Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose - - # Replace the source module with the built module - Remove-Item -Path $ModulePath -Recurse -Force - Copy-Item -Path (Join-Path $OutputPath "CippExtensions") -Destination $ModulePath -Recurse -Force - - Write-Host "Module built and replaced successfully" - - # Clean up output directory - Remove-Item -Path $OutputPath -Recurse -Force # Create ZIP File in a New Source Directory - name: Prepare and Zip Release Files From b611861a9f30a99c3c47a4ba384a7e5ab15f10e0 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 17 Dec 2025 21:10:05 -0500 Subject: [PATCH 02/20] Enhance JIT admin schema and function permission caching Expanded the cippUser schema to include jitAdminStartDate and jitAdminCreatedBy properties. Added caching and loading of function permissions from JSON in Get-CIPPHttpFunctions and Invoke-ListFunctionParameters for improved performance and consistency. Refactored Set-CIPPUserJITAdmin to correctly set schema properties and extract CreatedBy from headers. --- Config/schemaDefinitions.json | 47 +++++++++++++++--- .../Authentication/Get-CIPPHttpFunctions.ps1 | 45 ++++++++++++++--- .../Invoke-ListFunctionParameters.ps1 | 49 +++++++++++++++---- .../CIPPCore/Public/Set-CIPPUserJITAdmin.ps1 | 7 ++- 4 files changed, 123 insertions(+), 25 deletions(-) diff --git a/Config/schemaDefinitions.json b/Config/schemaDefinitions.json index 9a451842ed8e..4c2d78d561b0 100644 --- a/Config/schemaDefinitions.json +++ b/Config/schemaDefinitions.json @@ -2,15 +2,46 @@ { "id": "cippUser", "description": "CIPP User Schema", - "targetTypes": ["User"], + "targetTypes": [ + "User" + ], "properties": [ - { "name": "jitAdminEnabled", "type": "Boolean" }, - { "name": "jitAdminExpiration", "type": "DateTime" }, - { "name": "jitAdminReason", "type": "String" }, - { "name": "mailboxType", "type": "String" }, - { "name": "archiveEnabled", "type": "Boolean" }, - { "name": "autoExpandingArchiveEnabled", "type": "Boolean" }, - { "name": "perUserMfaState", "type": "String" } + { + "name": "jitAdminEnabled", + "type": "Boolean" + }, + { + "name": "jitAdminExpiration", + "type": "DateTime" + }, + { + "name": "jitAdminReason", + "type": "String" + }, + { + "name": "jitAdminStartDate", + "type": "DateTime" + }, + { + "name": "jitAdminCreatedBy", + "type": "String" + }, + { + "name": "mailboxType", + "type": "String" + }, + { + "name": "archiveEnabled", + "type": "Boolean" + }, + { + "name": "autoExpandingArchiveEnabled", + "type": "Boolean" + }, + { + "name": "perUserMfaState", + "type": "String" + } ], "status": "Available" } diff --git a/Modules/CIPPCore/Public/Authentication/Get-CIPPHttpFunctions.ps1 b/Modules/CIPPCore/Public/Authentication/Get-CIPPHttpFunctions.ps1 index 178ee74fb4ab..ad0efeee88ca 100644 --- a/Modules/CIPPCore/Public/Authentication/Get-CIPPHttpFunctions.ps1 +++ b/Modules/CIPPCore/Public/Authentication/Get-CIPPHttpFunctions.ps1 @@ -5,15 +5,48 @@ function Get-CIPPHttpFunctions { ) try { + # Load permissions from cache + if (-not $global:CIPPFunctionPermissions) { + $CIPPCoreModule = Get-Module -Name CIPPCore + if ($CIPPCoreModule) { + $PermissionsFileJson = Join-Path $CIPPCoreModule.ModuleBase 'lib' 'data' 'function-permissions.json' + + if (Test-Path $PermissionsFileJson) { + try { + $jsonData = Get-Content -Path $PermissionsFileJson -Raw | ConvertFrom-Json -AsHashtable + $global:CIPPFunctionPermissions = [System.Collections.Hashtable]::new([StringComparer]::OrdinalIgnoreCase) + foreach ($key in $jsonData.Keys) { + $global:CIPPFunctionPermissions[$key] = $jsonData[$key] + } + Write-Information "Loaded $($global:CIPPFunctionPermissions.Count) function permissions from JSON cache" + } catch { + Write-Warning "Failed to load function permissions from JSON: $($_.Exception.Message)" + } + } + } + } + $Functions = Get-Command -Module CIPPCore | Where-Object { $_.Visibility -eq 'Public' -and $_.Name -match 'Invoke-*' } $Results = foreach ($Function in $Functions) { - $Help = Get-Help $Function - if ($Help.Functionality -notmatch 'Entrypoint') { continue } - if ($Help.Role -eq 'Public') { continue } + $FunctionName = $Function.Name + if ($global:CIPPFunctionPermissions -and $global:CIPPFunctionPermissions.ContainsKey($FunctionName)) { + $PermissionData = $global:CIPPFunctionPermissions[$FunctionName] + $Functionality = $PermissionData['Functionality'] + $Role = $PermissionData['Role'] + $Description = $PermissionData['Description'] + } else { + $Help = Get-Help $Function + $Functionality = $Help.Functionality + $Role = $Help.Role + $Description = $Help.Description + } + + if ($Functionality -notmatch 'Entrypoint') { continue } + if ($Role -eq 'Public') { continue } [PSCustomObject]@{ - Function = $Function.Name - Role = $Help.Role - Description = $Help.Description + Function = $FunctionName + Role = $Role + Description = $Description } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 index 7a0d704fe7ed..9f849ae23f30 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 @@ -21,6 +21,27 @@ function Invoke-ListFunctionParameters { $CommonParameters = @('Verbose', 'Debug', 'ErrorAction', 'WarningAction', 'InformationAction', 'ErrorVariable', 'WarningVariable', 'InformationVariable', 'OutVariable', 'OutBuffer', 'PipelineVariable', 'TenantFilter', 'APIName', 'Headers', 'ProgressAction', 'WhatIf', 'Confirm', 'Headers', 'NoAuthCheck') $TemporaryBlacklist = 'Get-CIPPAuthentication', 'Invoke-CippWebhookProcessing', 'Invoke-ListFunctionParameters', 'New-CIPPAPIConfig', 'New-CIPPGraphSubscription' try { + # Load permissions from cache + if (-not $global:CIPPFunctionPermissions) { + $CIPPCoreModule = Get-Module -Name CIPPCore + if ($CIPPCoreModule) { + $PermissionsFileJson = Join-Path $CIPPCoreModule.ModuleBase 'lib' 'data' 'function-permissions.json' + + if (Test-Path $PermissionsFileJson) { + try { + $jsonData = Get-Content -Path $PermissionsFileJson -Raw | ConvertFrom-Json -AsHashtable + $global:CIPPFunctionPermissions = [System.Collections.Hashtable]::new([StringComparer]::OrdinalIgnoreCase) + foreach ($key in $jsonData.Keys) { + $global:CIPPFunctionPermissions[$key] = $jsonData[$key] + } + Write-Information "Loaded $($global:CIPPFunctionPermissions.Count) function permissions from JSON cache" + } catch { + Write-Warning "Failed to load function permissions from JSON: $($_.Exception.Message)" + } + } + } + } + if ($Module -eq 'ExchangeOnlineManagement') { $ExoRequest = @{ AvailableCmdlets = $true @@ -37,16 +58,26 @@ function Invoke-ListFunctionParameters { } $Results = foreach ($Function in $Functions) { if ($Function -In $TemporaryBlacklist) { continue } - $GetHelp = @{ - Name = $Function - } - if ($Module -eq 'ExchangeOnlineManagement') { - $GetHelp.Path = 'ExchangeOnlineHelp' + $FunctionName = $Function.Name + if ($Module -ne 'ExchangeOnlineManagement' -and $global:CIPPFunctionPermissions -and $global:CIPPFunctionPermissions.ContainsKey($FunctionName)) { + $PermissionData = $global:CIPPFunctionPermissions[$FunctionName] + $Functionality = $PermissionData['Functionality'] + $Synopsis = $PermissionData['Description'] + } else { + $GetHelp = @{ + Name = $Function + } + if ($Module -eq 'ExchangeOnlineManagement') { + $GetHelp.Path = 'ExchangeOnlineHelp' + } + $Help = Get-Help @GetHelp + $Functionality = $Help.Functionality + $Synopsis = $Help.Synopsis } - $Help = Get-Help @GetHelp - $ParamsHelp = ($Help | Select-Object -ExpandProperty parameters).parameter | Select-Object name, @{n = 'description'; exp = { $_.description.Text } } - if ($Help.Functionality -in $IgnoreList) { continue } - if ($Help.Functionality -match 'Entrypoint') { continue } + + $ParamsHelp = @() + if ($Functionality -in $IgnoreList) { continue } + if ($Functionality -match 'Entrypoint') { continue } $Parameters = foreach ($Key in $Function.Parameters.Keys) { if ($CommonParameters -notcontains $Key) { $Param = $Function.Parameters.$Key diff --git a/Modules/CIPPCore/Public/Set-CIPPUserJITAdmin.ps1 b/Modules/CIPPCore/Public/Set-CIPPUserJITAdmin.ps1 index bf5d054d1ee0..2fdcb5864535 100644 --- a/Modules/CIPPCore/Public/Set-CIPPUserJITAdmin.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPUserJITAdmin.ps1 @@ -70,7 +70,7 @@ function Set-CIPPUserJITAdmin { forceChangePasswordNextSignInWithMfa = $false password = $Password } - $Schema.id = @{ + "$($Schema.id)" = @{ jitAdminEnabled = $false jitAdminExpiration = $Expiration.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') jitAdminStartDate = if ($StartDate) { $StartDate.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') } else { $null } @@ -127,8 +127,11 @@ function Set-CIPPUserJITAdmin { New-GraphPOSTRequest -type PATCH -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)" -tenantid $TenantFilter -body $Json | Out-Null } catch {} } + $CreatedBy = if ($Headers) { + ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Headers.'x-ms-client-principal')) | ConvertFrom-Json).userDetails + } else { 'Unknown' } - Set-CIPPUserJITAdminProperties -TenantFilter $TenantFilter -UserId $UserObj.id -Enabled -Expiration $Expiration -StartDate $StartDate -Reason $Reason -CreatedBy (if ($Headers) { ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Headers.'x-ms-client-principal')) | ConvertFrom-Json).userDetails } else { 'Unknown' }) | Out-Null + Set-CIPPUserJITAdminProperties -TenantFilter $TenantFilter -UserId $UserObj.id -Enabled -Expiration $Expiration -StartDate $StartDate -Reason $Reason -CreatedBy $CreatedBy | Out-Null $Message = "Added admin roles to user $($UserObj.displayName) ($($UserObj.userPrincipalName)). Reason: $Reason" $LogData = @{ UserPrincipalName = $UserObj.userPrincipalName From 54fd2bca7b48bbf2a1fe3c2d429c64476276e3e9 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 17 Dec 2025 21:13:32 -0500 Subject: [PATCH 03/20] restore modulebuilder in dev --- .github/workflows/dev_api.yml | 54 +++++++++++++++++++++++++++++ Tools/Build-FunctionPermissions.ps1 | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dev_api.yml b/.github/workflows/dev_api.yml index deea847bd266..cde768a8cc6f 100644 --- a/.github/workflows/dev_api.yml +++ b/.github/workflows/dev_api.yml @@ -33,6 +33,60 @@ jobs: path: "~/.local/share/powershell/Modules" key: ${{ runner.os }}-ModuleBuilder + - name: Install ModuleBuilder + if: steps.cacher.outputs.cache-hit != 'true' + shell: pwsh + run: | + Set-PSRepository PSGallery -InstallationPolicy Trusted + Install-Module ModuleBuilder -AllowClobber -Force + + - name: Build CIPPCore Module + shell: pwsh + run: | + $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CIPPCore" + $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" + + Write-Host "Building module from: $ModulePath" + Write-Host "Output directory: $OutputPath" + + # Generate function permissions before replacing the source module + $ToolsPath = Join-Path $env:GITHUB_WORKSPACE "Tools" + $ScriptPath = Join-Path $ToolsPath "Build-FunctionPermissions.ps1" + pwsh -File $ScriptPath -ModulePath $ModulePath + + # Build the module using ModuleBuilder + Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose + + # Replace the source module with the built module + Remove-Item -Path $ModulePath -Recurse -Force + Copy-Item -Path (Join-Path $OutputPath "CIPPCore") -Destination $ModulePath -Recurse -Force + + Write-Host "Module built and replaced successfully" + + # Clean up output directory + Remove-Item -Path $OutputPath -Recurse -Force + + - name: Build CippExtensions Module + shell: pwsh + run: | + $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CippExtensions" + $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" + + Write-Host "Building module from: $ModulePath" + Write-Host "Output directory: $OutputPath" + + # Build the module using ModuleBuilder + Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose + + # Replace the source module with the built module + Remove-Item -Path $ModulePath -Recurse -Force + Copy-Item -Path (Join-Path $OutputPath "CippExtensions") -Destination $ModulePath -Recurse -Force + + Write-Host "Module built and replaced successfully" + + # Clean up output directory + Remove-Item -Path $OutputPath -Recurse -Force + - name: Login to Azure uses: azure/login@v2 with: diff --git a/Tools/Build-FunctionPermissions.ps1 b/Tools/Build-FunctionPermissions.ps1 index 47a3b2e8de39..38586cc37ff9 100644 --- a/Tools/Build-FunctionPermissions.ps1 +++ b/Tools/Build-FunctionPermissions.ps1 @@ -70,7 +70,7 @@ foreach ($command in $commands | Sort-Object -Property Name | Select-Object -Uni $functionality = '' } - if ($role -or $functionality) { + if ($role -and $functionality) { $permissions[$command.Name] = @{ Role = $role Functionality = $functionality From 91ec24300304615a02d9e96983b080a20623bb80 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 17 Dec 2025 21:39:10 -0500 Subject: [PATCH 04/20] fix missing permission --- .../Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 index a8312cdc0bcb..7bc640c58c7c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 @@ -2,6 +2,8 @@ function Invoke-ListGDAPAccessAssignments { <# .FUNCTIONALITY Entrypoint,AnyTenant + .ROLE + CIPP.Core.Read #> [CmdletBinding()] param($Request, $TriggerMetadata) From b39c5f359a68c3449b020521788826e9ddf935bc Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 17 Dec 2025 21:43:25 -0500 Subject: [PATCH 05/20] undo modulebuilder --- .github/workflows/dev_api.yml | 61 ------------------- .../Authentication/Get-CIPPHttpFunctions.ps1 | 45 ++------------ .../Invoke-ListFunctionParameters.ps1 | 57 ++++------------- 3 files changed, 19 insertions(+), 144 deletions(-) diff --git a/.github/workflows/dev_api.yml b/.github/workflows/dev_api.yml index cde768a8cc6f..3d92bc00c7d3 100644 --- a/.github/workflows/dev_api.yml +++ b/.github/workflows/dev_api.yml @@ -26,67 +26,6 @@ jobs: with: persist-credentials: false - - name: Setup PowerShell module cache - id: cacher - uses: actions/cache@v3 - with: - path: "~/.local/share/powershell/Modules" - key: ${{ runner.os }}-ModuleBuilder - - - name: Install ModuleBuilder - if: steps.cacher.outputs.cache-hit != 'true' - shell: pwsh - run: | - Set-PSRepository PSGallery -InstallationPolicy Trusted - Install-Module ModuleBuilder -AllowClobber -Force - - - name: Build CIPPCore Module - shell: pwsh - run: | - $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CIPPCore" - $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" - - Write-Host "Building module from: $ModulePath" - Write-Host "Output directory: $OutputPath" - - # Generate function permissions before replacing the source module - $ToolsPath = Join-Path $env:GITHUB_WORKSPACE "Tools" - $ScriptPath = Join-Path $ToolsPath "Build-FunctionPermissions.ps1" - pwsh -File $ScriptPath -ModulePath $ModulePath - - # Build the module using ModuleBuilder - Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose - - # Replace the source module with the built module - Remove-Item -Path $ModulePath -Recurse -Force - Copy-Item -Path (Join-Path $OutputPath "CIPPCore") -Destination $ModulePath -Recurse -Force - - Write-Host "Module built and replaced successfully" - - # Clean up output directory - Remove-Item -Path $OutputPath -Recurse -Force - - - name: Build CippExtensions Module - shell: pwsh - run: | - $ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CippExtensions" - $OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output" - - Write-Host "Building module from: $ModulePath" - Write-Host "Output directory: $OutputPath" - - # Build the module using ModuleBuilder - Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose - - # Replace the source module with the built module - Remove-Item -Path $ModulePath -Recurse -Force - Copy-Item -Path (Join-Path $OutputPath "CippExtensions") -Destination $ModulePath -Recurse -Force - - Write-Host "Module built and replaced successfully" - - # Clean up output directory - Remove-Item -Path $OutputPath -Recurse -Force - - name: Login to Azure uses: azure/login@v2 with: diff --git a/Modules/CIPPCore/Public/Authentication/Get-CIPPHttpFunctions.ps1 b/Modules/CIPPCore/Public/Authentication/Get-CIPPHttpFunctions.ps1 index ad0efeee88ca..178ee74fb4ab 100644 --- a/Modules/CIPPCore/Public/Authentication/Get-CIPPHttpFunctions.ps1 +++ b/Modules/CIPPCore/Public/Authentication/Get-CIPPHttpFunctions.ps1 @@ -5,48 +5,15 @@ function Get-CIPPHttpFunctions { ) try { - # Load permissions from cache - if (-not $global:CIPPFunctionPermissions) { - $CIPPCoreModule = Get-Module -Name CIPPCore - if ($CIPPCoreModule) { - $PermissionsFileJson = Join-Path $CIPPCoreModule.ModuleBase 'lib' 'data' 'function-permissions.json' - - if (Test-Path $PermissionsFileJson) { - try { - $jsonData = Get-Content -Path $PermissionsFileJson -Raw | ConvertFrom-Json -AsHashtable - $global:CIPPFunctionPermissions = [System.Collections.Hashtable]::new([StringComparer]::OrdinalIgnoreCase) - foreach ($key in $jsonData.Keys) { - $global:CIPPFunctionPermissions[$key] = $jsonData[$key] - } - Write-Information "Loaded $($global:CIPPFunctionPermissions.Count) function permissions from JSON cache" - } catch { - Write-Warning "Failed to load function permissions from JSON: $($_.Exception.Message)" - } - } - } - } - $Functions = Get-Command -Module CIPPCore | Where-Object { $_.Visibility -eq 'Public' -and $_.Name -match 'Invoke-*' } $Results = foreach ($Function in $Functions) { - $FunctionName = $Function.Name - if ($global:CIPPFunctionPermissions -and $global:CIPPFunctionPermissions.ContainsKey($FunctionName)) { - $PermissionData = $global:CIPPFunctionPermissions[$FunctionName] - $Functionality = $PermissionData['Functionality'] - $Role = $PermissionData['Role'] - $Description = $PermissionData['Description'] - } else { - $Help = Get-Help $Function - $Functionality = $Help.Functionality - $Role = $Help.Role - $Description = $Help.Description - } - - if ($Functionality -notmatch 'Entrypoint') { continue } - if ($Role -eq 'Public') { continue } + $Help = Get-Help $Function + if ($Help.Functionality -notmatch 'Entrypoint') { continue } + if ($Help.Role -eq 'Public') { continue } [PSCustomObject]@{ - Function = $FunctionName - Role = $Role - Description = $Description + Function = $Function.Name + Role = $Help.Role + Description = $Help.Description } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 index 9f849ae23f30..da83bee09207 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 @@ -21,27 +21,6 @@ function Invoke-ListFunctionParameters { $CommonParameters = @('Verbose', 'Debug', 'ErrorAction', 'WarningAction', 'InformationAction', 'ErrorVariable', 'WarningVariable', 'InformationVariable', 'OutVariable', 'OutBuffer', 'PipelineVariable', 'TenantFilter', 'APIName', 'Headers', 'ProgressAction', 'WhatIf', 'Confirm', 'Headers', 'NoAuthCheck') $TemporaryBlacklist = 'Get-CIPPAuthentication', 'Invoke-CippWebhookProcessing', 'Invoke-ListFunctionParameters', 'New-CIPPAPIConfig', 'New-CIPPGraphSubscription' try { - # Load permissions from cache - if (-not $global:CIPPFunctionPermissions) { - $CIPPCoreModule = Get-Module -Name CIPPCore - if ($CIPPCoreModule) { - $PermissionsFileJson = Join-Path $CIPPCoreModule.ModuleBase 'lib' 'data' 'function-permissions.json' - - if (Test-Path $PermissionsFileJson) { - try { - $jsonData = Get-Content -Path $PermissionsFileJson -Raw | ConvertFrom-Json -AsHashtable - $global:CIPPFunctionPermissions = [System.Collections.Hashtable]::new([StringComparer]::OrdinalIgnoreCase) - foreach ($key in $jsonData.Keys) { - $global:CIPPFunctionPermissions[$key] = $jsonData[$key] - } - Write-Information "Loaded $($global:CIPPFunctionPermissions.Count) function permissions from JSON cache" - } catch { - Write-Warning "Failed to load function permissions from JSON: $($_.Exception.Message)" - } - } - } - } - if ($Module -eq 'ExchangeOnlineManagement') { $ExoRequest = @{ AvailableCmdlets = $true @@ -57,27 +36,17 @@ function Invoke-ListFunctionParameters { $Functions = Get-Command @CommandQuery | Where-Object { $_.Visibility -eq 'Public' } } $Results = foreach ($Function in $Functions) { - if ($Function -In $TemporaryBlacklist) { continue } - $FunctionName = $Function.Name - if ($Module -ne 'ExchangeOnlineManagement' -and $global:CIPPFunctionPermissions -and $global:CIPPFunctionPermissions.ContainsKey($FunctionName)) { - $PermissionData = $global:CIPPFunctionPermissions[$FunctionName] - $Functionality = $PermissionData['Functionality'] - $Synopsis = $PermissionData['Description'] - } else { - $GetHelp = @{ - Name = $Function - } - if ($Module -eq 'ExchangeOnlineManagement') { - $GetHelp.Path = 'ExchangeOnlineHelp' - } - $Help = Get-Help @GetHelp - $Functionality = $Help.Functionality - $Synopsis = $Help.Synopsis + if ($Function -in $TemporaryBlacklist) { continue } + $GetHelp = @{ + Name = $Function + } + if ($Module -eq 'ExchangeOnlineManagement') { + $GetHelp.Path = 'ExchangeOnlineHelp' } - - $ParamsHelp = @() - if ($Functionality -in $IgnoreList) { continue } - if ($Functionality -match 'Entrypoint') { continue } + $Help = Get-Help @GetHelp + $ParamsHelp = ($Help | Select-Object -ExpandProperty parameters).parameter | Select-Object name, @{n = 'description'; exp = { $_.description.Text } } + if ($Help.Functionality -in $IgnoreList) { continue } + if ($Help.Functionality -match 'Entrypoint') { continue } $Parameters = foreach ($Key in $Function.Parameters.Keys) { if ($CommonParameters -notcontains $Key) { $Param = $Function.Parameters.$Key @@ -103,8 +72,8 @@ function Invoke-ListFunctionParameters { $StatusCode = [HttpStatusCode]::BadRequest } return [HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($Results) - } + StatusCode = $StatusCode + Body = @($Results) + } } From dda57731e5dac3926d3aad82f2200ac5ae9c7f46 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 17 Dec 2025 21:44:22 -0500 Subject: [PATCH 06/20] Update Invoke-ListGDAPAccessAssignments.ps1 --- .../Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 index 7bc640c58c7c..053e11c3cdb8 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 @@ -3,7 +3,7 @@ function Invoke-ListGDAPAccessAssignments { .FUNCTIONALITY Entrypoint,AnyTenant .ROLE - CIPP.Core.Read + Tenant.Relationship.Read #> [CmdletBinding()] param($Request, $TriggerMetadata) From f3c5e3dbb86686d65f21f86e039935a18a641144 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 17 Dec 2025 21:46:07 -0500 Subject: [PATCH 07/20] handle errors with cached data --- Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 index 82677b90ddbe..effa274d8360 100644 --- a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 +++ b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 @@ -209,8 +209,8 @@ function Test-CIPPAuditLogRules { } else { # Use cached lookups $Users = ($Lookups | Where-Object { $_.RowKey -eq 'users' }).Data | ConvertFrom-Json - $Groups = (($Lookups | Where-Object { $_.RowKey -eq 'groups' }).Data | ConvertFrom-Json) ?? @() - $Devices = (($Lookups | Where-Object { $_.RowKey -eq 'devices' }).Data | ConvertFrom-Json) ?? @() + $Groups = (($Lookups | Where-Object { $_.RowKey -eq 'groups' }).Data | ConvertFrom-Json -ErrorAction SilentlyContinue) ?? @() + $Devices = (($Lookups | Where-Object { $_.RowKey -eq 'devices' }).Data | ConvertFrom-Json -ErrorAction SilentlyContinue) ?? @() $ServicePrincipals = ($Lookups | Where-Object { $_.RowKey -eq 'servicePrincipals' }).Data | ConvertFrom-Json Write-Information "Using cached directory lookups for tenant $TenantFilter" } From d31530a34dcf5f181ee30c4774c19c9a1758e6f1 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 18 Dec 2025 14:18:06 +0100 Subject: [PATCH 08/20] log messages --- .../CIPPCore/Public/Functions/Test-CIPPStandardLicense.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Functions/Test-CIPPStandardLicense.ps1 b/Modules/CIPPCore/Public/Functions/Test-CIPPStandardLicense.ps1 index caf6edd5c6b4..b0bec0aee247 100644 --- a/Modules/CIPPCore/Public/Functions/Test-CIPPStandardLicense.ps1 +++ b/Modules/CIPPCore/Public/Functions/Test-CIPPStandardLicense.ps1 @@ -45,7 +45,7 @@ function Test-CIPPStandardLicense { if ($Capabilities.Count -le 0) { if (!$SkipLog.IsPresent) { - Write-LogMessage -API 'Standards' -tenant $TenantFilter -message "Tenant does not have the required capability to run standard $StandardName`: The tenant needs one of the following service plans: $($RequiredCapabilities -join ',')" -sev Error + Write-LogMessage -API 'Standards' -tenant $TenantFilter -message "Tenant does not have the required capability to run standard $StandardName`: The tenant needs one of the following service plans: $($RequiredCapabilities -join ',')" -sev Info Set-CIPPStandardsCompareField -FieldName "standards.$StandardName" -FieldValue "License Missing: This tenant is not licensed for the following capabilities: $($RequiredCapabilities -join ',')" -Tenant $TenantFilter Write-Verbose "Tenant does not have the required capability to run standard $StandardName - $($RequiredCapabilities -join ','). Exiting" } @@ -57,7 +57,7 @@ function Test-CIPPStandardLicense { if (!$SkipLog.IsPresent) { # Sanitize exception message to prevent JSON parsing issues - remove characters that could interfere with JSON detection $SanitizedMessage = $_.Exception.Message -replace '[{}\[\]]', '' - Write-LogMessage -API 'Standards' -tenant $TenantFilter -message "Error checking license capabilities for standard $StandardName`: $SanitizedMessage" -sev Error + Write-LogMessage -API 'Standards' -tenant $TenantFilter -message "Error checking license capabilities for standard $StandardName`: $SanitizedMessage" -sev Info Set-CIPPStandardsCompareField -FieldName "standards.$StandardName" -FieldValue "License Missing: Error checking license capabilities - $SanitizedMessage" -Tenant $TenantFilter } return $false From 9903a0b5b7725e96b629cf952cb7bc9dda85c303 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:09:24 +0100 Subject: [PATCH 09/20] fixes object --- .../Alerts/Get-CIPPAlertGlobalAdminAllowList.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertGlobalAdminAllowList.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertGlobalAdminAllowList.ps1 index a9ad871008dd..82ae1ebc1cfb 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertGlobalAdminAllowList.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertGlobalAdminAllowList.ps1 @@ -46,8 +46,8 @@ function Get-CIPPAlertGlobalAdminAllowList { $UpnPrefix = ($admin.userPrincipalName -split '@')[0].ToLowerInvariant() if ($AllowedLookup -notcontains $UpnPrefix) { [PSCustomObject]@{ - Admin = $admin - UpnPrefix = $UpnPrefix + Admin = $admin + UpnPrefix = $UpnPrefix } } } @@ -69,10 +69,10 @@ function Get-CIPPAlertGlobalAdminAllowList { } else { $NonCompliantUpns = @($UnapprovedAdmins.Admin.userPrincipalName) $AlertData = @([PSCustomObject]@{ - Message = "Found $($NonCompliantUpns.Count) Global Administrator account(s) not in the approved allow list." - NonCompliantUsers = $NonCompliantUpns - ApprovedPrefixes = if ($AllowedAdmins) { $AllowedAdmins -join ', ' } else { 'Not provided' } - Tenant = $TenantFilter + Message = "Found $($NonCompliantUpns.Count) Global Administrator account(s) not in the approved allow list." + NonCompliantUsers = $NonCompliantUpns -join ', ' + ApprovedPrefixes = if ($AllowedAdmins) { $AllowedAdmins -join ', ' } else { 'Not provided' } + Tenant = $TenantFilter }) } From 7cf0f3608899781538c3a976ad6e89bcb8540402 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:31:44 +0100 Subject: [PATCH 10/20] Fixed --- .../Public/Alerts/Get-CIPPAlertMXRecordChanged.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertMXRecordChanged.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertMXRecordChanged.ps1 index 138ba2870631..429f342e60fa 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertMXRecordChanged.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertMXRecordChanged.ps1 @@ -30,12 +30,12 @@ function Get-CIPPAlertMXRecordChanged { # Update cache with current data foreach ($Domain in $DomainData) { $CacheEntity = @{ - PartitionKey = $TenantFilter - RowKey = $Domain.Domain - Domain = $Domain.Domain - ActualMXRecords = $Domain.ActualMXRecords - LastRefresh = $Domain.LastRefresh - MailProvider = $Domain.MailProvider + PartitionKey = [string]$TenantFilter + RowKey = [string]$Domain.Domain + Domain = [string]$Domain.Domain + ActualMXRecords = [string]$Domain.ActualMXRecords + LastRefresh = [string]$Domain.LastRefresh + MailProvider = [string]$Domain.MailProvider } Add-CIPPAzDataTableEntity @CacheTable -Entity $CacheEntity -Force } From 8480bf26a88d90dbd016ae11ed1bbd2bdf2144ba Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 12:15:09 -0500 Subject: [PATCH 11/20] prevent null permissions --- .../Public/Authentication/Get-CippAllowedPermissions.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Authentication/Get-CippAllowedPermissions.ps1 b/Modules/CIPPCore/Public/Authentication/Get-CippAllowedPermissions.ps1 index a52728c6d5d7..27affd350b11 100644 --- a/Modules/CIPPCore/Public/Authentication/Get-CippAllowedPermissions.ps1 +++ b/Modules/CIPPCore/Public/Authentication/Get-CippAllowedPermissions.ps1 @@ -32,7 +32,7 @@ function Get-CippAllowedPermissions { $AllPermissionCacheTable = Get-CIPPTable -tablename 'cachehttppermissions' $AllPermissionsRow = Get-CIPPAzDataTableEntity @AllPermissionCacheTable -Filter "PartitionKey eq 'HttpFunctions' and RowKey eq 'HttpFunctions' and Version eq '$($Version)'" - if (-not $AllPermissionsRow) { + if (-not $AllPermissionsRow.Permissions) { $AllPermissions = Get-CIPPHttpFunctions -ByRole | Select-Object -ExpandProperty Permission $Entity = @{ PartitionKey = 'HttpFunctions' From e97d981517d9b09c15dda0a56504138f5661b09f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 12:15:24 -0500 Subject: [PATCH 12/20] filter list to only tenants that have standards applied --- .../Standards/Invoke-CIPPStandardsRun.ps1 | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 index f8b4316ad842..b7839a91624a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 @@ -53,21 +53,16 @@ function Invoke-CIPPStandardsRun { Test-CIPPRerun -ClearAll -TenantFilter $TenantFilter -Type 'Standard' } - # Get tenant list for batch processing - write-host "Getting tenants for filter: $TenantFilter" - $AllTenantsList = if ($TenantFilter -eq 'allTenants') { - Get-Tenants - } else { - Get-Tenants | Where-Object { - $_.defaultDomainName -eq $TenantFilter -or $_.customerId -eq $TenantFilter - } + $StandardsParams = @{ + TenantFilter = $TenantFilter + runManually = $runManually } - - if ($AllTenantsList.Count -eq 0) { - Write-Information "No tenants found for filter $TenantFilter" - return + if ($TemplateID) { + $StandardsParams['TemplateId'] = $TemplateID } + $AllTenantsList = Get-CIPPStandards @StandardsParams | Select-Object -ExpandProperty Tenant | Sort-Object -Unique + # Build batch of per-tenant list activities $Batch = foreach ($Tenant in $AllTenantsList) { $BatchItem = @{ From 4463cc6ba90101df8ee74366307ef7f3c5b481a9 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 13:16:13 -0500 Subject: [PATCH 13/20] Improve error logging in Get-CIPPAlertRestrictedUsers Replaces Write-AlertMessage with Write-LogMessage for better error reporting, including severity, API context, and detailed exception data. --- Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 index 23921f7899a7..8898cf4b0302 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 @@ -37,6 +37,6 @@ Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData } } catch { - Write-AlertMessage -tenant $($TenantFilter) -message "Could not get restricted users for $($TenantFilter): $(Get-NormalizedError -message $_.Exception.message)" + Write-LogMessage -tenant $($TenantFilter) -message "Could not get restricted users for $($TenantFilter): $(Get-NormalizedError -message $_.Exception.message)" -severity 'Error' -API 'Get-CIPPAlertRestrictedUsers' -LogData (Get-CippException -Exception $_) } } From 1d4bfad4ff53ec557de5a9e38f97617f3766060d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 13:16:18 -0500 Subject: [PATCH 14/20] Update Invoke-CIPPStandardsRun.ps1 --- .../HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 index b7839a91624a..81810ce00d00 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 @@ -67,7 +67,7 @@ function Invoke-CIPPStandardsRun { $Batch = foreach ($Tenant in $AllTenantsList) { $BatchItem = @{ FunctionName = 'CIPPStandardsList' - TenantFilter = $Tenant.defaultDomainName + TenantFilter = $Tenant runManually = $runManually } if ($TemplateID) { From 126af848f9c14cdf81b927e7c332b56e505bf8f0 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 13:50:51 -0500 Subject: [PATCH 15/20] fix release notes --- .../Tools/GitHub/Invoke-ListGitHubReleaseNotes.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListGitHubReleaseNotes.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListGitHubReleaseNotes.ps1 index fd6852d65e3e..059785cfb760 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListGitHubReleaseNotes.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListGitHubReleaseNotes.ps1 @@ -29,12 +29,17 @@ $Table = Get-CIPPTable -TableName cacheGitHubReleaseNotes $PartitionKey = 'GitHubReleaseNotes' $Filter = "PartitionKey eq '$PartitionKey'" - $Rows = Get-CIPPAzDataTableEntity @Table -filter $Filter | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-24) + $Rows = Get-CIPPAzDataTableEntity @Table -filter $Filter try { if ($Rows) { $Releases = ConvertFrom-Json -InputObject $Rows.GitHubReleases -Depth 10 - } else { + if ($Releases.releaseTag -notmatch $global:CippVersion) { + $Releases = $null + } + } + + if (-not $Releases) { $Releases = Invoke-GitHubApiRequest -Path $ReleasePath $Releases = $Releases | ForEach-Object { [ordered]@{ @@ -48,7 +53,6 @@ commitish = $_.target_commitish } } - $Results = @{ GitHubReleases = [string](ConvertTo-Json -Depth 10 -InputObject $Releases) RowKey = [string]'GitHubReleaseNotes' From d57712b0eb42400b275223f81a07b26b665b8781 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 17:12:45 -0500 Subject: [PATCH 16/20] Improve error handling for repo permissions JSON conversion Adds -ErrorAction SilentlyContinue and -Compress to ConvertTo-Json and ConvertFrom-Json calls for repository permissions. This change prevents errors from interrupting execution if JSON conversion fails and ensures more robust handling of permission data. --- .../HTTP Functions/Tools/GitHub/Invoke-ListCommunityRepos.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListCommunityRepos.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListCommunityRepos.ps1 index e0869e456abd..79256990242f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListCommunityRepos.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListCommunityRepos.ps1 @@ -42,7 +42,7 @@ function Invoke-ListCommunityRepos { WriteAccess = $Repo.WriteAccess DefaultBranch = $Repo.DefaultBranch UploadBranch = $Repo.DefaultBranch - Permissions = [string]($Repo.RepoPermissions | ConvertTo-Json) + Permissions = [string]($Repo.RepoPermissions | ConvertTo-Json -ErrorAction SilentlyContinue -Compress) } Add-CIPPAzDataTableEntity @Table -Entity $Entity $DefaultsMissing = $true @@ -65,7 +65,7 @@ function Invoke-ListCommunityRepos { WriteAccess = $_.WriteAccess DefaultBranch = $_.DefaultBranch UploadBranch = $_.UploadBranch ?? $_.DefaultBranch - RepoPermissions = $_.Permissions | ConvertFrom-Json + RepoPermissions = ($_.Permissions | ConvertFrom-Json -ErrorAction SilentlyContinue) ?? @{} } } From c90423ec439d162f085f643c0d19a8e65db17a5d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 17:12:55 -0500 Subject: [PATCH 17/20] Handle null values in directory lookups Added null-coalescing operators to ensure $Users and $ServicePrincipals are always arrays, preventing potential errors when responses or cached data are missing or null. Also added -ErrorAction SilentlyContinue to ConvertFrom-Json calls for more robust error handling. --- .../CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 index effa274d8360..cbd8229cf7fe 100644 --- a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 +++ b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 @@ -176,10 +176,10 @@ function Test-CIPPAuditLogRules { } ) $Response = New-GraphBulkRequest -TenantId $TenantFilter -Requests $Requests - $Users = ($Response | Where-Object { $_.id -eq 'users' }).body.value + $Users = ($Response | Where-Object { $_.id -eq 'users' }).body.value ?? @() $Groups = ($Response | Where-Object { $_.id -eq 'groups' }).body.value ?? @() $Devices = ($Response | Where-Object { $_.id -eq 'devices' }).body.value ?? @() - $ServicePrincipals = ($Response | Where-Object { $_.id -eq 'servicePrincipals' }).body.value + $ServicePrincipals = ($Response | Where-Object { $_.id -eq 'servicePrincipals' }).body.value ?? @() # Cache the lookups for 1 day $Entities = @( @{ @@ -208,10 +208,10 @@ function Test-CIPPAuditLogRules { Write-Information "Cached directory lookups for tenant $TenantFilter" } else { # Use cached lookups - $Users = ($Lookups | Where-Object { $_.RowKey -eq 'users' }).Data | ConvertFrom-Json + $Users = (($Lookups | Where-Object { $_.RowKey -eq 'users' }).Data | ConvertFrom-Json -ErrorAction SilentlyContinue) ?? @() $Groups = (($Lookups | Where-Object { $_.RowKey -eq 'groups' }).Data | ConvertFrom-Json -ErrorAction SilentlyContinue) ?? @() $Devices = (($Lookups | Where-Object { $_.RowKey -eq 'devices' }).Data | ConvertFrom-Json -ErrorAction SilentlyContinue) ?? @() - $ServicePrincipals = ($Lookups | Where-Object { $_.RowKey -eq 'servicePrincipals' }).Data | ConvertFrom-Json + $ServicePrincipals = (($Lookups | Where-Object { $_.RowKey -eq 'servicePrincipals' }).Data | ConvertFrom-Json -ErrorAction SilentlyContinue) ?? @() Write-Information "Using cached directory lookups for tenant $TenantFilter" } From 41a2e3dc910726b1e54d1d84e23e2ac848fc698b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 17:13:41 -0500 Subject: [PATCH 18/20] Improve release notes cache validation by major.minor Updated the logic to check for cached GitHub release notes to match the current major.minor version series using semantic versioning, instead of only matching the full release tag. This ensures that release notes are refreshed only when the major.minor version changes, improving cache efficiency. --- .../GitHub/Invoke-ListGitHubReleaseNotes.ps1 | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListGitHubReleaseNotes.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListGitHubReleaseNotes.ps1 index 059785cfb760..8769763e6435 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListGitHubReleaseNotes.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ListGitHubReleaseNotes.ps1 @@ -32,14 +32,31 @@ $Rows = Get-CIPPAzDataTableEntity @Table -filter $Filter try { + $Latest = $false if ($Rows) { $Releases = ConvertFrom-Json -InputObject $Rows.GitHubReleases -Depth 10 - if ($Releases.releaseTag -notmatch $global:CippVersion) { - $Releases = $null + $CurrentVersion = [semver]$global:CippVersion + $CurrentMajorMinor = "$($CurrentVersion.Major).$($CurrentVersion.Minor)" + + foreach ($Release in $Releases) { + $Version = $Release.releaseTag -replace 'v', '' + try { + $ReleaseVersion = [semver]$Version + $ReleaseMajorMinor = "$($ReleaseVersion.Major).$($ReleaseVersion.Minor)" + + # Check if we have cached notes for the current major.minor version series + if ($ReleaseMajorMinor -eq $CurrentMajorMinor) { + $Latest = $true + break + } + } catch { + # Skip invalid semver versions + continue + } } } - if (-not $Releases) { + if (-not $Latest) { $Releases = Invoke-GitHubApiRequest -Path $ReleasePath $Releases = $Releases | ForEach-Object { [ordered]@{ From f86aad31dad24bee492ed00451a381e9cfbd1359 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 18:23:55 -0500 Subject: [PATCH 19/20] Improve CA policy location and service exception handling Refactored location processing in New-CIPPCATemplate to reduce redundant API calls and ensure unique location info. Enhanced Set-CIPPCAPolicyServiceException with clearer logic, improved string handling, and added more informative logging. Minor bug fixes and code style improvements in Invoke-ExecCAServiceExclusion and New-CIPPCAPolicy for robustness. --- .../Invoke-ExecCAServiceExclusion.ps1 | 10 +-- Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 | 2 + .../CIPPCore/Public/New-CIPPCATemplate.ps1 | 70 ++++++++++--------- .../Set-CIPPCAPolicyServiceException.ps1 | 44 ++++++------ 4 files changed, 67 insertions(+), 59 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCAServiceExclusion.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCAServiceExclusion.ps1 index b167ed66cbf5..8d1010e3362f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCAServiceExclusion.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCAServiceExclusion.ps1 @@ -1,4 +1,4 @@ -Function Invoke-ExecCAServiceExclusion { +function Invoke-ExecCAServiceExclusion { <# .FUNCTIONALITY Entrypoint @@ -17,7 +17,7 @@ Function Invoke-ExecCAServiceExclusion { try { $result = Set-CIPPCAPolicyServiceException -TenantFilter $TenantFilter -PolicyId $ID $Body = @{ Results = $result } - Write-LogMessage -headers $Headers -API 'Set-CIPPCAPolicyServiceException' -message $Message -Sev 'Info' -tenant $TenantFilter + Write-LogMessage -headers $Headers -API 'Set-CIPPCAPolicyServiceException' -message $result -Sev 'Info' -tenant $TenantFilter } catch { $ErrorMessage = Get-CippException -Exception $_ $Body = @{ Results = "Failed to add service provider exception to policy $($ID): $($ErrorMessage.NormalizedError)" } @@ -25,7 +25,7 @@ Function Invoke-ExecCAServiceExclusion { } return ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) } diff --git a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 index fda3af4a638f..27ce66d595f7 100644 --- a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 @@ -204,6 +204,7 @@ function New-CIPPCAPolicy { Write-Information ($LocationLookupTable | ConvertTo-Json -Depth 10) foreach ($location in $JSONobj.conditions.locations.includeLocations) { + if ($null -eq $location) { continue } $lookup = $LocationLookupTable | Where-Object { $_.name -eq $location -or $_.displayName -eq $location -or $_.templateId -eq $location } if (!$lookup) { continue } Write-Information "Replacing named location - $location" @@ -212,6 +213,7 @@ function New-CIPPCAPolicy { } foreach ($location in $JSONobj.conditions.locations.excludeLocations) { + if ($null -eq $location) { continue } $lookup = $LocationLookupTable | Where-Object { $_.name -eq $location -or $_.displayName -eq $location -or $_.templateId -eq $location } if (!$lookup) { continue } Write-Information "Replacing named location - $location" diff --git a/Modules/CIPPCore/Public/New-CIPPCATemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPCATemplate.ps1 index 1d5c36f490b5..8d0c7d86ba7a 100644 --- a/Modules/CIPPCore/Public/New-CIPPCATemplate.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPCATemplate.ps1 @@ -13,6 +13,15 @@ function New-CIPPCATemplate { $NonEmptyProperties = $_.psobject.Properties | Where-Object { $null -ne $_.Value } | Select-Object -ExpandProperty Name $_ | Select-Object -Property $NonEmptyProperties } + + Write-Information "Processing CA Template for tenant $TenantFilter" + Write-Information ($JSON | ConvertTo-Json -Depth 10) + + # Function to check if a string is a GUID + function Test-IsGuid($string) { + return [guid]::tryparse($string, [ref][guid]::Empty) + } + if ($preloadedUsers) { $users = $preloadedUsers } else { @@ -23,18 +32,25 @@ function New-CIPPCATemplate { } else { $groups = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups?`$top=999&`$select=displayName,id" -tenantid $TenantFilter) } - $includelocations = New-Object System.Collections.ArrayList + + $namedLocations = $null + if ($JSON.conditions.locations.includeLocations -or $JSON.conditions.locations.excludeLocations) { + $namedLocations = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations' -tenantid $TenantFilter + } + + $AllLocations = [system.collections.generic.list[object]]::new() + + $includelocations = [system.collections.generic.list[object]]::new() $IncludeJSON = foreach ($Location in $JSON.conditions.locations.includeLocations) { - $locationinfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations' -tenantid $TenantFilter | Where-Object -Property id -EQ $location | Select-Object * -ExcludeProperty id, *time* + $locationinfo = $namedLocations | Where-Object -Property id -EQ $location | Select-Object * -ExcludeProperty id, *time* $null = if ($locationinfo) { $includelocations.add($locationinfo.displayName) } else { $includelocations.add($location) } $locationinfo } if ($includelocations) { $JSON.conditions.locations.includeLocations = $includelocations } - - $excludelocations = New-Object System.Collections.ArrayList + $excludelocations = [system.collections.generic.list[object]]::new() $ExcludeJSON = foreach ($Location in $JSON.conditions.locations.excludeLocations) { - $locationinfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations' -tenantid $TenantFilter | Where-Object -Property id -EQ $location | Select-Object * -ExcludeProperty id, *time* + $locationinfo = $namedLocations | Where-Object -Property id -EQ $location | Select-Object * -ExcludeProperty id, *time* $null = if ($locationinfo) { $excludelocations.add($locationinfo.displayName) } else { $excludelocations.add($location) } $locationinfo } @@ -44,11 +60,8 @@ function New-CIPPCATemplate { $JSON.conditions.users.includeUsers = @($JSON.conditions.users.includeUsers | ForEach-Object { $originalID = $_ if ($_ -in 'All', 'None', 'GuestOrExternalUsers') { return $_ } - try { - ($users | Where-Object { $_.id -eq $_ }).displayName - } catch { - return $originalID - } + $match = $users | Where-Object { $_.id -eq $originalID } + if ($match) { $match.displayName } else { $originalID } }) } @@ -56,47 +69,36 @@ function New-CIPPCATemplate { $JSON.conditions.users.excludeUsers = @($JSON.conditions.users.excludeUsers | ForEach-Object { if ($_ -in 'All', 'None', 'GuestOrExternalUsers') { return $_ } $originalID = $_ - - try { - ($users | Where-Object { $_.id -eq $_ }).displayName - } catch { - return $originalID - } + $match = $users | Where-Object { $_.id -eq $originalID } + if ($match) { $match.displayName } else { $originalID } }) } - # Function to check if a string is a GUID - function Test-IsGuid($string) { - return [guid]::tryparse($string, [ref][guid]::Empty) - } - if ($JSON.conditions.users.includeGroups) { $JSON.conditions.users.includeGroups = @($JSON.conditions.users.includeGroups | ForEach-Object { $originalID = $_ if ($_ -in 'All', 'None', 'GuestOrExternalUsers' -or -not (Test-IsGuid $_)) { return $_ } - try { - ($groups | Where-Object { $_.id -eq $_ }).displayName - } catch { - return $originalID - } + $match = $groups | Where-Object { $_.id -eq $originalID } + if ($match) { $match.displayName } else { $originalID } }) } if ($JSON.conditions.users.excludeGroups) { $JSON.conditions.users.excludeGroups = @($JSON.conditions.users.excludeGroups | ForEach-Object { $originalID = $_ - if ($_ -in 'All', 'None', 'GuestOrExternalUsers' -or -not (Test-IsGuid $_)) { return $_ } - try { - ($groups | Where-Object { $_.id -eq $_ }).displayName - } catch { - return $originalID - - } + $match = $groups | Where-Object { $_.id -eq $originalID } + if ($match) { $match.displayName } else { $originalID } }) } - $JSON | Add-Member -NotePropertyName 'LocationInfo' -NotePropertyValue @($IncludeJSON, $ExcludeJSON) + foreach ($Location in $IncludeJSON) { + $AllLocations.Add($Location) + } + foreach ($Location in $ExcludeJSON) { + $AllLocations.Add($Location) + } + $JSON | Add-Member -NotePropertyName 'LocationInfo' -NotePropertyValue @($AllLocations | Select-Object -Unique) -Force $JSON = (ConvertTo-Json -Compress -Depth 100 -InputObject $JSON) return $JSON } diff --git a/Modules/CIPPCore/Public/Set-CIPPCAPolicyServiceException.ps1 b/Modules/CIPPCore/Public/Set-CIPPCAPolicyServiceException.ps1 index 8b52c1d83d6f..51ca8ee0289b 100644 --- a/Modules/CIPPCore/Public/Set-CIPPCAPolicyServiceException.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPCAPolicyServiceException.ps1 @@ -11,20 +11,20 @@ function Set-CIPPCAPolicyServiceException { $policy = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($PolicyId)" -tenantid $TenantFilter -AsApp $true # If the policy is set to affect either all or all guests/external users - if ($policy.conditions.users.includeUsers -eq "All" -OR $policy.conditions.users.includeGuestsOrExternalUsers.externalTenants.membershipKind -eq "all") { + if ($policy.conditions.users.includeUsers -eq 'All' -or $policy.conditions.users.includeGuestsOrExternalUsers.externalTenants.membershipKind -eq 'all') { # Check if the policy already has the correct service provider exception if ($policy.conditions.users.excludeGuestsOrExternalUsers) { $excludeConfig = $policy.conditions.users.excludeGuestsOrExternalUsers # Check if serviceProvider is already in guestOrExternalUserTypes - $hasServiceProvider = $excludeConfig.guestOrExternalUserTypes -match "serviceProvider" + $hasServiceProvider = $excludeConfig.guestOrExternalUserTypes -match 'serviceProvider' # Check if externalTenants is properly configured if ($excludeConfig.externalTenants) { $externalTenants = $excludeConfig.externalTenants - $hasCorrectExternalTenants = ($externalTenants.membershipKind -eq "enumerated" -and - $externalTenants.members -contains $CSPtenantId) + $hasCorrectExternalTenants = ($externalTenants.membershipKind -eq 'enumerated' -and + $externalTenants.members -contains $CSPtenantId) # If already configured, exit without making changes if ($hasServiceProvider -and $hasCorrectExternalTenants) { @@ -38,11 +38,11 @@ function Set-CIPPCAPolicyServiceException { # Define data $excludeServiceProviderData = [pscustomobject]@{ - guestOrExternalUserTypes = "serviceProvider" - externalTenants = [pscustomobject]@{ - '@odata.type' = "#microsoft.graph.conditionalAccessEnumeratedExternalTenants" - membershipKind = "enumerated" - members = @( + guestOrExternalUserTypes = 'serviceProvider' + externalTenants = [pscustomobject]@{ + '@odata.type' = '#microsoft.graph.conditionalAccessEnumeratedExternalTenants' + membershipKind = 'enumerated' + members = @( $CSPtenantId ) } @@ -56,27 +56,31 @@ function Set-CIPPCAPolicyServiceException { if ($policy.conditions.users.excludeGuestsOrExternalUsers) { # If guestOrExternalUserTypes doesn't include type serviceProvider add it - if ($policy.conditions.users.excludeGuestsOrExternalUsers.guestOrExternalUserTypes -notmatch "serviceProvider") { - $policy.conditions.users.excludeGuestsOrExternalUsers.guestOrExternalUserTypes += ",serviceProvider" + if ($policy.conditions.users.excludeGuestsOrExternalUsers.guestOrExternalUserTypes -notmatch 'serviceProvider') { + $policy.conditions.users.excludeGuestsOrExternalUsers.guestOrExternalUserTypes += ',serviceProvider' } # If guestOrExternalUserTypes includes type serviceProvider and membershipKind is not all tenants - if ($policy.conditions.users.excludeGuestsOrExternalUsers.guestOrExternalUserTypes -match "serviceProvider" -AND $policy.conditions.users.excludeGuestsOrExternalUsers.externalTenants.membershipKind -ne "all") { + if ($policy.conditions.users.excludeGuestsOrExternalUsers.guestOrExternalUserTypes -match 'serviceProvider' -and $policy.conditions.users.excludeGuestsOrExternalUsers.externalTenants.membershipKind -ne 'all') { # If membershipKind is enumerated and members does not include our tenant add it - if ($policy.conditions.users.excludeGuestsOrExternalUsers.externalTenants.membershipKind -eq "enumerated" -AND $policy.conditions.users.excludeGuestsOrExternalUsers.externalTenants.members -notmatch $CSPtenantId) { + if ($policy.conditions.users.excludeGuestsOrExternalUsers.externalTenants.membershipKind -eq 'enumerated' -and $policy.conditions.users.excludeGuestsOrExternalUsers.externalTenants.members -notmatch $CSPtenantId) { $policy.conditions.users.excludeGuestsOrExternalUsers.externalTenants.members += $($CSPtenantId) } } } + $Json = $policy | Select-Object * -ExcludeProperty TemplateId, createdDateTime, modifiedDateTime | ConvertTo-Json -Depth 20 - } + Write-Information 'Updated policy JSON:' + Write-Information $Json - # Patch policy with updated data. - # TemplateId,createdDateTime,modifiedDateTime can't be written back so exclude them using -ExcludeProperty - if ($PSCmdlet.ShouldProcess($PolicyId, "Update policy with service provider exception")) { - $patch = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($policy.id)" -tenantid $TenantFilter -type PATCH -body ($policy | Select-Object * -ExcludeProperty TemplateId,createdDateTime,modifiedDateTime | ConvertTo-Json -Depth 20) -AsApp $true - return "Successfully added service provider to policy $PolicyId" + # Patch policy with updated data. + # TemplateId,createdDateTime,modifiedDateTime can't be written back so exclude them using -ExcludeProperty + if ($PSCmdlet.ShouldProcess($PolicyId, 'Update policy with service provider exception')) { + $patch = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($policy.id)" -tenantid $TenantFilter -type PATCH -body ($policy | Select-Object * -ExcludeProperty TemplateId, createdDateTime, modifiedDateTime | ConvertTo-Json -Depth 20) -AsApp $true + return "Successfully added service provider to policy $PolicyId" + } + } else { + return "Policy $PolicyId does not target all users or all guest/external users. No changes made." } - } From e2be69cd9acca5330bc08b604b8cdaa93a3fabc8 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 18 Dec 2025 18:58:54 -0500 Subject: [PATCH 20/20] version up --- host.json | 11 ++--------- version_latest.txt | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/host.json b/host.json index e8c0cc6d4337..40ef5d0005a7 100644 --- a/host.json +++ b/host.json @@ -8,13 +8,6 @@ "version": "[4.26.0, 5.0.0)" }, "functionTimeout": "00:10:00", - - "logging": { - "console": { - "isEnabled": true, - "enableStructuredLogging": true - } - }, "extensions": { "durableTask": { "maxConcurrentActivityFunctions": 1, @@ -23,9 +16,9 @@ "distributedTracingEnabled": false, "version": "None" }, - "defaultVersion": "8.8.0", + "defaultVersion": "8.8.1", "versionMatchStrategy": "Strict", "versionFailureStrategy": "Fail" } } -} +} \ No newline at end of file diff --git a/version_latest.txt b/version_latest.txt index 3b6825376add..eec6dacbd482 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -8.8.0 +8.8.1