diff --git a/.github/workflows/dev_api.yml b/.github/workflows/dev_api.yml index 53bae27dbc81..3d92bc00c7d3 100644 --- a/.github/workflows/dev_api.yml +++ b/.github/workflows/dev_api.yml @@ -25,67 +25,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 - name: Login to Azure uses: azure/login@v2 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 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/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 }) } 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 } 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 $_) } } 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' 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/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ListGDAPAccessAssignments.ps1 index a8312cdc0bcb..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 @@ -2,6 +2,8 @@ function Invoke-ListGDAPAccessAssignments { <# .FUNCTIONALITY Entrypoint,AnyTenant + .ROLE + Tenant.Relationship.Read #> [CmdletBinding()] param($Request, $TriggerMetadata) 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..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 @@ -53,26 +53,21 @@ 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 = @{ FunctionName = 'CIPPStandardsList' - TenantFilter = $Tenant.defaultDomainName + TenantFilter = $Tenant runManually = $runManually } if ($TemplateID) { 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) ?? @{} } } 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..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 @@ -29,12 +29,34 @@ $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 { + $Latest = $false if ($Rows) { $Releases = ConvertFrom-Json -InputObject $Rows.GitHubReleases -Depth 10 - } else { + $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 $Latest) { $Releases = Invoke-GitHubApiRequest -Path $ReleasePath $Releases = $Releases | ForEach-Object { [ordered]@{ @@ -48,7 +70,6 @@ commitish = $_.target_commitish } } - $Results = @{ GitHubReleases = [string](ConvertTo-Json -Depth 10 -InputObject $Releases) RowKey = [string]'GitHubReleaseNotes' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 index 7a0d704fe7ed..da83bee09207 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListFunctionParameters.ps1 @@ -36,7 +36,7 @@ function Invoke-ListFunctionParameters { $Functions = Get-Command @CommandQuery | Where-Object { $_.Visibility -eq 'Public' } } $Results = foreach ($Function in $Functions) { - if ($Function -In $TemporaryBlacklist) { continue } + if ($Function -in $TemporaryBlacklist) { continue } $GetHelp = @{ Name = $Function } @@ -72,8 +72,8 @@ function Invoke-ListFunctionParameters { $StatusCode = [HttpStatusCode]::BadRequest } return [HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($Results) - } + StatusCode = $StatusCode + Body = @($Results) + } } 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 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." } - } 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 diff --git a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 index 82677b90ddbe..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 - $Groups = (($Lookups | Where-Object { $_.RowKey -eq 'groups' }).Data | ConvertFrom-Json) ?? @() - $Devices = (($Lookups | Where-Object { $_.RowKey -eq 'devices' }).Data | ConvertFrom-Json) ?? @() - $ServicePrincipals = ($Lookups | Where-Object { $_.RowKey -eq 'servicePrincipals' }).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 -ErrorAction SilentlyContinue) ?? @() Write-Information "Using cached directory lookups for tenant $TenantFilter" } 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 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