From b9c17542593ae1f11825877ffa1b4a4325b25b2f Mon Sep 17 00:00:00 2001 From: Esco Date: Tue, 15 Apr 2025 09:33:15 +0200 Subject: [PATCH 01/56] chore: show PhishingSim current state if false --- .../Standards/Invoke-CIPPStandardPhishingSimulations.ps1 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 index ee792a946796..accaabfcc287 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 @@ -167,7 +167,11 @@ function Invoke-CIPPStandardPhishingSimulations { If ($StateIsCorrect -eq $true) { $FieldValue = $true } Else { - $FieldValue = $CurrentState ? $CurrentState : $false + $FieldValue = [PSCustomObject]@{ + Domains = $RuleState.Domains + SenderIpRanges = $RuleState.SenderIpRanges + PhishingSimUrls = $SimUrlState.value + } } Set-CIPPStandardsCompareField -FieldName 'standards.PhishingSimulations' -FieldValue $FieldValue -Tenant $Tenant } From 3f1483e0d5ea986beca1e49373fe3451ab51da31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 15 Apr 2025 23:09:27 +0200 Subject: [PATCH 02/56] Casing fixes --- Modules/CIPPCore/Public/New-CIPPUserTask.ps1 | 60 ++++++++++---------- Modules/CIPPCore/Public/New-CippUser.ps1 | 28 ++++----- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 index f0738334ee7d..1e19ea9180fa 100644 --- a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 @@ -1,7 +1,7 @@ function New-CIPPUserTask { [CmdletBinding()] param ( - $userobj, + $UserObj, $APIName = 'New User Task', $TenantFilter, $Headers @@ -9,55 +9,55 @@ function New-CIPPUserTask { $Results = [System.Collections.Generic.List[string]]::new() try { - $CreationResults = New-CIPPUser -userobj $UserObj -APIName $APINAME -Headers $Headers - $results.add('Created New User.') - $results.add("Username: $($CreationResults.username)") - $results.add("Password: $($CreationResults.password)") + $CreationResults = New-CIPPUser -UserObj $UserObj -APIName $APIName -Headers $Headers + $Results.Add('Created New User.') + $Results.Add("Username: $($CreationResults.Username)") + $Results.Add("Password: $($CreationResults.Password)") } catch { - $results.add("Failed to create user. $($_.Exception.Message)" ) - return @{'Results' = $results } + $Results.Add("Failed to create user. $($_.Exception.Message)" ) + return @{'Results' = $Results } } try { - if ($userobj.licenses.value) { - $LicenseResults = Set-CIPPUserLicense -UserId $CreationResults.username -TenantFilter $UserObj.tenantFilter -AddLicenses $UserObj.licenses.value -Headers $Headers + if ($UserObj.licenses.value) { + $LicenseResults = Set-CIPPUserLicense -UserId $CreationResults.Username -TenantFilter $UserObj.tenantFilter -AddLicenses $UserObj.licenses.value -Headers $Headers $Results.Add($LicenseResults) } } catch { - Write-LogMessage -headers $Headers -API $APINAME -tenant $($userobj.tenantFilter) -message "Failed to assign the license. Error:$($_.Exception.Message)" -Sev 'Error' - $body = $results.add("Failed to assign the license. $($_.Exception.Message)") + Write-LogMessage -headers $Headers -API $APIName -tenant $($UserObj.tenantFilter) -message "Failed to assign the license. Error:$($_.Exception.Message)" -Sev 'Error' + $Results.Add("Failed to assign the license. $($_.Exception.Message)") } try { - if ($Userobj.AddedAliases) { - $AliasResults = Add-CIPPAlias -user $CreationResults.username -Aliases ($UserObj.AddedAliases -split '\s') -UserprincipalName $CreationResults.Username -TenantFilter $UserObj.tenantFilter -APIName $APINAME -Headers $Headers - $results.add($AliasResults) + if ($UserObj.AddedAliases) { + $AliasResults = Add-CIPPAlias -user $CreationResults.Username -Aliases ($UserObj.AddedAliases -split '\s') -UserprincipalName $CreationResults.Username -TenantFilter $UserObj.tenantFilter -APIName $APIName -Headers $Headers + $Results.Add($AliasResults) } } catch { - Write-LogMessage -headers $Headers -API $APINAME -tenant $($userobj.tenantFilter) -message "Failed to create the Aliases. Error:$($_.Exception.Message)" -Sev 'Error' - $body = $results.add("Failed to create the Aliases: $($_.Exception.Message)") + Write-LogMessage -headers $Headers -API $APIName -tenant $($UserObj.tenantFilter) -message "Failed to create the Aliases. Error:$($_.Exception.Message)" -Sev 'Error' + $Results.Add("Failed to create the Aliases: $($_.Exception.Message)") } - if ($userobj.copyFrom.value) { - Write-Host "Copying from $($userObj.copyFrom.value)" - $CopyFrom = Set-CIPPCopyGroupMembers -Headers $Headers -CopyFromId $userObj.copyFrom.value -UserID $CreationResults.Username -TenantFilter $UserObj.tenantFilter - $CopyFrom.Success | ForEach-Object { $results.Add($_) } - $CopyFrom.Error | ForEach-Object { $results.Add($_) } + if ($UserObj.copyFrom.value) { + Write-Host "Copying from $($UserObj.copyFrom.value)" + $CopyFrom = Set-CIPPCopyGroupMembers -Headers $Headers -CopyFromId $UserObj.copyFrom.value -UserID $CreationResults.Username -TenantFilter $UserObj.tenantFilter + $CopyFrom.Success | ForEach-Object { $Results.Add($_) } + $CopyFrom.Error | ForEach-Object { $Results.Add($_) } } - if ($userobj.setManager) { - $ManagerResult = Set-CIPPManager -user $CreationResults.username -Manager $userObj.setManager.value -TenantFilter $UserObj.tenantFilter -APIName 'Set Manager' -Headers $Headers - $results.add($ManagerResult) + if ($UserObj.setManager) { + $ManagerResult = Set-CIPPManager -user $CreationResults.Username -Manager $UserObj.setManager.value -TenantFilter $UserObj.tenantFilter -APIName 'Set Manager' -Headers $Headers + $Results.Add($ManagerResult) } - if ($userobj.setSponsor) { - $SponsorResult = Set-CIPPManager -user $CreationResults.username -Manager $userObj.setSponsor.value -TenantFilter $UserObj.tenantFilter -APIName 'Set Sponsor' -Headers $Headers - $results.add($SponsorResult) + if ($UserObj.setSponsor) { + $SponsorResult = Set-CIPPManager -user $CreationResults.Username -Manager $UserObj.setSponsor.value -TenantFilter $UserObj.tenantFilter -APIName 'Set Sponsor' -Headers $Headers + $Results.Add($SponsorResult) } return @{ - Results = $results - username = $CreationResults.username - password = $CreationResults.password + Results = $Results + Username = $CreationResults.Username + Password = $CreationResults.Password CopyFrom = $CopyFrom } } diff --git a/Modules/CIPPCore/Public/New-CippUser.ps1 b/Modules/CIPPCore/Public/New-CippUser.ps1 index 0b33305f8f79..e5dde2e8bc73 100644 --- a/Modules/CIPPCore/Public/New-CippUser.ps1 +++ b/Modules/CIPPCore/Public/New-CippUser.ps1 @@ -1,7 +1,7 @@ function New-CIPPUser { [CmdletBinding()] param ( - $userobj, + $UserObj, $Aliases = 'Scheduled', $RestoreValues, $APIName = 'New User', @@ -9,21 +9,21 @@ function New-CIPPUser { ) try { - $userobj = $userobj | ConvertTo-Json -Depth 10 | ConvertFrom-Json -Depth 10 + $UserObj = $UserObj | ConvertTo-Json -Depth 10 | ConvertFrom-Json -Depth 10 Write-Host $UserObj.PrimDomain.value $Aliases = ($UserObj.AddedAliases) -split '\s' $password = if ($UserObj.password) { $UserObj.password } else { New-passwordString } - $UserprincipalName = "$($userobj.username)@$($UserObj.Domain ? $UserObj.Domain : $UserObj.PrimDomain.value)" - Write-Host "Creating user $UserprincipalName" + $UserPrincipalName = "$($UserObj.username)@$($UserObj.Domain ? $UserObj.Domain : $UserObj.PrimDomain.value)" + Write-Host "Creating user $UserPrincipalName" Write-Host "tenant filter is $($UserObj.tenantFilter)" $BodyToship = [pscustomobject] @{ - 'givenName' = $UserObj.givenname + 'givenName' = $UserObj.givenName 'surname' = $UserObj.surname 'accountEnabled' = $true 'displayName' = $UserObj.displayName 'department' = $UserObj.Department - 'mailNickname' = $UserObj.Username ? $userobj.username : $userobj.mailNickname - 'userPrincipalName' = $UserprincipalName + 'mailNickname' = $UserObj.Username ? $UserObj.username : $UserObj.mailNickname + 'userPrincipalName' = $UserPrincipalName 'usageLocation' = $UserObj.usageLocation.value ? $UserObj.usageLocation.value : $UserObj.usageLocation 'city' = $UserObj.City 'country' = $UserObj.Country @@ -37,7 +37,7 @@ function New-CIPPUser { 'password' = $password } } - if ($userobj.businessPhones) { $bodytoShip | Add-Member -NotePropertyName businessPhones -NotePropertyValue @($UserObj.businessPhones) } + if ($UserObj.businessPhones) { $bodytoShip | Add-Member -NotePropertyName businessPhones -NotePropertyValue @($UserObj.businessPhones) } if ($UserObj.defaultAttributes) { $UserObj.defaultAttributes | Get-Member -MemberType NoteProperty | ForEach-Object { Write-Host "Editing user and adding $($_.Name) with value $($UserObj.defaultAttributes.$($_.Name).value)" @@ -50,7 +50,7 @@ function New-CIPPUser { $bodyToShip = ConvertTo-Json -Depth 10 -InputObject $BodyToship -Compress Write-Host "Shipping: $bodyToShip" $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/users' -tenantId $UserObj.tenantFilter -type POST -body $BodyToship -verbose - Write-LogMessage -headers $Headers -API $APINAME -tenant $($UserObj.tenantFilter) -message "Created user $($UserObj.displayname) with id $($GraphRequest.id) " -Sev 'Info' + Write-LogMessage -headers $Headers -API $APIName -tenant $($UserObj.tenantFilter) -message "Created user $($UserObj.displayName) with id $($GraphRequest.id)" -Sev 'Info' try { $PasswordLink = New-PwPushLink -Payload $password @@ -62,13 +62,15 @@ function New-CIPPUser { } $Results = @{ Results = ('Created New User.') - Username = $UserprincipalName + Username = $UserPrincipalName Password = $password } } catch { - Write-LogMessage -headers $Headers -API $APINAME -tenant $($UserObj.tenantFilter) -message "Failed to create user. Error:$($_.Exception.Message)" -Sev 'Error' - $results = @{ Results = ("Failed to create user. $($_.Exception.Message)" ) } - throw "Failed to create user $($_.Exception.Message)" + $ErrorMessage = Get-CippException -Exception $_ + $Result = "Failed to create user. Error:$($ErrorMessage.NormalizedError)" + Write-LogMessage -headers $Headers -API $APIName -tenant $($UserObj.tenantFilter) -message "Failed to create user. Error:$($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + $Results = @{ Results = $Result } + throw $Result } return $Results } From 0cc7127b896979df55ef95841cd445af59debed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 15 Apr 2025 23:11:29 +0200 Subject: [PATCH 03/56] Add easy copyable password output to New-CIPPUserTask results - Resolves https://github.com/KelvinTegelaar/CIPP/issues/3964 --- Modules/CIPPCore/Public/New-CIPPUserTask.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 index 1e19ea9180fa..33b68024b059 100644 --- a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 @@ -13,6 +13,7 @@ function New-CIPPUserTask { $Results.Add('Created New User.') $Results.Add("Username: $($CreationResults.Username)") $Results.Add("Password: $($CreationResults.Password)") + $Results.Add("$($CreationResults.Password)") } catch { $Results.Add("Failed to create user. $($_.Exception.Message)" ) return @{'Results' = $Results } From 9e2cc5b94c496d260ef7ffec1e9ce9f1b46521ee Mon Sep 17 00:00:00 2001 From: Zac Richards <107489668+Zacgoose@users.noreply.github.com> Date: Wed, 16 Apr 2025 16:32:08 +0800 Subject: [PATCH 04/56] Add new on hold types to mailbox info query --- .../Email-Exchange/Administration/Invoke-ListMailboxes.ps1 | 6 ++++-- .../Administration/Users/Invoke-ListUserMailboxDetails.ps1 | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 index b3ac4aebd388..ddd5d619a055 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 @@ -17,7 +17,7 @@ Function Invoke-ListMailboxes { # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.tenantFilter try { - $Select = 'id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses,WhenSoftDeleted,IsInactiveMailbox,ForwardingSmtpAddress,DeliverToMailboxAndForward,ForwardingAddress,HiddenFromAddressListsEnabled,ExternalDirectoryObjectId,MessageCopyForSendOnBehalfEnabled,MessageCopyForSentAsEnabled,PersistedCapabilities,LitigationHoldEnabled,LitigationHoldDate,LitigationHoldDuration' + $Select = 'id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses,WhenSoftDeleted,IsInactiveMailbox,ForwardingSmtpAddress,DeliverToMailboxAndForward,ForwardingAddress,HiddenFromAddressListsEnabled,ExternalDirectoryObjectId,MessageCopyForSendOnBehalfEnabled,MessageCopyForSentAsEnabled,PersistedCapabilities,LitigationHoldEnabled,LitigationHoldDate,LitigationHoldDuration,ComplianceTagHoldApplied,RetentionHoldEnabled' $ExoRequest = @{ tenantid = $TenantFilter cmdlet = 'Get-Mailbox' @@ -73,7 +73,9 @@ Function Invoke-ListMailboxes { LitigationHoldEnabled, LitigationHoldDate, LitigationHoldDuration, - @{ Name = 'LicensedForLitigationHold'; Expression = { ($_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise') } } + @{ Name = 'LicensedForLitigationHold'; Expression = { ($_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise') } }, + ComplianceTagHoldApplied, + RetentionHoldEnabled, $StatusCode = [HttpStatusCode]::OK } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 index ba87bb0ed755..ad1c46d9f216 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 @@ -182,6 +182,8 @@ function Invoke-ListUserMailboxDetails { ForwardAndDeliver = $MailboxDetailedRequest.DeliverToMailboxAndForward ForwardingAddress = $ForwardingAddress LitigationHold = $MailboxDetailedRequest.LitigationHoldEnabled + RetentionHold = $MailboxDetailedRequest.RetentionHoldEnabled + ComplianceTagHold = $MailboxDetailedRequest.ComplianceTagHoldApplied HiddenFromAddressLists = $MailboxDetailedRequest.HiddenFromAddressListsEnabled EWSEnabled = $CASRequest.EwsEnabled MailboxMAPIEnabled = $CASRequest.MAPIEnabled From cfb922f8ae34440b2e830ed682bd992a67fdfb24 Mon Sep 17 00:00:00 2001 From: Zac Richards <107489668+Zacgoose@users.noreply.github.com> Date: Wed, 16 Apr 2025 22:21:40 +0800 Subject: [PATCH 05/56] Create Invoke-ExecSetRetentionHold function --- .../Invoke-ExecSetRetentionHold.ps1 | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecSetRetentionHold.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecSetRetentionHold.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecSetRetentionHold.ps1 new file mode 100644 index 000000000000..68ba966fa9bf --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecSetRetentionHold.ps1 @@ -0,0 +1,50 @@ +function Invoke-ExecSetRetentionHold { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Exchange.Mailbox.ReadWrite + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $Request.Params.CIPPEndpoint + $Headers = $Request.Headers + Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message 'Accessed this API' -Sev 'Debug' + + # Interact with the query or body of the request + $TenantFilter = $Request.Body.tenantFilter + $RetentionHoldState = -not $Request.Body.disable -as [bool] + $Identity = $Request.Body.Identity + $UserPrincipalName = $Request.Body.UPN + + # Set the parameters for the EXO request + $ExoRequest = @{ + tenantid = $TenantFilter + cmdlet = 'Set-Mailbox' + cmdParams = @{ + Identity = $Identity + RetentionHoldEnabled = $RetentionHoldState + } + } + + # Execute the EXO request + try { + $null = New-ExoRequest @ExoRequest + $Results = "Retention hold for $UserPrincipalName with Id $Identity has been set to $RetentionHoldState" + + Write-LogMessage -API $APIName -tenant $TenantFilter -message $Results -sev Info + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-CippException -Exception $_ + $Results = "Could not set retention hold for $UserPrincipalName with Id $Identity to $RetentionHoldState. Error: $($ErrorMessage.NormalizedError)" + Write-LogMessage -API $APIName -tenant $TenantFilter -message $Results -sev Error -LogData $ErrorMessage + $StatusCode = [HttpStatusCode]::InternalServerError + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @{ Results = $Results } + }) +} From 64c89bdf1ed6c7fa773021323974d2f3bcf1f03a Mon Sep 17 00:00:00 2001 From: Zac Richards <107489668+Zacgoose@users.noreply.github.com> Date: Wed, 16 Apr 2025 23:59:53 +0800 Subject: [PATCH 06/56] Addition of InPlaceHold, EDiscoverHold, PurviewRetentionHold and ExcludedFromOrgWideHold --- .../Administration/Invoke-ListMailboxes.ps1 | 3 +- .../Users/Invoke-ListUserMailboxDetails.ps1 | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 index ddd5d619a055..f3232fa205c4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 @@ -17,7 +17,7 @@ Function Invoke-ListMailboxes { # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.tenantFilter try { - $Select = 'id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses,WhenSoftDeleted,IsInactiveMailbox,ForwardingSmtpAddress,DeliverToMailboxAndForward,ForwardingAddress,HiddenFromAddressListsEnabled,ExternalDirectoryObjectId,MessageCopyForSendOnBehalfEnabled,MessageCopyForSentAsEnabled,PersistedCapabilities,LitigationHoldEnabled,LitigationHoldDate,LitigationHoldDuration,ComplianceTagHoldApplied,RetentionHoldEnabled' + $Select = 'id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses,WhenSoftDeleted,IsInactiveMailbox,ForwardingSmtpAddress,DeliverToMailboxAndForward,ForwardingAddress,HiddenFromAddressListsEnabled,ExternalDirectoryObjectId,MessageCopyForSendOnBehalfEnabled,MessageCopyForSentAsEnabled,PersistedCapabilities,LitigationHoldEnabled,LitigationHoldDate,LitigationHoldDuration,ComplianceTagHoldApplied,RetentionHoldEnabled,InPlaceHolds' $ExoRequest = @{ tenantid = $TenantFilter cmdlet = 'Get-Mailbox' @@ -76,6 +76,7 @@ Function Invoke-ListMailboxes { @{ Name = 'LicensedForLitigationHold'; Expression = { ($_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise') } }, ComplianceTagHoldApplied, RetentionHoldEnabled, + InPlaceHolds, $StatusCode = [HttpStatusCode]::OK } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 index ad1c46d9f216..927d64168b0e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 @@ -177,6 +177,39 @@ function Invoke-ListUserMailboxDetails { $TotalArchiveItemCount = try { [math]::Round($ArchiveSizeRequest.ItemCount, 2) } catch { 0 } } + # Parse InPlaceHolds to determine hold types if avaliable + $InPlaceHold = $false + $EDiscoveryHold = $false + $PurviewRetentionHold = $false + $ExcludedFromOrgWideHold = $false + + # Check if InPlaceHolds property exists and has values + if ($MailboxDetailedRequest.InPlaceHolds) { + foreach ($hold in $MailboxDetailedRequest.InPlaceHolds) { + # eDiscovery hold - starts with UniH + if ($hold -like 'UniH*') { + $EDiscoveryHold = $true + } + # In-Place Hold - no prefix or starts with cld + # Check if it doesn't match any of the other known prefixes + elseif (($hold -like 'cld*' -or + ($hold -notlike 'UniH*' -and + $hold -notlike 'mbx*' -and + $hold -notlike 'skp*' -and + $hold -notlike '-mbx*'))) { + $InPlaceHold = $true + } + # Microsoft Purview retention policy - starts with mbx or skp + elseif ($hold -like 'mbx*' -or $hold -like 'skp*') { + $PurviewRetentionHold = $true + } + # Excluded from organization-wide Microsoft Purview retention policy - starts with -mbx + elseif ($hold -like '-mbx*') { + $ExcludedFromOrgWideHold = $true + } + } + } + # Build the GraphRequest object $GraphRequest = [ordered]@{ ForwardAndDeliver = $MailboxDetailedRequest.DeliverToMailboxAndForward @@ -184,6 +217,10 @@ function Invoke-ListUserMailboxDetails { LitigationHold = $MailboxDetailedRequest.LitigationHoldEnabled RetentionHold = $MailboxDetailedRequest.RetentionHoldEnabled ComplianceTagHold = $MailboxDetailedRequest.ComplianceTagHoldApplied + InPlaceHold = $InPlaceHold + EDiscoveryHold = $EDiscoveryHold + PurviewRetentionHold = $PurviewRetentionHold + ExcludedFromOrgWideHold = $ExcludedFromOrgWideHold HiddenFromAddressLists = $MailboxDetailedRequest.HiddenFromAddressListsEnabled EWSEnabled = $CASRequest.EwsEnabled MailboxMAPIEnabled = $CASRequest.MAPIEnabled From 5613ca8568541b877dcb683de81c1ea61542d9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 16 Apr 2025 21:07:19 +0200 Subject: [PATCH 07/56] fix: dkim standard not enabling dkim sometimes if supportedServices is showing wrong data --- .../Standards/Invoke-CIPPStandardAddDKIM.ps1 | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 index d3edaf90dacf..e7e068a6394c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 @@ -33,9 +33,8 @@ function Invoke-CIPPStandardAddDKIM { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'AddDKIM' -Settings $Settings - - $AllDomains = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains?$top=999' -tenantid $Tenant | Where-Object { $_.supportedServices -contains 'Email' -or $_.id -like '*mail.onmicrosoft.com' }).id - $DKIM = (New-ExoRequest -tenantid $tenant -cmdlet 'Get-DkimSigningConfig') | Select-Object Domain, Enabled, Status + $AllDomains = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AcceptedDomain').DomainName + $DKIM = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-DkimSigningConfig') | Select-Object Domain, Enabled, Status # List of domains for each way to enable DKIM $NewDomains = $AllDomains | Where-Object { $DKIM.Domain -notcontains $_ } @@ -44,10 +43,10 @@ function Invoke-CIPPStandardAddDKIM { If ($Settings.remediate -eq $true) { if ($null -eq $NewDomains -and $null -eq $SetDomains) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'DKIM is already enabled for all available domains.' -sev Info + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'DKIM is already enabled for all available domains.' -sev Info } else { $ErrorCounter = 0 - Write-LogMessage -API 'Standards' -tenant $tenant -message "Trying to enable DKIM for:$($NewDomains -join ', ' ) $($SetDomains.Domain -join ', ')" -sev Info + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Trying to enable DKIM for:$($NewDomains -join ', ' ) $($SetDomains.Domain -join ', ')" -sev Info # New-domains $Request = $NewDomains | ForEach-Object { @@ -58,12 +57,12 @@ function Invoke-CIPPStandardAddDKIM { } } } - if ($null -ne $Request) { $BatchResults = New-ExoBulkRequest -tenantid $tenant -cmdletArray @($Request) -useSystemMailbox $true } + if ($null -ne $Request) { $BatchResults = New-ExoBulkRequest -tenantid $Tenant -cmdletArray @($Request) -useSystemMailbox $true } $BatchResults | ForEach-Object { if ($_.error) { $ErrorCounter ++ $ErrorMessage = Get-NormalizedError -Message $_.error - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable DKIM. Error: $ErrorMessage" -sev Error + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to enable DKIM. Error: $ErrorMessage" -sev Error } } @@ -76,21 +75,21 @@ function Invoke-CIPPStandardAddDKIM { } } } - if ($null -ne $Request) { $BatchResults = New-ExoBulkRequest -tenantid $tenant -cmdletArray @($Request) -useSystemMailbox $true } + if ($null -ne $Request) { $BatchResults = New-ExoBulkRequest -tenantid $Tenant -cmdletArray @($Request) -useSystemMailbox $true } $BatchResults | ForEach-Object { if ($_.error) { $ErrorCounter ++ $ErrorMessage = Get-NormalizedError -Message $_.error - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set DKIM. Error: $ErrorMessage" -sev Error + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to set DKIM. Error: $ErrorMessage" -sev Error } } if ($ErrorCounter -eq 0) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled DKIM for all domains in tenant' -sev Info + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Enabled DKIM for all domains in tenant' -sev Info } elseif ($ErrorCounter -gt 0 -and $ErrorCounter -lt ($NewDomains.Count + $SetDomains.Count)) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Failed to enable DKIM for some domains in tenant' -sev Error + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Failed to enable DKIM for some domains in tenant' -sev Error } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Failed to enable DKIM for all domains in tenant' -sev Error + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Failed to enable DKIM for all domains in tenant' -sev Error } } } From 2633df4db08bc126f4040eb2d66541407f01f1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 16 Apr 2025 22:58:17 +0200 Subject: [PATCH 08/56] Refactor to use EXO batch request for extra BRRR --- .../Standards/Invoke-CIPPStandardAddDKIM.ps1 | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 index e7e068a6394c..ceec4b0ba1b4 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 @@ -33,8 +33,42 @@ function Invoke-CIPPStandardAddDKIM { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'AddDKIM' -Settings $Settings - $AllDomains = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AcceptedDomain').DomainName - $DKIM = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-DkimSigningConfig') | Select-Object Domain, Enabled, Status + + $DkimRequest = @( + @{ + CmdletInput = @{ + CmdletName = 'Get-AcceptedDomain' + Parameters = @{} + } + }, + @{ + CmdletInput = @{ + CmdletName = 'Get-DkimSigningConfig' + Parameters = @{} + } + } + ) + + $BatchResults = New-ExoBulkRequest -tenantid $Tenant -cmdletArray $DkimRequest -useSystemMailbox $true + + # Check for errors in the batch results. Cannot continue if there are errors. + $ErrorCounter = 0 + $ErrorMessages = [System.Collections.Generic.List[string]]::new() + $BatchResults | ForEach-Object { + if ($_.error) { + $ErrorCounter++ + $ErrorMessage = Get-NormalizedError -Message $_.error + $ErrorMessages.Add($ErrorMessage) + } + } + if ($ErrorCounter -gt 0) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to get DKIM config. Error: $($ErrorMessages -join ', ')" -sev Error + return + } + + + $AllDomains = ($BatchResults | Where-Object { $_.DomainName }).DomainName + $DKIM = $BatchResults | Where-Object { $_.Domain } | Select-Object Domain, Enabled, Status # List of domains for each way to enable DKIM $NewDomains = $AllDomains | Where-Object { $DKIM.Domain -notcontains $_ } From b9ba0cc18e87ef480064f31ec004060fc26020c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 16 Apr 2025 23:13:53 +0200 Subject: [PATCH 09/56] fix: remove a comma and change formatting to improve readability --- .../Administration/Invoke-ListMailboxes.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 index ddd5d619a055..903de1c9c978 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 @@ -56,15 +56,15 @@ Function Invoke-ListMailboxes { } } - $GraphRequest = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, WhenSoftDeleted, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, - + $GraphRequest = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, WhenSoftDeleted, + @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, @{ Name = 'displayName'; Expression = { $_.'DisplayName' } }, @{ Name = 'primarySmtpAddress'; Expression = { $_.'PrimarySMTPAddress' } }, @{ Name = 'recipientType'; Expression = { $_.'RecipientType' } }, @{ Name = 'recipientTypeDetails'; Expression = { $_.'RecipientTypeDetails' } }, @{ Name = 'AdditionalEmailAddresses'; Expression = { ($_.'EmailAddresses' | Where-Object { $_ -clike 'smtp:*' }).Replace('smtp:', '') -join ', ' } }, - @{Name = 'ForwardingSmtpAddress'; Expression = { $_.'ForwardingSmtpAddress' -replace 'smtp:', '' } }, - @{Name = 'InternalForwardingAddress'; Expression = { $_.'ForwardingAddress' } }, + @{ Name = 'ForwardingSmtpAddress'; Expression = { $_.'ForwardingSmtpAddress' -replace 'smtp:', '' } }, + @{ Name = 'InternalForwardingAddress'; Expression = { $_.'ForwardingAddress' } }, DeliverToMailboxAndForward, HiddenFromAddressListsEnabled, ExternalDirectoryObjectId, @@ -75,7 +75,7 @@ Function Invoke-ListMailboxes { LitigationHoldDuration, @{ Name = 'LicensedForLitigationHold'; Expression = { ($_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise') } }, ComplianceTagHoldApplied, - RetentionHoldEnabled, + RetentionHoldEnabled $StatusCode = [HttpStatusCode]::OK } catch { From 4b6ae2e3511ef0b4126fae22853e58da57ec6228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 17 Apr 2025 00:20:07 +0200 Subject: [PATCH 10/56] chore: sync select statements --- .../Administration/Invoke-ListMailboxes.ps1 | 1 + .../Users/Invoke-ListUserMailboxDetails.ps1 | 32 ++++++++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 index 36881b2b1166..6d8498553b18 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 @@ -77,6 +77,7 @@ Function Invoke-ListMailboxes { ComplianceTagHoldApplied, RetentionHoldEnabled, InPlaceHolds + # This select also exists in ListUserMailboxDetails and should be updated if this is changed here $StatusCode = [HttpStatusCode]::OK } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 index 877fed913935..cd075ca9d67f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 @@ -178,9 +178,9 @@ function Invoke-ListUserMailboxDetails { } # Parse InPlaceHolds to determine hold types if avaliable - $InPlaceHold = $false - $EDiscoveryHold = $false - $PurviewRetentionHold = $false + $InPlaceHold = $false + $EDiscoveryHold = $false + $PurviewRetentionHold = $false $ExcludedFromOrgWideHold = $false # Check if InPlaceHolds property exists and has values @@ -192,10 +192,10 @@ function Invoke-ListUserMailboxDetails { } # In-Place Hold - no prefix or starts with cld # Check if it doesn't match any of the other known prefixes - elseif (($hold -like 'cld*' -or - ($hold -notlike 'UniH*' -and - $hold -notlike 'mbx*' -and - $hold -notlike 'skp*' -and + elseif (($hold -like 'cld*' -or + ($hold -notlike 'UniH*' -and + $hold -notlike 'mbx*' -and + $hold -notlike 'skp*' -and $hold -notlike '-mbx*'))) { $InPlaceHold = $true } @@ -240,20 +240,28 @@ function Invoke-ListUserMailboxDetails { AutoExpandingArchive = $AutoExpandingArchiveEnabled RecipientTypeDetails = $MailboxDetailedRequest.RecipientTypeDetails Mailbox = $MailboxDetailedRequest - MailboxActionsData = ($MailboxDetailedRequest | Select-Object id, ExchangeGuid, ArchiveGuid, WhenSoftDeleted, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, + MailboxActionsData = ($MailboxDetailedRequest | Select-Object id, ExchangeGuid, ArchiveGuid, WhenSoftDeleted, + @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, @{ Name = 'displayName'; Expression = { $_.'DisplayName' } }, @{ Name = 'primarySmtpAddress'; Expression = { $_.'PrimarySMTPAddress' } }, @{ Name = 'recipientType'; Expression = { $_.'RecipientType' } }, @{ Name = 'recipientTypeDetails'; Expression = { $_.'RecipientTypeDetails' } }, @{ Name = 'AdditionalEmailAddresses'; Expression = { ($_.'EmailAddresses' | Where-Object { $_ -clike 'smtp:*' }).Replace('smtp:', '') -join ', ' } }, - @{Name = 'ForwardingSmtpAddress'; Expression = { $_.'ForwardingSmtpAddress' -replace 'smtp:', '' } }, - @{Name = 'InternalForwardingAddress'; Expression = { $_.'ForwardingAddress' } }, + @{ Name = 'ForwardingSmtpAddress'; Expression = { $_.'ForwardingSmtpAddress' -replace 'smtp:', '' } }, + @{ Name = 'InternalForwardingAddress'; Expression = { $_.'ForwardingAddress' } }, DeliverToMailboxAndForward, HiddenFromAddressListsEnabled, ExternalDirectoryObjectId, MessageCopyForSendOnBehalfEnabled, - MessageCopyForSentAsEnabled) - } # Select statement taken from ListMailboxes to save a EXO request + MessageCopyForSentAsEnabled, + LitigationHoldEnabled, + LitigationHoldDate, + LitigationHoldDuration, + @{ Name = 'LicensedForLitigationHold'; Expression = { ($_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise') } }, + ComplianceTagHoldApplied, + RetentionHoldEnabled, + InPlaceHolds) + } # Select statement taken from ListMailboxes to save a EXO request. If updated here, update in ListMailboxes as well. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK From 57a304c56f8d63af04f1f7edeeccf8647685b7ad Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 17 Apr 2025 10:29:06 +0200 Subject: [PATCH 11/56] Add or update the Azure App Service build and deployment workflow config --- .github/workflows/dev_cippahmcc.yml | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/dev_cippahmcc.yml diff --git a/.github/workflows/dev_cippahmcc.yml b/.github/workflows/dev_cippahmcc.yml new file mode 100644 index 000000000000..545a60fa955e --- /dev/null +++ b/.github/workflows/dev_cippahmcc.yml @@ -0,0 +1,30 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/azure/functions-action +# More GitHub Actions for Azure: https://github.com/Azure/actions + +name: Build and deploy Powershell project to Azure Function App - cippahmcc + +on: + push: + branches: + - dev + workflow_dispatch: + +env: + AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root + +jobs: + deploy: + runs-on: windows-latest + + steps: + - name: 'Checkout GitHub Action' + uses: actions/checkout@v4 + + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + id: fa + with: + app-name: 'cippahmcc' + slot-name: 'Production' + package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} + publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_D6317AAB492A474D91B7A6CD29E53BA3 }} \ No newline at end of file From 13591ae72a6c42b73a9f496c9c633e01195e44ab Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 17 Apr 2025 09:21:50 -0400 Subject: [PATCH 12/56] Update Invoke-ListMailboxes.ps1 --- .../Email-Exchange/Administration/Invoke-ListMailboxes.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 index f3232fa205c4..e32e486a50fa 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1 @@ -56,8 +56,8 @@ Function Invoke-ListMailboxes { } } - $GraphRequest = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, WhenSoftDeleted, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, - + $GraphRequest = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, WhenSoftDeleted, + @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, @{ Name = 'displayName'; Expression = { $_.'DisplayName' } }, @{ Name = 'primarySmtpAddress'; Expression = { $_.'PrimarySMTPAddress' } }, @{ Name = 'recipientType'; Expression = { $_.'RecipientType' } }, @@ -76,7 +76,7 @@ Function Invoke-ListMailboxes { @{ Name = 'LicensedForLitigationHold'; Expression = { ($_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise') } }, ComplianceTagHoldApplied, RetentionHoldEnabled, - InPlaceHolds, + InPlaceHolds $StatusCode = [HttpStatusCode]::OK } catch { From 82b6dd2bedfc84deb102e31f06f386a6182f3c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= <31723128+kris6673@users.noreply.github.com> Date: Thu, 17 Apr 2025 18:12:25 +0200 Subject: [PATCH 13/56] Update Invoke-ListUserMailboxDetails.ps1 --- .../Administration/Users/Invoke-ListUserMailboxDetails.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 index 877fed913935..2c962de9d93d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 @@ -164,7 +164,7 @@ function Invoke-ListUserMailboxDetails { $ProhibitSendQuotaString = $MailboxDetailedRequest.ProhibitSendQuota -split ' ' $ProhibitSendReceiveQuotaString = $MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' ' $TotalItemSizeString = $StatsRequest.TotalItemSize -split ' ' - $TotalArchiveItemSizeString = Get-ExoOnlineStringBytes -SizeString $ArchiveSizeRequest.TotalItemSize.Value + $TotalArchiveItemSizeString = (Get-ExoOnlineStringBytes -SizeString $ArchiveSizeRequest.TotalItemSize) / 1GB $ProhibitSendQuota = try { [math]::Round([float]($ProhibitSendQuotaString[0]), 2) } catch { 0 } $ProhibitSendReceiveQuota = try { [math]::Round([float]($ProhibitSendReceiveQuotaString[0]), 2) } catch { 0 } From c725f602185a2418c9c6520ac8f43987e257581d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= <31723128+kris6673@users.noreply.github.com> Date: Thu, 17 Apr 2025 18:12:51 +0200 Subject: [PATCH 14/56] Update Get-ExoOnlineStringBytes.ps1 --- Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 b/Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 index 067bd894b8b4..636921b147e9 100644 --- a/Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 +++ b/Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 @@ -4,7 +4,7 @@ function Get-ExoOnlineStringBytes { # This exists because various exo cmdlets like to return a human readable string like "3.322 KB (3,402 bytes)" but not the raw bytes value if ($SizeString -match '\(([0-9,]+) bytes\)') { - return [int]($Matches[1] -replace ',','') + return [int64]($Matches[1] -replace ',','') } return 0 From f980c1364b46974330d589264da88bd8f711ae14 Mon Sep 17 00:00:00 2001 From: Jr7468 Date: Thu, 17 Apr 2025 19:49:53 +0100 Subject: [PATCH 15/56] Add Exchange Recipient Limits Management Adds new HTTP function `Invoke-ExecSetRecipientLimits` to manage Exchange mailbox recipient limits through the CIPP API. The function allows setting recipient limits for specific mailboxes with proper error handling and logging. --- .../Invoke-ExecSetRecipientLimits.ps1 | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecSetRecipientLimits.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecSetRecipientLimits.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecSetRecipientLimits.ps1 new file mode 100644 index 000000000000..ecc94c1da21c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecSetRecipientLimits.ps1 @@ -0,0 +1,50 @@ +function Invoke-ExecSetRecipientLimits { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Exchange.Mailbox.ReadWrite + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $Request.Params.CIPPEndpoint + $Headers = $Request.Headers + Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message 'Accessed this API' -Sev 'Debug' + + # Interact with the query or body of the request + $TenantFilter = $Request.Body.tenantFilter + $recipientLimit = $Request.Body.recipientLimit + $Identity = $Request.Body.Identity + $UserPrincipalName = $Request.Body.userid + + # Set the parameters for the EXO request + $ExoRequest = @{ + tenantid = $TenantFilter + cmdlet = 'Set-Mailbox' + cmdParams = @{ + Identity = $Identity + RecipientLimits = $recipientLimit + } + } + + # Execute the EXO request + try { + $null = New-ExoRequest @ExoRequest + $Results = "Recipient limit for $UserPrincipalName has been set to $recipientLimit" + + Write-LogMessage -API $APIName -tenant $TenantFilter -message $Results -sev Info + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-CippException -Exception $_ + $Results = "Could not set recipient limit for $UserPrincipalName to $recipientLimit. Error: $($ErrorMessage.NormalizedError)" + Write-LogMessage -API $APIName -tenant $TenantFilter -message $Results -sev Error -LogData $ErrorMessage + $StatusCode = [HttpStatusCode]::InternalServerError + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @{ Results = $Results } + }) +} From ebcaa63a699d10d9513635a7d323153f5fa3caf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 17 Apr 2025 20:49:58 +0200 Subject: [PATCH 16/56] feat: add Teams Meeting Recording Expiration standard --- .../Invoke-CIPPStandardStaleEntraDevices.ps1 | 4 +- ...tandardTeamsMeetingRecordingExpiration.ps1 | 81 +++++++++++++++++++ Tools/Update-StandardsComments.ps1 | 6 +- 3 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 index 148144a0da29..0b76b4d35cb9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 @@ -7,8 +7,8 @@ function Invoke-CIPPStandardStaleEntraDevices { .SYNOPSIS (Label) Cleanup stale Entra devices .DESCRIPTION - (Helptext) Cleans up Entra devices that have not connected/signed in for the specified number of days. - (DocsDescription) Cleans up Entra devices that have not connected/signed in for the specified number of days. First disables and later deletes the devices. More info can be found in the [Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/devices/manage-stale-devices) + (Helptext) Remediate is currently not available. Cleans up Entra devices that have not connected/signed in for the specified number of days. + (DocsDescription) Remediate is currently not available. Cleans up Entra devices that have not connected/signed in for the specified number of days. First disables and later deletes the devices. More info can be found in the [Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/devices/manage-stale-devices) .NOTES CAT Entra (AAD) Standards diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 new file mode 100644 index 000000000000..5704afc73934 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 @@ -0,0 +1,81 @@ +function Invoke-CIPPStandardTeamsMeetingRecordingExpiration { + <# + .FUNCTIONALITY + Internal + .COMPONENT + (APIName) TeamsMeetingRecordingExpiration + .SYNOPSIS + (Label) Set Teams Meeting Recording Expiration + .DESCRIPTION + (Helptext) Sets the default number of days after which Teams meeting recordings automatically expire. Valid values are -1 (Never Expire) or between 1 and 99999. The default value is 120 days. + (DocsDescription) Allows administrators to configure a default expiration period (in days) for Teams meeting recordings. Recordings older than this period will be automatically moved to the recycle bin. This setting helps manage storage consumption and enforce data retention policies. + .NOTES + CAT + Teams Standards + TAG + ADDEDCOMPONENT + {"type":"number","name":"standards.TeamsMeetingRecordingExpiration.ExpirationDays","label":"Recording Expiration Days (e.g., 365)","required":true} + IMPACT + Medium Impact + ADDEDDATE + 2025-04-17 + POWERSHELLEQUIVALENT + Set-CsTeamsMeetingPolicy -Identity Global -MeetingRecordingExpirationDays \ + RECOMMENDEDBY + UPDATECOMMENTBLOCK + Run the Tools\Update-StandardsComments.ps1 script to update this comment block + .LINK + https://docs.cipp.app/user-documentation/tenant/standards/list-standards/teams-standards#medium-impact + #> + ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'TeamsMeetingRecordingExpiration' + + param($Tenant, $Settings) + + # Input validation + $ExpirationDays = try { [int64]$Settings.ExpirationDays } catch { Write-Warning "Invalid ExpirationDays value provided: $($Settings.ExpirationDays)"; return } + if (($ExpirationDays -ne -1) -and ($ExpirationDays -lt 1 -or $ExpirationDays -gt 99999)) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Invalid ExpirationDays value: $ExpirationDays. Must be -1 (Never Expire) or between 1 and 99999." -sev Error + return + } + + $CurrentExpirationDays = (New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' }).NewMeetingRecordingExpirationDays + $StateIsCorrect = if ($CurrentExpirationDays -eq $ExpirationDays) { $true } else { $false } + + if ($Settings.remediate -eq $true) { + Write-Host 'Time to remediate' + if ($StateIsCorrect -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Teams Meeting Recording Expiration Policy already set to $ExpirationDays days." -sev Info + } else { + $cmdParams = @{ + Identity = 'Global' + NewMeetingRecordingExpirationDays = $ExpirationDays + } + + try { + New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Set-CsTeamsMeetingPolicy' -CmdParams $cmdParams + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Successfully updated Teams Meeting Recording Expiration Policy to $ExpirationDays days." -sev Info + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to set Teams Meeting Recording Expiration Policy. Error: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage + } + } + } + + if ($Settings.alert -eq $true) { + if ($StateIsCorrect -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Teams Meeting Recording Expiration Policy is set correctly ($($CurrentExpirationDays) days)." -sev Info + } else { + Write-StandardsAlert -message "Teams Meeting Recording Expiration Policy is not set correctly. Current: $CurrentExpirationDays days, Desired: $ExpirationDays days." -object $CurrentExpirationDays -tenant $Tenant -standardName 'TeamsMeetingRecordingExpiration' -standardId $Settings.standardId + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Teams Meeting Recording Expiration Policy is not set correctly (Current: $CurrentExpirationDays, Desired: $ExpirationDays)." -sev Info + } + } + + if ($Settings.report -eq $true) { + Add-CIPPBPAField -FieldName 'TeamsMeetingRecordingExpiration' -FieldValue $CurrentExpirationDays -StoreAs string -Tenant $Tenant + + $CurrentExpirationDays = [PSCustomObject]@{ + ExpirationDays = [string]$CurrentExpirationDays + } + Set-CIPPStandardsCompareField -FieldName 'standards.TeamsMeetingRecordingExpiration' -FieldValue $CurrentExpirationDays -Tenant $Tenant + } +} diff --git a/Tools/Update-StandardsComments.ps1 b/Tools/Update-StandardsComments.ps1 index 08493bda4fbe..ffd0b9c16fed 100644 --- a/Tools/Update-StandardsComments.ps1 +++ b/Tools/Update-StandardsComments.ps1 @@ -106,12 +106,10 @@ foreach ($Standard in $StandardsInfo) { $NewComment.Add(" $(ConvertTo-Json -InputObject $Value -Depth 5 -Compress)`n") } continue - } - elseif ($Property.Value -is [System.Management.Automation.PSCustomObject]) { + } elseif ($Property.Value -is [System.Management.Automation.PSCustomObject]) { $NewComment.Add(" $(ConvertTo-Json -InputObject $Property.Value -Depth 5 -Compress)`n") continue - } - else { + } else { if ($null -ne $Property.Value) { $NewComment.Add(" $(EscapeMarkdown($Property.Value.ToString()))`n") } From 8c338ebba14c12e41df1a2aade8a012a6c0b6260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 17 Apr 2025 23:54:47 +0200 Subject: [PATCH 17/56] feat: Better alert message for NewAppApproval alert --- .../Alerts/Get-CIPPAlertNewAppApproval.ps1 | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 index 69889ea73ec2..f3f5d66cf6f2 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 @@ -13,9 +13,25 @@ function Get-CIPPAlertNewAppApproval { $Headers ) try { - $Approvals = New-GraphGetRequest -Uri "https://graph.microsoft.com/v1.0/identityGovernance/appConsent/appConsentRequests?`$filter=userConsentRequests/any (u:u/status eq 'InProgress')" -tenantid $TenantFilter + $Approvals = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/identityGovernance/appConsent/appConsentRequests?`$filter=userConsentRequests/any (u:u/status eq 'InProgress')" -tenantid $TenantFilter if ($Approvals.count -gt 0) { - $AlertData = "There are $($Approvals.count) App Approval(s) pending." + $AlertData = [System.Collections.Generic.List[string]]::new() + foreach ($App in $Approvals) { + $userConsentRequests = New-GraphGetRequest -Uri "https://graph.microsoft.com/v1.0/identityGovernance/appConsent/appConsentRequests/$($App.id)/userConsentRequests" -tenantid $TenantFilter + $userConsentRequests | ForEach-Object { + $consentUrl = if ($App.consentType -eq 'Static') { + # if something is going wrong here you've probably stumbled on a fourth variation - rvdwegen + "https://login.microsoftonline.com/$($TenantFilter)/adminConsent?client_id=$($App.appId)&bf_id=$($App.id)&redirect_uri=https://entra.microsoft.com/TokenAuthorize" + } elseif ($App.pendingScopes.displayName) { + "https://login.microsoftonline.com/$($TenantFilter)/v2.0/adminConsent?client_id=$($App.appId)&scope=$($App.pendingScopes.displayName -Join(' '))&bf_id=$($App.id)&redirect_uri=https://entra.microsoft.com/TokenAuthorize" + } else { + "https://login.microsoftonline.com/$($TenantFilter)/adminConsent?client_id=$($App.appId)&bf_id=$($App.id)&redirect_uri=https://entra.microsoft.com/TokenAuthorize" + } + + $Message = "App name: $($App.appDisplayName) - Request user: $($_.createdBy.user.userPrincipalName) - Reason: $($_.reason)`nApp Id: $($App.appId) - Scopes: $($App.pendingScopes.displayName)`nConsent URL: $consentUrl" + $AlertData.Add($Message) + } + } Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData } } catch { From a1b996b59209c0cdffee7354162b2b9efb016a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 18 Apr 2025 00:27:17 +0200 Subject: [PATCH 18/56] fix: intune app assignment after action was changed to POST --- .../Applications/Invoke-ExecAssignApp.ps1 | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAssignApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAssignApp.ps1 index 807f153a700c..8f7e77dbe5f4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAssignApp.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAssignApp.ps1 @@ -14,13 +14,10 @@ Function Invoke-ExecAssignApp { $Headers = $Request.Headers Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' - - - # Interact with query parameters or the body of the request. - $tenantfilter = $Request.Query.TenantFilter - $appFilter = $Request.Query.ID - $AssignTo = $Request.Query.AssignTo + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + $appFilter = $Request.Query.ID ?? $Request.Body.ID + $AssignTo = $Request.Query.AssignTo ?? $Request.Body.AssignTo $AssignBody = switch ($AssignTo) { 'AllUsers' { @@ -42,20 +39,23 @@ Function Invoke-ExecAssignApp { } } - $body = [pscustomobject]@{'Results' = "$($TenantFilter): Assigned app to $assignTo" } try { - $GraphRequest = New-Graphpostrequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appFilter/assign" -tenantid $TenantFilter -body $Assignbody - Write-LogMessage -headers $Request.Headers -API $APINAME -tenant $($tenantfilter) -message "Assigned $($appFilter) to $assignTo" -Sev 'Info' + $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appFilter/assign" -tenantid $TenantFilter -body $AssignBody + $Result = "Successfully assigned app $($appFilter) to $($AssignTo)" + Write-LogMessage -headers $Headers -API $APIName -tenant $($TenantFilter) -message $Result -Sev Info + $StatusCode = [HttpStatusCode]::OK } catch { - Write-LogMessage -headers $Request.Headers -API $APINAME -tenant $($tenantfilter) -message "Failed to assign app $($appFilter): $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed to assign. $($_.Exception.Message)" } + $ErrorMessage = Get-CippException -Exception $_ + $Result = "Failed to assign app $($appFilter) to $($AssignTo). Error: $($ErrorMessage.NormalizedError)" + Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message $Result -Sev 'Error' -LogData $ErrorMessage + $StatusCode = [HttpStatusCode]::InternalServerError } # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body + StatusCode = $StatusCode + Body = @{ Results = $Result } }) } From 5793d1ba548b0c11518324082d2a597137f68ea4 Mon Sep 17 00:00:00 2001 From: Jr7468 Date: Thu, 17 Apr 2025 23:30:55 +0100 Subject: [PATCH 19/56] Refactor the password output of the AddUser endpoint so that the password is returned as a copyField --- .../Administration/Users/Invoke-AddUser.ps1 | 13 +++++++++---- Modules/CIPPCore/Public/New-CIPPUserTask.ps1 | 1 - 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUser.ps1 index c3233b0bf8f6..019ab34d981f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUser.ps1 @@ -38,15 +38,20 @@ Function Invoke-AddUser { } else { $CreationResults = New-CIPPUserTask -userobj $UserObj -APIName $APINAME -Headers $Request.Headers $body = [pscustomobject] @{ - 'Results' = $CreationResults.Results - 'Username' = $CreationResults.username - 'Password' = $CreationResults.password + 'Results' = @( + $CreationResults.Results[0], + $CreationResults.Results[1], + @{ + 'resultText' = $CreationResults.Results[2] + 'copyField' = $CreationResults.password + 'state' = 'success' + } + ) 'CopyFrom' = @{ 'Success' = $CreationResults.CopyFrom.Success 'Error' = $CreationResults.CopyFrom.Error } } - } # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ diff --git a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 index 33b68024b059..1e19ea9180fa 100644 --- a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 @@ -13,7 +13,6 @@ function New-CIPPUserTask { $Results.Add('Created New User.') $Results.Add("Username: $($CreationResults.Username)") $Results.Add("Password: $($CreationResults.Password)") - $Results.Add("$($CreationResults.Password)") } catch { $Results.Add("Failed to create user. $($_.Exception.Message)" ) return @{'Results' = $Results } From efd8d04e99fe31eb912ed70bece55e6276c19cc8 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Sat, 19 Apr 2025 21:29:15 +0200 Subject: [PATCH 20/56] Add or update the Azure App Service build and deployment workflow config --- .github/workflows/dev_cippmpiii.yml | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/dev_cippmpiii.yml diff --git a/.github/workflows/dev_cippmpiii.yml b/.github/workflows/dev_cippmpiii.yml new file mode 100644 index 000000000000..6f9742a83d1d --- /dev/null +++ b/.github/workflows/dev_cippmpiii.yml @@ -0,0 +1,30 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/azure/functions-action +# More GitHub Actions for Azure: https://github.com/Azure/actions + +name: Build and deploy Powershell project to Azure Function App - cippmpiii + +on: + push: + branches: + - dev + workflow_dispatch: + +env: + AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: 'Checkout GitHub Action' + uses: actions/checkout@v4 + + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + id: fa + with: + app-name: 'cippmpiii' + slot-name: 'Production' + package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} + publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_BC5F21E993034DF2A3793489CE4705E4 }} \ No newline at end of file From c9af0efff76064a675f00e6d467f5d663e47b8fe Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:51:42 +0200 Subject: [PATCH 21/56] tenantfilter --- .../HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 index 06731cb29663..26ce2cd1d417 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 @@ -11,7 +11,7 @@ function Invoke-ExecBPA { $ConfigTable = Get-CIPPTable -tablename Config $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" - $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + $TenantFilter = $Request.Query.tenantFilter ? $Request.Query.tenantFilter : $Request.Body.tenantfilter if ($Config -and $Config.state -eq $true) { if ($env:CIPP_PROCESSOR -ne 'true') { From d4532cf85222b945088abdc53c31cb5e67170b6f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:03:31 +0200 Subject: [PATCH 22/56] push data fix --- .../Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 index 4229e704b46f..8834f1619550 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 @@ -19,7 +19,8 @@ function Push-BPACollectData { } } $Table = Get-CippTable -tablename 'cachebpav2' - $Rerun = Test-CIPPRerun -Type 'BPA' -Tenant $TenantName.defaultDomainName -API $Item.Template + + $Rerun = Test-CIPPRerun -Type 'BPA' -Tenant $Item.Tenant -API $Item.Template if ($Rerun) { Write-Host 'Detected rerun for BPA. Exiting cleanly' exit 0 From 73a2f2b6b172623acbeb90107f797cf67eee723c Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:08:02 +0200 Subject: [PATCH 23/56] push data fix --- .../Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 index 8834f1619550..aa7007c62fee 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 @@ -5,7 +5,7 @@ function Push-BPACollectData { #> param($Item) - $TenantName = Get-Tenants | Where-Object -Property defaultDomainName -EQ $Item.Tenant + $TenantName = Get-Tenants | Where-Object -Property defaultDomainName -EQ $Item.Tenant.value $BPATemplateTable = Get-CippTable -tablename 'templates' $Filter = "PartitionKey eq 'BPATemplate'" $TemplatesLoc = (Get-CIPPAzDataTableEntity @BPATemplateTable -Filter $Filter).JSON | ConvertFrom-Json From 39dde3c120428c7f461155ea0bdc53adbc894322 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:43:16 +0200 Subject: [PATCH 24/56] improvements --- .../Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 | 2 +- .../HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 index aa7007c62fee..8834f1619550 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/BPA/Push-BPACollectData.ps1 @@ -5,7 +5,7 @@ function Push-BPACollectData { #> param($Item) - $TenantName = Get-Tenants | Where-Object -Property defaultDomainName -EQ $Item.Tenant.value + $TenantName = Get-Tenants | Where-Object -Property defaultDomainName -EQ $Item.Tenant $BPATemplateTable = Get-CippTable -tablename 'templates' $Filter = "PartitionKey eq 'BPATemplate'" $TemplatesLoc = (Get-CIPPAzDataTableEntity @BPATemplateTable -Filter $Filter).JSON | ConvertFrom-Json diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 index 26ce2cd1d417..f9c6205aaac4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 @@ -11,7 +11,7 @@ function Invoke-ExecBPA { $ConfigTable = Get-CIPPTable -tablename Config $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" - $TenantFilter = $Request.Query.tenantFilter ? $Request.Query.tenantFilter : $Request.Body.tenantfilter + $TenantFilter = $Request.Query.tenantFilter ? $Request.Query.tenantFilter.value : $Request.Body.tenantfilter if ($Config -and $Config.state -eq $true) { if ($env:CIPP_PROCESSOR -ne 'true') { From 7986be7c76be34474c96894de619d5b33726c4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 22 Apr 2025 14:25:31 +0200 Subject: [PATCH 25/56] Refactor alert message to not be bad --- .../Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 index f3f5d66cf6f2..0f66c7c154c5 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 @@ -28,7 +28,15 @@ function Get-CIPPAlertNewAppApproval { "https://login.microsoftonline.com/$($TenantFilter)/adminConsent?client_id=$($App.appId)&bf_id=$($App.id)&redirect_uri=https://entra.microsoft.com/TokenAuthorize" } - $Message = "App name: $($App.appDisplayName) - Request user: $($_.createdBy.user.userPrincipalName) - Reason: $($_.reason)`nApp Id: $($App.appId) - Scopes: $($App.pendingScopes.displayName)`nConsent URL: $consentUrl" + $Message = [PSCustomObject]@{ + AppName = $App.appDisplayName + RequestUser = $_.createdBy.user.userPrincipalName + Reason = $_.reason + AppId = $App.appId + Scopes = ($App.pendingScopes.displayName -join ', ') + ConsentURL = $consentUrl + Tenant = $TenantFilter + } $AlertData.Add($Message) } } From 52ec4f33cf9546938c0a40519afb993246825ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 22 Apr 2025 14:31:38 +0200 Subject: [PATCH 26/56] Refactor Get-CIPPAlertDefenderIncidents to return structured PSCustomObject for incident data --- .../Public/Alerts/Get-CIPPAlertDefenderIncidents.ps1 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDefenderIncidents.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDefenderIncidents.ps1 index 150a3c587677..49be9d9a153a 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDefenderIncidents.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDefenderIncidents.ps1 @@ -13,7 +13,14 @@ function Get-CIPPAlertDefenderIncidents { ) try { $AlertData = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/security/incidents?`$top=50&`$filter=status eq 'active'" -tenantid $TenantFilter | ForEach-Object { - "Incident ID $($_.id): Created at $($_.createdDateTime). Severity: $($_.severity). `nIncident name: $($_.displayName). Incident URL: $($_.incidentWebUrl)." + [PSCustomObject]@{ + IncidentID = $_.id + CreatedAt = $_.createdDateTime + Severity = $_.severity + IncidentName = $_.displayName + IncidentUrl = $_.incidentWebUrl + Tenant = $TenantFilter + } } Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData From 4f00fd14c55add3474d8a6c6d46acddfd8e1add4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 22 Apr 2025 11:41:27 -0400 Subject: [PATCH 27/56] remove + objects in all tenants exxclusion use generic list --- .../Push-ExecOnboardTenantQueue.ps1 | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1 index f86338644252..210dbda78e27 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecOnboardTenantQueue.ps1 @@ -1,4 +1,4 @@ -Function Push-ExecOnboardTenantQueue { +function Push-ExecOnboardTenantQueue { <# .FUNCTIONALITY Entrypoint @@ -354,22 +354,29 @@ Function Push-ExecOnboardTenantQueue { if ($OnboardingSteps.Step4.Status -eq 'succeeded') { if ($Item.StandardsExcludeAllTenants -eq $true) { $AddExclusionObj = [PSCustomObject]@{ - label = $Tenant.defaultDomainName + label = '{0} ({1})' -f $Tenant.displayName, $Tenant.defaultDomainName value = $Tenant.defaultDomainName - addedFields = @{} + addedFields = @{ + customerId = $Tenant.customerId + defaultDomainName = $Tenant.defaultDomainName + } } $Table = Get-CippTable -tablename 'templates' $ExistingTemplates = Get-CippazDataTableEntity @Table -Filter "PartitionKey eq 'StandardsTemplateV2'" | Where-Object { $_.JSON -match 'AllTenants' } - foreach ($AllTenantesTemplate in $ExistingTemplates) { + foreach ($AllTenantsTemplate in $ExistingTemplates) { $object = $AllTenantesTemplate.JSON | ConvertFrom-Json - $NewExcludedTenants = $object.excludedTenants + $AddExclusionObj + $NewExcludedTenants = [system.collections.generic.list[object]]::new() + foreach ($Tenant in $object.excludedTenants) { + $NewExcludedTenants.Add($Tenant) + } + $NewExcludedTenants.Add($AddExclusionObj) $object.excludedTenants = $NewExcludedTenants $JSON = ConvertTo-Json -InputObject $object -Compress -Depth 10 $Table.Force = $true Add-CIPPAzDataTableEntity @Table -Entity @{ JSON = "$JSON" - RowKey = $AllTenantesTemplate.RowKey - GUID = $AllTenantesTemplate.GUID + RowKey = $AllTenantsTemplate.RowKey + GUID = $AllTenantsTemplate.GUID PartitionKey = 'StandardsTemplateV2' } } From 80a1931398993a97998524a5a0b5430322c7f2d7 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 22 Apr 2025 12:01:34 -0400 Subject: [PATCH 28/56] fix permission --- .../HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 index e63c40bc23d9..3a129d2ea5bb 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 @@ -3,7 +3,7 @@ function Invoke-ExecCippReplacemap { .FUNCTIONALITY Entrypoint .ROLE - CIPP.Extension.ReadWrite + Tenant.Config.ReadWrite #> [CmdletBinding()] param($Request, $TriggerMetadata) From 15689ddd2a8e59abb12b8c03199ab3feefb63de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 22 Apr 2025 20:14:58 +0200 Subject: [PATCH 29/56] fix wrong data type used for list --- Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 index 0f66c7c154c5..883413405345 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 @@ -15,7 +15,7 @@ function Get-CIPPAlertNewAppApproval { try { $Approvals = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/identityGovernance/appConsent/appConsentRequests?`$filter=userConsentRequests/any (u:u/status eq 'InProgress')" -tenantid $TenantFilter if ($Approvals.count -gt 0) { - $AlertData = [System.Collections.Generic.List[string]]::new() + $AlertData = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($App in $Approvals) { $userConsentRequests = New-GraphGetRequest -Uri "https://graph.microsoft.com/v1.0/identityGovernance/appConsent/appConsentRequests/$($App.id)/userConsentRequests" -tenantid $TenantFilter $userConsentRequests | ForEach-Object { From 2b6a95eb8170216134ab687a4f19824c2c63ec3b Mon Sep 17 00:00:00 2001 From: Esco Date: Wed, 23 Apr 2025 10:39:15 +0200 Subject: [PATCH 30/56] fix: tweak standards compare --- ...IPPStandardDefaultPlatformRestrictions.ps1 | 31 ++++++++++--------- .../Standards/Invoke-CIPPStandardMDMScope.ps1 | 24 +++++++++----- ...-CIPPStandardPhishSimSpoofIntelligence.ps1 | 12 ++++--- ...Invoke-CIPPStandardPhishingSimulations.ps1 | 18 +++++------ ...IPPStandardSharePointMassDeletionAlert.ps1 | 26 +++++++--------- 5 files changed, 60 insertions(+), 51 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 index a29474ce86f9..e5399290ab48 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 @@ -58,6 +58,19 @@ function Invoke-CIPPStandardDefaultPlatformRestrictions { ($CurrentState.windowsRestriction.platformBlocked -eq $Settings.platformWindowsBlocked) -and ($CurrentState.windowsRestriction.personalDeviceEnrollmentBlocked -eq $Settings.personalWindowsBlocked) + $CompareField = [PSCustomObject]@{ + platformAndroidForWorkBlocked = $CurrentState.androidForWorkRestriction.platformBlocked + personalAndroidForWorkBlocked = $CurrentState.androidForWorkRestriction.personalDeviceEnrollmentBlocked + platformAndroidBlocked = $CurrentState.androidRestriction.platformBlocked + personalAndroidBlocked = $CurrentState.androidRestriction.personalDeviceEnrollmentBlocked + platformiOSBlocked = $CurrentState.iosRestriction.platformBlocked + personaliOSBlocked = $CurrentState.iosRestriction.personalDeviceEnrollmentBlocked + platformMacOSBlocked = $CurrentState.macOSRestriction.platformBlocked + personalMacOSBlocked = $CurrentState.macOSRestriction.personalDeviceEnrollmentBlocked + platformWindowsBlocked = $CurrentState.windowsRestriction.platformBlocked + personalWindowsBlocked = $CurrentState.windowsRestriction.personalDeviceEnrollmentBlocked + } + If ($Settings.remediate -eq $true) { if ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -Tenant $Tenant -Message 'DefaultPlatformRestrictions is already applied correctly.' -Sev Info @@ -109,29 +122,17 @@ function Invoke-CIPPStandardDefaultPlatformRestrictions { } If ($Settings.alert -eq $true) { - if ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -Tenant $Tenant -Message 'DefaultPlatformRestrictions is correctly set.' -Sev Info } else { - Write-StandardsAlert -message 'DefaultPlatformRestrictions is incorrectly set.' -object $StateIsCorrect -tenant $Tenant -standardName 'DefaultPlatformRestrictions' -standardId $Settings.standardId + Write-StandardsAlert -message 'DefaultPlatformRestrictions is incorrectly set.' -object $CompareField -tenant $Tenant -standardName 'DefaultPlatformRestrictions' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -Tenant $Tenant -Message 'DefaultPlatformRestrictions is incorrectly set.' -Sev Info } } If ($Settings.report -eq $true) { - $Table = [PSCustomObject]@{ - platformAndroidForWorkBlocked = $CurrentState.androidForWorkRestriction.platformBlocked - personalAndroidForWorkBlocked = $CurrentState.androidForWorkRestriction.personalDeviceEnrollmentBlocked - platformAndroidBlocked = $CurrentState.androidRestriction.platformBlocked - personalAndroidBlocked = $CurrentState.androidRestriction.personalDeviceEnrollmentBlocked - platformiOSBlocked = $CurrentState.iosRestriction.platformBlocked - personaliOSBlocked = $CurrentState.iosRestriction.personalDeviceEnrollmentBlocked - platformMacOSBlocked = $CurrentState.macOSRestriction.platformBlocked - personalMacOSBlocked = $CurrentState.macOSRestriction.personalDeviceEnrollmentBlocked - platformWindowsBlocked = $CurrentState.windowsRestriction.platformBlocked - personalWindowsBlocked = $CurrentState.windowsRestriction.personalDeviceEnrollmentBlocked - } - Set-CIPPStandardsCompareField -FieldName 'standards.DefaultPlatformRestrictions' -FieldValue $Table -TenantFilter $Tenant + $FieldValue = $StateIsCorrect ? $true : $CompareField + Set-CIPPStandardsCompareField -FieldName 'standards.DefaultPlatformRestrictions' -FieldValue $FieldValue -TenantFilter $Tenant Add-CIPPBPAField -FieldName 'DefaultPlatformRestrictions' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $Tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 index 433a62a6f64a..cf7d650b67a1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 @@ -34,10 +34,18 @@ function Invoke-CIPPStandardMDMScope { $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/mobileDeviceManagementPolicies/0000000a-0000-0000-c000-000000000000?$expand=includedGroups' -tenantid $Tenant $StateIsCorrect = ($CurrentInfo.termsOfUseUrl -eq 'https://portal.manage.microsoft.com/TermsofUse.aspx') -and - ($CurrentInfo.discoveryUrl -eq 'https://enrollment.manage.microsoft.com/enrollmentserver/discovery.svc') -and - ($CurrentInfo.complianceUrl -eq 'https://portal.manage.microsoft.com/?portalAction=Compliance') -and - ($CurrentInfo.appliesTo -eq $Settings.appliesTo) -and - ($Settings.appliesTo -ne 'selected' -or ($CurrentInfo.includedGroups.displayName -contains $Settings.customGroup)) + ($CurrentInfo.discoveryUrl -eq 'https://enrollment.manage.microsoft.com/enrollmentserver/discovery.svc') -and + ($CurrentInfo.complianceUrl -eq 'https://portal.manage.microsoft.com/?portalAction=Compliance') -and + ($CurrentInfo.appliesTo -eq $Settings.appliesTo) -and + ($Settings.appliesTo -ne 'selected' -or ($CurrentInfo.includedGroups.displayName -contains $Settings.customGroup)) + + $CompareField = [PSCustomObject]@{ + termsOfUseUrl = $CurrentInfo.termsOfUseUrl + discoveryUrl = $CurrentInfo.discoveryUrl + complianceUrl = $CurrentInfo.complianceUrl + appliesTo = $CurrentInfo.appliesTo + customGroup = $CurrentInfo.includedGroups.displayName + } If ($Settings.remediate -eq $true) { if ($StateIsCorrect -eq $true) { @@ -112,17 +120,17 @@ function Invoke-CIPPStandardMDMScope { } if ($Settings.alert -eq $true) { - if ($StateIsCorrect) { + if ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'MDM Scope is correctly configured' -sev Info } else { - Write-StandardsAlert -message 'MDM Scope is not correctly configured' -object $CurrentInfo -tenant $tenant -standardName 'MDMScope' -standardId $Settings.standardId + Write-StandardsAlert -message 'MDM Scope is not correctly configured' -object $CompareField -tenant $tenant -standardName 'MDMScope' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -tenant $tenant -message 'MDM Scope is not correctly configured' -sev Info } } if ($Settings.report -eq $true) { - $state = $StateIsCorrect ? $true : $CurrentInfo - Set-CIPPStandardsCompareField -FieldName 'standards.MDMScope' -FieldValue $state -TenantFilter $Tenant + $FieldValue = $StateIsCorrect ? $true : $CompareField + Set-CIPPStandardsCompareField -FieldName 'standards.MDMScope' -FieldValue $FieldValue -TenantFilter $Tenant Add-CIPPBPAField -FieldName 'MDMScope' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 index d8643a058c44..d50a2f57055b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 @@ -40,6 +40,11 @@ function Invoke-CIPPStandardPhishSimSpoofIntelligence { $StateIsCorrect = ($AddDomain.Count -eq 0 -and $RemoveDomain.Count -eq 0) + $CompareField = [PSCustomObject]@{ + "Missing Domains" = $AddDomain -join ', ' + "Incorrect Domains" = $RemoveDomain.SendingInfrastructure -join ', ' + } + If ($Settings.remediate -eq $true) { If ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -Tenant $Tenant -message 'Spoof Intelligence Allow list already correctly configured' -sev Info @@ -89,15 +94,14 @@ function Invoke-CIPPStandardPhishSimSpoofIntelligence { If ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -Tenant $Tenant -message 'Spoof Intelligence Allow list is correctly configured' -sev Info } Else { - Write-StandardsAlert -message 'Spoof Intelligence Allow list is not correctly configured' -object $CurrentState -tenant $Tenant -standardName 'PhishSimSpoofIntelligence' -standardId $Settings.standardId + Write-StandardsAlert -message 'Spoof Intelligence Allow list is not correctly configured' -object $CompareField -tenant $Tenant -standardName 'PhishSimSpoofIntelligence' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -Tenant $Tenant -message 'Spoof Intelligence Allow list is not correctly configured' -sev Info } } If ($Settings.report -eq $true) { - $CurrentState = $StateIsCorrect ? $true : $DomainState.SendingInfrastructure - - Set-CIPPStandardsCompareField -FieldName 'standards.PhishSimSpoofIntelligence' -FieldValue $CurrentState -Tenant $Tenant + $FieldValue = $StateIsCorrect ? $true : $CompareField + Set-CIPPStandardsCompareField -FieldName 'standards.PhishSimSpoofIntelligence' -FieldValue $FieldValue -Tenant $Tenant Add-CIPPBPAField -FieldName 'PhishSimSpoofIntelligence' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $Tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 index accaabfcc287..ca24d4c37370 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 @@ -66,6 +66,12 @@ function Invoke-CIPPStandardPhishingSimulations { # Check state for all components $StateIsCorrect = $PolicyIsCorrect -and $RuleIsCorrect -and $PhishingSimUrlsIsCorrect + $CompareField = [PSCustomObject]@{ + Domains = $RuleState.Domains -join ', ' + SenderIpRanges = $RuleState.SenderIpRanges -join ', ' + PhishingSimUrls = $SimUrlState.value -join ', ' + } + If ($Settings.remediate -eq $true) { If ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -Tenant $Tenant -message 'Advanced Phishing Simulations already correctly configured' -sev Info @@ -157,22 +163,14 @@ function Invoke-CIPPStandardPhishingSimulations { If ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -Tenant $Tenant -message 'Phishing Simulation Configuration is correctly configured' -sev Info } Else { - Write-StandardsAlert -message 'Phishing Simulation Configuration is not correctly configured' -object $CurrentState -tenant $Tenant -standardName 'PhishingSimulations' -standardId $Settings.standardId + Write-StandardsAlert -message 'Phishing Simulation Configuration is not correctly configured' -object $CompareField -tenant $Tenant -standardName 'PhishingSimulations' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -Tenant $Tenant -message 'Phishing Simulation Configuration is not correctly configured' -sev Info } } If ($Settings.report -eq $true) { + $FieldValue = $StateIsCorrect ? $true : $CompareField Add-CIPPBPAField -FieldName 'PhishingSimulations' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $Tenant - If ($StateIsCorrect -eq $true) { - $FieldValue = $true - } Else { - $FieldValue = [PSCustomObject]@{ - Domains = $RuleState.Domains - SenderIpRanges = $RuleState.SenderIpRanges - PhishingSimUrls = $SimUrlState.value - } - } Set-CIPPStandardsCompareField -FieldName 'standards.PhishingSimulations' -FieldValue $FieldValue -Tenant $Tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 index 08404d523264..444fee2a48a4 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 @@ -42,9 +42,15 @@ function Invoke-CIPPStandardSharePointMassDeletionAlert { $MissingEmailsInSettings = $Settings.NotifyUser.value | Where-Object { $_ -notin $CurrentState.NotifyUser } $StateIsCorrect = ($EmailsOutsideSettings.Count -eq 0) -and - ($MissingEmailsInSettings.Count -eq 0) -and - ($CurrentState.Threshold -eq $Settings.Threshold) -and - ($CurrentState.TimeWindow -eq $Settings.TimeWindow) + ($MissingEmailsInSettings.Count -eq 0) -and + ($CurrentState.Threshold -eq $Settings.Threshold) -and + ($CurrentState.TimeWindow -eq $Settings.TimeWindow) + + $CompareField = [PSCustomObject]@{ + 'Threshold' = $CurrentState.Threshold + 'TimeWindow' = $CurrentState.TimeWindow + 'NotifyUser' = $CurrentState.NotifyUser -join ', ' + } If ($Settings.remediate -eq $true) { If ($StateIsCorrect -eq $true) { @@ -88,22 +94,14 @@ function Invoke-CIPPStandardSharePointMassDeletionAlert { If ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -Tenant $Tenant -Message 'SharePoint mass deletion of files alert is enabled' -sev Info } Else { + Write-StandardsAlert -message 'SharePoint mass deletion of files alert is disabled' -object $CompareField -tenant $tenant -standardName 'SharePointMassDeletionAlert' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -Tenant $Tenant -Message 'SharePoint mass deletion of files alert is disabled' -sev Info } } If ($Settings.report -eq $true) { - If ($StateIsCorrect -eq $true) { - $Table = $true - } Else { - $Table = [PSCustomObject]@{ - Threshold = $CurrentState.Threshold - TimeWindow = $CurrentState.TimeWindow - NotifyUser = $CurrentState.NotifyUser - } - } - - Set-CIPPStandardsCompareField -FieldName 'standards.SharePointMassDeletionAlert' -FieldValue $Table -TenantFilter $Tenant + $FieldValue = $StateIsCorrect ? $true : $CompareField + Set-CIPPStandardsCompareField -FieldName 'standards.SharePointMassDeletionAlert' -FieldValue $FieldValue -TenantFilter $Tenant Add-CIPPBPAField -FieldName 'SharePointMassDeletionAlert' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $Tenant } } From 0e026882b43ae370546211b26e1cbfa2b34607b2 Mon Sep 17 00:00:00 2001 From: Esco Date: Wed, 23 Apr 2025 13:37:46 +0200 Subject: [PATCH 31/56] fix: TeamsExternalFileSharing identity --- ...e-CIPPStandardTeamsExternalFileSharing.ps1 | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 index 0923d4e6206b..b5bf59dcde73 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 @@ -38,23 +38,18 @@ function Invoke-CIPPStandardTeamsExternalFileSharing { Write-Host "TeamsExternalFileSharing: $($Settings | ConvertTo-Json)" $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' | Select-Object AllowGoogleDrive, AllowShareFile, AllowBox, AllowDropBox, AllowEgnyte - if ($null -eq $Settings.AllowGoogleDrive) { $Settings.AllowGoogleDrive = $false } - if ($null -eq $Settings.AllowShareFile) { $Settings.AllowShareFile = $false } - if ($null -eq $Settings.AllowBox) { $Settings.AllowBox = $false } - if ($null -eq $Settings.AllowDropBox) { $Settings.AllowDropBox = $false } - if ($null -eq $Settings.AllowEgnyte) { $Settings.AllowEgnyte = $false } - - $StateIsCorrect = ($CurrentState.AllowGoogleDrive -eq $Settings.AllowGoogleDrive) -and - ($CurrentState.AllowShareFile -eq $Settings.AllowShareFile) -and - ($CurrentState.AllowBox -eq $Settings.AllowBox) -and - ($CurrentState.AllowDropBox -eq $Settings.AllowDropBox) -and - ($CurrentState.AllowEgnyte -eq $Settings.AllowEgnyte) + $StateIsCorrect = ($CurrentState.AllowGoogleDrive -eq $Settings.AllowGoogleDrive ?? $false ) -and + ($CurrentState.AllowShareFile -eq $Settings.AllowShareFile ?? $false ) -and + ($CurrentState.AllowBox -eq $Settings.AllowBox ?? $false ) -and + ($CurrentState.AllowDropBox -eq $Settings.AllowDropBox ?? $false ) -and + ($CurrentState.AllowEgnyte -eq $Settings.AllowEgnyte ?? $false ) if ($Settings.remediate -eq $true) { if ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Teams External File Sharing already set.' -sev Info } else { $cmdParams = @{ + Identity = 'Global' AllowGoogleDrive = $Settings.AllowGoogleDrive AllowShareFile = $Settings.AllowShareFile AllowBox = $Settings.AllowBox @@ -84,7 +79,7 @@ function Invoke-CIPPStandardTeamsExternalFileSharing { if ($Settings.report -eq $true) { Add-CIPPBPAField -FieldName 'TeamsExternalFileSharing' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $Tenant - if ($StateIsCorrect) { + if ($StateIsCorrect -eq $true) { $FieldValue = $true } else { $FieldValue = $CurrentState From 2744af7dd04ce904366940dd3b5057b7d4d00bd4 Mon Sep 17 00:00:00 2001 From: Esco Date: Thu, 24 Apr 2025 12:11:42 +0200 Subject: [PATCH 32/56] chore: fix up AutopilotStatusPage --- .../Public/Set-CIPPDefaultAPEnrollment.ps1 | 14 ++-- ...Invoke-CIPPStandardAutopilotStatusPage.ps1 | 78 ++++++++----------- 2 files changed, 39 insertions(+), 53 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPDefaultAPEnrollment.ps1 b/Modules/CIPPCore/Public/Set-CIPPDefaultAPEnrollment.ps1 index c0ec587b0433..d02794c16c0a 100644 --- a/Modules/CIPPCore/Public/Set-CIPPDefaultAPEnrollment.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPDefaultAPEnrollment.ps1 @@ -23,7 +23,7 @@ function Set-CIPPDefaultAPEnrollment { 'displayName' = 'All users and all devices' 'description' = 'This is the default enrollment status screen configuration applied with the lowest priority to all users and all devices regardless of group membership.' 'showInstallationProgress' = [bool]$ShowProgress - 'blockDeviceSetupRetryByUser' = ![bool]$blockDevice + 'blockDeviceSetupRetryByUser' = ![bool]$BlockDevice 'allowDeviceResetOnInstallFailure' = [bool]$AllowReset 'allowLogCollectionOnInstallFailure' = [bool]$EnableLog 'customErrorMessage' = "$ErrorMessage" @@ -35,16 +35,16 @@ function Set-CIPPDefaultAPEnrollment { 'roleScopeTagIds' = @() } $Body = ConvertTo-Json -InputObject $ObjBody - $ExistingStatusPage = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations' -tenantid $($TenantFilter)) | Where-Object { $_.id -like '*DefaultWindows10EnrollmentCompletionPageConfiguration' } + $ExistingStatusPage = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations' -tenantid $TenantFilter) | Where-Object { $_.id -like '*DefaultWindows10EnrollmentCompletionPageConfiguration' } if ($PSCmdlet.ShouldProcess($ExistingStatusPage.ID, 'Set Default Enrollment Status Page')) { - $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations/$($ExistingStatusPage.ID)" -body $body -Type PATCH -tenantid $($TenantFilter) - "Successfully changed default enrollment status page for $($($TenantFilter))" - Write-LogMessage -Headers $User -API $APINAME -tenant $($TenantFilter) -message "Added Autopilot Enrollment Status Page $($Displayname)" -Sev 'Info' + $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations/$($ExistingStatusPage.ID)" -body $Body -Type PATCH -tenantid $TenantFilter + "Successfully changed default enrollment status page for $TenantFilter" + Write-LogMessage -Headers $User -API $APIName -tenant $TenantFilter -message "Added Autopilot Enrollment Status Page $($ExistingStatusPage.displayName)" -Sev 'Info' } } catch { $ErrorMessage = Get-CippException -Exception $_ - Write-LogMessage -Headers $User -API $APINAME -tenant $($TenantFilter) -message "Failed adding Autopilot Enrollment Status Page $($Displayname). Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage - throw "Failed to change default enrollment status page for $($($TenantFilter)): $($ErrorMessage.NormalizedError)" + Write-LogMessage -Headers $User -API $APIName -tenant $TenantFilter -message "Failed adding Autopilot Enrollment Status Page $($ExistingStatusPage.displayName). Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + throw "Failed to change default enrollment status page for $($TenantFilter): $($ErrorMessage.NormalizedError)" } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutopilotStatusPage.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutopilotStatusPage.ps1 index e91b53d2fd1b..f592f409ee59 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutopilotStatusPage.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutopilotStatusPage.ps1 @@ -36,16 +36,33 @@ function Invoke-CIPPStandardAutopilotStatusPage { https://docs.cipp.app/user-documentation/tenant/standards/list-standards/ #> param($Tenant, $Settings) + + # Get current Autopilot enrollment status page configuration + try { + $CurrentConfig = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations?`$expand=assignments&orderBy=priority&`$filter=deviceEnrollmentConfigurationType eq 'windows10EnrollmentCompletionPageConfiguration' and priority eq 0" -tenantid $Tenant | + Select-Object -Property id, displayName, priority, showInstallationProgress, blockDeviceSetupRetryByUser, allowDeviceResetOnInstallFailure, allowLogCollectionOnInstallFailure, customErrorMessage, installProgressTimeoutInMinutes, allowDeviceUseOnInstallFailure, trackInstallProgressForAutopilotOnly + + $StateIsCorrect = ($CurrentConfig.installProgressTimeoutInMinutes -eq $Settings.TimeOutInMinutes) -and + ($CurrentConfig.customErrorMessage -eq $Settings.ErrorMessage) -and + ($CurrentConfig.showInstallationProgress -eq $Settings.ShowProgress) -and + ($CurrentConfig.allowLogCollectionOnInstallFailure -eq $Settings.EnableLog) -and + ($CurrentConfig.trackInstallProgressForAutopilotOnly -eq $Settings.OBEEOnly) -and + ($CurrentConfig.blockDeviceSetupRetryByUser -eq !$Settings.BlockDevice) -and + ($CurrentConfig.allowDeviceResetOnInstallFailure -eq $Settings.AllowReset) -and + ($CurrentConfig.allowDeviceUseOnInstallFailure -eq $Settings.AllowFail) + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to check Autopilot Enrollment Status Page: $ErrorMessage" -sev Error + $StateIsCorrect = $false + } + + # Remediate if the state is not correct If ($Settings.remediate -eq $true) { - ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'APESP' - if ($Rerun -eq $true) { - exit 0 - } try { $Parameters = @{ TenantFilter = $Tenant ShowProgress = $Settings.ShowProgress - BlockDevice = $Settings.blockDevice + BlockDevice = $Settings.BlockDevice AllowReset = $Settings.AllowReset EnableLog = $Settings.EnableLog ErrorMessage = $Settings.ErrorMessage @@ -61,51 +78,20 @@ function Invoke-CIPPStandardAutopilotStatusPage { } } - # Get current Autopilot enrollment status page configuration - try { - $CurrentConfig = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations?`$filter=startswith(displayName,'Windows Autopilot')" -tenantid $Tenant - - # Check if the enrollment status page exists - $ESPConfig = $CurrentConfig.value | Where-Object { $_.displayName -like '*Enrollment Status Page*' } - - $ESPConfigured = $null -ne $ESPConfig - - # Check if settings match what's expected - $SettingsMismatch = $false - $MismatchDetails = @{} - - if ($ESPConfigured) { - # Check timeout setting - if ($ESPConfig.priority -ne 0) { - $SettingsMismatch = $true - $MismatchDetails.Priority = @{Expected = 0; Actual = $ESPConfig.priority } - } - - } - - $StateIsCorrect = $ESPConfigured -and (-not $SettingsMismatch) - } catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to check Autopilot Enrollment Status Page: $ErrorMessage" -sev Error - $StateIsCorrect = $false - } - + # Report if ($Settings.report -eq $true) { - $state = $StateIsCorrect -eq $true ? $true : $StateIsCorrect - Set-CIPPStandardsCompareField -FieldName 'standards.AutopilotStatusPage' -FieldValue $state -TenantFilter $tenant - Add-CIPPBPAField -FieldName 'AutopilotStatusPage' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $tenant + $FieldValue = $StateIsCorrect -eq $true ? $true : $CurrentConfig + Set-CIPPStandardsCompareField -FieldName 'standards.AutopilotStatusPage' -FieldValue $FieldValue -TenantFilter $Tenant + Add-CIPPBPAField -FieldName 'AutopilotStatusPage' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $Tenant } - if ($Settings.alert) { - if (!$ESPConfigured) { - Write-StandardsAlert -message 'Autopilot Enrollment Status Page is not configured' -object @{} -tenant $Tenant -standardName 'AutopilotStatusPage' -standardId $Settings.standardId - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Autopilot Enrollment Status Page is not configured' -sev Info - } elseif ($SettingsMismatch) { - Write-StandardsAlert -message 'Autopilot Enrollment Status Page settings do not match expected configuration' -object $MismatchDetails -tenant $Tenant -standardName 'AutopilotStatusPage' -standardId $Settings.standardId - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Autopilot Enrollment Status Page settings do not match expected configuration' -sev Info + # Alert + if ($Settings.alert -eq $true) { + if ($StateIsCorrect -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Autopilot Enrollment Status Page is configured correctly' -sev Info } else { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Autopilot Enrollment Status Page is configured correctly' -sev Info + Write-StandardsAlert -message 'Autopilot Enrollment Status Page settings do not match expected configuration' -object $CurrentConfig -tenant $Tenant -standardName 'AutopilotStatusPage' -standardId $Settings.standardId + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Autopilot Enrollment Status Page settings do not match expected configuration' -sev Info } } - } From bed5ba0045ec37a3f0178b7617ed5beec8f19f37 Mon Sep 17 00:00:00 2001 From: Esco Date: Wed, 23 Apr 2025 13:11:10 +0200 Subject: [PATCH 33/56] chore: fix up AutoPilotProfile fix: fixes AutopilotProfile SelfDeployingMode good catch by mruiter --- .../Set-CIPPDefaultAPDeploymentProfile.ps1 | 40 ++++----- .../Invoke-CIPPStandardAutopilotProfile.ps1 | 89 ++++++++++++------- 2 files changed, 75 insertions(+), 54 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPDefaultAPDeploymentProfile.ps1 b/Modules/CIPPCore/Public/Set-CIPPDefaultAPDeploymentProfile.ps1 index 159fe55e8326..502b663199ff 100644 --- a/Modules/CIPPCore/Public/Set-CIPPDefaultAPDeploymentProfile.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPDefaultAPDeploymentProfile.ps1 @@ -2,18 +2,18 @@ function Set-CIPPDefaultAPDeploymentProfile { [CmdletBinding(SupportsShouldProcess = $true)] param( $tenantFilter, - $displayname, + $displayName, $description, $devicenameTemplate, $allowWhiteGlove, $CollectHash, - $usertype, + $userType, $DeploymentMode, $hideChangeAccount, $AssignTo, $hidePrivacy, $hideTerms, - $Autokeyboard, + $AutoKeyboard, $Headers, $Language = 'os-default', $APIName = 'Add Default Enrollment Status Page' @@ -24,9 +24,9 @@ function Set-CIPPDefaultAPDeploymentProfile { try { $ObjBody = [pscustomobject]@{ '@odata.type' = '#microsoft.graph.azureADWindowsAutopilotDeploymentProfile' - 'displayName' = "$($displayname)" + 'displayName' = "$($displayName)" 'description' = "$($description)" - 'deviceNameTemplate' = "$($DeviceNameTemplate)" + 'deviceNameTemplate' = "$($devicenameTemplate)" 'language' = "$($Language)" 'enableWhiteGlove' = $([bool]($allowWhiteGlove)) 'deviceType' = 'windowsPc' @@ -38,19 +38,19 @@ function Set-CIPPDefaultAPDeploymentProfile { 'escapeLinkHidden' = $([bool]($hideChangeAccount)) 'privacySettingsHidden' = $([bool]($hidePrivacy)) 'eulaHidden' = $([bool]($hideTerms)) - 'userType' = "$usertype" - 'keyboardSelectionPageSkipped' = $([bool]($Autokeyboard)) + 'userType' = "$userType" + 'keyboardSelectionPageSkipped' = $([bool]($AutoKeyboard)) } } $Body = ConvertTo-Json -InputObject $ObjBody - $Profiles = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -tenantid $tenantfilter | Where-Object -Property displayName -EQ $displayname + $Profiles = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -tenantid $tenantFilter | Where-Object -Property displayName -EQ $displayName if ($Profiles.count -gt 1) { $Profiles | ForEach-Object { if ($_.id -ne $Profiles[0].id) { if ($PSCmdlet.ShouldProcess($_.displayName, 'Delete duplicate Autopilot profile')) { - $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($_.id)" -tenantid $tenantfilter -type DELETE - Write-LogMessage -Headers $User -API $APIName -tenant $($tenantfilter) -message "Deleted duplicate Autopilot profile $($displayname)" -Sev 'Info' + $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($_.id)" -tenantid $tenantFilter -type DELETE + Write-LogMessage -Headers $User -API $APIName -tenant $($tenantFilter) -message "Deleted duplicate Autopilot profile $($displayName)" -Sev 'Info' } } } @@ -59,30 +59,30 @@ function Set-CIPPDefaultAPDeploymentProfile { if (!$Profiles) { if ($PSCmdlet.ShouldProcess($displayName, 'Add Autopilot profile')) { $Type = 'Add' - $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -body $body -tenantid $tenantfilter - Write-LogMessage -Headers $User -API $APIName -tenant $($tenantfilter) -message "Added Autopilot profile $($displayname)" -Sev 'Info' + $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -body $body -tenantid $tenantFilter + Write-LogMessage -Headers $User -API $APIName -tenant $($tenantFilter) -message "Added Autopilot profile $($displayName)" -Sev 'Info' } } else { $Type = 'Edit' - $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($Profiles.id)" -tenantid $tenantfilter -body $body -type PATCH + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($Profiles.id)" -tenantid $tenantFilter -body $body -type PATCH $GraphRequest = $Profiles | Select-Object -Last 1 } if ($AssignTo -eq $true) { $AssignBody = '{"target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"}}' - if ($PSCmdlet.ShouldProcess($AssignTo, "Assign Autopilot profile $displayname")) { + if ($PSCmdlet.ShouldProcess($AssignTo, "Assign Autopilot profile $displayName")) { #Get assignments - $Assignments = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($GraphRequest.id)/assignments" -tenantid $tenantfilter + $Assignments = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($GraphRequest.id)/assignments" -tenantid $tenantFilter if (!$Assignments) { - $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($GraphRequest.id)/assignments" -tenantid $tenantfilter -type POST -body $AssignBody + $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($GraphRequest.id)/assignments" -tenantid $tenantFilter -type POST -body $AssignBody } - Write-LogMessage -Headers $User -API $APIName -tenant $($tenantfilter) -message "Assigned autopilot profile $($Displayname) to $AssignTo" -Sev 'Info' + Write-LogMessage -Headers $User -API $APIName -tenant $tenantFilter -message "Assigned autopilot profile $($displayName) to $AssignTo" -Sev 'Info' } } - "Successfully $($Type)ed profile for $($tenantfilter)" + "Successfully $($Type)ed profile for $tenantFilter" } catch { $ErrorMessage = Get-CippException -Exception $_ - Write-LogMessage -Headers $User -API $APIName -tenant $($tenantfilter) -message "Failed $($Type)ing Autopilot Profile $($Displayname). Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage - throw "Failed to add profile for $($tenantfilter): $($ErrorMessage.NormalizedError)" + Write-LogMessage -Headers $User -API $APIName -tenant $tenantFilter -message "Failed $($Type)ing Autopilot Profile $($displayName). Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + throw "Failed to add profile for $($tenantFilter): $($ErrorMessage.NormalizedError)" } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutopilotProfile.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutopilotProfile.ps1 index 7812954bc9ec..dac20405a8f3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutopilotProfile.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutopilotProfile.ps1 @@ -40,49 +40,49 @@ function Invoke-CIPPStandardAutopilotProfile { https://docs.cipp.app/user-documentation/tenant/standards/list-standards/ #> param($Tenant, $Settings) - ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'APConfig' - # Check if profile exists - $ProfileExists = $false + # Get the current configuration try { - $Profiles = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -tenantid $Tenant - $ProfileExists = ($Profiles.displayName -contains $settings.DisplayName) - } catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to check Autopilot profiles: $ErrorMessage" -sev 'Error' - } + $CurrentConfig = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -tenantid $Tenant | + Where-Object { $_.displayName -eq $Settings.DisplayName } | + Select-Object -Property displayName, description, deviceNameTemplate, language, enableWhiteGlove, extractHardwareHash, outOfBoxExperienceSetting, preprovisioningAllowed - if ($Settings.report -eq $true) { - $state = $ProfileExists -eq $true ? $true : $ProfileExists - Set-CIPPStandardsCompareField -FieldName 'standards.AutopilotProfile' -FieldValue $state -TenantFilter $tenant - Add-CIPPBPAField -FieldName 'AutopilotProfile' -FieldValue $ProfileExists -StoreAs bool -Tenant $tenant - } + if ($Settings.NotLocalAdmin -eq $true) { $userType = 'Standard' } else { $userType = 'Administrator' } + if ($Settings.SelfDeployingMode -eq $true) { $DeploymentMode = 'shared' } else { $DeploymentMode = 'singleUser' } + if ($Settings.AllowWhiteGlove -eq $true) {$Settings.HideChangeAccount = $true} - if ($Settings.alert -eq $true) { - if ($ProfileExists) { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Autopilot profile '$($settings.DisplayName)' exists" -sev Info - } else { - Write-StandardsAlert -message "Autopilot profile '$($settings.DisplayName)' does not exist" -object @{ProfileName = $settings.DisplayName } -tenant $tenant -standardName 'AutopilotProfile' -standardId $Settings.standardId - Write-LogMessage -API 'Standards' -tenant $tenant -message "Autopilot profile '$($settings.DisplayName)' does not exist" -sev Info - } + $StateIsCorrect = ($CurrentConfig.displayName -eq $Settings.DisplayName) -and + ($CurrentConfig.description -eq $Settings.Description) -and + ($CurrentConfig.deviceNameTemplate -eq $Settings.DeviceNameTemplate) -and + ([string]::IsNullOrWhiteSpace($CurrentConfig.language) -and [string]::IsNullOrWhiteSpace($Settings.Languages.value) -or $CurrentConfig.language -eq $Settings.Languages.value) -and + ($CurrentConfig.enableWhiteGlove -eq $Settings.AllowWhiteGlove) -and + ($CurrentConfig.extractHardwareHash -eq $Settings.CollectHash) -and + ($CurrentConfig.outOfBoxExperienceSetting.deviceUsageType -eq $DeploymentMode) -and + ($CurrentConfig.outOfBoxExperienceSetting.escapeLinkHidden -eq $Settings.HideChangeAccount) -and + ($CurrentConfig.outOfBoxExperienceSetting.privacySettingsHidden -eq $Settings.HidePrivacy) -and + ($CurrentConfig.outOfBoxExperienceSetting.eulaHidden -eq $Settings.HideTerms) -and + ($CurrentConfig.outOfBoxExperienceSetting.userType -eq $userType) -and + ($CurrentConfig.outOfBoxExperienceSetting.keyboardSelectionPageSkipped -eq $Settings.AutoKeyboard) + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to check Autopilot profile: $ErrorMessage" -sev Error + $StateIsCorrect = $false } + # Remediate if the state is not correct If ($Settings.remediate -eq $true) { - if ($ProfileExists) { - Write-LogMessage -API 'Standards' -tenant $tenant -message "Autopilot profile '$($settings.DisplayName)' already exists" -sev Info + if ($StateIsCorrect -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Autopilot profile '$($Settings.DisplayName)' already exists" -sev Info } else { try { - Write-Host $($settings | ConvertTo-Json -Depth 100) - if ($settings.NotLocalAdmin -eq $true) { $usertype = 'Standard' } else { $usertype = 'Administrator' } - $DeploymentMode = if ($settings.DeploymentMode -eq 'true') { 'shared' } else { 'singleUser' } - $Parameters = @{ - tenantFilter = $tenant - displayname = $settings.DisplayName - description = $settings.Description - usertype = $usertype + tenantFilter = $Tenant + displayName = $Settings.DisplayName + description = $Settings.Description + userType = $userType DeploymentMode = $DeploymentMode - assignto = $settings.AssignToAllDevices + AssignTo = $Settings.AssignToAllDevices devicenameTemplate = $Settings.DeviceNameTemplate allowWhiteGlove = $Settings.AllowWhiteGlove CollectHash = $Settings.CollectHash @@ -94,12 +94,33 @@ function Invoke-CIPPStandardAutopilotProfile { } Set-CIPPDefaultAPDeploymentProfile @Parameters - Write-LogMessage -API 'Standards' -tenant $tenant -message "Created Autopilot profile '$($settings.DisplayName)'" -sev Info + if ($null -eq $CurrentConfig) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created Autopilot profile '$($Settings.DisplayName)'" -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated Autopilot profile '$($Settings.DisplayName)'" -sev Info + } } catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to create Autopilot profile: $ErrorMessage" -sev 'Error' + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create Autopilot profile: $ErrorMessage" -sev 'Error' throw $ErrorMessage } } } + + # Report + If ($Settings.report -eq $true) { + $FieldValue = $StateIsCorrect -eq $true ? $true : $CurrentConfig + Set-CIPPStandardsCompareField -FieldName 'standards.AutopilotProfile' -FieldValue $FieldValue -TenantFilter $Tenant + Add-CIPPBPAField -FieldName 'AutopilotProfile' -FieldValue [bool]$StateIsCorrect -StoreAs bool -Tenant $Tenant + } + + # Alert + If ($Settings.alert -eq $true) { + If ($StateIsCorrect -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Autopilot profile '$($Settings.DisplayName)' exists" -sev Info + } else { + Write-StandardsAlert -message "Autopilot profile '$($Settings.DisplayName)' do not match expected configuration" -object $CurrentConfig -tenant $Tenant -standardName 'AutopilotProfile' -standardId $Settings.standardId + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Autopilot profile '$($Settings.DisplayName)' do not match expected configuration" -sev Info + } + } } From 408e4f66ac13f38c37336f81728bba3a14fbdeb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 24 Apr 2025 21:54:18 +0200 Subject: [PATCH 34/56] Fix: variable casing and improve logging messages in Invoke-RemoveStandardTemplate function --- .../Standards/Invoke-RemoveStandardTemplate.ps1 | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-RemoveStandardTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-RemoveStandardTemplate.ps1 index 74249b5bdec4..8230ebeb6932 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-RemoveStandardTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-RemoveStandardTemplate.ps1 @@ -12,22 +12,23 @@ Function Invoke-RemoveStandardTemplate { $APIName = $Request.Params.CIPPEndpoint $Headers = $Request.Headers - Write-LogMessage -Headers $Headers -API $APINAME -message 'Accessed this API' -Sev 'Debug' + Write-LogMessage -Headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' # Interact with query parameters or the body of the request. $ID = $Request.Body.ID ?? $Request.Query.ID try { $Table = Get-CippTable -tablename 'templates' - $Filter = "PartitionKey eq 'StandardsTemplateV2' and RowKey eq '$id'" - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity -Force @Table -Entity $clearRow - $Result = "Removed Standards Template named $($ClearRow.name) and id $($id)" - Write-LogMessage -Headers $Headers -API $APINAME -message $Result -Sev 'Info' + $Filter = "PartitionKey eq 'StandardsTemplateV2' and RowKey eq '$ID'" + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey, JSON + $TemplateName = (ConvertFrom-Json -InputObject $ClearRow.JSON).templateName + Remove-AzDataTableEntity -Force @Table -Entity $ClearRow + $Result = "Removed Standards Template named: '$($TemplateName)' with id: $($ID)" + Write-LogMessage -Headers $Headers -API $APIName -message $Result -Sev Info $StatusCode = [HttpStatusCode]::OK } catch { $ErrorMessage = Get-CippException -Exception $_ - $Result = "Failed to remove Standards template $ID. $($ErrorMessage.NormalizedError)" - Write-LogMessage -Headers $Headers -API $APINAME -message $Result -Sev 'Error' -LogData $ErrorMessage + $Result = "Failed to remove Standards template: $TemplateName with id: $ID. Error: $($ErrorMessage.NormalizedError)" + Write-LogMessage -Headers $Headers -API $APIName -message $Result -Sev Error -LogData $ErrorMessage $StatusCode = [HttpStatusCode]::InternalServerError } From fc499e3fd3169f5d6122134b8f2826a3e92d7e71 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Apr 2025 17:15:23 -0400 Subject: [PATCH 35/56] Update Get-CIPPAuthentication.ps1 --- Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 index e86fd4b2e128..979de65b3976 100644 --- a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 @@ -19,9 +19,19 @@ function Get-CIPPAuthentication { } } } else { + Write-Information 'Connecting to Azure' Connect-AzAccount -Identity $SubscriptionId = $env:WEBSITE_OWNER_NAME -split '\+' | Select-Object -First 1 - $null = Set-AzContext -SubscriptionId $SubscriptionId + try { + $Context = Get-AzContext + if ($Context.Subscription.Id -ne $SubscriptionId) { + Write-Information "Setting context to subscription $SubscriptionId" + $null = Set-AzContext -SubscriptionId $SubscriptionId + } + } catch { + Write-Information "ERROR: Could not set context to subscription $SubscriptionId." + } + $keyvaultname = ($env:WEBSITE_DEPLOYMENT_ID -split '-')[0] $Variables | ForEach-Object { Set-Item -Path env:$_ -Value (Get-AzKeyVaultSecret -VaultName $keyvaultname -Name $_ -AsPlainText -ErrorAction Stop) -Force From b7b5ac2a2e0b0ff04b2b208740cee420c3f4ce0d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 23 Apr 2025 13:26:15 -0400 Subject: [PATCH 36/56] add graph preset validation --- .../Tools/Invoke-ExecGraphExplorerPreset.ps1 | 30 +++++++++++++++++-- .../Invoke-ListGraphExplorerPresets.ps1 | 8 +++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 index 4c87012301e0..4b068585ffc6 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ExecGraphExplorerPreset { +function Invoke-ExecGraphExplorerPreset { <# .FUNCTIONALITY Entrypoint @@ -22,7 +22,7 @@ Function Invoke-ExecGraphExplorerPreset { switch ($Action) { 'Copy' { - $Id = $Request.Body.preset.id ? $Request.Body.preset.id: (New-Guid).Guid + $Id = $Request.Body.preset.id ? $Request.Body.preset.id : (New-Guid).Guid } 'Save' { $Id = $Request.Body.preset.id @@ -42,6 +42,32 @@ Function Invoke-ExecGraphExplorerPreset { $params.'$select' = ($params.'$select').value -join ',' } + if (!$Request.Body.preset.name) { + $Message = 'Error: Preset name is required' + $StatusCode = [HttpStatusCode]::BadRequest + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @{ + Results = $Message + Success = $false + } + }) + return + } + + if (!$Request.Body.preset.endpoint) { + $Message = 'Error: Preset endpoint is required' + $StatusCode = [HttpStatusCode]::BadRequest + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @{ + Results = $Message + Success = $false + } + }) + return + } + $Preset = [PSCustomObject]@{ PartitionKey = 'Preset' RowKey = [string]$Id diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 index ee84bc74bc19..7f372f03a1e7 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGraphExplorerPresets.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ListGraphExplorerPresets { +function Invoke-ListGraphExplorerPresets { <# .FUNCTIONALITY Entrypoint,AnyTenant @@ -19,14 +19,14 @@ Function Invoke-ListGraphExplorerPresets { try { $Table = Get-CIPPTable -TableName 'GraphPresets' - $Presets = Get-CIPPAzDataTableEntity @Table -Filter "Owner eq '$Username' or IsShared eq true" | Sort-Object -Property name + $Presets = Get-CIPPAzDataTableEntity @Table | Where-Object { $Username -eq $_.Owner -or $_.IsShared -eq $true } | Sort-Object -Property name $Results = foreach ($Preset in $Presets) { [PSCustomObject]@{ id = $Preset.Id name = $Preset.name IsShared = $Preset.IsShared IsMyPreset = $Preset.Owner -eq $Username - params = ConvertFrom-Json -InputObject $Preset.Params + params = (ConvertFrom-Json -InputObject $Preset.Params) } } @@ -35,6 +35,8 @@ Function Invoke-ListGraphExplorerPresets { $Results = $Results | Where-Object { ($_.params.endpoint -replace '^/', '') -eq $Endpoint } } } catch { + Write-Warning "Could not list presets. $($_.Exception.Message)" + Write-Information $_.InvocationInfo.PositionMessage $Results = @() } # Associate values to output bindings by calling 'Push-OutputBinding'. From f24f5f660006e354b34fef51104b3e85423cb75f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Apr 2025 17:51:22 -0400 Subject: [PATCH 37/56] Update Get-CIPPAuthentication.ps1 --- Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 index 979de65b3976..b24f460b8d62 100644 --- a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 @@ -24,6 +24,7 @@ function Get-CIPPAuthentication { $SubscriptionId = $env:WEBSITE_OWNER_NAME -split '\+' | Select-Object -First 1 try { $Context = Get-AzContext + Write-Information "Current context: $($Context.Subscription.Name)" if ($Context.Subscription.Id -ne $SubscriptionId) { Write-Information "Setting context to subscription $SubscriptionId" $null = Set-AzContext -SubscriptionId $SubscriptionId From 156f9665770bbf495a30f07a1d93dd801f99d681 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Apr 2025 17:57:12 -0400 Subject: [PATCH 38/56] Update Get-CIPPAuthentication.ps1 --- Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 index b24f460b8d62..787dce5877bb 100644 --- a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 @@ -24,7 +24,7 @@ function Get-CIPPAuthentication { $SubscriptionId = $env:WEBSITE_OWNER_NAME -split '\+' | Select-Object -First 1 try { $Context = Get-AzContext - Write-Information "Current context: $($Context.Subscription.Name)" + Write-Information "Current context: $($Context | ConvertTo-Json)" if ($Context.Subscription.Id -ne $SubscriptionId) { Write-Information "Setting context to subscription $SubscriptionId" $null = Set-AzContext -SubscriptionId $SubscriptionId From bd9a8eaa6d31ebfcd8c5056df444f2a42d88711f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Apr 2025 21:12:09 -0400 Subject: [PATCH 39/56] fix for null subscription --- Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 | 10 ++++++---- .../Public/Extension Functions/Get-ExtensionAPIKey.ps1 | 8 +++++++- .../Public/Extension Functions/Set-ExtensionAPIKey.ps1 | 8 +++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 index 787dce5877bb..ce8a064639dc 100644 --- a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 @@ -24,10 +24,12 @@ function Get-CIPPAuthentication { $SubscriptionId = $env:WEBSITE_OWNER_NAME -split '\+' | Select-Object -First 1 try { $Context = Get-AzContext - Write-Information "Current context: $($Context | ConvertTo-Json)" - if ($Context.Subscription.Id -ne $SubscriptionId) { - Write-Information "Setting context to subscription $SubscriptionId" - $null = Set-AzContext -SubscriptionId $SubscriptionId + if ($Context.Subscription) { + #Write-Information "Current context: $($Context | ConvertTo-Json)" + if ($Context.Subscription.Id -ne $SubscriptionId) { + Write-Information "Setting context to subscription $SubscriptionId" + $null = Set-AzContext -SubscriptionId $SubscriptionId + } } } catch { Write-Information "ERROR: Could not set context to subscription $SubscriptionId." diff --git a/Modules/CippExtensions/Public/Extension Functions/Get-ExtensionAPIKey.ps1 b/Modules/CippExtensions/Public/Extension Functions/Get-ExtensionAPIKey.ps1 index f9f7cc2b7901..856a9a4be1ef 100644 --- a/Modules/CippExtensions/Public/Extension Functions/Get-ExtensionAPIKey.ps1 +++ b/Modules/CippExtensions/Public/Extension Functions/Get-ExtensionAPIKey.ps1 @@ -23,7 +23,13 @@ function Get-ExtensionAPIKey { $keyvaultname = ($env:WEBSITE_DEPLOYMENT_ID -split '-')[0] $null = Connect-AzAccount -Identity $SubscriptionId = $env:WEBSITE_OWNER_NAME -split '\+' | Select-Object -First 1 - $null = Set-AzContext -SubscriptionId $SubscriptionId + $Context = Get-AzContext + if ($Context.Subscription) { + if ($Context.Subscription.Id -ne $SubscriptionId) { + Write-Information "Setting context to subscription $SubscriptionId" + $null = Set-AzContext -SubscriptionId $SubscriptionId + } + } $APIKey = (Get-AzKeyVaultSecret -VaultName $keyvaultname -Name $Extension -AsPlainText) } Set-Item -Path "env:$Var" -Value $APIKey -Force -ErrorAction SilentlyContinue diff --git a/Modules/CippExtensions/Public/Extension Functions/Set-ExtensionAPIKey.ps1 b/Modules/CippExtensions/Public/Extension Functions/Set-ExtensionAPIKey.ps1 index 191b4182e288..6e6dc0ed6344 100644 --- a/Modules/CippExtensions/Public/Extension Functions/Set-ExtensionAPIKey.ps1 +++ b/Modules/CippExtensions/Public/Extension Functions/Set-ExtensionAPIKey.ps1 @@ -26,7 +26,13 @@ function Set-ExtensionAPIKey { $keyvaultname = ($env:WEBSITE_DEPLOYMENT_ID -split '-')[0] $null = Connect-AzAccount -Identity $SubscriptionId = $env:WEBSITE_OWNER_NAME -split '\+' | Select-Object -First 1 - $null = Set-AzContext -SubscriptionId $SubscriptionId + $Context = Get-AzContext + if ($Context.Subscription) { + if ($Context.Subscription.Id -ne $SubscriptionId) { + Write-Information "Setting context to subscription $SubscriptionId" + $null = Set-AzContext -SubscriptionId $SubscriptionId + } + } $null = Set-AzKeyVaultSecret -VaultName $keyvaultname -Name $Extension -SecretValue (ConvertTo-SecureString -AsPlainText -Force -String $APIKey) } Set-Item -Path "env:$Var" -Value $APIKey -Force -ErrorAction SilentlyContinue From 456486b33bf469782f323055a87b0a24b8310a64 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Apr 2025 21:24:56 -0400 Subject: [PATCH 40/56] Update tenant refresh functionality --- .../Public/GraphHelper/Get-Tenants.ps1 | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 index 5e7e51cb961d..909516c33caa 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 @@ -104,8 +104,29 @@ function Get-Tenants { Add-CIPPAzDataTableEntity @TenantsTable -Entity $ExistingTenantInfo -Force | Out-Null } - if ($ExistingTenantInfo -and $ExistingTenantInfo.RequiresRefresh -eq $false -and $ExistingTenantInfo.displayName -eq $LatestRelationship.displayName) { + if ($ExistingTenantInfo -and $ExistingTenantInfo.RequiresRefresh -eq $false -and ($ExistingTenantInfo.displayName -eq $LatestRelationship.displayName -or $ExistingTenantInfo.displayName -eq $Alias)) { Write-Host 'Existing tenant found. We already have it cached, skipping.' + + $DisplayNameUpdated = $false + if (![string]::IsNullOrEmpty($Alias.Value)) { + if ($Alias.Value -ne $ExistingTenantInfo.displayName) { + Write-Host "Alias found for $($_.Name)." + $ExistingTenantInfo.displayName = $Alias.Value + $DisplayNameUpdated = $true + } + } else { + if ($LatestRelationship.displayName -ne $ExistingTenantInfo.displayName) { + Write-Host "Display name changed from relationship, updating." + $ExistingTenantInfo.displayName = $LatestRelationship.displayName + $DisplayNameUpdated = $true + } + } + + if ($DisplayNameUpdated) { + $ExistingTenantInfo.displayName = $LatestRelationship.displayName + Add-CIPPAzDataTableEntity @TenantsTable -Entity $ExistingTenantInfo -Force | Out-Null + } + $ExistingTenantInfo return } @@ -136,8 +157,8 @@ function Get-Tenants { } Write-Host 'finished getting domain' - if ($Aliases.PartitionKey -contains $_.Name -and ![string]::IsNullOrEmpty($Alias)) { - $Alias = $Aliases | Where-Object { $_.PartitionKey -eq $_.Name } + if (![string]::IsNullOrEmpty($Alias.Value)) { + Write-Host "Alias found for $($_.Name)." $displayName = $Alias.Value } else { $displayName = $LatestRelationship.displayName From ad2d8e1ab6fcab7e6187ab02b0c7b41a3f443d70 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Apr 2025 21:31:01 -0400 Subject: [PATCH 41/56] tweak get tenant alias update --- Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 index 909516c33caa..ff0a0809bb8b 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 @@ -108,10 +108,10 @@ function Get-Tenants { Write-Host 'Existing tenant found. We already have it cached, skipping.' $DisplayNameUpdated = $false - if (![string]::IsNullOrEmpty($Alias.Value)) { + if (![string]::IsNullOrEmpty($Alias)) { if ($Alias.Value -ne $ExistingTenantInfo.displayName) { Write-Host "Alias found for $($_.Name)." - $ExistingTenantInfo.displayName = $Alias.Value + $ExistingTenantInfo.displayName = $Alias $DisplayNameUpdated = $true } } else { @@ -157,9 +157,9 @@ function Get-Tenants { } Write-Host 'finished getting domain' - if (![string]::IsNullOrEmpty($Alias.Value)) { + if (![string]::IsNullOrEmpty($Alias)) { Write-Host "Alias found for $($_.Name)." - $displayName = $Alias.Value + $displayName = $Alias } else { $displayName = $LatestRelationship.displayName } From abfeee8bf2d3b8e7b3e822e08609507026fd2228 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Apr 2025 21:39:18 -0400 Subject: [PATCH 42/56] more tweaks --- Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 index ff0a0809bb8b..654cd1a355d7 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 @@ -75,7 +75,6 @@ function Get-Tenants { if (($BuildRequired -or $TriggerRefresh.IsPresent) -and $PartnerTenantState.state -ne 'owntenant') { # Get TenantProperties table $PropertiesTable = Get-CippTable -TableName 'TenantProperties' - $Aliases = Get-CIPPAzDataTableEntity @PropertiesTable -Filter "RowKey eq 'Alias'" #get the full list of tenants $GDAPRelationships = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships?`$filter=status eq 'active' and not startsWith(displayName,'MLT_')$RelationshipFilter&`$select=customer,autoExtendDuration,endDateTime&`$top=300" -NoAuthCheck:$true @@ -95,7 +94,7 @@ function Get-Tenants { # Write-Host "Processing $($_.Name), $($_.displayName) to add to tenant list." $ExistingTenantInfo = Get-CIPPAzDataTableEntity @TenantsTable -Filter "PartitionKey eq 'Tenants' and RowKey eq '$($_.Name)'" - $Alias = ($Aliases | Where-Object { $_.PartitionKey -eq $_.Name }).Value + $Alias = (Get-AzDataTableEntity @PropertiesTable -Filter "PartitionKey eq '$($_.Name)' and RowKey eq 'Alias'").Value if ($TriggerRefresh.IsPresent -and $ExistingTenantInfo.customerId) { # Reset error count @@ -116,7 +115,7 @@ function Get-Tenants { } } else { if ($LatestRelationship.displayName -ne $ExistingTenantInfo.displayName) { - Write-Host "Display name changed from relationship, updating." + Write-Host 'Display name changed from relationship, updating.' $ExistingTenantInfo.displayName = $LatestRelationship.displayName $DisplayNameUpdated = $true } From 10e7b1b2332b922464a8704d9cd6f16f7133c09e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Apr 2025 22:10:41 -0400 Subject: [PATCH 43/56] logging --- Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 index 654cd1a355d7..48ba5d6afb34 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 @@ -96,6 +96,10 @@ function Get-Tenants { $Alias = (Get-AzDataTableEntity @PropertiesTable -Filter "PartitionKey eq '$($_.Name)' and RowKey eq 'Alias'").Value + if ($Alias) { + Write-Host "Alias found for $($_.Name) - $Alias." + } + if ($TriggerRefresh.IsPresent -and $ExistingTenantInfo.customerId) { # Reset error count Write-Host "Resetting error count for $($_.Name)" @@ -103,12 +107,14 @@ function Get-Tenants { Add-CIPPAzDataTableEntity @TenantsTable -Entity $ExistingTenantInfo -Force | Out-Null } + $LatestRelationship = $_.Group | Sort-Object -Property relationshipEnd | Select-Object -Last 1 + if ($ExistingTenantInfo -and $ExistingTenantInfo.RequiresRefresh -eq $false -and ($ExistingTenantInfo.displayName -eq $LatestRelationship.displayName -or $ExistingTenantInfo.displayName -eq $Alias)) { Write-Host 'Existing tenant found. We already have it cached, skipping.' $DisplayNameUpdated = $false if (![string]::IsNullOrEmpty($Alias)) { - if ($Alias.Value -ne $ExistingTenantInfo.displayName) { + if ($Alias -ne $ExistingTenantInfo.displayName) { Write-Host "Alias found for $($_.Name)." $ExistingTenantInfo.displayName = $Alias $DisplayNameUpdated = $true @@ -129,7 +135,7 @@ function Get-Tenants { $ExistingTenantInfo return } - $LatestRelationship = $_.Group | Sort-Object -Property relationshipEnd | Select-Object -Last 1 + $AutoExtend = ($_.Group | Where-Object { $_.autoExtend -eq $true } | Measure-Object).Count -gt 0 if (!$SkipDomains.IsPresent) { try { @@ -157,7 +163,7 @@ function Get-Tenants { Write-Host 'finished getting domain' if (![string]::IsNullOrEmpty($Alias)) { - Write-Host "Alias found for $($_.Name)." + Write-Information "Setting display name to $Alias." $displayName = $Alias } else { $displayName = $LatestRelationship.displayName From 7e89ee66823238f331f787cc5ffc3bf985a8bcc1 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Apr 2025 22:19:42 -0400 Subject: [PATCH 44/56] Update Get-Tenants.ps1 --- Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 index 48ba5d6afb34..d9423da54ce4 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 @@ -107,8 +107,6 @@ function Get-Tenants { Add-CIPPAzDataTableEntity @TenantsTable -Entity $ExistingTenantInfo -Force | Out-Null } - $LatestRelationship = $_.Group | Sort-Object -Property relationshipEnd | Select-Object -Last 1 - if ($ExistingTenantInfo -and $ExistingTenantInfo.RequiresRefresh -eq $false -and ($ExistingTenantInfo.displayName -eq $LatestRelationship.displayName -or $ExistingTenantInfo.displayName -eq $Alias)) { Write-Host 'Existing tenant found. We already have it cached, skipping.' @@ -135,7 +133,7 @@ function Get-Tenants { $ExistingTenantInfo return } - + $LatestRelationship = $_.Group | Sort-Object -Property relationshipEnd | Select-Object -Last 1 $AutoExtend = ($_.Group | Where-Object { $_.autoExtend -eq $true } | Measure-Object).Count -gt 0 if (!$SkipDomains.IsPresent) { try { From fd0495b0a25babff1eaff29174812da5d4a7fbd8 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sat, 26 Apr 2025 11:24:13 -0400 Subject: [PATCH 45/56] fix delete standard template action --- .../Standards/Invoke-RemoveStandardTemplate.ps1 | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-RemoveStandardTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-RemoveStandardTemplate.ps1 index 74249b5bdec4..fbe7fff5f520 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-RemoveStandardTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-RemoveStandardTemplate.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-RemoveStandardTemplate { +function Invoke-RemoveStandardTemplate { <# .FUNCTIONALITY Entrypoint,AnyTenant @@ -19,9 +19,20 @@ Function Invoke-RemoveStandardTemplate { try { $Table = Get-CippTable -tablename 'templates' $Filter = "PartitionKey eq 'StandardsTemplateV2' and RowKey eq '$id'" - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey, JSON + if (!$ClearRow) { + $Result = "Standards template with id $ID not found" + Write-LogMessage -Headers $Headers -API $APINAME -message $Result -Sev 'Error' + $StatusCode = [HttpStatusCode]::NotFound + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @{'Results' = $Result } + }) + return + } + $Template = $ClearRow.JSON | ConvertFrom-Json Remove-AzDataTableEntity -Force @Table -Entity $clearRow - $Result = "Removed Standards Template named $($ClearRow.name) and id $($id)" + $Result = "Removed Standards Template named $($Template.templateName) ($($id))" Write-LogMessage -Headers $Headers -API $APINAME -message $Result -Sev 'Info' $StatusCode = [HttpStatusCode]::OK } catch { From d6e7e7502c32992619e48226907a345d7e4c6f68 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Sun, 27 Apr 2025 19:38:22 +0200 Subject: [PATCH 46/56] value --- .../HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 index f9c6205aaac4..f6e208d59837 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 @@ -11,7 +11,7 @@ function Invoke-ExecBPA { $ConfigTable = Get-CIPPTable -tablename Config $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" - $TenantFilter = $Request.Query.tenantFilter ? $Request.Query.tenantFilter.value : $Request.Body.tenantfilter + $TenantFilter = $Request.Query.tenantFilter ? $Request.Query.tenantFilter.value : $Request.Body.tenantfilter.value if ($Config -and $Config.state -eq $true) { if ($env:CIPP_PROCESSOR -ne 'true') { From 233e0ca53e4e20abec566702b62590f4d82d6894 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Sun, 27 Apr 2025 20:10:50 +0200 Subject: [PATCH 47/56] fix sherweb for edit user --- .../Administration/Users/Invoke-EditUser.ps1 | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 index 22da8a7ff36b..05350c840529 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 @@ -88,22 +88,46 @@ Function Invoke-EditUser { try { if ($licenses -or $UserObj.removeLicenses) { - $CurrentLicenses = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)" -tenantid $UserObj.tenantFilter - #if the list of skuIds in $CurrentLicenses.assignedLicenses is EXACTLY the same as $licenses, we don't need to do anything, but the order in both can be different. - if (($CurrentLicenses.assignedLicenses.skuId -join ',') -eq ($licenses -join ',') -and $UserObj.removeLicenses -eq $false) { - Write-Host "$($CurrentLicenses.assignedLicenses.skuId -join ',') $(($licenses -join ','))" - $null = $results.Add( 'Success. User license is already correct.' ) + if ($UserObj.sherwebLicense) { + $License = Set-SherwebSubscription -TenantFilter $UserObj.tenantFilter -SKU $UserObj.sherwebLicense -Add 1 + $null = $results.Add('Added Sherweb License, scheduling assignment') + $taskObject = [PSCustomObject]@{ + TenantFilter = $UserObj.tenantFilter + Name = "Assign License: $Username" + Command = @{ + value = 'Set-CIPPUserLicense' + } + Parameters = [pscustomobject]@{ + userId = $UserObj.id + APIName = 'Sherweb License Assignment' + AddLicenses = $licenses + } + ScheduledTime = $Request.Body.scheduled.date + PostExecution = @{ + Webhook = [bool]$Request.Body.PostExecution.webhook + Email = [bool]$Request.Body.PostExecution.email + PSA = [bool]$Request.Body.PostExecution.psa + } + } + Add-CIPPScheduledTask -Task $taskObject -hidden $false -Headers $Headers } else { - if ($UserObj.removeLicenses) { - $licResults = Set-CIPPUserLicense -UserId $UserObj.id -TenantFilter $UserObj.tenantFilter -RemoveLicenses $CurrentLicenses.assignedLicenses.skuId -Headers $Request.Headers - $null = $results.Add($licResults) + $CurrentLicenses = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)" -tenantid $UserObj.tenantFilter + #if the list of skuIds in $CurrentLicenses.assignedLicenses is EXACTLY the same as $licenses, we don't need to do anything, but the order in both can be different. + if (($CurrentLicenses.assignedLicenses.skuId -join ',') -eq ($licenses -join ',') -and $UserObj.removeLicenses -eq $false) { + Write-Host "$($CurrentLicenses.assignedLicenses.skuId -join ',') $(($licenses -join ','))" + $null = $results.Add( 'Success. User license is already correct.' ) } else { - #Remove all objects from $CurrentLicenses.assignedLicenses.skuId that are in $licenses - $RemoveLicenses = $CurrentLicenses.assignedLicenses.skuId | Where-Object { $_ -notin $licenses } - $licResults = Set-CIPPUserLicense -UserId $UserObj.id -TenantFilter $UserObj.tenantFilter -RemoveLicenses $RemoveLicenses -AddLicenses $licenses -Headers $Request.headers - $null = $results.Add($licResults) - } + if ($UserObj.removeLicenses) { + $licResults = Set-CIPPUserLicense -UserId $UserObj.id -TenantFilter $UserObj.tenantFilter -RemoveLicenses $CurrentLicenses.assignedLicenses.skuId -Headers $Request.Headers + $null = $results.Add($licResults) + } else { + #Remove all objects from $CurrentLicenses.assignedLicenses.skuId that are in $licenses + $RemoveLicenses = $CurrentLicenses.assignedLicenses.skuId | Where-Object { $_ -notin $licenses } + $licResults = Set-CIPPUserLicense -UserId $UserObj.id -TenantFilter $UserObj.tenantFilter -RemoveLicenses $RemoveLicenses -AddLicenses $licenses -Headers $Request.headers + $null = $results.Add($licResults) + } + } } } From cd42c9d9d7ca92af20ffba84cac43b504c8959d9 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Sun, 27 Apr 2025 20:13:38 +0200 Subject: [PATCH 48/56] comment --- .../Identity/Administration/Users/Invoke-EditUser.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 index 05350c840529..196259ceae6b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 @@ -102,7 +102,7 @@ Function Invoke-EditUser { APIName = 'Sherweb License Assignment' AddLicenses = $licenses } - ScheduledTime = $Request.Body.scheduled.date + ScheduledTime = 0 #right now, which is in the next 15 minutes and should cover most cases. PostExecution = @{ Webhook = [bool]$Request.Body.PostExecution.webhook Email = [bool]$Request.Body.PostExecution.email From 2a3c576ea783d4db041f2ce745861cf47f8b3b36 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Sun, 27 Apr 2025 20:43:30 +0200 Subject: [PATCH 49/56] .vlaue fix --- .../Identity/Administration/Users/Invoke-EditUser.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 index 196259ceae6b..ee3d32ec66ff 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 @@ -88,8 +88,8 @@ Function Invoke-EditUser { try { if ($licenses -or $UserObj.removeLicenses) { - if ($UserObj.sherwebLicense) { - $License = Set-SherwebSubscription -TenantFilter $UserObj.tenantFilter -SKU $UserObj.sherwebLicense -Add 1 + if ($UserObj.sherwebLicense.value) { + $License = Set-SherwebSubscription -TenantFilter $UserObj.tenantFilter -SKU $UserObj.sherwebLicense.value -Add 1 $null = $results.Add('Added Sherweb License, scheduling assignment') $taskObject = [PSCustomObject]@{ TenantFilter = $UserObj.tenantFilter From c5f49097f32aac5063cadd43f00278915f3f0b1c Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Sun, 27 Apr 2025 21:21:00 +0200 Subject: [PATCH 50/56] fixes value --- .../Identity/Administration/Users/Invoke-EditUser.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 index ee3d32ec66ff..c092fb305baf 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 @@ -93,7 +93,7 @@ Function Invoke-EditUser { $null = $results.Add('Added Sherweb License, scheduling assignment') $taskObject = [PSCustomObject]@{ TenantFilter = $UserObj.tenantFilter - Name = "Assign License: $Username" + Name = "Assign License: $UserPrincipalName" Command = @{ value = 'Set-CIPPUserLicense' } From 62c23b5dd83027bd5e1120a3518420e5c62f45c0 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Sun, 27 Apr 2025 21:28:06 +0200 Subject: [PATCH 51/56] choco app casing --- .../Activity Triggers/Applications/Push-UploadApplication.ps1 | 2 +- .../HTTP Functions/Endpoint/Applications/Invoke-AddChocoApp.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Applications/Push-UploadApplication.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Applications/Push-UploadApplication.ps1 index 3fb3c46d482b..0c0efc07e2ff 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Applications/Push-UploadApplication.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Applications/Push-UploadApplication.ps1 @@ -26,7 +26,7 @@ function Push-UploadApplication { $intunewinFilesize = (Get-Item "AddMSPApp\$($ChocoApp.MSPAppName).intunewin") $Infile = "AddMSPApp\$($ChocoApp.MSPAppName).intunewin" } else { - [xml]$Intunexml = Get-Content 'AddChocoApp\choco.app.xml' + [xml]$Intunexml = Get-Content 'AddChocoApp\Choco.app.xml' $intunewinFilesize = (Get-Item 'AddChocoApp\IntunePackage.intunewin') $Infile = "AddChocoApp\$($intunexml.ApplicationInfo.FileName)" } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddChocoApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddChocoApp.ps1 index 9c7ed6fd384f..a212a7111946 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddChocoApp.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddChocoApp.ps1 @@ -15,7 +15,7 @@ Function Invoke-AddChocoApp { Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' $ChocoApp = $Request.Body - $intuneBody = Get-Content 'AddChocoApp\choco.app.json' | ConvertFrom-Json + $intuneBody = Get-Content 'AddChocoApp\Choco.app.json' | ConvertFrom-Json $AssignTo = $Request.Body.AssignTo $intuneBody.description = $ChocoApp.description $intuneBody.displayName = $ChocoApp.ApplicationName From 48629a6115fa599c49ddfe4f49acbfce92751dba Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 28 Apr 2025 10:45:46 +0200 Subject: [PATCH 52/56] fixes chocoapp --- .../Activity Triggers/Applications/Push-UploadApplication.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Applications/Push-UploadApplication.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Applications/Push-UploadApplication.ps1 index 0c0efc07e2ff..9abeee5a440e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Applications/Push-UploadApplication.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Applications/Push-UploadApplication.ps1 @@ -26,7 +26,7 @@ function Push-UploadApplication { $intunewinFilesize = (Get-Item "AddMSPApp\$($ChocoApp.MSPAppName).intunewin") $Infile = "AddMSPApp\$($ChocoApp.MSPAppName).intunewin" } else { - [xml]$Intunexml = Get-Content 'AddChocoApp\Choco.app.xml' + [xml]$Intunexml = Get-Content 'AddChocoApp\Choco.App.xml' $intunewinFilesize = (Get-Item 'AddChocoApp\IntunePackage.intunewin') $Infile = "AddChocoApp\$($intunexml.ApplicationInfo.FileName)" } From a3b3ecbbc62677f1bb7a8c74550e0cc93c11a995 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 28 Apr 2025 11:00:41 -0400 Subject: [PATCH 53/56] Update Invoke-ExecCippReplacemap.ps1 --- .../HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 index 3a129d2ea5bb..e8cfe001d518 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecCippReplacemap.ps1 @@ -10,7 +10,7 @@ function Invoke-ExecCippReplacemap { $Table = Get-CippTable -tablename 'CippReplacemap' $Action = $Request.Query.Action ?? $Request.Body.Action - $customerId = $Request.Query.customerId ?? $Request.Body.customerId + $customerId = $Request.Query.tenantId ?? $Request.Body.tenantId if (!$customerId) { Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ From 7e2baf0a9801d88205566a9ee30687f5cd075d2e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 28 Apr 2025 11:02:41 -0400 Subject: [PATCH 54/56] remove immybot --- .../Endpoint/Applications/Invoke-AddMSPApp.ps1 | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddMSPApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddMSPApp.ps1 index b3d4e927c74c..c7b3b8803d61 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddMSPApp.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-AddMSPApp.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-AddMSPApp { +function Invoke-AddMSPApp { <# .FUNCTIONALITY Entrypoint @@ -37,10 +37,6 @@ Function Invoke-AddMSPApp { $installCommandLine = "powershell.exe -ExecutionPolicy Bypass .\install.ps1 -OrgKey $($InstallParams.Orgkey."$($Tenant.customerId)") -acctkey $($InstallParams.AccountKey)" $uninstallCommandLine = 'powershell.exe -ExecutionPolicy Bypass .\install.ps1 -Uninstall' } - 'Immybot' { - $installCommandLine = "powershell.exe -ExecutionPolicy Bypass .\install.ps1 -url $($InstallParams.ClientURL."$($tenant.customerId)")" - $UninstallCommandLine = 'powershell.exe -ExecutionPolicy Bypass .\uninstall.ps1' - } 'syncro' { $installCommandLine = "powershell.exe -ExecutionPolicy Bypass .\install.ps1 -URL $($InstallParams.ClientURL."$($Tenant.customerId)")" $uninstallCommandLine = 'powershell.exe -ExecutionPolicy Bypass .\uninstall.ps1' From 47c5a7ac4268401a3ae71a997a6ad680f723fa21 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 28 Apr 2025 16:03:05 -0400 Subject: [PATCH 55/56] fix security group template --- .../Standards/Invoke-CIPPStandardGroupTemplate.ps1 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGroupTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGroupTemplate.ps1 index f299e4762242..1b334a8a5f90 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGroupTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGroupTemplate.ps1 @@ -30,7 +30,7 @@ function Invoke-CIPPStandardGroupTemplate { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'GroupTemplate' - If ($Settings.remediate -eq $true) { + if ($Settings.remediate -eq $true) { #Because the list name changed from TemplateList to groupTemplate by someone :@, we'll need to set it back to TemplateList $Settings.groupTemplate ? ($Settings | Add-Member -NotePropertyName 'TemplateList' -NotePropertyValue $Settings.groupTemplate) : $null Write-Host "Settings: $($Settings.TemplateList | ConvertTo-Json)" @@ -42,11 +42,11 @@ function Invoke-CIPPStandardGroupTemplate { $email = if ($groupobj.domain) { "$($groupobj.username)@$($groupobj.domain)" } else { "$($groupobj.username)@$($Tenant)" } $CheckExististing = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/groups?$top=999' -tenantid $tenant | Where-Object -Property displayName -EQ $groupobj.displayname $BodyToship = [pscustomobject] @{ - 'displayName' = $groupobj.Displayname - 'description' = $groupobj.Description - 'mailNickname' = $groupobj.username - mailEnabled = [bool]$false - securityEnabled = [bool]$true + 'displayName' = $groupobj.Displayname + 'description' = $groupobj.Description + 'mailNickname' = $groupobj.username + mailEnabled = [bool]$false + securityEnabled = [bool]$true } if ($groupobj.groupType -eq 'AzureRole') { $BodyToship | Add-Member -NotePropertyName 'isAssignableToRole' -NotePropertyValue $true @@ -58,7 +58,7 @@ function Invoke-CIPPStandardGroupTemplate { } if (!$CheckExististing) { $ActionType = 'create' - if ($groupobj.groupType -in 'Generic', 'azurerole', 'dynamic') { + if ($groupobj.groupType -in 'Generic', 'azurerole', 'dynamic', 'Security') { $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/groups' -tenantid $tenant -type POST -body (ConvertTo-Json -InputObject $BodyToship -Depth 10) -verbose } else { if ($groupobj.groupType -eq 'dynamicdistribution') { From cd24286558277c5b9ecc80623773beb51cf4ba64 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 28 Apr 2025 16:26:01 -0400 Subject: [PATCH 56/56] Update version_latest.txt --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index a5f017a0a348..3488e2954e25 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -7.5.1 +7.5.2