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 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 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 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 diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 index 69889ea73ec2..883413405345 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 @@ -13,9 +13,33 @@ 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[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 { + $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 = [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) + } + } Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData } } catch { 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..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)" } 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 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' } } 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..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 @@ -3,14 +3,14 @@ function Invoke-ExecCippReplacemap { .FUNCTIONALITY Entrypoint .ROLE - CIPP.Extension.ReadWrite + Tenant.Config.ReadWrite #> [CmdletBinding()] param($Request, $TriggerMetadata) $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]@{ 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 } + }) +} 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 } + }) +} 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..b22280ba01b3 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,InPlaceHolds' $ExoRequest = @{ tenantid = $TenantFilter cmdlet = 'Get-Mailbox' @@ -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, @@ -73,7 +73,12 @@ 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, + 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/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 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' 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 } }) } 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/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 index 22da8a7ff36b..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 @@ -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.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 + Name = "Assign License: $UserPrincipalName" + Command = @{ + value = 'Set-CIPPUserLicense' + } + Parameters = [pscustomobject]@{ + userId = $UserObj.id + APIName = 'Sherweb License Assignment' + AddLicenses = $licenses + } + 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 + 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) + } + } } } 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 41832006069f..e1181a6893fc 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 } @@ -177,11 +177,50 @@ 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 ForwardingAddress = $ForwardingAddress 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 @@ -201,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 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..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.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') { 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..0597ca7fb4a3 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 @@ -12,31 +12,29 @@ 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 } - # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode Body = @{'Results' = $Result } }) - - } 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'. diff --git a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 index e86fd4b2e128..ce8a064639dc 100644 --- a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 @@ -19,9 +19,22 @@ 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) { + #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." + } + $keyvaultname = ($env:WEBSITE_DEPLOYMENT_ID -split '-')[0] $Variables | ForEach-Object { Set-Item -Path env:$_ -Value (Get-AzKeyVaultSecret -VaultName $keyvaultname -Name $_ -AsPlainText -ErrorAction Stop) -Force diff --git a/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 b/Modules/CIPPCore/Public/GraphHelper/Get-Tenants.ps1 index 5e7e51cb961d..d9423da54ce4 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,11 @@ 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 ($Alias) { + Write-Host "Alias found for $($_.Name) - $Alias." + } if ($TriggerRefresh.IsPresent -and $ExistingTenantInfo.customerId) { # Reset error count @@ -104,8 +107,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)) { + if ($Alias -ne $ExistingTenantInfo.displayName) { + Write-Host "Alias found for $($_.Name)." + $ExistingTenantInfo.displayName = $Alias + $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,9 +160,9 @@ function Get-Tenants { } Write-Host 'finished getting domain' - if ($Aliases.PartitionKey -contains $_.Name -and ![string]::IsNullOrEmpty($Alias)) { - $Alias = $Aliases | Where-Object { $_.PartitionKey -eq $_.Name } - $displayName = $Alias.Value + if (![string]::IsNullOrEmpty($Alias)) { + Write-Information "Setting display name to $Alias." + $displayName = $Alias } else { $displayName = $LatestRelationship.displayName } 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 } 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/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-CIPPStandardAddDKIM.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 index d3edaf90dacf..ceec4b0ba1b4 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAddDKIM.ps1 @@ -34,8 +34,41 @@ function Invoke-CIPPStandardAddDKIM { #$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 + $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 $_ } @@ -44,10 +77,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 +91,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 +109,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 } } } 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 + } + } } 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 } } - } 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-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') { 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 ee792a946796..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,18 +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 = $CurrentState ? $CurrentState : $false - } 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 } } 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-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 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/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 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") } 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