From ac7c095b08b1c37f6a0f6dcff36ac881e1108262 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sat, 9 Sep 2023 09:50:04 +0100 Subject: [PATCH 01/77] Initial Commit --- ExecExtensionMapping/function.json | 6 + ExecExtensionMapping/run.ps1 | 85 +-- ExecExtensionNinjaOneQueue/function.json | 16 + ExecExtensionNinjaOneQueue/run.ps1 | 12 + ExecExtensionNinjaOneSync/function.json | 22 + ExecExtensionNinjaOneSync/run.ps1 | 38 + MailProviders/Mesh.json | 9 + Modules/CippExtensions/CippExtensions.psd1 | Bin 10120 -> 11136 bytes Modules/CippExtensions/CippExtensions.psm1 | 3 +- .../NinjaOne/Get-NinjaOneFieldMapping.ps1 | 72 ++ .../NinjaOne/Get-NinjaOneOrgMapping.ps1 | 38 + .../NinjaOne/Get-NinjaOneToken.ps1 | 25 + .../NinjaOne/Invoke-NinjaOneOrgMapping.ps1 | 105 +++ .../Invoke-NinjaOneOrgMappingTenant.ps1 | 73 ++ .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 694 ++++++++++++++++++ .../NinjaOne/NinjaOneHelper.ps1 | 163 ++++ .../NinjaOne/Set-NinjaOneFieldMapping.ps1 | 22 + .../NinjaOne/Set-NinjaOneOrgMapping.ps1 | 22 + .../Private/Get-HaloMapping.ps1 | 42 ++ .../Private/Set-HaloMapping.ps1 | 22 + Scheduler_NinjaOne/function.json | 16 + Scheduler_NinjaOne/run.ps1 | 44 ++ 22 files changed, 1483 insertions(+), 46 deletions(-) create mode 100644 ExecExtensionNinjaOneQueue/function.json create mode 100644 ExecExtensionNinjaOneQueue/run.ps1 create mode 100644 ExecExtensionNinjaOneSync/function.json create mode 100644 ExecExtensionNinjaOneSync/run.ps1 create mode 100644 MailProviders/Mesh.json create mode 100644 Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/Get-NinjaOneOrgMapping.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMappingTenant.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/Set-NinjaOneOrgMapping.ps1 create mode 100644 Modules/CippExtensions/Private/Get-HaloMapping.ps1 create mode 100644 Modules/CippExtensions/Private/Set-HaloMapping.ps1 create mode 100644 Scheduler_NinjaOne/function.json create mode 100644 Scheduler_NinjaOne/run.ps1 diff --git a/ExecExtensionMapping/function.json b/ExecExtensionMapping/function.json index 306b0c51e560..73fe27294a9b 100644 --- a/ExecExtensionMapping/function.json +++ b/ExecExtensionMapping/function.json @@ -14,6 +14,12 @@ "type": "http", "direction": "out", "name": "Response" + }, + { + "type": "queue", + "direction": "out", + "name": "NinjaProcess", + "queueName": "NinjaOneQueue" } ] } \ No newline at end of file diff --git a/ExecExtensionMapping/run.ps1 b/ExecExtensionMapping/run.ps1 index b7d24f1a1b32..ee0efb835ef7 100644 --- a/ExecExtensionMapping/run.ps1 +++ b/ExecExtensionMapping/run.ps1 @@ -12,60 +12,55 @@ Write-Host 'PowerShell HTTP trigger function processed a request.' $Table = Get-CIPPTable -TableName CippMapping if ($Request.Query.List) { - #Get available mappings - $Mappings = [pscustomobject]@{} - Get-AzDataTableEntity @Table | ForEach-Object { - $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.HaloPSAName)"; value = "$($_.HaloPSA)" } - } - #Get Available TEnants - $Tenants = Get-Tenants - #Get available halo clients - $Table = Get-CIPPTable -TableName Extensionsconfig - try { - $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json -ErrorAction Stop).HaloPSA - $Token = Get-HaloToken -configuration $Configuration - $i = 1 - $RawHaloClients = do { - $Result = Invoke-RestMethod -Uri "$($Configuration.ResourceURL)/Client?page_no=$i&page_size=999&pageinate=true" -ContentType 'application/json' -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } - $Result.clients | Select-Object * -ExcludeProperty logo - $i++ - $pagecount = [Math]::Ceiling($Result.record_count / 999) - } while ($i -le $pagecount) - } catch { $RawHaloClients = @() } - $HaloClients = $RawHaloClients | ForEach-Object { - [PSCustomObject]@{ - label = $_.name - value = $_.id + switch ($Request.Query.List) { + 'Halo' { + $body = Get-HaloMapping -CIPPMapping $Table } - } - $HaloClients = $RawHaloClients | ForEach-Object { - [PSCustomObject]@{ - name = $_.name - value = "$($_.id)" + + 'NinjaOrgs' { + $Body = Get-NinjaOneOrgMapping -CIPPMapping $Table + } + + 'NinjaFields' { + $Body = Get-NinjaOneFieldMapping -CIPPMapping $Table } } - $MappingObj = [PSCustomObject]@{ - Tenants = @($Tenants) - HaloClients = @($HaloClients) - Mappings = $Mappings - } - $body = $MappingObj } + try { if ($Request.Query.AddMapping) { - foreach ($Mapping in ([pscustomobject]$Request.body.mappings).psobject.properties) { - $AddObject = @{ - PartitionKey = 'Mapping' - RowKey = "$($mapping.name)" - 'HaloPSA' = "$($mapping.value.value)" - 'HaloPSAName' = "$($mapping.value.label)" + switch ($Request.Query.AddMapping) { + 'Halo' { + $body = Set-HaloMapping -CIPPMapping $Table -APIName $APIName -Request $Request + } + + 'NinjaOrgs' { + $Body = Set-NinjaOneOrgMapping -CIPPMapping $Table -APIName $APIName -Request $Request + } + + 'NinjaFields' { + $Body = Set-NinjaOneFieldMapping -CIPPMapping $Table -APIName $APIName -Request $Request + } + } + } +} +catch { + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } +} + +try { + if ($Request.Query.AutoMapping) { + switch ($Request.Query.AutoMapping) { + 'NinjaOrgs' { + Push-OutputBinding -Name NinjaProcess -Value @{'NinjaAction' = 'StartAutoMapping' } + $Body = [pscustomobject]@{'Results' = 'Automapping Request has been queued. Exact name matches will appear first and matches on device names and serials will take longer.' } } - Add-AzDataTableEntity @Table -Entity $AddObject -Force - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Added mapping for $($mapping.name)." -Sev 'Info' + } - $body = [pscustomobject]@{'Results' = 'Successfully edited mapping table.' } } -} catch { +} +catch { Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } } diff --git a/ExecExtensionNinjaOneQueue/function.json b/ExecExtensionNinjaOneQueue/function.json new file mode 100644 index 000000000000..058a42bd6db9 --- /dev/null +++ b/ExecExtensionNinjaOneQueue/function.json @@ -0,0 +1,16 @@ +{ + "bindings": [ + { + "name": "QueueItem", + "type": "queueTrigger", + "direction": "in", + "queueName": "NinjaOneQueue" + }, + { + "type": "queue", + "direction": "out", + "name": "NinjaProcess", + "queueName": "NinjaOneQueue" + } + ] +} diff --git a/ExecExtensionNinjaOneQueue/run.ps1 b/ExecExtensionNinjaOneQueue/run.ps1 new file mode 100644 index 000000000000..bcb1695ae15b --- /dev/null +++ b/ExecExtensionNinjaOneQueue/run.ps1 @@ -0,0 +1,12 @@ +# Input bindings are passed in via param block. +param($QueueItem, $TriggerMetadata) + +# Write out the queue message and metadata to the information log. +Write-Host "PowerShell NinjaOne queue trigger function processed work item: $($QueueItem.NinjaAction)" + + +Switch ($QueueItem.NinjaAction) { + 'StartAutoMapping' { Invoke-NinjaOneOrgMapping } + 'AutoMapTenant' { Invoke-NinjaOneOrgMappingTenant -QueueItem $QueueItem } + 'SyncTenant' { Invoke-NinjaOneTenantSync -QueueItem $QueueItem } +} diff --git a/ExecExtensionNinjaOneSync/function.json b/ExecExtensionNinjaOneSync/function.json new file mode 100644 index 000000000000..9626c8400cc1 --- /dev/null +++ b/ExecExtensionNinjaOneSync/function.json @@ -0,0 +1,22 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "Request", + "methods": ["get", "post"] + }, + { + "type": "http", + "direction": "out", + "name": "Response" + }, + { + "type": "queue", + "direction": "out", + "name": "NinjaProcess", + "queueName": "NinjaOneQueue" + } + ] +} diff --git a/ExecExtensionNinjaOneSync/run.ps1 b/ExecExtensionNinjaOneSync/run.ps1 new file mode 100644 index 000000000000..47559de1acc1 --- /dev/null +++ b/ExecExtensionNinjaOneSync/run.ps1 @@ -0,0 +1,38 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($Request, $TriggerMetadata) + +$APIName = $TriggerMetadata.FunctionName +Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + +$Table = Get-CIPPTable -TableName NinjaOneSettings + +$CIPPMapping = Get-CIPPTable -TableName CippMapping +$Filter = "PartitionKey eq 'NinjaOrgsMapping'" +$TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } + +foreach ($Tenant in $TenantsToProcess) { + Push-OutputBinding -Name NinjaProcess -Value @{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + } + +} + +$AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaLastRunTime' + 'SettingValue' = Get-Date +} + +Add-AzDataTableEntity @Table -Entity $AddObject -Force + +Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' + +$Results = [pscustomobject]@{"Results" = "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" } + +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) -clobber \ No newline at end of file diff --git a/MailProviders/Mesh.json b/MailProviders/Mesh.json new file mode 100644 index 000000000000..3109cc8c4876 --- /dev/null +++ b/MailProviders/Mesh.json @@ -0,0 +1,9 @@ +{ + "Name": "Mesh Email Security", + "_MxComment": "https://docs.emailsecurity.app/help-center/connection-details", + "MxMatch": "emailsecurity.app", + "_SpfComment": "https://docs.emailsecurity.app/help-center/connection-details", + "SpfInclude": "spf1.emailsecurity.app", + "_DkimComment": "No configuration found", + "Selectors": [""] +} \ No newline at end of file diff --git a/Modules/CippExtensions/CippExtensions.psd1 b/Modules/CippExtensions/CippExtensions.psd1 index 76606b0bd22a8e084a56e1e39685f04587c9cb19..cbeb001222fef7974b8737ac0af08388cdee1328 100644 GIT binary patch delta 706 zcmeD1ZwTM8MQn0|n1G-!Ln1=~5N0yuF{Cr7Gw4iqR2EfMX9#9UWhi0LW$*yX=P=|m zU{yDHlbFTiRiX;&eqjAsK(+orb5en15zsX3dJc$k@uR7q+%Kl853>I|Mh z`^tds%Ldx2%Rqu&XL9t00By+w`Z5m~Hqfw|4D^pSy1j0|@JI!QTMG6tFhztfi8=}T z7}^?4~j}0exP?;10x~G?Rg>1H)X1 mo@|C}dcJp(wg#rK-$dN<< delta 16 XcmZn&@9^KSMQpN)xY_1MQu71=Iz
+

Current Licenses

+
" + + $post = "" + + $LicensesParsed = $Licenses | where-object { $_.PrepaidUnits.Enabled -gt 0 } | Select-Object @{N = 'License Name'; E = { $($LicenseLookup.$($_.SkuPartNumber)) } }, @{N = 'Active'; E = { $_.PrepaidUnits.Enabled } }, @{N = 'Consumed'; E = { $_.ConsumedUnits } }, @{N = 'Unused'; E = { $_.PrepaidUnits.Enabled - $_.ConsumedUnits } } + } + + write-verbose "$(Get-Date) - Parsing Devices" + # Get all devices from Intune + $devices = Get-GraphBulkResultByID -Results $TenantResults -ID 'Devices' + + write-verbose "$(Get-Date) - Parsing Device Compliance Polcies" + # Fetch Compliance Policy Status + $DeviceCompliancePolicies = Get-GraphBulkResultByID -Results $TenantResults -ID 'DeviceCompliancePolicies' + + # Get the status of each device for each policy + [System.Collections.Generic.List[PSCustomObject]]$PolicyRequestArray = @() + foreach ($CompliancePolicy in $DeviceCompliancePolicies) { + $PolicyRequestArray.add(@{ + id = $CompliancePolicy.id + method = 'GET' + url = "/deviceManagement/deviceCompliancePolicies/$($CompliancePolicy.id)/deviceStatuses" + }) + } + + try { + $PolicyReturn = New-GraphBulkRequest -Requests $PolicyRequestArray -tenantid $TenantFilter -NoAuthCheck $True + } + catch { + $PolicyReturn = $null + } + + $DeviceComplianceDetails = foreach ($Result in $PolicyReturn) { + [pscustomobject]@{ + ID = ($DeviceCompliancePolicies | where-object { $_.id -eq $Result.id }).id + DisplayName = ($DeviceCompliancePolicies | where-object { $_.id -eq $Result.id }).DisplayName + DeviceStatuses = $Result.body.value + } + } + + write-verbose "$(Get-Date) - Parsing Apps" + # Fetch Apps + $DeviceApps = Get-GraphBulkResultByID -Results $TenantResults -ID 'DeviceApps' + + # Fetch the App status for each device + [System.Collections.Generic.List[PSCustomObject]]$RequestArray = @() + foreach ($InstalledApp in $DeviceApps | where-object { $_.isAssigned -eq $True }) { + $RequestArray.add(@{ + id = $InstalledApp.id + method = 'GET' + url = "/deviceAppManagement/mobileApps/$($InstalledApp.id)/deviceStatuses" + }) + } + + try { + $InstalledAppDetailsReturn = New-GraphBulkRequest -Requests $RequestArray -tenantid $TenantFilter -NoAuthCheck $True + } + catch { + $InstalledAppDetailsReturn = $null + } + $DeviceAppInstallDetails = foreach ($Result in $InstalledAppDetailsReturn) { + [pscustomobject]@{ + ID = $Result.id + DisplayName = ($DeviceApps | where-object { $_.id -eq $Result.id }).DisplayName + InstalledAppDetails = $result.body.value + } + } + + write-verbose "$(Get-Date) - Parsing Groups" + # Fetch Groups + $AllGroups = Get-GraphBulkResultByID -Results $TenantResults -ID 'Groups' + + # Fetch the App status for each device + [System.Collections.Generic.List[PSCustomObject]]$GroupRequestArray = @() + foreach ($Group in $AllGroups) { + $GroupRequestArray.add(@{ + id = $Group.id + method = 'GET' + url = "/groups/$($Group.id)/members" + }) + } + + try { + $GroupMembersReturn = New-GraphBulkRequest -Requests $GroupRequestArray -tenantid $TenantFilter -NoAuthCheck $True + } + catch { + $GroupMembersReturn = $null + } + $Groups = foreach ($Result in $GroupMembersReturn) { + [pscustomobject]@{ + ID = $Result.id + DisplayName = ($AllGroups | where-object { $_.id -eq $Result.id }).DisplayName + Members = $result.body.value + } + } + + write-verbose "$(Get-Date) - Parsing Conditional Access Polcies" + # Fetch and parse conditional access polcies + $AllConditionalAccessPolcies = Get-GraphBulkResultByID -Results $TenantResults -ID 'ConditionalAccess' + + $ConditionalAccessMembers = foreach ($CAPolicy in $AllConditionalAccessPolcies) { + #Setup User Array + [System.Collections.Generic.List[PSCustomObject]]$CAMembers = @() + + # Check for All Include + if ($CAPolicy.conditions.users.includeUsers -contains 'All') { + $Users | foreach-object { $null = $CAMembers.add($_.id) } + } + else { + # Add any specific all users to the array + $CAPolicy.conditions.users.includeUsers | foreach-object { $null = $CAMembers.add($_) } + } + + # Now all members of groups + foreach ($CAIGroup in $CAPolicy.conditions.users.includeGroups) { + foreach ($Member in ($Groups | where-object { $_.id -eq $CAIGroup }).Members) { + $null = $CAMembers.add($Member.id) + } + } + + # Now all members of roles + foreach ($CAIRole in $CAPolicy.conditions.users.includeRoles) { + foreach ($Member in ($Roles | where-object { $_.id -eq $CAIRole }).Members) { + $null = $CAMembers.add($Member.id) + } + } + + # Parse to Unique members + $CAMembers = $CAMembers | select-object -unique + + if ($CAMembers) { + # Now remove excluded users + $CAPolicy.conditions.users.excludeUsers | foreach-object { $null = $CAMembers.remove($_) } + + # Excluded Groups + foreach ($CAEGroup in $CAPolicy.conditions.users.excludeGroups) { + foreach ($Member in ($Groups | where-object { $_.id -eq $CAEGroup }).Members) { + $null = $CAMembers.remove($Member.id) + } + } + + # Excluded Roles + foreach ($CAIRole in $CAPolicy.conditions.users.excludeRoles) { + foreach ($Member in ($Roles | where-object { $_.id -eq $CAERole }).Members) { + $null = $CAMembers.remove($Member.id) + } + } + } + + [pscustomobject]@{ + ID = $CAPolicy.id + DisplayName = $CAPolicy.DisplayName + Members = $CAMembers + } + } + + write-verbose "$(Get-Date) - Fetching One Drive Details" + try { + $OneDriveDetails = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getOneDriveUsageAccountDetail(period='D7')" -tenantid $TenantFilter | convertfrom-csv + } + catch { + $OneDriveDetails = $null + } + + write-verbose "$(Get-Date) - Fetching CAS Mailbox Details" + try { + $CASFull = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true + } + catch { + $CASFull = $null + } + + write-verbose "$(Get-Date) - Fetching Mailbox Details" + try { + $MailboxDetailedFull = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-Mailbox' + } + catch { + $MailboxDetailedFull = $null + } + + write-verbose "$(Get-Date) - Fetching Mailbox Stats" + try { + $MailboxStatsFull = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/reports/getMailboxUsageDetail(period='D7')" -tenantid $TenantFilter | convertfrom-csv + } + catch { + $MailboxStatsFull = $null + } + + + + + $FetchEnd = Get-Date + + Write-Host "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" + + + ############################ Format and Synchronize to NinjaOne ############################ + + if ($MappedFields.TenantLinks) { + + $ManagementLinksData = @( + @{ + Name = 'M365 Admin Portal' + Link = "https://portal.office.com/Partner/BeginClientSession.aspx?CTID=$($customer.CustomerId)&CSDEST=o365admincenter" + Icon = 'fas fa-cogs' + }, + @{ + Name = 'Exchange Admin Portal' + Link = "https://outlook.office365.com/ecp/?rfr=Admin_o365&exsvurl=1&delegatedOrg=$($Customer.DefaultDomainName)" + Icon = 'fas fa-mail-bulk' + }, + @{ + Name = 'Entra Admin' + Link = "https://aad.portal.azure.com/$($Customer.DefaultDomainName)" + Icon = 'fas fa-users-cog' + }, + @{ + Name = 'Endpoint Management' + Link = "https://endpoint.microsoft.com/$($customer.DefaultDomainName)/" + Icon = 'fas fa-laptop' + }, + @{ + Name = 'Skype For Business' + Link = "https://portal.office.com/Partner/BeginClientSession.aspx?CTID=$($Customer.CustomerId)&CSDEST=MicrosoftCommunicationsOnline" + Icon = 'fab fa-skype' + }, + @{ + Name = 'Teams Admin' + Link = "https://admin.teams.microsoft.com/?delegatedOrg=$($Customer.DefaultDomainName)" + Icon = 'fas fa-users' + }, + @{ + Name = 'Azure Portal' + Link = "https://portal.azure.com/$($customer.DefaultDomainName)" + Icon = 'fas fa-server' + }, + @{ + Name = 'MFA Portal' + Link = "https://account.activedirectory.windowsazure.com/usermanagement/multifactorverification.aspx?tenantId=$($Customer.CustomerId)&culture=en-us&requestInitiatedContext=users')" + Icon = 'fas fa-key' + } + + ) + + $LinksHTML = Get-NinjaOneLinks -Data $ManagementLinksData + + $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.TenantLinks -NotePropertyValue @{'html' = '
', '') + $DevicesTable = ($ParsedDevices | ConvertTo-HTML -As Table).Replace('
', '
') + + + $HTML = @" + +
+ $TenantSummaryCard + $UserSummaryCardHTML +

User Details

+ $UsersTable +

Device Details

+ $DevicesTable +
+"@ + + #Get available Ninja clients + + + $Token = Get-NinjaOneToken -configuration $Configuration + + Write-Host "Got to End. OrgUpdate: $($NinjaOrgUpdate | ConvertTo-Json -Depth 100)" + + $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($MappedTenant.NinjaOne)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaOrgUpdate | ConvertTo-Json -Depth 100) + + Write-Host "Ninja Result: $($Result.content)" + } + catch { + Write-Host "FATAL ERROR: $_" + } +} \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 b/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 new file mode 100644 index 000000000000..adbb2977d747 --- /dev/null +++ b/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 @@ -0,0 +1,163 @@ +function Get-NinjaOneTitle($Title, $Icon, $TitleLink, $TitleSize, $TitleClass) { + Return $(if ($TitleSize) { "<$TitleSize" }else { '' + $(if ($Icon) { '  ' }) + $Title + $(if ($TitleLink) { '  ' }) + $(if ($TitleSize) { "" }else { '' }) +} + +### HTML Formatters ### +# Bar Graph +function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string]$TitleLink, [switch]$KeyInLine, [switch]$NoCount) { + <# + Example: + $Data = @( + @{ + Label = 'Licensed' + Amount = 3 + Colour = '#55ACBF' + }, + @{ + Label = 'Unlicensed' + Amount = 1 + Colour = '#3633B7' + }, + @{ + Label = 'Guests' + Amount = 10 + Colour = '#8063BF' + } + ) + + Get-NinjaInLineBarGraph -Title "Users" -Data $Data -KeyInLine + + #> + + $Data = $Data | Sort-Object Amount -Descending + + $Total = ($Data.Amount | measure-object -sum).sum + [System.Collections.Generic.List[String]]$OutputHTML = @() + + if ($Title) { + $OutputHTML.add((Get-NinjaOneTitle -Icon $Icon -Title ($Title + $(if (!$NoCount) { " ($Total)" })) -TitleLink $TitleLink)) + } + + $OutputHTML.add('
') + + foreach ($Item in $Data) { + $OutputHTML.add(@" +
+"@) + + } + + $OutputHTML.add('
') + + if ($KeyInline) { + $OutputHTML.add('
    ') + } else { + $OutputHTML.add('
      ') + } + + foreach ($Item in $Data) { + $OutputHTML.add(@" +
    • $($Item.Label) ($($Item.Amount))
    • +"@) + + } + + $OutputHTML.add('
    ') + + return $OutputHTML -join '' + + +} + +#### List of Links +function Get-NinjaOneLinks ($Data, $Title, [string]$Icon, [string]$TitleLink) { + <# +$ManagementLinksData = @( + @{ + Name = 'M365 Admin Portal' + Link = "https://portal.office.com/Partner/BeginClientSession.aspx?CTID=$($customer.CustomerId)&CSDEST=o365admincenter" + Icon = 'fas fa-cogs' + }, + @{ + Name = 'Exchange Admin Portal' + Link = "https://outlook.office365.com/ecp/?rfr=Admin_o365&exsvurl=1&delegatedOrg=$($Customer.DefaultDomainName)" + Icon = 'fas fa-mail-bulk' + }, + @{ + Name = 'Entra Admin' + Link = "https://aad.portal.azure.com/$($Customer.DefaultDomainName)" + Icon = 'fas fa-users-cog' + }) + + Get-NinjaOneLinks -Title 'M365 Admin Links' -Data $ManagementLinksData +#> + + [System.Collections.Generic.List[String]]$OutputHTML = @() + + $OutputHTML.add('
    ') + + if ($Title) { + $OutputHTML.add((Get-NinjaOneTitle -Icon $Icon -Title $Title -TitleLink $TitleLink)) + } + + foreach ($Item in $Data) { + $OutputHTML.add(@" + $(if ($Item.Icon){"  "})$($Item.Name) +"@) + + + } + + $OutputHTML.add('
    ') + + return $OutputHTML -join '' + +} + +function Get-NinjaOneCard($Title, $Body, [string]$Icon, [string]$TitleLink) { + <# + $Info = 'This is the body of a card it is wrapped in a paragraph' + + Get-NinjaOneCard -Title "Tenant Details" -Data $Info + #> + + [System.Collections.Generic.List[String]]$OutputHTML = @() + + $OutputHTML.add('
    + + [System.Collections.Generic.List[String]]$ItemsHTML = @() + + foreach ($Item in $Data.PSObject.Properties) { + $ItemsHTML.add('

    ' + $Item.Name + '
    ' + $Item.Value + '

    ') + } + + return Get-NinjaOneCard -Title $Title -Body ($ItemsHTML -join '') -Icon $Icon -TitleLink $TitleLink + + +} diff --git a/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 b/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 new file mode 100644 index 000000000000..fa9c0c5f3390 --- /dev/null +++ b/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 @@ -0,0 +1,22 @@ +function Set-NinjaOneFieldMapping { + [CmdletBinding()] + param ( + $CIPPMapping, + $APIName, + $Request + ) + + foreach ($Mapping in ([pscustomobject]$Request.body.mappings).psobject.properties) { + $AddObject = @{ + PartitionKey = 'NinjaFieldMapping' + RowKey = "$($mapping.name)" + 'NinjaOne' = "$($mapping.value.value)" + 'NinjaOneName' = "$($mapping.value.label)" + } + Add-AzDataTableEntity @CIPPMapping -Entity $AddObject -Force + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Added mapping for $($mapping.name)." -Sev 'Info' + } + $Result = [pscustomobject]@{'Results' = "Successfully edited mapping table." } + + Return $Result +} \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/Set-NinjaOneOrgMapping.ps1 b/Modules/CippExtensions/NinjaOne/Set-NinjaOneOrgMapping.ps1 new file mode 100644 index 000000000000..e65dcfe22593 --- /dev/null +++ b/Modules/CippExtensions/NinjaOne/Set-NinjaOneOrgMapping.ps1 @@ -0,0 +1,22 @@ +function Set-NinjaOneOrgMapping { + [CmdletBinding()] + param ( + $CIPPMapping, + $APIName, + $Request + ) + + foreach ($Mapping in ([pscustomobject]$Request.body.mappings).psobject.properties) { + $AddObject = @{ + PartitionKey = 'NinjaOrgsMapping' + RowKey = "$($mapping.name)" + 'NinjaOne' = "$($mapping.value.value)" + 'NinjaOneName' = "$($mapping.value.label)" + } + Add-AzDataTableEntity @CIPPMapping -Entity $AddObject -Force + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Added mapping for $($mapping.name)." -Sev 'Info' + } + $Result = [pscustomobject]@{'Results' = "Successfully edited mapping table." } + + Return $Result +} \ No newline at end of file diff --git a/Modules/CippExtensions/Private/Get-HaloMapping.ps1 b/Modules/CippExtensions/Private/Get-HaloMapping.ps1 new file mode 100644 index 000000000000..dcf0fbf77d39 --- /dev/null +++ b/Modules/CippExtensions/Private/Get-HaloMapping.ps1 @@ -0,0 +1,42 @@ +function Get-HaloMapping { + [CmdletBinding()] + param ( + $CIPPMapping + ) + #Get available mappings + $Mappings = [pscustomobject]@{} + $Filter = "RowKey eq 'Mapping'" + Get-AzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { + $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.HaloPSAName)"; value = "$($_.HaloPSA)" } + } + #Get Available TEnants + $Tenants = Get-Tenants + #Get available halo clients + $Table = Get-CIPPTable -TableName Extensionsconfig + try { + $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json).HaloPSA + $Token = Get-HaloToken -configuration $Configuration + $i = 1 + $RawHaloClients = do { + $Result = Invoke-RestMethod -Uri "$($Configuration.ResourceURL)/Client?page_no=$i&page_size=999&pageinate=true" -ContentType 'application/json' -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } + $Result.clients | Select-Object * -ExcludeProperty logo + $i++ + $pagecount = [Math]::Ceiling($Result.record_count / 999) + } while ($i -le $pagecount) + } + catch { $RawHaloClients = @() } + $HaloClients = $RawHaloClients | ForEach-Object { + [PSCustomObject]@{ + name = $_.name + value = "$($_.id)" + } + } + $MappingObj = [PSCustomObject]@{ + Tenants = @($Tenants) + HaloClients = @($HaloClients) + Mappings = $Mappings + } + + return $MappingObj + +} \ No newline at end of file diff --git a/Modules/CippExtensions/Private/Set-HaloMapping.ps1 b/Modules/CippExtensions/Private/Set-HaloMapping.ps1 new file mode 100644 index 000000000000..865034fe6a29 --- /dev/null +++ b/Modules/CippExtensions/Private/Set-HaloMapping.ps1 @@ -0,0 +1,22 @@ +function Set-HaloMapping { + [CmdletBinding()] + param ( + $CIPPMapping, + $APIName, + $Request + ) + + foreach ($Mapping in ([pscustomobject]$Request.body.mappings).psobject.properties) { + $AddObject = @{ + PartitionKey = 'Mapping' + RowKey = "$($mapping.name)" + 'HaloPSA' = "$($mapping.value.value)" + 'HaloPSAName' = "$($mapping.value.label)" + } + Add-AzDataTableEntity @CIPPMapping -Entity $AddObject -Force + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Added mapping for $($mapping.name)." -Sev 'Info' + } + $Result = [pscustomobject]@{'Results' = "Successfully edited mapping table." } + + Return $Result +} \ No newline at end of file diff --git a/Scheduler_NinjaOne/function.json b/Scheduler_NinjaOne/function.json new file mode 100644 index 000000000000..a0b7faadd73d --- /dev/null +++ b/Scheduler_NinjaOne/function.json @@ -0,0 +1,16 @@ +{ + "bindings": [ + { + "name": "Timer", + "schedule": "0 */15 * * * *", + "direction": "in", + "type": "timerTrigger" + }, + { + "type": "queue", + "direction": "out", + "name": "NinjaProcess", + "queueName": "NinjaOneQueue" + } + ] +} diff --git a/Scheduler_NinjaOne/run.ps1 b/Scheduler_NinjaOne/run.ps1 new file mode 100644 index 000000000000..434a450ee81f --- /dev/null +++ b/Scheduler_NinjaOne/run.ps1 @@ -0,0 +1,44 @@ +using namespace System.Net + +param($Timer) + +$Table = Get-CIPPTable -TableName NinjaOneSettings +$Settings = (Get-AzDataTableEntity @Table) +$TimeSetting = ($Settings | Where-Object { $_.RowKey -eq 'NinjaSyncTime' }).SettingValue + +if (($TimeSetting | Measure-Object).count -ne 1) { + $TimeSetting = Get-Random -Minimum 0 -Maximum 96 + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaSyncTime' + 'SettingValue' = $TimeSetting + } + Add-AzDataTableEntity @Table -Entity $AddObject -Force +} + +$LastRunTime = ($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue +$CurrentInterval = ($currentHour * 4) + [math]::Floor($currentMinute / 15) + +if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $TimeSetting -eq $CurrentInterval) { + $CIPPMapping = Get-CIPPTable -TableName CippMapping + $Filter = "PartitionKey eq 'NinjaOrgsMapping'" + $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } + + foreach ($Tenant in $TenantsToProcess) { + Push-OutputBinding -Name NinjaProcess -Value @{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + } + + } + + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaLastRunTime' + 'SettingValue' = Get-Date + } + Add-AzDataTableEntity @Table -Entity $AddObject -Force + + Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' + +} \ No newline at end of file From e5370030af2b4ff7d417100ce68aa3d905f4eae3 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sun, 10 Sep 2023 18:40:19 +0100 Subject: [PATCH 02/77] NinjaOne Links and Tenant Summary v1 Added a v1 of NinjaOne Links and Tenant Summary --- ExecExtensionNinjaOneSync/run.ps1 | 2 +- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 586 ++++++++++++++---- .../NinjaOne/NinjaOneHelper.ps1 | 119 +++- .../NinjaOne/Set-NinjaOneFieldMapping.ps1 | 12 +- .../NinjaOne/Set-NinjaOneOrgMapping.ps1 | 5 +- Scheduler_NinjaOne/run.ps1 | 6 +- 6 files changed, 589 insertions(+), 141 deletions(-) diff --git a/ExecExtensionNinjaOneSync/run.ps1 b/ExecExtensionNinjaOneSync/run.ps1 index 47559de1acc1..564767a2346d 100644 --- a/ExecExtensionNinjaOneSync/run.ps1 +++ b/ExecExtensionNinjaOneSync/run.ps1 @@ -23,7 +23,7 @@ foreach ($Tenant in $TenantsToProcess) { $AddObject = @{ PartitionKey = 'NinjaConfig' RowKey = 'NinjaLastRunTime' - 'SettingValue' = Get-Date + 'SettingValue' = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffK") } Add-AzDataTableEntity @Table -Entity $AddObject -Force diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index f88c7225e44b..fad3356d2ead 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -5,11 +5,21 @@ function Invoke-NinjaOneTenantSync { ) try { + $Table = Get-CIPPTable -TableName NinjaOneSettings + $NinjaSettings = (Get-AzDataTableEntity @Table) + $CIPPUrl = ($NinjaSettings | Where-Object { $_.RowKey -eq 'CIPPURL' }).SettingValue + + $StartTime = Get-Date $MappedTenant = $QueueItem.MappedTenant $Customer = Get-Tenants | where-object { $_.customerId -eq $MappedTenant.RowKey } - $TenantFilter = $Customer.customerId + + if (($Customer | Measure-Object).count -ne 1) { + Throw "Unable to match the recieved ID to a tenant QueueItem: $($QueueItem | ConvertTo-Json -Depth 100 | Out-String) Matched Customer: $($Customer| ConvertTo-Json -Depth 100 | Out-String)" + } + + $TenantFilter = $Customer.defaultDomainName write-host "$(Get-Date) - Starting NinjaOne Sync $($customer.DisplayName)" @@ -87,23 +97,50 @@ function Invoke-NinjaOneTenantSync { method = 'GET' url = '/security/secureScoreControlProfiles' } + ) write-verbose "$(Get-Date) - Fetching Bulk Data" try { $TenantResults = New-GraphBulkRequest -Requests $TenantRequests -tenantid $TenantFilter -NoAuthCheck $True + } catch { + Throw "Failed to fetch bulk company data: $_" } - catch { - Write-Error "Failed to fetch bulk company data" - } - $Users = Get-GraphBulkResultByID -Results $TenantResults -ID 'Users' + $Users = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Users' + + $SignInLogs = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'SignInLogs' + + $SecureScore = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'SecureScore' + + [System.Collections.Generic.List[PSCustomObject]]$SecureScoreProfiles = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'SecureScoreControlProfiles' - $SecureScore = Get-GraphBulkResultByID -Results $TenantResults -ID 'SecureScore' + $CurrentSecureScore = ($SecureScore | Sort-Object createDateTiime -Descending)[0] + $MaxSecureScoreRank = ($SecureScoreProfiles.rank | Measure-Object -Maximum).maximum + + $MaxSecureScore = $CurrentSecureScore.maxScore + + [System.Collections.Generic.List[PSCustomObject]]$SecureScoreParsed = Foreach ($Score in $CurrentSecureScore.controlScores) { + $MatchedProfile = $SecureScoreProfiles | Where-Object { $_.id -eq $Score.controlName } + [PSCustomObject]@{ + Category = $Score.controlCategory + 'Recommended Action' = $MatchedProfile.title + 'Score Impact' = [System.Math]::Round((((($MatchedProfile.maxScore) - ($Score.score)) / $MaxSecureScore) * 100), 2) + Link = "https://security.microsoft.com/securescore?actionId=$($Score.controlName)&viewid=actions&tid=$($Customer.customerId)" + name = $Score.controlName + score = $Score.score + IsApplicable = $Score.IsApplicable + scoreInPercentage = $Score.scoreInPercentage + maxScore = $MatchedProfile.maxScore + rank = $MatchedProfile.rank + adjustedRank = $MaxSecureScoreRank - $MatchedProfile.rank + + } + } - $SecureScoreProfiles = Get-GraphBulkResultByID -Results $TenantResults -ID 'SecureScoreControlProfiles' + $SecureScoreParsed | ConvertTo-Json | Out-File D:\Temp\ParsedScore.json - $TenantDetails = Get-GraphBulkResultByID -Results $TenantResults -ID 'TenantDetails' + $TenantDetails = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'TenantDetails' write-verbose "$(Get-Date) - Parsing Users" # Grab licensed users @@ -111,7 +148,7 @@ function Invoke-NinjaOneTenantSync { write-verbose "$(Get-Date) - Parsing Roles" # Get All Roles - $AllRoles = Get-GraphBulkResultByID -Results $TenantResults -ID 'AllRoles' + $AllRoles = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'AllRoles' $SelectList = 'id', 'displayName', 'userPrincipalName' @@ -126,8 +163,7 @@ function Invoke-NinjaOneTenantSync { try { $MemberReturn = New-GraphBulkRequest -Requests $RolesRequestArray -tenantid $TenantFilter -NoAuthCheck $True - } - catch { + } catch { $MemberReturn = $null } @@ -147,9 +183,8 @@ function Invoke-NinjaOneTenantSync { write-verbose "$(Get-Date) - Fetching Domains" try { - $RawDomains = Get-GraphBulkResultByID -Results $TenantResults -ID 'RawDomains' - } - catch { + $RawDomains = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'RawDomains' + } catch { $RawDomains = $null } $customerDomains = ($RawDomains | Where-Object { $_.IsVerified -eq $True }).id -join ', ' | Out-String @@ -157,26 +192,20 @@ function Invoke-NinjaOneTenantSync { write-verbose "$(Get-Date) - Parsing Licenses" # Get Licenses - $Licenses = Get-GraphBulkResultByID -Results $TenantResults -ID 'Licenses' + $Licenses = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Licenses' # Get the license overview for the tenant if ($Licenses) { - $pre = "
    -

    Current Licenses

    -
    " - - $post = "
    " - - $LicensesParsed = $Licenses | where-object { $_.PrepaidUnits.Enabled -gt 0 } | Select-Object @{N = 'License Name'; E = { $($LicenseLookup.$($_.SkuPartNumber)) } }, @{N = 'Active'; E = { $_.PrepaidUnits.Enabled } }, @{N = 'Consumed'; E = { $_.ConsumedUnits } }, @{N = 'Unused'; E = { $_.PrepaidUnits.Enabled - $_.ConsumedUnits } } + $LicensesParsed = $Licenses | where-object { $_.PrepaidUnits.Enabled -gt 0 } | Select-Object @{N = 'License Name'; E = { (Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $_.SkuPartNumber).Tolower()) } }, @{N = 'Active'; E = { $_.PrepaidUnits.Enabled } }, @{N = 'Consumed'; E = { $_.ConsumedUnits } }, @{N = 'Unused'; E = { $_.PrepaidUnits.Enabled - $_.ConsumedUnits } } } write-verbose "$(Get-Date) - Parsing Devices" # Get all devices from Intune - $devices = Get-GraphBulkResultByID -Results $TenantResults -ID 'Devices' + $devices = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Devices' write-verbose "$(Get-Date) - Parsing Device Compliance Polcies" # Fetch Compliance Policy Status - $DeviceCompliancePolicies = Get-GraphBulkResultByID -Results $TenantResults -ID 'DeviceCompliancePolicies' + $DeviceCompliancePolicies = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'DeviceCompliancePolicies' # Get the status of each device for each policy [System.Collections.Generic.List[PSCustomObject]]$PolicyRequestArray = @() @@ -190,8 +219,7 @@ function Invoke-NinjaOneTenantSync { try { $PolicyReturn = New-GraphBulkRequest -Requests $PolicyRequestArray -tenantid $TenantFilter -NoAuthCheck $True - } - catch { + } catch { $PolicyReturn = $null } @@ -205,7 +233,7 @@ function Invoke-NinjaOneTenantSync { write-verbose "$(Get-Date) - Parsing Apps" # Fetch Apps - $DeviceApps = Get-GraphBulkResultByID -Results $TenantResults -ID 'DeviceApps' + $DeviceApps = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'DeviceApps' # Fetch the App status for each device [System.Collections.Generic.List[PSCustomObject]]$RequestArray = @() @@ -219,8 +247,7 @@ function Invoke-NinjaOneTenantSync { try { $InstalledAppDetailsReturn = New-GraphBulkRequest -Requests $RequestArray -tenantid $TenantFilter -NoAuthCheck $True - } - catch { + } catch { $InstalledAppDetailsReturn = $null } $DeviceAppInstallDetails = foreach ($Result in $InstalledAppDetailsReturn) { @@ -233,7 +260,7 @@ function Invoke-NinjaOneTenantSync { write-verbose "$(Get-Date) - Parsing Groups" # Fetch Groups - $AllGroups = Get-GraphBulkResultByID -Results $TenantResults -ID 'Groups' + $AllGroups = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Groups' # Fetch the App status for each device [System.Collections.Generic.List[PSCustomObject]]$GroupRequestArray = @() @@ -247,8 +274,7 @@ function Invoke-NinjaOneTenantSync { try { $GroupMembersReturn = New-GraphBulkRequest -Requests $GroupRequestArray -tenantid $TenantFilter -NoAuthCheck $True - } - catch { + } catch { $GroupMembersReturn = $null } $Groups = foreach ($Result in $GroupMembersReturn) { @@ -261,7 +287,7 @@ function Invoke-NinjaOneTenantSync { write-verbose "$(Get-Date) - Parsing Conditional Access Polcies" # Fetch and parse conditional access polcies - $AllConditionalAccessPolcies = Get-GraphBulkResultByID -Results $TenantResults -ID 'ConditionalAccess' + $AllConditionalAccessPolcies = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'ConditionalAccess' $ConditionalAccessMembers = foreach ($CAPolicy in $AllConditionalAccessPolcies) { #Setup User Array @@ -270,8 +296,7 @@ function Invoke-NinjaOneTenantSync { # Check for All Include if ($CAPolicy.conditions.users.includeUsers -contains 'All') { $Users | foreach-object { $null = $CAMembers.add($_.id) } - } - else { + } else { # Add any specific all users to the array $CAPolicy.conditions.users.includeUsers | foreach-object { $null = $CAMembers.add($_) } } @@ -322,37 +347,59 @@ function Invoke-NinjaOneTenantSync { write-verbose "$(Get-Date) - Fetching One Drive Details" try { $OneDriveDetails = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getOneDriveUsageAccountDetail(period='D7')" -tenantid $TenantFilter | convertfrom-csv - } - catch { + } catch { + Write-Error "Failed to fetch Onedrive Details: $_" $OneDriveDetails = $null } write-verbose "$(Get-Date) - Fetching CAS Mailbox Details" try { - $CASFull = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true - } - catch { + $CASFull = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox" -Tenantid $Customer.defaultDomainName -scope ExchangeOnline -noPagination $true + } catch { + Write-Error "Failed to fetch CAS Details: $_" $CASFull = $null } write-verbose "$(Get-Date) - Fetching Mailbox Details" try { - $MailboxDetailedFull = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-Mailbox' - } - catch { + $MailboxDetailedFull = New-ExoRequest -TenantID $Customer.defaultDomainName -cmdlet 'Get-Mailbox' + } catch { + Write-Error "Failed to fetch Mailbox Details: $_" $MailboxDetailedFull = $null } + write-verbose "$(Get-Date) - Fetching Blocked Mailbox Details" + try { + $BlockedSenders = New-ExoRequest -TenantID $Customer.defaultDomainName -cmdlet 'Get-BlockedSenderAddress' + } catch { + Write-Error "Failed to fetch Blocked Sender Details: $_" + $BlockedSenders = $null + } + write-verbose "$(Get-Date) - Fetching Mailbox Stats" try { $MailboxStatsFull = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/reports/getMailboxUsageDetail(period='D7')" -tenantid $TenantFilter | convertfrom-csv - } - catch { + } catch { + Write-Error "Failed to fetch Mailbox Stats: $_" $MailboxStatsFull = $null } + + + # Fetch Standards + $Table = Get-CippTable -tablename 'standards' + + $Filter = "PartitionKey eq 'standards'" + + try { + if ($Request.query.TenantFilter) { + $tenants = (Get-AzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -Depth 15 -ErrorAction Stop | Where-Object Tenant -EQ $Request.query.tenantFilter + } else { + $Tenants = (Get-AzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -Depth 15 -ErrorAction Stop + } + } catch {} + - $FetchEnd = Get-Date @@ -370,46 +417,93 @@ function Invoke-NinjaOneTenantSync { Icon = 'fas fa-cogs' }, @{ - Name = 'Exchange Admin Portal' - Link = "https://outlook.office365.com/ecp/?rfr=Admin_o365&exsvurl=1&delegatedOrg=$($Customer.DefaultDomainName)" + Name = 'Exchange Portal' + Link = "https://admin.exchange.microsoft.com/?landingpage=homepage&form=mac_sidebar&delegatedOrg=$($Customer.DefaultDomainName)#" Icon = 'fas fa-mail-bulk' }, @{ - Name = 'Entra Admin' - Link = "https://aad.portal.azure.com/$($Customer.DefaultDomainName)" + Name = 'Entra Portal' + Link = "https://entra.microsoft.com/$($Customer.DefaultDomainName)" Icon = 'fas fa-users-cog' }, @{ - Name = 'Endpoint Management' + Name = 'Intune Portal' Link = "https://endpoint.microsoft.com/$($customer.DefaultDomainName)/" Icon = 'fas fa-laptop' }, @{ - Name = 'Skype For Business' - Link = "https://portal.office.com/Partner/BeginClientSession.aspx?CTID=$($Customer.CustomerId)&CSDEST=MicrosoftCommunicationsOnline" - Icon = 'fab fa-skype' + Name = 'Sharepoint Admin' + Link = "https://admin.microsoft.com/Partner/beginclientsession.aspx?CTID=$($Customer.CustomerId)&CSDEST=SharePoint" + Icon = 'fas fa-shapes' }, @{ Name = 'Teams Admin' Link = "https://admin.teams.microsoft.com/?delegatedOrg=$($Customer.DefaultDomainName)" Icon = 'fas fa-users' }, + @{ + Name = 'Security Portal' + Link = "https://security.microsoft.com/?tid=$($Customer.CustomerId)" + Icon = 'fas fa-building-shield' + }, + @{ + Name = 'Compliance Portal' + Link = "https://compliance.microsoft.com/?tid=$($Customer.CustomerId)" + Icon = 'fas fa-user-shield' + }, @{ Name = 'Azure Portal' Link = "https://portal.azure.com/$($customer.DefaultDomainName)" Icon = 'fas fa-server' }, @{ - Name = 'MFA Portal' - Link = "https://account.activedirectory.windowsazure.com/usermanagement/multifactorverification.aspx?tenantId=$($Customer.CustomerId)&culture=en-us&requestInitiatedContext=users')" - Icon = 'fas fa-key' + Name = 'CIPP Tenant Admin' + Link = "https://$CIPPUrl/home?customerId=$($Customer.CustomerId)" + Icon = 'fas fa-shield-halved' } ) - $LinksHTML = Get-NinjaOneLinks -Data $ManagementLinksData + $M365LinksHTML = Get-NinjaOneLinks -Data $ManagementLinksData -Title 'Portals' -SmallCols 2 -MedCols 3 -LargeCols 4 -XLCols 5 - $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.TenantLinks -NotePropertyValue @{'html' = '
    ' + $CIPPLinksHTML + '
     
    ' + + $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.TenantLinks -NotePropertyValue @{'html' = $LinksHtml } } @@ -417,24 +511,50 @@ function Invoke-NinjaOneTenantSync { if ($MappedFields.TenantSummary) { # Tenant Overview Card + $ParsedAdmins = [PSCustomObject]@{} + + $AdminUsers | Select-Object displayname, userPrincipalName -unique | ForEach-Object { + $ParsedAdmins | Add-Member -NotePropertyName $_.displayname -NotePropertyValue $_.userPrincipalName + } $TenantDetailsItems = [PSCustomObject]@{ 'Tenant Name' = $Customer.displayName 'Default Domain' = $Customer.defaultDomainName 'Tenant ID' = $Customer.customerId - 'Domains' = $customerDomains - 'Admin Users' = ($AdminUsers | ForEach-Object { "$($_.displayname) ($($_.userPrincipalName))" }) -join ', ' 'Creation Date' = $TenantDetails.createdDateTime + 'Domains' = $customerDomains + 'Admin Users' = ($AdminUsers | ForEach-Object { "$($_.DisplayName)" }) -join ', ' + } - $TenantSummaryCard = Get-NinjaOneInfoCard -Title "Tenant Details" -Data $TenantDetailsItems + $TenantSummaryCard = Get-NinjaOneInfoCard -Title "Tenant Details" -Data $TenantDetailsItems -Icon 'fas fa-building' - # Users details card + ### Users details card $TotalUsersCount = ($Users | measure-object).count $GuestUsersCount = ($Users | where-object { $_.UserType -eq 'Guest' } | measure-object).count $LicensedUsersCount = ($licensedUsers | measure-object).count $UnlicensedUsersCount = $TotalUsersCount - $GuestUsersCount - $LicensedUsersCount + $UsersEnabledCount = ($Users | where-object { $_.accountEnabled -eq $True } | Measure-Object).count + + # Enabled Users + $Data = @( + @{ + Label = 'Sign-In Enabled' + Amount = $UsersEnabledCount + Colour = '#008001' + }, + @{ + Label = 'Sign-In Blocked' + Amount = $TotalUsersCount - $UsersEnabledCount + Colour = '#ec1c24' + } + ) + + + $UsersEnabledChartHTML = Get-NinjaInLineBarGraph -Title "User Status" -Data $Data -KeyInLine + + # User Types $Data = @( @{ @@ -452,18 +572,295 @@ function Invoke-NinjaOneTenantSync { Amount = $GuestUsersCount Colour = '#8063BF' } + ) + + $UsersTypesChartHTML = Get-NinjaInLineBarGraph -Title "User Types" -Data $Data -KeyInLine + + # Create the Users Card + + $TitleLink = "https://$CIPPUrl/identity/administration/users?customerId=$($Customer.customerId)" + + $UsersCardBodyHTML = $UsersEnabledChartHTML + $UsersTypesChartHTML + + $UserSummaryCardHTML = Get-NinjaOneCard -Title 'User Details' -Body $UsersCardBodyHTML -Icon 'fas fa-users' -TitleLink $TitleLink + + + + ### Device Details Card + $TotalDeviceswCount = ($Devices | Measure-Object).count + $ComplianceDevicesCount = ($Devices | Where-Object { $_.complianceState -eq 'compliant' } | Measure-Object).count + $WindowsCount = ($Devices | Where-Object { $_.operatingSystem -eq 'Windows' } | Measure-Object).count + $IOSCount = ($Devices | Where-Object { $_.operatingSystem -eq 'iOS' } | Measure-Object).count + $AndroidCount = ($Devices | Where-Object { $_.operatingSystem -eq 'Android' } | Measure-Object).count + $MacOSCount = ($Devices | Where-Object { $_.operatingSystem -eq 'macOS' } | Measure-Object).count + $OnlineInLast30Days = ($Devices | Where-Object { $_.lastSyncDateTime -gt ((Get-Date).AddDays(-30)) } | Measure-Object).Count + + + # Compliance Devices + $Data = @( + @{ + Label = 'Compliant' + Amount = $ComplianceDevicesCount + Colour = '#008001' + }, + @{ + Label = 'Non Compliant' + Amount = $TotalDeviceswCount - $ComplianceDevicesCount + Colour = '#ec1c24' + } ) - $TitleLink = "https://entra.microsoft.com/$($Customer.DefaultDomainName)#view/Microsoft_AAD_UsersAndTenants/UserManagementMenuBlade/~/AllUsers" - $UsersChartHTML = Get-NinjaInLineBarGraph -Title "Users" -Data $Data -KeyInLine + $DeviceComplianceChartHTML = Get-NinjaInLineBarGraph -Title "Device Compliance" -Data $Data -KeyInLine + + # Device OS Types + + $Data = @( + @{ + Label = 'Windows' + Amount = $WindowsCount + Colour = '#0078D7' + }, + @{ + Label = 'macOS' + Amount = $MacOSCount + Colour = '#A3AAAE' + }, + @{ + Label = 'Android' + Amount = $AndroidCount + Colour = '#3DDC84' + }, + @{ + Label = 'iOS' + Amount = $IOSCount + Colour = '#007AFF' + } + ) + + $DeviceOsChartHTML = Get-NinjaInLineBarGraph -Title "Device Operating Systems" -Data $Data -KeyInLine + + # Last online time + + $Data = @( + @{ + Label = 'Online in last 30 days' + Amount = $OnlineInLast30Days + Colour = '#008001' + }, + @{ + Label = 'Not seen for 30+ days' + Amount = $TotalDeviceswCount - $OnlineInLast30Days + Colour = '#CCCCCC' + } + ) + + $DeviceOnlineChartHTML = Get-NinjaInLineBarGraph -Title "Devices Online in the last 30 days" -Data $Data -KeyInLine + + # Create the Devices Card + + $TitleLink = "https://$CIPPUrl/endpoint/reports/devices?customerId=$($Customer.customerId)" + + $DeviceCardBodyHTML = $DeviceComplianceChartHTML + $DeviceOsChartHTML + $DeviceOnlineChartHTML + + $DeviceSummaryCardHTML = Get-NinjaOneCard -Title 'Device Details' -Body $DeviceCardBodyHTML -Icon 'fas fa-network-wired' -TitleLink $TitleLink - $UserSummaryCardHTML = Get-NinjaOneCard -Title 'User Details' -Body $UsersChartHTML -Icon 'fas fa-users' -TitleLink $TitleLink + #### Secure Score Card + $Top5Actions = ($SecureScoreParsed | Where-Object { $_.scoreInPercentage -ne 100 } | Sort-Object 'Score Impact', adjustedRank -Descending) | Select-Object -First 5 + # Score Chart + $Data = [PSCustomObject]@( + @{ + Label = 'Current Score' + Amount = $CurrentSecureScore.currentScore + Colour = '#008001' + }, + @{ + Label = 'Points to Obtain' + Amount = $MaxSecureScoreRank - $CurrentSecureScore.currentScore + Colour = '#CCCCCC' + } + ) + $SecureScoreHTML = Get-NinjaInLineBarGraph -Title "Secure Score - $([System.Math]::Round((($CurrentSecureScore.currentScore / $MaxSecureScoreRank) * 100),2))%" -Data $Data -KeyInLine -NoCount -NoSort + + # Recommended Actions HTML + $RecommendedActionsHTML = $Top5Actions | Select-Object 'Recommended Action', @{n = 'Score Impact'; e = { "+$($_.'Score Impact')%" } }, Category, @{n = 'Link'; e = { '' } } | ConvertTo-Html -As Table -Fragment + $TitleLink = "https://security.microsoft.com/securescore?viewid=overview&tid=$($Customer.CustomerId)" + + $SecureScoreCardBodyHTML = $SecureScoreHTML + [System.Web.HttpUtility]::HtmlDecode($RecommendedActionsHTML) -replace '
', '' + $SecureScoreCardBodyHTML = $SecureScoreCardBodyHTML -replace '', '' + + $SecureScoreSummaryCardHTML = Get-NinjaOneCard -Title 'Secure Score' -Body $SecureScoreCardBodyHTML -Icon 'fas fa-shield' -TitleLink $TitleLink + + + ### CIPP Applied Standards Cards + $StandardsDefinitions = Get-Content 'config/standards.json' | ConvertFrom-Json -Depth 100 + + $Table = Get-CippTable -tablename 'standards' + + $Filter = "PartitionKey eq 'standards'" + + $AllStandards = (Get-AzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -Depth 100 + + $AppliedStandards = ($AllStandards | Where-Object { $_.Tenant -eq $Customer.defaultDomainName -or $_.Tenant -eq 'AllTenants' }) + + $ParsedStandards = foreach ($Standard in $AppliedStandards) { + [PSCustomObject]$Standards = $Standard.Standards + $Standards.PSObject.Properties | foreach-object { + $CheckValue = $_ + if ($CheckValue.value) { + $MatchedStandard = $StandardsDefinitions | where-object { ($_.name -split 'standards.')[1] -eq $CheckValue.name } + if (($MatchedStandard | Measure-Object).count -eq 1) { + '
  • ' + $($MatchedStandard.label) + ' (' + ($($Standard.Tenant)) + ')
  • ' + } + } + } + + } + + $TitleLink = "https://$CIPPUrl/tenant/standards/list-applied-standards?customerId=$($Customer.customerId)" + + $CIPPStandardsBodyHTML = '
      ' + $ParsedStandards + '
    ' + + $CIPPStandardsSummaryCardHTML = Get-NinjaOneCard -Title 'CIPP Applied Standards' -Body $CIPPStandardsBodyHTML -Icon 'fas fa-shield-halved' -TitleLink $TitleLink + + ### License Card + $LicenseTableHTML = $LicensesParsed | Sort-Object 'License Name' | ConvertTo-HTML -As Table -Fragment + $LicenseTableHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseTableHTML) -replace '
    ', '') -replace '', '' + + $TitleLink = "https://$CIPPUrl/tenant/administration/list-licenses?customerId=$($Customer.customerId)" + $LicensesSummaryCardHTML = Get-NinjaOneCard -Title 'Licenses' -Body $LicenseTableHTML -Icon 'fas fa-chart-bar' -TitleLink $TitleLink + + + ### Summary Stats + + [System.Collections.Generic.List[PSCustomObject]]$WidgetData = @() + + # Licensed Users + $WidgetData.add([PSCustomObject]@{ + Value = ($licensedUsers | Measure-Object).count + Description = 'Licensed Users' + Colour = '#CCCCCC' + Link = "https://$CIPPUrl/identity/administration/users?customerId=$($Customer.customerId)" + }) + + # Devices + $WidgetData.add([PSCustomObject]@{ + Value = ($Devices | Measure-Object).count + Description = 'Devices' + Colour = '#CCCCCC' + Link = "https://$CIPPUrl/endpoint/reports/devices?customerId=$($Customer.customerId)" + }) + + # Groups + $WidgetData.add([PSCustomObject]@{ + Value = ($AllGroups | Measure-Object).count + Description = 'Groups' + Colour = '#CCCCCC' + Link = "https://$CIPPUrl/identity/administration/groups?customerId=$($Customer.customerId)" + }) + + # Roles + $WidgetData.add([PSCustomObject]@{ + Value = ($AllRoles | Measure-Object).count + Description = 'Roles' + Colour = '#CCCCCC' + Link = "https://$CIPPUrl/identity/administration/roles?customerId=$($Customer.customerId)" + }) + + # Exchange + if ( 'exchange' -in $TenantDetails.assignedPlans.service) { + $ExchangeStatus = '' + } else { + $ExchangeStatus = '' + } + $WidgetData.add([PSCustomObject]@{ + Value = $ExchangeStatus + Description = 'Exchange' + Colour = '#CCCCCC' + Link = "https://admin.exchange.microsoft.com/?landingpage=homepage&form=mac_sidebar&delegatedOrg=$($Customer.DefaultDomainName)#" + }) + + # AAD Premium + if ( 'AADPremiumService' -in $TenantDetails.assignedPlans.service) { + $AADPremiumStatus = '' + } else { + $AADPremiumStatus = '' + } + $WidgetData.add([PSCustomObject]@{ + Value = $AADPremiumStatus + Description = 'AAD Premium' + Colour = '#CCCCCC' + Link = "https://entra.microsoft.com/$($Customer.customerId)/#view/Microsoft_AAD_IAM/TenantOverview.ReactView" + }) + + # WindowsDefenderATP + if ( 'WindowsDefenderATP' -in $TenantDetails.assignedPlans.service) { + $DefenderStatus = '' + } else { + $DefenderStatus = '' + } + $WidgetData.add([PSCustomObject]@{ + Value = $DefenderStatus + Description = 'Windows Defender' + Colour = '#CCCCCC' + Link = "https://security.microsoft.com/machines?category=endpoints&tid=$($Customer.DefaultDomainName)#" + }) + + # On Prem Sync + if ( $TenantDetails.onPremisesSyncEnabled -eq $true) { + $OnPremSyncStatus = '' + } else { + $OnPremSyncStatus = '' + } + $WidgetData.add([PSCustomObject]@{ + Value = $OnPremSyncStatus + Description = 'AD Connect' + Colour = '#CCCCCC' + Link = "https://entra.microsoft.com/$($Customer.customerId)/#view/Microsoft_AAD_IAM/DirectoriesADConnectBlade" + }) + + + + # Blocked Senders + $BlockedSenderCount = ($BlockedSenders | Measure-Object).count + if ($BlockedSenderCount -eq 0) { + $BlockedSenderColour = '#008001' + } else { + $BlockedSenderColour = '#ec1c24' + } + $WidgetData.add([PSCustomObject]@{ + Value = $BlockedSenderCount + Description = 'Blocked Senders' + Colour = $BlockedSenderColour + Link = "https://security.microsoft.com/restrictedentities?tid=$($Customer.customerId)" + }) + + Write-Host 'Summary Details' + $SummaryDetailsCardHTML = Get-NinjaOneWidgetCard -Title 'Summary Details' -Data $WidgetData -Icon 'fas fa-building' -TitleLink 'http://example.com' -SmallCols 2 -MedCols 3 -LargeCols 4 -XLCols 6 -NoCard + + + # Create the Tenant Summary Field + $TenantSummaryHTML = '
    ' + $SummaryDetailsCardHTML + '
    ' + + '
    ' + + '
    ' + $TenantSummaryCard + + '
    ' + $LicensesSummaryCardHTML + + '
    ' + $DeviceSummaryCardHTML + + '
    ' + $CIPPStandardsSummaryCardHTML + + '
    ' + $SecureScoreSummaryCardHTML + + '
    ' + $UserSummaryCardHTML + + '
     
    ' + + $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.TenantSummary -NotePropertyValue @{'html' = $TenantSummaryHTML } + + + + } + + if ($MappedFields.UsersSummary) { - # Device Details Card [System.Collections.Generic.List[PSCustomObject]]$ParsedDevices = Foreach ($Device in $Devices) { # Match Users [System.Collections.Generic.List[String]]$DeviceUsers = @() @@ -511,45 +908,7 @@ function Invoke-NinjaOneTenantSync { } } - - # Check if OS is end of life - Switch ($Device.operatingSystem) { - 'Android' { - $Version = ($Device.osVersion).Split(".") - if ($Version[1] -eq '0' -and $Version[0] -notin @('1', '2', '8') ) { - $ParsedVersion = $Version[0] - } - else { - $ParsedVersion = $Device.osVersion - } - $Supported = Get-EndOfLifeStatus -EOLData $EndOfLifeAndroid -Cycle $ParsedVersion - - } - 'Windows' { - Switch ($Device.skufamily) { - 'Home' { $Filter = '(W)' } - 'Pro' { $Filter = '(W)' } - 'Enterprise' { $Filter = '(E)' } - } - $Version = ($Device.osVersion).Split(".") - $Supported = Get-EndOfLifeStatus -EOLData $EndOfLifeWindows -Version "$($Version[0]).$($Version[1]).$($Version[2])" -Filter $Filter - } - 'macOS' { - $Version = ($Device.osVersion).Split(".") - if ($Version[0] -ne '10' ) { - $ParsedVersion = $Version[0] - } - else { - $ParsedVersion = $Device.osVersion - } - $Supported = Get-EndOfLifeStatus -EOLData $EndOfLifeMacOS -Cycle $ParsedVersion - } - 'iOS' { - $Version = $Device.model -split 'iPhone ' - $Supported = Get-EndOfLifeStatus -EOLData $EndOfLifeIoS -Filter $Version[1] - } - } - + [PSCustomObject]@{ Name = $Device.deviceName @@ -575,7 +934,6 @@ function Invoke-NinjaOneTenantSync { UserDetails = $DeviceUsersDetail CompliancePolicies = $DevicePolcies Groups = $DeviceGroups - Supported = $Supported } @@ -599,9 +957,8 @@ function Invoke-NinjaOneTenantSync { $UserLic = $_ $SkuPartNumber = ($Licenses | Where-Object { $_.SkuId -eq $UserLic }).SkuPartNumber try { - "$($LicenseLookup.$SkuPartNumber)" - } - catch { + "$((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $SkuPartNumber).Tolower()))" + } catch { "$SkuPartNumber" } }) -join ', ' @@ -616,8 +973,7 @@ function Invoke-NinjaOneTenantSync { Total = $UserMailTotal Percent = ($UserMailUse / $UserMailTotal) * 100 } - } - else { + } else { $MailboxUse = [PSCustomObject]@{ Enabled = $False Used = 0 @@ -636,8 +992,7 @@ function Invoke-NinjaOneTenantSync { Total = $UserOneDriveTotal Percent = ($UserOneDriveUse / $UserOneDriveTotal) * 100 } - } - else { + } else { $OneDriveUse = [PSCustomObject]@{ Enabled = $False Used = 0 @@ -687,8 +1042,7 @@ function Invoke-NinjaOneTenantSync { $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($MappedTenant.NinjaOne)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaOrgUpdate | ConvertTo-Json -Depth 100) Write-Host "Ninja Result: $($Result.content)" - } - catch { + } catch { Write-Host "FATAL ERROR: $_" } } \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 b/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 index adbb2977d747..a32f3a880399 100644 --- a/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 +++ b/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 @@ -1,10 +1,10 @@ function Get-NinjaOneTitle($Title, $Icon, $TitleLink, $TitleSize, $TitleClass) { - Return $(if ($TitleSize) { "<$TitleSize" }else { '' + $(if ($Icon) { '  ' }) + $Title + $(if ($TitleLink) { '  ' }) + $(if ($TitleSize) { "" }else { '' }) + Return $(if ($TitleSize) { '<' + $TitleSize + ' style="font-family: sans-serif"' }else { '' + $(if ($Icon) { '  ' }) + $Title + $(if ($TitleLink) { '  ' }) + $(if ($TitleSize) { "" }else { '' }) } ### HTML Formatters ### # Bar Graph -function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string]$TitleLink, [switch]$KeyInLine, [switch]$NoCount) { +function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string]$TitleLink, [switch]$KeyInLine, [switch]$NoCount, [switch]$NoSort) { <# Example: $Data = @( @@ -29,7 +29,9 @@ function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string] #> - $Data = $Data | Sort-Object Amount -Descending + if (!$NoSort) { + $Data = $Data | Sort-Object Amount -Descending + } $Total = ($Data.Amount | measure-object -sum).sum [System.Collections.Generic.List[String]]$OutputHTML = @() @@ -50,14 +52,14 @@ function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string] $OutputHTML.add('') if ($KeyInline) { - $OutputHTML.add('
      ') + $OutputHTML.add('
        ') } else { - $OutputHTML.add('
          ') + $OutputHTML.add('
            ') } foreach ($Item in $Data) { $OutputHTML.add(@" -
          • $($Item.Label) ($($Item.Amount))
          • +
          • $($Item.Label) ($($Item.Amount))
          • "@) } @@ -70,7 +72,7 @@ function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string] } #### List of Links -function Get-NinjaOneLinks ($Data, $Title, [string]$Icon, [string]$TitleLink) { +function Get-NinjaOneLinks ($Data, $Title, [string]$Icon, [string]$TitleLink, [int]$SmallCols, [int]$MedCols, [int]$LargeCols, [int]$XLCols) { <# $ManagementLinksData = @( @{ @@ -94,18 +96,30 @@ $ManagementLinksData = @( [System.Collections.Generic.List[String]]$OutputHTML = @() - $OutputHTML.add('
            ') + $OutputHTML.add('
            ') if ($Title) { - $OutputHTML.add((Get-NinjaOneTitle -Icon $Icon -Title $Title -TitleLink $TitleLink)) + $OutputHTML.add('
            ' + $(if ($Icon) { '  ' }) + $Title + '
            ') + + if ($TitleLink) { + $OutputHTML.add('') + } + + $OutputHTML.add('
            ') } + $OutputHTML.add('
            ') + + $CSSCols = Get-NinjaOneCSSCol -SmallCols $SmallCols -MedCols $MedCols -LargeCols $LargeCols -XLCols $XLCols + + foreach ($Item in $Data) { + + $OutputHTML.add(@" - $(if ($Item.Icon){"  "})$($Item.Name) + "@) - } $OutputHTML.add('
            ') @@ -114,7 +128,71 @@ $ManagementLinksData = @( } -function Get-NinjaOneCard($Title, $Body, [string]$Icon, [string]$TitleLink) { + +function Get-NinjaOneWidgetCard($Title, $Data, [string]$Icon, [string]$TitleLink, [int]$SmallCols, [int]$MedCols, [int]$LargeCols, [int]$XLCols, [Switch]$NoCard) { + <# + $Data = @( + @{ + Value = 20 + Description = 'Users' + Colour = '#CCCCCC' + Link = 'https://example.com/users' + }, + @{ + Value = 42 + Description = 'Devices' + Colour = '#CCCCCC' + Link = 'https://example.com/devices' + } + ) + + $HTML = Get-NinjaOneWidgetCard -Title 'Summary Details' -Data $Data -Icon 'fas fa-building' -TitleLink 'http://example.com' -Columns 3 + + #> + + $CSSCols = Get-NinjaOneCSSCol -SmallCols $SmallCols -MedCols $MedCols -LargeCols $LargeCols -XLCols $XLCols + + + [System.Collections.Generic.List[String]]$OutputHTML = @() + + $OutputHTML.add('
            ') + + + foreach ($Item in $Data) { + + $HTML = @" + +"@ + + $OutputHTML.add($HTML) + + } + + $OutputHTML.add('
            ') + + if ($NoCard) { + return $OutputHTML -join '' + } else { + Return Get-NinjaOneCard -Title $Title -Body ($OutputHTML -join '') -Icon $Icon -TitleLink $TitleLink + } + +} + +Function Get-NinjaOneCSSCol($SmallCols, $MedCols, $LargeCols, $XLCols) { + $SmallCSS = "col-sm-$([Math]::Floor(12 / $SmallCols))" + $MediumCSS = "col-md-$([Math]::Floor(12 / $MedCols))" + $LargeCSS = "col-lg-$([Math]::Floor(12 / $LargeCols))" + $XLCSS = "col-xl-$([Math]::Floor(12 / $XLCols))" + + Return "$SmallCSS $MediumCSS $LargeCSS $XLCSS" +} + +function Get-NinjaOneCard($Title, $Body, [string]$Icon, [string]$TitleLink, [String]$Classes) { <# $Info = 'This is the body of a card it is wrapped in a paragraph' @@ -123,13 +201,20 @@ function Get-NinjaOneCard($Title, $Body, [string]$Icon, [string]$TitleLink) { [System.Collections.Generic.List[String]]$OutputHTML = @() - $OutputHTML.add('
            ') if ($Title) { - $OutputHTML.add((Get-NinjaOneTitle -Icon $Icon -Title $Title -TitleLink $TitleLink -TitleSize 'h4' -TitleClass 'card-title')) + $OutputHTML.add('
            ' + $(if ($Icon) { '  ' }) + $Title + '
            ') + + if ($TitleLink) { + $OutputHTML.add('') + } + + $OutputHTML.add('
            ') } - $OutputHTML.add('

            ') + $OutputHTML.add('

            ' + $Body + '

            ') $OutputHTML.add('
            ') @@ -154,10 +239,10 @@ function Get-NinjaOneInfoCard($Title, $Data, [string]$Icon, [string]$TitleLink) [System.Collections.Generic.List[String]]$ItemsHTML = @() foreach ($Item in $Data.PSObject.Properties) { - $ItemsHTML.add('

            ' + $Item.Name + '
            ' + $Item.Value + '

            ') + $ItemsHTML.add('

            ' + $Item.Name + '
            ' + $Item.Value + '

            ') } return Get-NinjaOneCard -Title $Title -Body ($ItemsHTML -join '') -Icon $Icon -TitleLink $TitleLink - } + diff --git a/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 b/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 index fa9c0c5f3390..115cbbefdc3a 100644 --- a/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 @@ -5,11 +5,19 @@ function Set-NinjaOneFieldMapping { $APIName, $Request ) + + $SettingsTable = Get-CIPPTable -TableName NinjaOneSettings + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'CIPPURL' + 'SettingValue' = ($Request.Url -split '/')[2] + } + Add-AzDataTableEntity @SettingsTable -Entity $AddObject -Force foreach ($Mapping in ([pscustomobject]$Request.body.mappings).psobject.properties) { $AddObject = @{ - PartitionKey = 'NinjaFieldMapping' - RowKey = "$($mapping.name)" + PartitionKey = 'NinjaFieldMapping' + RowKey = "$($mapping.name)" 'NinjaOne' = "$($mapping.value.value)" 'NinjaOneName' = "$($mapping.value.label)" } diff --git a/Modules/CippExtensions/NinjaOne/Set-NinjaOneOrgMapping.ps1 b/Modules/CippExtensions/NinjaOne/Set-NinjaOneOrgMapping.ps1 index e65dcfe22593..67f9e10dfb03 100644 --- a/Modules/CippExtensions/NinjaOne/Set-NinjaOneOrgMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Set-NinjaOneOrgMapping.ps1 @@ -6,10 +6,11 @@ function Set-NinjaOneOrgMapping { $Request ) + foreach ($Mapping in ([pscustomobject]$Request.body.mappings).psobject.properties) { $AddObject = @{ - PartitionKey = 'NinjaOrgsMapping' - RowKey = "$($mapping.name)" + PartitionKey = 'NinjaOrgsMapping' + RowKey = "$($mapping.name)" 'NinjaOne' = "$($mapping.value.value)" 'NinjaOneName' = "$($mapping.value.label)" } diff --git a/Scheduler_NinjaOne/run.ps1 b/Scheduler_NinjaOne/run.ps1 index 434a450ee81f..ed5514142559 100644 --- a/Scheduler_NinjaOne/run.ps1 +++ b/Scheduler_NinjaOne/run.ps1 @@ -7,7 +7,7 @@ $Settings = (Get-AzDataTableEntity @Table) $TimeSetting = ($Settings | Where-Object { $_.RowKey -eq 'NinjaSyncTime' }).SettingValue if (($TimeSetting | Measure-Object).count -ne 1) { - $TimeSetting = Get-Random -Minimum 0 -Maximum 96 + [int]$TimeSetting = Get-Random -Minimum 0 -Maximum 96 $AddObject = @{ PartitionKey = 'NinjaConfig' RowKey = 'NinjaSyncTime' @@ -16,7 +16,7 @@ if (($TimeSetting | Measure-Object).count -ne 1) { Add-AzDataTableEntity @Table -Entity $AddObject -Force } -$LastRunTime = ($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue +$LastRunTime = Get-Date(($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue) $CurrentInterval = ($currentHour * 4) + [math]::Floor($currentMinute / 15) if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $TimeSetting -eq $CurrentInterval) { @@ -35,7 +35,7 @@ if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $Ti $AddObject = @{ PartitionKey = 'NinjaConfig' RowKey = 'NinjaLastRunTime' - 'SettingValue' = Get-Date + 'SettingValue' = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffK") } Add-AzDataTableEntity @Table -Entity $AddObject -Force From 58ffbc98505a4eec8a375de78b0215add12d0d44 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sun, 10 Sep 2023 23:05:15 +0100 Subject: [PATCH 03/77] Added NinjaOne Users Table Added the sync of a users details table to NinjaOne Fields --- .../NinjaOne/Invoke-NinjaOneOrgMapping.ps1 | 48 ++- .../Invoke-NinjaOneOrgMappingTenant.ps1 | 6 +- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 397 +++++++++++------- 3 files changed, 271 insertions(+), 180 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 index ef8dc7d15a38..aba6264ff23b 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 @@ -59,44 +59,46 @@ function Invoke-NinjaOneOrgMapping { } while ($ResultCount.count -eq $PageSize) - $NinjaDevices = $NinjaDevicesRaw | Where-Object {$null -ne $_.system.serialNumber -and $_.system.serialNumber -notin $ExcludeSerials} | ForEach-Object { + $NinjaDevices = $NinjaDevicesRaw | Where-Object { $null -ne $_.system.serialNumber -and $_.system.serialNumber -notin $ExcludeSerials } | ForEach-Object { [pscustomobject]@{ - ID = $_.id - SystemName = $_.systemName - Serial = $_.system.serialNumber - OrgID = $_.organizationId + ID = $_.id + SystemName = $_.systemName + DNSName = $_.dnsName + Serial = $_.system.serialNumber + BiosSerialNumber = $_.system.biosSerialNumber + OrgID = $_.organizationId } } # Remove any devices with duplicate serials - $ParsedNinjaDevices = $NinjaDevices | Where-Object {$_.Serial -in (($NinjaDevices | Group-Object Serial | where-object {$_.count -eq 1}).name)} + $ParsedNinjaDevices = $NinjaDevices | Where-Object { $_.Serial -in (($NinjaDevices | Group-Object Serial | where-object { $_.count -eq 1 }).name) } # First lets match on Org names - foreach ($Tenant in $Tenants | Where-Object {$_.customerId -notin $MatchedM365Tenants.customerId}) { - $MatchedOrg = $NinjaOrgs | where-object { $_.name -eq $Tenant.displayName } - if (($MatchedOrg | Measure-Object).count -eq 1) { - $MatchedM365Tenants.add($Tenant) - $MatchedNinjaOrgs.add($MatchedOrg) - $AddObject = @{ - PartitionKey = 'NinjaOrgsMapping' - RowKey = "$($Tenant.customerId)" - 'NinjaOne' = "$($MatchedOrg.id)" - 'NinjaOneName' = "$($MatchedOrg.name)" - } - Add-AzDataTableEntity @CIPPMapping -Entity $AddObject -Force - Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "Added mapping from Organization name match for $($Tenant.customerId). to $($($MatchedOrg.name))" -Sev 'Info' + foreach ($Tenant in $Tenants | Where-Object { $_.customerId -notin $MatchedM365Tenants.customerId }) { + $MatchedOrg = $NinjaOrgs | where-object { $_.name -eq $Tenant.displayName } + if (($MatchedOrg | Measure-Object).count -eq 1) { + $MatchedM365Tenants.add($Tenant) + $MatchedNinjaOrgs.add($MatchedOrg) + $AddObject = @{ + PartitionKey = 'NinjaOrgsMapping' + RowKey = "$($Tenant.customerId)" + 'NinjaOne' = "$($MatchedOrg.id)" + 'NinjaOneName' = "$($MatchedOrg.name)" } + Add-AzDataTableEntity @CIPPMapping -Entity $AddObject -Force + Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "Added mapping from Organization name match for $($Tenant.customerId). to $($($MatchedOrg.name))" -Sev 'Info' } + } # Now Let match on remaining Tenants - Foreach ($Tenant in $Tenants | Where-Object {$_.customerId -notin $MatchedM365Tenants.customerId}) { + Foreach ($Tenant in $Tenants | Where-Object { $_.customerId -notin $MatchedM365Tenants.customerId }) { Push-OutputBinding -Name NinjaProcess -Value @{ - 'NinjaAction' = 'AutoMapTenant' - 'M365Tenant' = $Tenant - 'NinjaOrgs' = $NinjaOrgs | Where-Object {$_.id -notin $MatchedNinjaOrgs} + 'NinjaAction' = 'AutoMapTenant' + 'M365Tenant' = $Tenant + 'NinjaOrgs' = $NinjaOrgs | Where-Object { $_.id -notin $MatchedNinjaOrgs } 'NinjaDevices' = $ParsedNinjaDevices } diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMappingTenant.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMappingTenant.ps1 index 7ae3c6342633..317770c3bb78 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMappingTenant.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMappingTenant.ps1 @@ -28,9 +28,9 @@ function Invoke-NinjaOneOrgMappingTenant { [System.Collections.Generic.List[PSCustomObject]]$MatchedDevices = @() # Match devices on serial - $DevicesToMatchSerial = $M365Devices | where-object { $_.DeviceSerial -notin $ExcludeSerials -and $null -ne $_.DeviceSerial } + $DevicesToMatchSerial = $M365Devices | where-object { $null -ne $_.DeviceSerial } foreach ($SerialMatchDevice in $DevicesToMatchSerial) { - $MatchedDevice = $NinjaDevices | where-object { $_.serial -eq $SerialMatchDevice.DeviceSerial -and $_.OrgID -notin $MatchedNinjaOrgs.id } + $MatchedDevice = $NinjaDevices | where-object { $_.Serial -eq $SerialMatchDevice.DeviceSerial -or $_.BiosSerialNumber -eq $SerialMatchDevice.DeviceSerial } if (($MatchedDevice | measure-object).count -eq 1) { $Match = [pscustomobject]@{ M365 = $SerialMatchDevice @@ -43,7 +43,7 @@ function Invoke-NinjaOneOrgMappingTenant { # Try to match on Name $DevicesToMatchName = $M365Devices | where-object { $_ -notin $MatchedDevices.M365 } foreach ($NameMatchDevice in $DevicesToMatchName) { - $MatchedDevice = $NinjaDevices | where-object { $_.SystemName -eq $NameMatchDevice.DeviceName -and $_.OrgID -notin $MatchedNinjaOrgs.id } + $MatchedDevice = $NinjaDevices | where-object { $_.SystemName -eq $NameMatchDevice.DeviceName -or $_.DNSName -eq $NameMatchDevice.DeviceName } if (($MatchedDevice | measure-object).count -eq 1) { $Match = [pscustomobject]@{ M365 = $NameMatchDevice diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index fad3356d2ead..36b12121c58b 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -5,13 +5,15 @@ function Invoke-NinjaOneTenantSync { ) try { + $StartTime = Get-Date + write-host "$(Get-Date) - Starting NinjaOne Sync $($customer.DisplayName)" + + # Fetch Custom NinjaOne Settings $Table = Get-CIPPTable -TableName NinjaOneSettings $NinjaSettings = (Get-AzDataTableEntity @Table) $CIPPUrl = ($NinjaSettings | Where-Object { $_.RowKey -eq 'CIPPURL' }).SettingValue - - $StartTime = Get-Date - + # Parse out the Tenant we are processing $MappedTenant = $QueueItem.MappedTenant $Customer = Get-Tenants | where-object { $_.customerId -eq $MappedTenant.RowKey } @@ -20,12 +22,14 @@ function Invoke-NinjaOneTenantSync { } $TenantFilter = $Customer.defaultDomainName + $NinjaOneOrg = $MappedTenant.NinjaOne - write-host "$(Get-Date) - Starting NinjaOne Sync $($customer.DisplayName)" + # Get the NinjaOne general extension settings. $Table = Get-CIPPTable -TableName Extensionsconfig $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json).NinjaOne + # Pull the list of field Mappings so we know which fields to render. $MappedFields = [pscustomobject]@{} $CIPPMapping = Get-CIPPTable -TableName CippMapping $Filter = "PartitionKey eq 'NinjaFieldMapping'" @@ -33,8 +37,21 @@ function Invoke-NinjaOneTenantSync { $MappedFields | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue $($_.NinjaOne) } - $NinjaOrgUpdate = [PSCustomObject]@{} + # Get NinjaOne Devices + $Token = Get-NinjaOneToken -configuration $Configuration + $After = 0 + $PageSize = 1000 + $NinjaDevices = do { + $Result = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/devices-detailed?pageSize=$PageSize&after=$After&df=org = $($NinjaOneOrg)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + $Result + $ResultCount = ($Result.id | Measure-Object -Maximum) + $After = $ResultCount.maximum + + } while ($ResultCount.count -eq $PageSize) + # Create the object we will use for the Org update + $NinjaOrgUpdate = [PSCustomObject]@{} + # Build bulk requests array. [System.Collections.Generic.List[PSCustomObject]]$TenantRequests = @( @{ @@ -408,6 +425,88 @@ function Invoke-NinjaOneTenantSync { ############################ Format and Synchronize to NinjaOne ############################ + + # Parse Devices + [System.Collections.Generic.List[PSCustomObject]]$ParsedDevices = Foreach ($Device in $Devices) { + # Match Users + [System.Collections.Generic.List[String]]$DeviceUsers = @() + [System.Collections.Generic.List[String]]$DeviceUserIDs = @() + [System.Collections.Generic.List[PSCustomObject]]$DeviceUsersDetail = @() + Foreach ($DeviceUser in $Device.usersloggedon) { + $FoundUser = ($Users | Where-Object { $_.id -eq $DeviceUser.userid }) + $DeviceUsers.add($FoundUser.DisplayName) + $DeviceUserIDs.add($DeviceUser.userId) + $DeviceUsersDetail.add([pscustomobject]@{ + id = $FoundUser.Id + name = $FoundUser.displayName + upn = $FoundUser.userPrincipalName + lastlogin = ($DeviceUser.lastLogOnDateTime).ToString("yyyy-MM-dd") + } + ) + } + + # Compliance Polciies + [System.Collections.Generic.List[PSCustomObject]]$DevicePolcies = @() + foreach ($Policy in $DeviceComplianceDetails) { + if ($device.deviceName -in $Policy.DeviceStatuses.deviceDisplayName) { + $Status = $Policy.DeviceStatuses | Where-Object { $_.deviceDisplayName -eq $device.deviceName } + foreach ($Stat in $Status) { + if ($Stat.status -ne 'unknown') { + $DevicePolcies.add([PSCustomObject]@{ + Name = $Policy.DisplayName + User = $Stat.username + Status = $Stat.status + 'Last Report' = "$(Get-Date($Stat.lastReportedDateTime[0]) -Format 'yyyy-MM-dd HH:mm:ss')" + 'Grace Expiry' = "$(Get-Date($Stat.complianceGracePeriodExpirationDateTime[0]) -Format 'yyyy-MM-dd HH:mm:ss')" + }) + } + } + + } + } + + # Device Groups + $DeviceGroups = foreach ($Group in $Groups) { + if ($device.azureADDeviceId -in $Group.members.deviceId) { + [PSCustomObject]@{ + Name = $Group.displayName + } + } + } + + + + [PSCustomObject]@{ + Name = $Device.deviceName + SerialNumber = $Device.serialNumber + OS = $Device.operatingSystem + OSVersion = $Device.osversion + Compliance = $Device.complianceState + LastSync = $Device.lastSyncDateTime + PrimaryUser = $Device.userDisplayName + Owner = $Device.ownerType + DeviceType = $Device.DeviceType + Make = $Device.make + Model = $Device.model + ManagementState = $Device.managementState + RegistrationState = $Device.deviceRegistrationState + JailBroken = $Device.jailBroken + EnrollmentType = $Device.deviceEnrollmentType + EntraIDRegistration = $Device.azureADRegistered + EntraIDID = $Device.azureADDeviceId + JoinType = $Device.joinType + SecurityPatchLevel = $Device.securityPatchLevel + Users = $DeviceUsers -join ', ' + UserIDs = $DeviceUserIDs + UserDetails = $DeviceUsersDetail + CompliancePolicies = $DevicePolcies + Groups = $DeviceGroups + + } + + } + + ### M365 Links Section if ($MappedFields.TenantLinks) { $ManagementLinksData = @( @@ -510,7 +609,7 @@ function Invoke-NinjaOneTenantSync { if ($MappedFields.TenantSummary) { - # Tenant Overview Card + ### Tenant Overview Card $ParsedAdmins = [PSCustomObject]@{} $AdminUsers | Select-Object displayname, userPrincipalName -unique | ForEach-Object { @@ -860,188 +959,178 @@ function Invoke-NinjaOneTenantSync { } if ($MappedFields.UsersSummary) { + # Parse all users: + [System.Collections.Generic.List[PSCustomObject]]$ParsedUsers = Foreach ($User in $Users | where-object { $_.userType -ne 'Guest' -and $_.accountEnabled -eq $True }) { + + $UserDevices = foreach ($UserDevice in $ParsedDevices | where-object { $User.id -in $_.UserIDS }) { + # First lets match on serial + $MatchedNinjaDevice = $NinjaDevices | Where-Object { $_.system.biosSerialNumber -eq $UserDevice.SerialNumber -or $_.system.serialNumber -eq $UserDevice.SerialNumber } - [System.Collections.Generic.List[PSCustomObject]]$ParsedDevices = Foreach ($Device in $Devices) { - # Match Users - [System.Collections.Generic.List[String]]$DeviceUsers = @() - [System.Collections.Generic.List[String]]$DeviceUserIDs = @() - [System.Collections.Generic.List[PSCustomObject]]$DeviceUsersDetail = @() - Foreach ($DeviceUser in $Device.usersloggedon) { - $FoundUser = ($Users | Where-Object { $_.id -eq $DeviceUser.userid }) - $DeviceUsers.add($FoundUser.DisplayName) - $DeviceUserIDs.add($DeviceUser.userId) - $DeviceUsersDetail.add([pscustomobject]@{ - id = $FoundUser.Id - name = $FoundUser.displayName - upn = $FoundUser.userPrincipalName - lastlogin = ($DeviceUser.lastLogOnDateTime).ToString("yyyy-MM-dd") - } - ) - } + # See if we found just one device, if not match on name + if (($MatchedNinjaDevice | Measure-Object).count -ne 1) { + $MatchedNinjaDevice = $NinjaDevices | Where-Object { $_.systemName -eq $UserDevice.Name -or $_.dnsName -eq $UserDevice.Name } + } - # Compliance Polciies - [System.Collections.Generic.List[PSCustomObject]]$DevicePolcies = @() - foreach ($Policy in $DeviceComplianceDetails) { - if ($device.deviceName -in $Policy.DeviceStatuses.deviceDisplayName) { - $Status = $Policy.DeviceStatuses | Where-Object { $_.deviceDisplayName -eq $device.deviceName } - foreach ($Stat in $Status) { - if ($Stat.status -ne 'unknown') { - $DevicePolcies.add([PSCustomObject]@{ - Name = $Policy.DisplayName - User = $Stat.username - Status = $Stat.status - 'Last Report' = "$(Get-Date($Stat.lastReportedDateTime[0]) -Format 'yyyy-MM-dd HH:mm:ss')" - 'Grace Expiry' = "$(Get-Date($Stat.complianceGracePeriodExpirationDateTime[0]) -Format 'yyyy-MM-dd HH:mm:ss')" - }) - } - } - + # Check on a match again and set name + if (($MatchedNinjaDevice | Measure-Object).count -eq 1) { + $ParsedDeviceName = '' + $UserDevice.Name + '' + Write-Host "Parsed Device Name: $ParsedDeviceName" + } else { + $ParsedDeviceName = $UserDevice.Name + } + + # Set Last Login Time + $LastLoginTime = ($UserDevice.UserDetails | where-object { $_.id -eq $User.id }).lastLogin + if (!$LastLoginTime) { + $LastLoginTime = 'Unknown' } - } - # Device Groups - $DeviceGroups = foreach ($Group in $Groups) { - if ($device.azureADDeviceId -in $Group.members.deviceId) { - [PSCustomObject]@{ - Name = $Group.displayName - } + # Set Compliance Status + if ($UserDevice.Compliance -eq 'compliant') { + $ComplianceIcon = '' + } else { + $ComplianceIcon = '' } - } - + # OS Icon + $OSIcon = Switch ($UserDevice.OS) { + 'Windows' { '' } + 'iOS' { '' } + 'Android' { '' } + 'macOS' { '' } + } - [PSCustomObject]@{ - Name = $Device.deviceName - OS = $Device.operatingSystem - OSVersion = $Device.osversion - Compliance = $Device.complianceState - LastSync = $Device.lastSyncDateTime - PrimaryUser = $Device.userDisplayName - Owner = $Device.ownerType - DeviceType = $Device.DeviceType - Make = $Device.make - Model = $Device.model - ManagementState = $Device.managementState - RegistrationState = $Device.deviceRegistrationState - JailBroken = $Device.jailBroken - EnrollmentType = $Device.deviceEnrollmentType - EntraIDRegistration = $Device.azureADRegistered - EntraIDID = $Device.azureADDeviceId - JoinType = $Device.joinType - SecurityPatchLevel = $Device.securityPatchLevel - Users = $DeviceUsers -join ', ' - UserIDs = $DeviceUserIDs - UserDetails = $DeviceUsersDetail - CompliancePolicies = $DevicePolcies - Groups = $DeviceGroups - - } - - } + '
          • ' + "$ComplianceIcon $OSIcon $($ParsedDeviceName) ($LastLoginTime)
          • " + + } - $TotalDevices = ($Devices | Measure-Object).count - $OSGroups = $Devices | Group-Object operatingSystem - - } + $userLicenses = ($user.AssignedLicenses.SkuID | ForEach-Object { + $UserLic = $_ + try { + $SkuPartNumber = ($Licenses | Where-Object { $_.SkuId -eq $UserLic }).SkuPartNumber + '
          • ' + "$((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $SkuPartNumber).Tolower()))
          • " + } catch {} + }) -join '' + + $UserMailboxStats = $MailboxStatsFull | where-object { $_.'User Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 + $UserMailUse = $UserMailboxStats.'Storage Used (Byte)' / 1GB + $UserMailTotal = $UserMailboxStats.'Prohibit Send/Receive Quota (Byte)' / 1GB + if ($UserMailTotal) { + $MailboxUse = [PSCustomObject]@{ + Enabled = $True + Used = $UserMailUse + Total = $UserMailTotal + Percent = ($UserMailUse / $UserMailTotal) * 100 + } - # Parse all users: - [System.Collections.Generic.List[PSCustomObject]]$ParsedUsers = Foreach ($User in $Users | where-object { $_.userType -ne 'Guest' -and $_.accountEnabled -eq $True }) { - - $UserDevices = foreach ($UserDevice in $ParsedDevices | where-object { $User.id -in $_.UserIDS }) { - "$($UserDevice.Name) (Last Login: $(($UserDevice.UserDetails | where-object {$_.id -eq $User.id}).lastLogin))" - } + $MailboxUseColor = if ($MailboxUse.Percent -ge 95) { + '#ec1c24' + } elseif ($MailboxUse.Percent -ge 85) { + '#FFA500' + } else { + '#008001' + } + $MailboxParsed = '
            ' - $userLicenses = ($user.AssignedLicenses.SkuID | ForEach-Object { - $UserLic = $_ - $SkuPartNumber = ($Licenses | Where-Object { $_.SkuId -eq $UserLic }).SkuPartNumber - try { - "$((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $SkuPartNumber).Tolower()))" - } catch { - "$SkuPartNumber" + } else { + $MailboxUse = [PSCustomObject]@{ + Enabled = $False + Used = 0 + Total = 0 + Percent = 0 } - }) -join ', ' - - $UserMailboxStats = $MailboxStatsFull | where-object { $_.'User Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 - $UserMailUse = $UserMailboxStats.'Storage Used (Byte)' / 1GB - $UserMailTotal = $UserMailboxStats.'Prohibit Send/Receive Quota (Byte)' / 1GB - if ($UserMailTotal) { - $MailboxUse = [PSCustomObject]@{ - Enabled = $True - Used = $UserMailUse - Total = $UserMailTotal - Percent = ($UserMailUse / $UserMailTotal) * 100 - } - } else { - $MailboxUse = [PSCustomObject]@{ - Enabled = $False - Used = 0 - Total = 0 - Percent = 0 + + $MailboxParsed = 'Not Enabled' } - } - $UserOneDriveStats = $OneDriveDetails | where-object { $_.'Owner Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 - $UserOneDriveUse = $UserOneDriveStats.'Storage Used (Byte)' / 1GB - $UserOneDriveTotal = $UserOneDriveStats.'Storage Allocated (Byte)' / 1GB - if ($UserOneDriveTotal) { - $OneDriveUse = [PSCustomObject]@{ - Enabled = $True - Used = $UserOneDriveUse - Total = $UserOneDriveTotal - Percent = ($UserOneDriveUse / $UserOneDriveTotal) * 100 + + $UserOneDriveStats = $OneDriveDetails | where-object { $_.'Owner Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 + $UserOneDriveUse = $UserOneDriveStats.'Storage Used (Byte)' / 1GB + $UserOneDriveTotal = $UserOneDriveStats.'Storage Allocated (Byte)' / 1GB + if ($UserOneDriveTotal) { + $OneDriveUse = [PSCustomObject]@{ + Enabled = $True + Used = $UserOneDriveUse + Total = $UserOneDriveTotal + Percent = ($UserOneDriveUse / $UserOneDriveTotal) * 100 + } + + $OneDriveUseColor = if ($OneDriveUse.Percent -ge 95) { + '#ec1c24' + } elseif ($MailboxUse.Percent -ge 85) { + '#FFA500' + } else { + '#008001' + } + + $OneDriveParsed = '
            ' + + } else { + $OneDriveUse = [PSCustomObject]@{ + Enabled = $False + Used = 0 + Total = 0 + Percent = 0 + } + + $OneDriveParsed = 'Not Enabled' } - } else { - $OneDriveUse = [PSCustomObject]@{ - Enabled = $False - Used = 0 - Total = 0 - Percent = 0 + + # Actions + $ActionsHTML = @" +   +   +   +"@ + + [PSCustomObject]@{ + Name = $User.displayName + UPN = $User.userPrincipalName + Aliases = ($User.proxyAddresses -replace 'SMTP:', '') -join ', ' + Licenses = "
              $userLicenses
            " + Mailbox = $MailboxUse + MailboxParsed = $MailboxParsed + OneDrive = $OneDriveUse + OneDriveParsed = $OneDriveParsed + Devices = "
              $($UserDevices -join '')
            " + Actions = $ActionsHTML } - } - [PSCustomObject]@{ - Name = $User.displayName - UPN = $User.userPrincipalName - Aliases = ($User.proxyAddresses -replace 'SMTP:', '') -join ', ' - Devices = $UserDevices -join ', ' - Licenses = $userLicenses - Mailbox = $MailboxUse - OneDrive = $OneDriveUse } + + $UsersTableFornatted = $ParsedUsers | Select-Object Name, + @{n = 'User Principal Name'; e = { $_.UPN } }, + #Aliases, + Licenses, + @{n = 'Mailbox Usage'; e = { $_.MailboxParsed } }, + @{n = 'One Drive Usage'; e = { $_.OneDriveParsed } }, + @{n = 'Devices (Last Login)'; e = { $_.Devices } }, + Actions + - } + $UsersTableHTML = $UsersTableFornatted | ConvertTo-HTML -As Table -Fragment + $UsersTableHTML = ([System.Web.HttpUtility]::HtmlDecode($UsersTableHTML) -replace '
    ', '') -replace '', '' + + $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.UsersSummary -NotePropertyValue @{'html' = $UsersTableHTML } - $UsersTable = ($ParsedUsers | ConvertTo-HTML -As Table).Replace('', '
    ') - $DevicesTable = ($ParsedDevices | ConvertTo-HTML -As Table).Replace('
    ', '
    ') + } - $HTML = @" - -
    - $TenantSummaryCard - $UserSummaryCardHTML -

    User Details

    - $UsersTable -

    Device Details

    - $DevicesTable -
    -"@ #Get available Ninja clients $Token = Get-NinjaOneToken -configuration $Configuration - Write-Host "Got to End. OrgUpdate: $($NinjaOrgUpdate | ConvertTo-Json -Depth 100)" $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($MappedTenant.NinjaOne)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaOrgUpdate | ConvertTo-Json -Depth 100) - - Write-Host "Ninja Result: $($Result.content)" + Write-Host "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" + } catch { Write-Host "FATAL ERROR: $_" } From ee5bfc92a11fa561f84899a96e60b5cdd8be74d9 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sat, 16 Sep 2023 19:50:43 +0100 Subject: [PATCH 04/77] Webhooks rework and Graph Subscriptions First version of reworking webhooks to use queues and starting to implement graph subscriptions further. --- .../Invoke-CIPPGraphWebhookProcessing.ps1 | 11 ++++ .../Public/Invoke-CIPPGraphWebhookRenewal.ps1 | 23 ++++++++ .../Public/New-CIPPGraphSubscription.ps1 | 25 ++++---- PublicWebhooks/function.json | 6 ++ PublicWebhooks/run.ps1 | 46 ++------------- PublicWebhooksProcess/function.json | 10 ++++ PublicWebhooksProcess/run.ps1 | 59 +++++++++++++++++++ .../function.json | 10 ++++ Scheduler_RenewGraphSubscriptions/run.ps1 | 10 ++++ 9 files changed, 147 insertions(+), 53 deletions(-) create mode 100644 Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookProcessing.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookRenewal.ps1 create mode 100644 PublicWebhooksProcess/function.json create mode 100644 PublicWebhooksProcess/run.ps1 create mode 100644 Scheduler_RenewGraphSubscriptions/function.json create mode 100644 Scheduler_RenewGraphSubscriptions/run.ps1 diff --git a/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookProcessing.ps1 new file mode 100644 index 000000000000..ad04c364c432 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookProcessing.ps1 @@ -0,0 +1,11 @@ +function Invoke-CippGraphWebhookProcessing { + [CmdletBinding()] + param ( + $Data, + $CIPPID, + $WebhookInfo + ) + # To do # + + +} diff --git a/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookRenewal.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookRenewal.ps1 new file mode 100644 index 000000000000..ab2686655b39 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookRenewal.ps1 @@ -0,0 +1,23 @@ +function Invoke-CippGraphWebhookRenewal { + $RenewalDate = (Get-Date).AddDays(1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") + $body = @{ + "expirationDateTime" = "$RenewalDate" + } | ConvertTo-Json + + + $WebhookTable = Get-CIPPTable -TableName webhookTable + $WebhookData = Get-AzDataTableEntity @WebhookTable | Where-Object { $null -ne $_.SubscriptionID -and $_.SubscriptionID -ne '' -and ((Get-Date($_.Expiration)) -le ((Get-Date).AddHours(2))) } + + foreach ($UpdateSub in $WebhookData) { + try { + $TenantFilter = $UpdateSub.PartitionKey + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/subscriptions/$($UpdateSub.SubscriptionID)" -tenantid $TenantFilter -type PATCH -body $body -Verbose + $UpdateSub.Expiration = $RenewalDate + $null = Add-AzDataTableEntity @WebhookTable -Entity $UpdateSub -Force + Write-LogMessage -user 'CIPP' -API 'Renew_Graph_Subscriptions' -message "Renewed Subscription:$($UpdateSub.SubscriptionID)" -Sev "Info" -tenant $TenantFilter + + } catch { + Write-LogMessage -user 'CIPP' -API 'Renew_Graph_Subscriptions' -message "Failed to renew Webhook Subscription: $($UpdateSub.SubscriptionID)" -Sev "Error" -tenant $TenantFilter + } + } +} diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index ae42c1828b65..985950fb582e 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -13,13 +13,6 @@ function New-CIPPGraphSubscription { $ExecutingUser ) $CIPPID = (New-Guid).GUID - $expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") - $params = @{ - changeType = $TypeofSubscription - notificationUrl = "$BaseURL/API/PublicWebhooks?EventType=$EventType&CIPPID=$CIPPID" - resource = $Resource - expirationDateTime = $expiredate - } | ConvertTo-Json $WebhookTable = Get-CIPPTable -TableName webhookTable try { @@ -42,8 +35,16 @@ function New-CIPPGraphSubscription { WebhookNotificationUrl = [string]$Auditlog.webhook.address } $null = Add-AzDataTableEntity @WebhookTable -Entity $WebhookRow - } - else { + } else { + $expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") + $params = @{ + changeType = $TypeofSubscription + notificationUrl = "$BaseURL/API/PublicWebhooks?EventType=$EventType&CIPPID=$CIPPID?Type=GraphSubscription" + resource = $Resource + expirationDateTime = $expiredate + } | ConvertTo-Json + + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/subscriptions" -tenantid $TenantFilter -type POST -body $params -verbose #If creation is succesfull, we store the GUID in the storage table webhookTable to make sure we can check against this later on. #We store the GUID as rowkey, the event type, the resource, and the expiration date as properties, we also add the Tenant name so we can easily find this later on. @@ -54,8 +55,7 @@ function New-CIPPGraphSubscription { EventType = [string]$EventType Resource = [string]$Resource Expiration = [string]$expiredate - Operations = [string]$operations - AllowedLocations = [string]$AllowedLocations + SubscriptionID = [string]$GraphRequest.id WebhookNotificationUrl = [string]$GraphRequest.notificationUrl } $null = Add-AzDataTableEntity @WebhookTable -Entity $WebhookRow @@ -64,8 +64,7 @@ function New-CIPPGraphSubscription { } Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter)" -Sev "Info" -tenant $TenantFilter return "Created Webhook subscription for $($TenantFilter)" - } - catch { + } catch { Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to create Webhook Subscription: $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter Return "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)" } diff --git a/PublicWebhooks/function.json b/PublicWebhooks/function.json index 4ee273331c44..f59adac3eb84 100644 --- a/PublicWebhooks/function.json +++ b/PublicWebhooks/function.json @@ -11,6 +11,12 @@ "type": "http", "direction": "out", "name": "Response" + }, + { + "type": "queue", + "direction": "out", + "name": "QueueWebhook", + "queueName": "webhooksqueue" } ] } diff --git a/PublicWebhooks/run.ps1 b/PublicWebhooks/run.ps1 index 3e06a1cea831..86b31883a712 100644 --- a/PublicWebhooks/run.ps1 +++ b/PublicWebhooks/run.ps1 @@ -2,50 +2,16 @@ using namespace System.Net # Input bindings are passed in via param block. param($Request, $TriggerMetadata) -$WebhookTable = Get-CIPPTable -TableName webhookTable -$Webhooks = Get-AzDataTableEntity @WebhookTable -Write-Host "Received request" -Write-Host "CIPPID: $($request.Query.CIPPID)" -$url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 -Write-Host $url -if ($Request.CIPPID -in $Webhooks.CIPPID) { - Write-Host "Found matching CIPPID" - $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID - $operations = $Webhookinfo.Operations -split ',' - if ($Request.query.ValidationToken -or $Request.body.validationCode) { - Write-Host "Validation token received" - $body = $request.query.ValidationToken - } +Push-OutputBinding -Name QueueWebhook -Value $Request - foreach ($ReceivedItem In ($Request.body)) { - $ReceivedItem = [pscustomobject]$ReceivedItem - $TenantFilter = (Get-Tenants | Where-Object -Property customerId -EQ $ReceivedItem.TenantId).defaultDomainName - Write-Host "TenantFilter: $TenantFilter" - $Data = New-GraphPostRequest -type GET -uri "https://manage.office.com/api/v1.0/$($ReceivedItem.tenantId)/activity/feed/audit/$($ReceivedItem.contentid)" -tenantid $TenantFilter -scope "https://manage.office.com/.default" - Write-Host "Data to process found: $(($ReceivedItem.operation).count) items" - Write-Host "Operations to process for this client: $($Webhookinfo.Operations)" - foreach ($Item in $Data) { - Write-Host "Processing $($item.operation)" - if ($item.operation -in $operations) { - Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations - } - if ($item.operation -eq "UserLoggedIn" -and "UserLoggedInFromUnknownLocation" -in $operations) { - Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations - } - if ($item.operation -eq "UserLoggedIn" -and "AdminLoggedIn" -in $operations) { - Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations - } - $body = "OK" - } - } - -} -else { - $body = "This webhook is not authorized." +if ($Request.query.ValidationToken -or $Request.body.validationCode) { + Write-Host "Validation token received" + $body = $request.query.ValidationToken +} else { + $Body = 'Webhook Recieved' } - # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK diff --git a/PublicWebhooksProcess/function.json b/PublicWebhooksProcess/function.json new file mode 100644 index 000000000000..d358059b9e50 --- /dev/null +++ b/PublicWebhooksProcess/function.json @@ -0,0 +1,10 @@ +{ + "bindings": [ + { + "name": "QueueItem", + "type": "queueTrigger", + "direction": "in", + "queueName": "webhooksqueue" + } + ] +} diff --git a/PublicWebhooksProcess/run.ps1 b/PublicWebhooksProcess/run.ps1 new file mode 100644 index 000000000000..5a26becf3416 --- /dev/null +++ b/PublicWebhooksProcess/run.ps1 @@ -0,0 +1,59 @@ +using namespace System.Net + +# Input bindings are passed in via param block. +param($QueueItem, $TriggerMetadata) + +$Request = $QueueItem + +$WebhookTable = Get-CIPPTable -TableName webhookTable +$Webhooks = Get-AzDataTableEntity @WebhookTable +Write-Host "Received request" +Write-Host "CIPPID: $($request.Query.CIPPID)" +$url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 +Write-Host $url +if ($Request.query.CIPPID-in $Webhooks.CIPPID) { + Write-Host "Found matching CIPPID" + $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID + + if ($Request.Query.Type = 'GraphSubscription') { + # Graph Subscriptions + [pscustomobject]$ReceivedItem = $Request.Body.value + Invoke-CippGraphWebhookProcessing -Data $ReceivedItem -CIPPID $request.Query.CIPPID -WebhookInfo $Webhookinfo + + } else { + # Auditlog Subscriptions + $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID + $operations = $Webhookinfo.Operations -split ',' + foreach ($ReceivedItem In ($Request.body)) { + $ReceivedItem = [pscustomobject]$ReceivedItem + $TenantFilter = (Get-Tenants | Where-Object -Property customerId -EQ $ReceivedItem.TenantId).defaultDomainName + Write-Host "TenantFilter: $TenantFilter" + $Data = New-GraphPostRequest -type GET -uri "https://manage.office.com/api/v1.0/$($ReceivedItem.tenantId)/activity/feed/audit/$($ReceivedItem.contentid)" -tenantid $TenantFilter -scope "https://manage.office.com/.default" + Write-Host "Data to process found: $(($ReceivedItem.operation).count) items" + Write-Host "Operations to process for this client: $($Webhookinfo.Operations)" + foreach ($Item in $Data) { + Write-Host "Processing $($item.operation)" + if ($item.operation -in $operations) { + Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations + } + if ($item.operation -eq "UserLoggedIn" -and "UserLoggedInFromUnknownLocation" -in $operations) { + Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations + } + if ($item.operation -eq "UserLoggedIn" -and "AdminLoggedIn" -in $operations) { + Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations + } + $body = "OK" + } + } + } + +} else { + Write-Host 'Unauthorised Webhook' +} + + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) diff --git a/Scheduler_RenewGraphSubscriptions/function.json b/Scheduler_RenewGraphSubscriptions/function.json new file mode 100644 index 000000000000..d2e7f34face4 --- /dev/null +++ b/Scheduler_RenewGraphSubscriptions/function.json @@ -0,0 +1,10 @@ +{ + "bindings": [ + { + "name": "Timer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 0 * * *" + } + ] +} diff --git a/Scheduler_RenewGraphSubscriptions/run.ps1 b/Scheduler_RenewGraphSubscriptions/run.ps1 new file mode 100644 index 000000000000..b688e87e8b17 --- /dev/null +++ b/Scheduler_RenewGraphSubscriptions/run.ps1 @@ -0,0 +1,10 @@ +# Input bindings are passed in via param block. +param($Timer) + +# Get the current universal time in the default string format. +try { + Write-LogMessage -API "Scheduler_RenewGraphSubscriptions" -tenant "none" -message "Starting Graph Subscription Renewal" -sev Info + Invoke-CippGraphWebhookRenewal +} catch { + Write-LogMessage -API "Scheduler_RenewGraphSubscriptions" -tenant "none" -message "Failed to renew graph subscriptions" -sev Info +} \ No newline at end of file From 2a82489468d10d5b10d896ddedc594e17aa01ae6 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sun, 17 Sep 2023 19:36:29 +0100 Subject: [PATCH 05/77] Synchronization of Users to Documents V1 --- GraphHelper.psm1 | 111 +-- Modules/CippExtensions/CippExtensions.psd1 | Bin 11136 -> 11224 bytes .../Invoke-NinjaOneDocumentTemplate.ps1 | 37 + .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 733 ++++++++++++++---- .../NinjaOne/NinjaOneHelper.ps1 | 44 +- 5 files changed, 663 insertions(+), 262 deletions(-) create mode 100644 Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDocumentTemplate.ps1 diff --git a/GraphHelper.psm1 b/GraphHelper.psm1 index ea9269776748..cdd9a7f52ec7 100644 --- a/GraphHelper.psm1 +++ b/GraphHelper.psm1 @@ -73,8 +73,7 @@ function Get-GraphToken($tenantid, $scope, $AsApp, $AppID, $refreshToken, $Retur if ($script:AccessTokens.$TokenKey -and [int](Get-Date -UFormat %s -Millisecond 0) -lt $script:AccessTokens.$TokenKey.expires_on) { Write-Host 'Graph: cached token' $AccessToken = $script:AccessTokens.$TokenKey - } - else { + } else { Write-Host 'Graph: new token' $AccessToken = (Invoke-RestMethod -Method post -Uri "https://login.microsoftonline.com/$($tenantid)/oauth2/v2.0/token" -Body $Authbody -ErrorAction Stop) $ExpiresOn = [int](Get-Date -UFormat %s -Millisecond 0) + $AccessToken.expires_in @@ -86,8 +85,7 @@ function Get-GraphToken($tenantid, $scope, $AsApp, $AppID, $refreshToken, $Retur if ($ReturnRefresh) { $header = $AccessToken } else { $header = @{ Authorization = "Bearer $($AccessToken.access_token)" } } return $header #Write-Host $header['Authorization'] - } - catch { + } catch { # Track consecutive Graph API failures $TenantsTable = Get-CippTable -tablename Tenants $Filter = "PartitionKey eq 'Tenants' and (defaultDomainName eq '{0}' or customerId eq '{0}')" -f $tenantid @@ -105,8 +103,7 @@ function Get-GraphToken($tenantid, $scope, $AsApp, $AppID, $refreshToken, $Retur $Tenant.LastGraphError = if ( $_.ErrorDetails.Message) { $msg = $_.ErrorDetails.Message | ConvertFrom-Json "$($msg.error):$($msg.error_description)" - } - else { + } else { $_.Exception.message } $Tenant.GraphErrorCount++ @@ -119,8 +116,7 @@ function Get-GraphToken($tenantid, $scope, $AsApp, $AppID, $refreshToken, $Retur function Write-LogMessage ($message, $tenant = 'None', $API = 'None', $user, $sev) { try { $username = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($user)) | ConvertFrom-Json).userDetails - } - catch { + } catch { $username = $user } @@ -163,8 +159,7 @@ function New-GraphGetRequest { if ($scope -eq 'ExchangeOnline') { $AccessToken = Get-ClassicAPIToken -resource 'https://outlook.office365.com' -Tenantid $tenantid $headers = @{ Authorization = "Bearer $($AccessToken.access_token)" } - } - else { + } else { $headers = Get-GraphToken -tenantid $tenantid -scope $scope -AsApp $asapp } @@ -192,13 +187,11 @@ function New-GraphGetRequest { if ($CountOnly) { $Data.'@odata.count' $nextURL = $null - } - else { + } else { if ($data.value) { $data.value } else { ($Data) } if ($noPagination) { $nextURL = $null } else { $nextURL = $data.'@odata.nextLink' } } - } - catch { + } catch { $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message if ($Message -eq $null) { $Message = $($_.Exception.Message) } if ($Message -ne 'Request not applicable to target tenant.' -and $Tenant) { @@ -212,8 +205,7 @@ function New-GraphGetRequest { $Tenant.LastGraphError = '' Update-AzDataTableEntity @TenantsTable -Entity $Tenant return $ReturnedData - } - else { + } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' } } @@ -228,22 +220,19 @@ function New-GraphPOSTRequest ($uri, $tenantid, $body, $type, $scope, $AsApp, $N try { $ReturnedData = (Invoke-RestMethod -Uri $($uri) -Method $TYPE -Body $body -Headers $headers -ContentType 'application/json; charset=utf-8') - } - catch { + } catch { #setting ErrorMess because the error from a failed json conversion overwrites the exception. $ErrorMess = $($_.Exception.Message) try { $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction Stop).error.message - } - catch { + } catch { $Message = $ErrorMess } throw $Message } return $ReturnedData - } - else { + } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' } } @@ -261,8 +250,7 @@ function Get-ClassicAPIToken($tenantID, $Resource) { if ($script:classictoken.$TokenKey -and [int](Get-Date -UFormat %s -Millisecond 0) -lt $script:classictoken.$TokenKey.expires_on) { Write-Host 'Classic: cached token' return $script:classictoken.$TokenKey - } - else { + } else { Write-Host 'Using classic' $uri = "https://login.microsoftonline.com/$($TenantID)/oauth2/token" $Body = @{ @@ -276,8 +264,7 @@ function Get-ClassicAPIToken($tenantID, $Resource) { if (!$script:classictoken) { $script:classictoken = [HashTable]::Synchronized(@{}) } $script:classictoken.$TokenKey = Invoke-RestMethod $uri -Body $body -ContentType 'application/x-www-form-urlencoded' -ErrorAction SilentlyContinue -Method post return $script:classictoken.$TokenKey - } - catch { + } catch { # Track consecutive Graph API failures $TenantsTable = Get-CippTable -tablename Tenants $Filter = "PartitionKey eq 'Tenants' and (defaultDomainName eq '{0}' or customerId eq '{0}')" -f $tenantid @@ -318,14 +305,12 @@ function New-TeamsAPIGetRequest($Uri, $tenantID, $Method = 'GET', $Resource = '4 } $Data if ($noPagination) { $nextURL = $null } else { $nextURL = $data.NextLink } - } - catch { + } catch { throw "Failed to make Teams API Get Request $_" } } until ($null -eq $NextURL) return $ReturnedData - } - else { + } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' } } @@ -347,14 +332,12 @@ function New-ClassicAPIGetRequest($TenantID, $Uri, $Method = 'GET', $Resource = } $Data if ($noPagination) { $nextURL = $null } else { $nextURL = $data.NextLink } - } - catch { + } catch { throw "Failed to make Classic Get Request $_" } } until ($null -eq $NextURL) return $ReturnedData - } - else { + } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' } } @@ -374,13 +357,11 @@ function New-ClassicAPIPostRequest($TenantID, $Uri, $Method = 'POST', $Resource } - } - catch { + } catch { throw "Failed to make Classic Get Request $_" } return $ReturnedData - } - else { + } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' } } @@ -401,8 +382,7 @@ function Get-AuthorisedRequest { $SkipList = Get-Tenants -SkipList if (($env:PartnerTenantAvailable -eq $true -and $SkipList.customerId -notcontains $TenantID -and $SkipList.defaultDomainName -notcontains $TenantID) -or (($Tenants.customerId -contains $TenantID -or $Tenants.defaultDomainName -contains $TenantID) -and $TenantID -ne $env:TenantId)) { return $true - } - else { + } else { return $false } } @@ -427,11 +407,9 @@ function Get-Tenants { if ($IncludeAll.IsPresent) { $Filter = "PartitionKey eq 'Tenants'" - } - elseif ($IncludeErrors.IsPresent) { + } elseif ($IncludeErrors.IsPresent) { $Filter = "PartitionKey eq 'Tenants' and Excluded eq false" - } - else { + } else { $Filter = "PartitionKey eq 'Tenants' and Excluded eq false and GraphErrorCount lt 50" } $IncludedTenantsCache = Get-AzDataTableEntity @TenantsTable -Filter $Filter @@ -439,10 +417,8 @@ function Get-Tenants { if (($IncludedTenantsCache | Measure-Object).Count -gt 0) { try { $LastRefresh = ($IncludedTenantsCache | Where-Object { $_.customerId } | Sort-Object LastRefresh -Descending | Select-Object -First 1).LastRefresh | Get-Date -ErrorAction Stop - } - catch { $LastRefresh = $false } - } - else { + } catch { $LastRefresh = $false } + } else { $LastRefresh = $false } if (!$LastRefresh -or $LastRefresh -lt (Get-Date).Addhours(-24).ToUniversalTime()) { @@ -450,8 +426,7 @@ function Get-Tenants { Write-Host "Renewing. Cache not hit. $LastRefresh" $TenantList = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/tenants?`$top=999" -tenantid $env:TenantID ) | Select-Object id, @{l = 'customerId'; e = { $_.tenantId } }, @{l = 'DefaultdomainName'; e = { [string]($_.contract.defaultDomainName) } } , @{l = 'MigratedToNewTenantAPI'; e = { $true } }, DisplayName, domains, @{n = 'delegatedPrivilegeStatus'; exp = { $_.tenantStatusInformation.delegatedPrivilegeStatus } } | Where-Object -Property defaultDomainName -NotIn $SkipListCache.defaultDomainName - } - catch { + } catch { Write-Host "Get-Tenants - Lighthouse Error, using contract/delegatedAdminRelationship calls. Error: $($_.Exception.Message)" [System.Collections.Generic.List[PSCustomObject]]$BulkRequests = @( @{ @@ -560,8 +535,7 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc $tenant = (get-tenants | Where-Object -Property defaultDomainName -EQ $tenantid).customerId if ($cmdParams) { $Params = $cmdParams - } - else { + } else { $Params = @{} } $ExoBody = ConvertTo-Json -Depth 5 -InputObject @{ @@ -591,21 +565,18 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc } try { $ReturnedData = Invoke-RestMethod "https://outlook.office365.com/adminapi/beta/$($tenant)/InvokeCommand" -Method POST -Body $ExoBody -Headers $Headers -ContentType 'application/json; charset=utf-8' - } - catch { + } catch { $ErrorMess = $($_.Exception.Message) $ReportedError = ($_.ErrorDetails | ConvertFrom-Json -ErrorAction SilentlyContinue) $Message = if ($ReportedError.error.details.message) { $ReportedError.error.details.message - } - elseif ($ReportedError.error.message) { $ReportedError.error.message } + } elseif ($ReportedError.error.message) { $ReportedError.error.message } else { $ReportedError.error.innererror.internalException.message } if ($null -eq $Message) { $Message = $ErrorMess } throw $Message } return $ReturnedData.value - } - else { + } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' } } @@ -688,8 +659,7 @@ function Get-CIPPMSolUsers { if ($null -eq $page) { $Page = (Invoke-RestMethod -Uri 'https://provisioningapi.microsoftonline.com/provisioningwebservice.svc' -Method post -Body $MSOLXML -ContentType 'application/soap+xml; charset=utf-8').envelope.body.ListUsersResponse.listusersresult.returnvalue $Page.results.user - } - else { + } else { $Page = (Invoke-RestMethod -Uri 'https://provisioningapi.microsoftonline.com/provisioningwebservice.svc' -Method post -Body $MSOLXML -ContentType 'application/soap+xml; charset=utf-8').envelope.body.NavigateUserResultsResponse.NavigateUserResultsResult.returnvalue $Page.results.user } @@ -714,17 +684,14 @@ function New-DeviceLogin { if ($TenantID) { $ReturnCode = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$($TenantID)/oauth2/v2.0/devicecode" -Method POST -Body "client_id=$($Clientid)&scope=$encodedscope+offline_access+profile+openid" - } - else { + } else { $ReturnCode = Invoke-RestMethod -Uri 'https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode' -Method POST -Body "client_id=$($Clientid)&scope=$encodedscope+offline_access+profile+openid" } - } - else { + } else { $Checking = Invoke-RestMethod -SkipHttpErrorCheck -Uri 'https://login.microsoftonline.com/organizations/oauth2/v2.0/token' -Method POST -Body "client_id=$($Clientid)&scope=$encodedscope+offline_access+profile+openid&grant_type=device_code&device_code=$($device_code)" if ($checking.refresh_token) { $ReturnCode = $Checking - } - else { + } else { $returncode = $Checking.error } } @@ -742,8 +709,7 @@ function New-passwordString { if ($PasswordType -eq 'Correct-Battery-Horse') { $Words = Get-Content .\words.txt (Get-Random -InputObject $words -Count 4) -join '-' - } - else { + } else { -join ('abcdefghkmnrstuvwxyzABCDEFGHKLMNPRSTUVWXYZ23456789$%&*#'.ToCharArray() | Get-Random -Count $count) } } @@ -790,8 +756,7 @@ function New-GraphBulkRequest { $MoreData.body.value = $NewValues } - } - catch { + } catch { $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message if ($Message -eq $null) { $Message = $($_.Exception.Message) } if ($Message -ne 'Request not applicable to target tenant.') { @@ -806,8 +771,7 @@ function New-GraphBulkRequest { Update-AzDataTableEntity @TenantsTable -Entity $Tenant return $ReturnedData.responses - } - else { + } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' } } @@ -815,8 +779,7 @@ function New-GraphBulkRequest { function Get-GraphBulkResultByID ($Results, $ID, [switch]$Value) { if ($Value) { ($Results | Where-Object { $_.id -eq $ID }).body.value - } - else { + } else { ($Results | Where-Object { $_.id -eq $ID }).body } } diff --git a/Modules/CippExtensions/CippExtensions.psd1 b/Modules/CippExtensions/CippExtensions.psd1 index cbeb001222fef7974b8737ac0af08388cdee1328..784f124c9279b502d95c01757a0cc4c887badfd4 100644 GIT binary patch delta 52 zcmZn&zY)ISk<{cXViJ?BB;@2>81fmC8A=&)8B!VY7)lsIfOIZH0YeT$B9NE5`M%Ub F0RXp~4?+L{ delta 12 TcmcZ+-Vnawk3ISGC`JWo diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDocumentTemplate.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDocumentTemplate.ps1 new file mode 100644 index 000000000000..d2b9bc3ace7e --- /dev/null +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDocumentTemplate.ps1 @@ -0,0 +1,37 @@ +function Invoke-NinjaOneDocumentTemplate { + [CmdletBinding()] + param ( + $Template, + $Token, + $ID + ) + + if (!$Token) { + $Table = Get-CIPPTable -TableName Extensionsconfig + $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json).NinjaOne + $Token = Get-NinjaOneToken -configuration $Configuration + } + + if (!$ID) { + $DocumentTemplates = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/document-templates/" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + $DocumentTemplate = $DocumentTemplates | Where-Object { $_.name -eq $Template.name } + } else { + $DocumentTemplate = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/document-templates/$($ID)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + } + + $MatchedCount = ($DocumentTemplate | Measure-Object).count + if ($MatchedCount -eq 1) { + # Matched a single document template + $NinjaDocumentTemplate = $DocumentTemplate + } elseif ($MatchedCount -eq 0) { + # Create a new Document Template + $Body = $Template | ConvertTo-Json -depth 100 + $NinjaDocumentTemplate = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/document-templates/" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body $Body).content | ConvertFrom-Json -depth 100 + } else { + # Matched multiple templates. Should be impossible but lets check anyway :D + Throw "Multiiple Documents Matched the Provided Criteria" + } + + return $NinjaDocumentTemplate + +} \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 36b12121c58b..ad9e2ff47c7a 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -49,8 +49,113 @@ function Invoke-NinjaOneTenantSync { } while ($ResultCount.count -eq $PageSize) - # Create the object we will use for the Org update + + # Get NinjaOne User Documents + $UserDocTemplate = [PSCustomObject]@{ + name = 'CIPP - Microsoft 365 Users' + allowMultiple = $true + fields = @( + [PSCustomObject]@{ + fieldLabel = 'User Links' + fieldName = 'cippUserLinks' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } + } + }, + [PSCustomObject]@{ + fieldLabel = 'User Summary' + fieldName = 'cippUserSummary' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } + } + }, + [PSCustomObject]@{ + fieldLabel = 'User Devices' + fieldName = 'cippUserDevices' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } + } + }, + [PSCustomObject]@{ + fieldLabel = 'User Groups' + fieldName = 'cippUserGroups' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } + } + }, + [PSCustomObject]@{ + fieldLabel = 'User ID' + fieldName = 'cippUserID' + fieldType = 'TEXT' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + }, + [PSCustomObject]@{ + fieldLabel = 'User UPN' + fieldName = 'cippUserUPN' + fieldType = 'TEXT' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + } + ) + } + + $NinjaOneUsersTemplate = Invoke-NinjaOneDocumentTemplate -Template $UserDocTemplate -Token $Token + + # Get NinjaOne Users + #[System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents?organizationIDs=$($NinjaOneOrg)&templateIds=$($NinjaOneUsersTemplate.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100)."$NinjaOneOrg" + [System.Collections.Generic.List[PSCustomObject]]$NinjaOneOrgDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($NinjaOneOrg)/documents" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100) + + foreach ($NinjaDoc in $NinjaOneOrgDocs) { + $ParsedFields = [pscustomobject]@{} + foreach ($Field in $NinjaDoc.Fields) { + if ($Field.value.text) { + $FieldVal = $Field.value.text + } else { + $FieldVal = $Field.value + } + $ParsedFields | Add-Member -NotePropertyName $Field.name -NotePropertyValue $FieldVal + } + $NinjaDoc | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force + } + + + [System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = $NinjaOneOrgDocs | Where-Object { $_.documentTemplateId -eq $NinjaOneUsersTemplate.id } + + # Create the update objects we will use to update NinjaOne $NinjaOrgUpdate = [PSCustomObject]@{} + $NinjaUserUpdates = [System.Collections.Generic.List[PSCustomObject]]@() + $NinjaUserCreation = [System.Collections.Generic.List[PSCustomObject]]@() # Build bulk requests array. [System.Collections.Generic.List[PSCustomObject]]$TenantRequests = @( @@ -126,8 +231,6 @@ function Invoke-NinjaOneTenantSync { $Users = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Users' - $SignInLogs = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'SignInLogs' - $SecureScore = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'SecureScore' [System.Collections.Generic.List[PSCustomObject]]$SecureScoreProfiles = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'SecureScoreControlProfiles' @@ -474,13 +577,27 @@ function Invoke-NinjaOneTenantSync { } } - + # First lets match on serial + $MatchedNinjaDevice = $NinjaDevices | Where-Object { $_.system.biosSerialNumber -eq $Device.SerialNumber -or $_.system.serialNumber -eq $Device.SerialNumber } + + # See if we found just one device, if not match on name + if (($MatchedNinjaDevice | Measure-Object).count -ne 1) { + $MatchedNinjaDevice = $NinjaDevices | Where-Object { $_.systemName -eq $Device.Name -or $_.dnsName -eq $Device.Name } + } + + # Check on a match again and set name + if (($MatchedNinjaDevice | Measure-Object).count -eq 1) { + $ParsedDeviceName = '' + $Device.deviceName + '' + } else { + $ParsedDeviceName = $Device.deviceName + } [PSCustomObject]@{ Name = $Device.deviceName SerialNumber = $Device.serialNumber OS = $Device.operatingSystem OSVersion = $Device.osversion + Enrolled = $Device.enrolledDateTime Compliance = $Device.complianceState LastSync = $Device.lastSyncDateTime PrimaryUser = $Device.userDisplayName @@ -501,13 +618,431 @@ function Invoke-NinjaOneTenantSync { UserDetails = $DeviceUsersDetail CompliancePolicies = $DevicePolcies Groups = $DeviceGroups - + NinjaDevice = $MatchedNinjaDevice + DeviceLink = $ParsedDeviceName } } + ########## Create / Update User Objects + $ParsedUsers = foreach ($user in $licensedUsers) { + try { + $NinjaOneUser = $NinjaOneUserDocs | Where-Object { $_.ParsedFields.cippUserID -eq $User.ID } + if (($NinjaOneUser | Measure-Object).count -gt 1) { + Throw "Multiple Users with the same ID found" + } + + $UserGroups = foreach ($Group in $Groups) { + if ($User.id -in $Group.Members.id) { + $FoundGroup = $AllGroups | Where-Object { $_.id -eq $Group.id } + [PSCustomObject]@{ + 'Display Name' = $FoundGroup.displayName + 'Mail Enabled' = $FoundGroup.mailEnabled + 'Mail' = $FoundGroup.mail + 'Security Group' = $FoundGroup.securityEnabled + 'Group Types' = $FoundGroup.groupTypes -join ',' + } + } + } + + $UserPolicies = foreach ($cap in $ConditionalAccessMembers) { + if ($User.id -in $Cap.Members) { + $temp = [PSCustomObject]@{ + displayName = $cap.displayName + } + $temp + } + } + + $PermsRequest = '' + $StatsRequest = '' + $MailboxDetailedRequest = '' + $CASRequest = '' + + $CASRequest = $CASFull | Where-Object { $_.ExternalDirectoryObjectId -eq $User.iD } + $MailboxDetailedRequest = $MailboxDetailedFull | Where-Object { $_.ExternalDirectoryObjectId -eq $User.iD } + $StatsRequest = $MailboxStatsFull | Where-Object { $_.'User Principal Name' -eq $User.UserPrincipalName } + + try { + $PermsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($User.ID)')/MailboxPermission" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true -NoAuthCheck $True + } catch { + $PermsRequest = $null + } + + $ParsedPerms = foreach ($Perm in $PermsRequest) { + if ($Perm.User -ne 'NT AUTHORITY\SELF') { + [pscustomobject]@{ + User = $Perm.User + AccessRights = $Perm.PermissionList.AccessRights -join ', ' + } + } + } + + try { + $TotalItemSize = [math]::Round($StatsRequest.'Storage Used (Byte)' / 1Gb, 2) + } catch { + $TotalItemSize = 0 + } + + $UserMailSettings = [pscustomobject]@{ + ForwardAndDeliver = $MailboxDetailedRequest.DeliverToMailboxAndForward + ForwardingAddress = $MailboxDetailedRequest.ForwardingAddress + ' ' + $MailboxDetailedRequest.ForwardingSmtpAddress + LitiationHold = $MailboxDetailedRequest.LitigationHoldEnabled + HiddenFromAddressLists = $MailboxDetailedRequest.HiddenFromAddressListsEnabled + EWSEnabled = $CASRequest.EwsEnabled + MailboxMAPIEnabled = $CASRequest.MAPIEnabled + MailboxOWAEnabled = $CASRequest.OWAEnabled + MailboxImapEnabled = $CASRequest.ImapEnabled + MailboxPopEnabled = $CASRequest.PopEnabled + MailboxActiveSyncEnabled = $CASRequest.ActiveSyncEnabled + Permissions = $ParsedPerms + ProhibitSendQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendQuota -split ' GB')[0], 2) + ProhibitSendReceiveQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' GB')[0], 2) + ItemCount = [math]::Round($StatsRequest.'Item Count', 2) + TotalItemSize = $TotalItemSize + } + + + $UserDevicesDetailsRaw = $ParsedDevices | where-object { $User.id -in $_.UserIDS } + + $UserDevices = foreach ($UserDevice in $ParsedDevices | where-object { $User.id -in $_.UserIDS }) { + + $MatchedNinjaDevice = $UserDevice.NinjaDevice + $ParsedDeviceName = $UserDevice.DeviceLink + + # Set Last Login Time + $LastLoginTime = ($UserDevice.UserDetails | where-object { $_.id -eq $User.id }).lastLogin + if (!$LastLoginTime) { + $LastLoginTime = 'Unknown' + } + + # Set Compliance Status + if ($UserDevice.Compliance -eq 'compliant') { + $ComplianceIcon = '' + } else { + $ComplianceIcon = '' + } + + # OS Icon + $OSIcon = Switch ($UserDevice.OS) { + 'Windows' { '' } + 'iOS' { '' } + 'Android' { '' } + 'macOS' { '' } + } + + '
  • ' + "$ComplianceIcon $OSIcon $($ParsedDeviceName) ($LastLoginTime)
  • " + + } + + $aliases = (($user.ProxyAddresses | Where-Object { $_ -cnotmatch 'SMTP' -and $_ -notmatch '.onmicrosoft.com' }) -replace 'SMTP:', ' ') -join ', ' + + $userLicenses = ($user.AssignedLicenses.SkuID | ForEach-Object { + $UserLic = $_ + try { + $SkuPartNumber = ($Licenses | Where-Object { $_.SkuId -eq $UserLic }).SkuPartNumber + '
  • ' + "$((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $SkuPartNumber).Tolower()))
  • " + } catch {} + }) -join '' + + + $UserOneDriveStats = $OneDriveDetails | where-object { $_.'Owner Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 + $UserOneDriveUse = $UserOneDriveStats.'Storage Used (Byte)' / 1GB + $UserOneDriveTotal = $UserOneDriveStats.'Storage Allocated (Byte)' / 1GB + if ($UserOneDriveTotal) { + $OneDriveUse = [PSCustomObject]@{ + Enabled = $True + Used = $UserOneDriveUse + Total = $UserOneDriveTotal + Percent = ($UserOneDriveUse / $UserOneDriveTotal) * 100 + } + + $OneDriveUseColor = if ($OneDriveUse.Percent -ge 95) { + '#ec1c24' + } elseif ($MailboxUse.Percent -ge 85) { + '#FFA500' + } else { + '#008001' + } + + $OneDriveParsed = '
    ' + + } else { + $OneDriveUse = [PSCustomObject]@{ + Enabled = $False + Used = 0 + Total = 0 + Percent = 0 + } + + $OneDriveParsed = 'Not Enabled' + } + + + if ($UserOneDriveStats) { + $OneDriveCardData = [PSCustomObject]@{ + 'One Drive URL' = '' + ($UserOneDriveStats.'Site URL') + '' + 'Is Deleted' = "$($UserOneDriveStats.'Is Deleted')" + 'Last Activity Date' = "$($UserOneDriveStats.'Last Activity Date')" + 'File Count' = "$($UserOneDriveStats.'File Count')" + 'Active File Count' = "$($UserOneDriveStats.'Active File Count')" + 'Storage Used (Byte)' = "$($UserOneDriveStats.'Storage Used (Byte)')" + 'Storage Allocated (Byte)' = "$($UserOneDriveStats.'Storage Allocated (Byte)')" + 'One Drive Usage' = $OneDriveParsed + + } + } else { + $OneDriveCardData = [PSCustomObject]@{ + 'One Drive' = 'Disabled' + } + } + + $UserMailboxStats = $MailboxStatsFull | where-object { $_.'User Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 + $UserMailUse = $UserMailboxStats.'Storage Used (Byte)' / 1GB + $UserMailTotal = $UserMailboxStats.'Prohibit Send/Receive Quota (Byte)' / 1GB + if ($UserMailTotal) { + $MailboxUse = [PSCustomObject]@{ + Enabled = $True + Used = $UserMailUse + Total = $UserMailTotal + Percent = ($UserMailUse / $UserMailTotal) * 100 + } + + $MailboxUseColor = if ($MailboxUse.Percent -ge 95) { + '#ec1c24' + } elseif ($MailboxUse.Percent -ge 85) { + '#FFA500' + } else { + '#008001' + } + + $MailboxParsed = '
    ' + + } else { + $MailboxUse = [PSCustomObject]@{ + Enabled = $False + Used = 0 + Total = 0 + Percent = 0 + } + + $MailboxParsed = 'Not Enabled' + } + + if ($UserMailSettings) { + $MailboxDetailsCardData = [PSCustomObject]@{ + 'Permissions' = "$($UserMailSettings.Permissions | ConvertTo-Html -Fragment | Out-String)" + 'Prohibit Send Quota' = "$($UserMailSettings.ProhibitSendQuota)" + 'Prohibit Send Receive Quota' = "$($UserMailSettings.ProhibitSendReceiveQuota)" + 'Item Count' = "$($UserMailSettings.ProhibitSendReceiveQuota)" + 'Total Mailbox Size' = "$($UserMailSettings.ItemCount)" + 'Mailbox Usage' = $MailboxParsed + } + + $MailboxSettingsCard = [PSCustomObject]@{ + 'Forward and Deliver' = "$($UserMailSettings.ForwardAndDeliver)" + 'Forwarding Address' = "$($UserMailSettings.ForwardingAddress)" + 'Litiation Hold' = "$($UserMailSettings.LitiationHold)" + 'Hidden From Address Lists' = "$($UserMailSettings.HiddenFromAddressLists)" + 'EWS Enabled' = "$($UserMailSettings.EWSEnabled)" + 'MAPI Enabled' = "$($UserMailSettings.MailboxMAPIEnabled)" + 'OWA Enabled' = "$($UserMailSettings.MailboxOWAEnabled)" + 'IMAP Enabled' = "$($UserMailSettings.MailboxImapEnabled)" + 'POP Enabled' = "$($UserMailSettings.MailboxPopEnabled)" + 'Active Sync Enabled' = "$($UserMailSettings.MailboxActiveSyncEnabled)" + } + } else { + $MailboxDetailsCardData = [PSCustomObject]@{ + Exchange = 'Disabled' + } + $MailboxSettingsCard = [PSCustomObject]@{ + Exchange = 'Disabled' + } + } + + # Format Conditional Access Polcies + $UserPoliciesFormatted = '
      ' + foreach ($Policy in $UserPolicies) { + $UserPoliciesFormatted = $UserPoliciesFormatted + "
    • $($Policy.displayName)
    • " + } + $UserPoliciesFormatted = $UserPoliciesFormatted + '
    ' + + $UserOverviewCard = [PSCustomObject]@{ + 'User Name' = "$($User.displayName)" + 'User Principal Name' = "$($User.userPrincipalName)" + 'User ID' = "$($User.ID)" + 'User Enabled' = "$($User.accountEnabled)" + 'Job Title' = "$($User.jobTitle)" + 'Mobile Phone' = "$($User.mobilePhone)" + 'Business Phones' = "$($User.businessPhones -join ', ')" + 'Office Location' = "$($User.officeLocation)" + 'Aliases' = "$aliases" + 'Licenses' = "$($userLicenses)" + } + + $Microsoft365UserLinksData = @( + @{ + Name = 'Entra ID' + Link = "https://aad.portal.azure.com/$($Customer.defaultDomainName)/#blade/Microsoft_AAD_IAM/UserDetailsMenuBlade/Profile/userId/$($User.id)" + Icon = 'fas fa-users-cog' + }, + @{ + Name = 'Sign-In Logs' + Link = "https://aad.portal.azure.com/$($Customer.defaultDomainName)/#blade/Microsoft_AAD_IAM/UserDetailsMenuBlade/SignIns/userId/$($User.id)" + Icon = 'fas fa-users-cog' + }, + @{ + Name = 'Teams Admin' + Link = "https://admin.teams.microsoft.com/users/$($User.id)/account?delegatedOrg=$($Customer.defaultDomainName)" + Icon = 'fas fa-users' + }, + @{ + Name = 'Intune (User)' + Link = "https://endpoint.microsoft.com/$($Customer.defaultDomainName)/#blade/Microsoft_AAD_IAM/UserDetailsMenuBlade/Profile/userId/$($User.ID)" + Icon = 'fas fa-laptop' + }, + @{ + Name = 'Intune (Devices)' + Link = "https://endpoint.microsoft.com/$($Customer.defaultDomainName)/#blade/Microsoft_AAD_IAM/UserDetailsMenuBlade/Devices/userId/$($User.ID)" + Icon = 'fas fa-laptop' + } + ) + + $CIPPUserLinksData = @( + @{ + Name = 'View User' + Link = "https://$($CIPPURL).auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/users/view?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Icon = 'far fa-eye' + }, + @{ + Name = 'Edit User' + Link = "https://$($CIPPURL).auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/users/edit?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Icon = 'fas fa-users-cog' + }, + @{ + Name = 'Research Compromise' + Link = "https://$($CIPPURL).auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/ViewBec?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Icon = 'fas fa-user-secret' + } + ) + + # Actions + $ActionsHTML = @" +   +   +   +"@ + + + + # Return Data for Users Summary Table + [PSCustomObject]@{ + Name = $User.displayName + UPN = $User.userPrincipalName + Aliases = ($User.proxyAddresses -replace 'SMTP:', '') -join ', ' + Licenses = "
      $userLicenses
    " + Mailbox = $MailboxUse + MailboxParsed = $MailboxParsed + OneDrive = $OneDriveUse + OneDriveParsed = $OneDriveParsed + Devices = "
      $($UserDevices -join '')
    " + Actions = $ActionsHTML + } + + + # Format into Ninja HTML + # Links + $M365UserLinksHTML = Get-NinjaOneLinks -Data $Microsoft365UserLinksData -Title 'Portals' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 + $CIPPUserLinksHTML = Get-NinjaOneLinks -Data $CIPPUserLinksData -Title 'CIPP Links' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 + $UserLinksHTML = '
    ' + $M365UserLinksHTML + '
    ' + $CIPPUserLinksHTML + '
    ' + + # UsersSummaryCards: + $UserOverviewCardHTML = Get-NinjaOneInfoCard -Title "User Details" -Data $UserOverviewCard -Icon 'fas fa-user' + $MailboxDetailsCardHTML = Get-NinjaOneInfoCard -Title "Mailbox Details" -Data $MailboxDetailsCardData -Icon 'fas fa-envelope' + $MailboxSettingsCardHTML = Get-NinjaOneInfoCard -Title "Mailbox Settings" -Data $MailboxSettingsCard -Icon 'fas fa-envelope' + $OneDriveCardHTML = Get-NinjaOneInfoCard -Title "OneDrive Details" -Data $OneDriveCardData -Icon 'fas fa-envelope' + $UserPolciesCard = Get-NinjaOneCard -Title "Assigned Conditional Access Policies" -Body $UserPoliciesFormatted + + + $UserSummaryHTML = '
    ' + + '
    ' + $UserOverviewCardHTML + + '
    ' + $MailboxDetailsCardHTML + + '
    ' + $MailboxSettingsCardHTML + + '
    ' + $OneDriveCardHTML + + '
    ' + $UserPolciesCard + + '
    ' + $DeviceSummaryCardHTML + + '
    ' + + + $UserDeviceDetailsTable = $UserDevicesDetailsRaw | Select-Object @{N = 'Name'; E = { $_.DeviceLink } }, + @{n = 'Enrolled'; e = { $_.Enrolled } }, + @{n = 'Last Sync'; e = { $_.LastSync } }, + @{n = 'OS'; e = { $_.OS } }, + @{n = 'OS Version'; e = { $_.OSVersion } }, + @{n = 'State'; e = { $_.Compliance } }, + @{n = 'Model'; e = { $_.Model } }, + @{n = 'Manufacturer'; e = { $_.Make } } + + $UserDeviceDetailHTML = $UserDeviceDetailsTable | ConvertTo-Html -As Table -Fragment + $UserDeviceDetailHTML = ([System.Web.HttpUtility]::HtmlDecode($UserDeviceDetailHTML) -replace '
    ', '') -replace '', '' + + + $UserFields = @{ + cippUserLinks = @{'html' = $UserLinksHTML } + cippUserSummary = @{'html' = $UserSummaryHTML } + cippUserGroups = @{'html' = "$($UserGroups | ConvertTo-HTML -As Table -Fragment)" } + cippUserDevices = @{'html' = $UserDeviceDetailHTML } + cippUserID = $User.id + cippUserUPN = $User.userPrincipalName + } + + if ($NinjaOneUser) { + $UpdateObject = [PSCustomObject]@{ + documentId = $NinjaOneUser.documentId + documentName = "$($User.displayName) ($($User.userPrincipalName))" + fields = $UserFields + } + $NinjaUserUpdates.Add($UpdateObject) + } else { + $CreateObject = [PSCustomObject]@{ + documentName = "$($User.displayName) ($($User.userPrincipalName))" + documentTemplateId = ($NinjaOneUsersTemplate.id) + organizationId = [int]$NinjaOneOrg + fields = $UserFields + } + $NinjaUserCreation.Add($CreateObject) + } + + } catch { + Write-Error "User $($User.UserPrincipalName): A fatal error occured while processing user $_" + } + } + + try { + # Create New Users + if (($NinjaUserCreation | Measure-Object).count -ge 1) { + Write-Host "Creating NinjaOne Users" + $CreatedUsers = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserCreation | ConvertTo-Json -Depth 100) -EA Stop + } + } Catch { + Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" + } + + try { + # Update Users + if (($NinjaUserUpdates | Measure-Object).count -ge 1) { + Write-Host "Updating NinjaOne Users" + $UpdatedUsers = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserUpdates | ConvertTo-Json -Depth 100) -EA Stop + Write-Host "Completed Update" + } + } Catch { + Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" + } + + ### M365 Links Section if ($MappedFields.TenantLinks) { + Write-Host "Tenant Links" $ManagementLinksData = @( @{ @@ -563,7 +1098,7 @@ function Invoke-NinjaOneTenantSync { ) - $M365LinksHTML = Get-NinjaOneLinks -Data $ManagementLinksData -Title 'Portals' -SmallCols 2 -MedCols 3 -LargeCols 4 -XLCols 5 + $M365LinksHTML = Get-NinjaOneLinks -Data $ManagementLinksData -Title 'Portals' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 $CIPPLinksData = @( @{ @@ -598,9 +1133,9 @@ function Invoke-NinjaOneTenantSync { } ) - $CIPPLinksHTML = Get-NinjaOneLinks -Data $CIPPLinksData -Title 'CIPP Actions' -SmallCols 1 -MedCols 2 -LargeCols 2 -XLCols 3 + $CIPPLinksHTML = Get-NinjaOneLinks -Data $CIPPLinksData -Title 'CIPP Actions' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 - $LinksHtml = '
    ' + $M365LinksHtml + '
    ' + $CIPPLinksHTML + '
     
    ' + $LinksHtml = '
    ' + $CIPPLinksHTML + '
    ' $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.TenantLinks -NotePropertyValue @{'html' = $LinksHtml } @@ -608,6 +1143,7 @@ function Invoke-NinjaOneTenantSync { if ($MappedFields.TenantSummary) { + Write-Host "Tenant Summary" ### Tenant Overview Card $ParsedAdmins = [PSCustomObject]@{} @@ -629,6 +1165,7 @@ function Invoke-NinjaOneTenantSync { $TenantSummaryCard = Get-NinjaOneInfoCard -Title "Tenant Details" -Data $TenantDetailsItems -Icon 'fas fa-building' ### Users details card + Write-Host "User Details" $TotalUsersCount = ($Users | measure-object).count $GuestUsersCount = ($Users | where-object { $_.UserType -eq 'Guest' } | measure-object).count $LicensedUsersCount = ($licensedUsers | measure-object).count @@ -686,6 +1223,7 @@ function Invoke-NinjaOneTenantSync { ### Device Details Card + Write-Host "Device Details" $TotalDeviceswCount = ($Devices | Measure-Object).count $ComplianceDevicesCount = ($Devices | Where-Object { $_.complianceState -eq 'compliant' } | Measure-Object).count $WindowsCount = ($Devices | Where-Object { $_.operatingSystem -eq 'Windows' } | Measure-Object).count @@ -765,6 +1303,7 @@ function Invoke-NinjaOneTenantSync { $DeviceSummaryCardHTML = Get-NinjaOneCard -Title 'Device Details' -Body $DeviceCardBodyHTML -Icon 'fas fa-network-wired' -TitleLink $TitleLink #### Secure Score Card + Write-Host "Secure Score Details" $Top5Actions = ($SecureScoreParsed | Where-Object { $_.scoreInPercentage -ne 100 } | Sort-Object 'Score Impact', adjustedRank -Descending) | Select-Object -First 5 # Score Chart @@ -788,13 +1327,14 @@ function Invoke-NinjaOneTenantSync { $TitleLink = "https://security.microsoft.com/securescore?viewid=overview&tid=$($Customer.CustomerId)" - $SecureScoreCardBodyHTML = $SecureScoreHTML + [System.Web.HttpUtility]::HtmlDecode($RecommendedActionsHTML) -replace '
    ', '' - $SecureScoreCardBodyHTML = $SecureScoreCardBodyHTML -replace '', '' + $SecureScoreCardBodyHTML = $SecureScoreHTML + [System.Web.HttpUtility]::HtmlDecode($RecommendedActionsHTML) -replace '', '' + $SecureScoreCardBodyHTML = $SecureScoreCardBodyHTML -replace '', '' $SecureScoreSummaryCardHTML = Get-NinjaOneCard -Title 'Secure Score' -Body $SecureScoreCardBodyHTML -Icon 'fas fa-shield' -TitleLink $TitleLink ### CIPP Applied Standards Cards + Write-Host "Applied Standards" $StandardsDefinitions = Get-Content 'config/standards.json' | ConvertFrom-Json -Depth 100 $Table = Get-CippTable -tablename 'standards' @@ -812,7 +1352,7 @@ function Invoke-NinjaOneTenantSync { if ($CheckValue.value) { $MatchedStandard = $StandardsDefinitions | where-object { ($_.name -split 'standards.')[1] -eq $CheckValue.name } if (($MatchedStandard | Measure-Object).count -eq 1) { - '
  • ' + $($MatchedStandard.label) + ' (' + ($($Standard.Tenant)) + ')
  • ' + '
  • ' + $($MatchedStandard.label) + ' (' + ($($Standard.Tenant)) + ')
  • ' } } } @@ -826,14 +1366,16 @@ function Invoke-NinjaOneTenantSync { $CIPPStandardsSummaryCardHTML = Get-NinjaOneCard -Title 'CIPP Applied Standards' -Body $CIPPStandardsBodyHTML -Icon 'fas fa-shield-halved' -TitleLink $TitleLink ### License Card + Write-Host "License Details" $LicenseTableHTML = $LicensesParsed | Sort-Object 'License Name' | ConvertTo-HTML -As Table -Fragment - $LicenseTableHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseTableHTML) -replace '
    ', '') -replace '', '' + $LicenseTableHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseTableHTML) -replace '', '') -replace '', '' $TitleLink = "https://$CIPPUrl/tenant/administration/list-licenses?customerId=$($Customer.customerId)" $LicensesSummaryCardHTML = Get-NinjaOneCard -Title 'Licenses' -Body $LicenseTableHTML -Icon 'fas fa-chart-bar' -TitleLink $TitleLink ### Summary Stats + Write-Host "Widget Details" [System.Collections.Generic.List[PSCustomObject]]$WidgetData = @() @@ -942,15 +1484,16 @@ function Invoke-NinjaOneTenantSync { # Create the Tenant Summary Field - $TenantSummaryHTML = '
    ' + $SummaryDetailsCardHTML + '
    ' + - '
    ' + - '
    ' + $TenantSummaryCard + - '
    ' + $LicensesSummaryCardHTML + - '
    ' + $DeviceSummaryCardHTML + - '
    ' + $CIPPStandardsSummaryCardHTML + - '
    ' + $SecureScoreSummaryCardHTML + - '
    ' + $UserSummaryCardHTML + - '
     
    ' + Write-Host "Complete Tenant Summary" + $TenantSummaryHTML = '
    ' + $SummaryDetailsCardHTML + '
    ' + + '
    ' + + '
    ' + $TenantSummaryCard + + '
    ' + $LicensesSummaryCardHTML + + '
    ' + $DeviceSummaryCardHTML + + '
    ' + $CIPPStandardsSummaryCardHTML + + '
    ' + $SecureScoreSummaryCardHTML + + '
    ' + $UserSummaryCardHTML + + '
    ' $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.TenantSummary -NotePropertyValue @{'html' = $TenantSummaryHTML } @@ -959,148 +1502,7 @@ function Invoke-NinjaOneTenantSync { } if ($MappedFields.UsersSummary) { - # Parse all users: - [System.Collections.Generic.List[PSCustomObject]]$ParsedUsers = Foreach ($User in $Users | where-object { $_.userType -ne 'Guest' -and $_.accountEnabled -eq $True }) { - - $UserDevices = foreach ($UserDevice in $ParsedDevices | where-object { $User.id -in $_.UserIDS }) { - # First lets match on serial - $MatchedNinjaDevice = $NinjaDevices | Where-Object { $_.system.biosSerialNumber -eq $UserDevice.SerialNumber -or $_.system.serialNumber -eq $UserDevice.SerialNumber } - - # See if we found just one device, if not match on name - if (($MatchedNinjaDevice | Measure-Object).count -ne 1) { - $MatchedNinjaDevice = $NinjaDevices | Where-Object { $_.systemName -eq $UserDevice.Name -or $_.dnsName -eq $UserDevice.Name } - } - - # Check on a match again and set name - if (($MatchedNinjaDevice | Measure-Object).count -eq 1) { - $ParsedDeviceName = '' + $UserDevice.Name + '' - Write-Host "Parsed Device Name: $ParsedDeviceName" - } else { - $ParsedDeviceName = $UserDevice.Name - } - - # Set Last Login Time - $LastLoginTime = ($UserDevice.UserDetails | where-object { $_.id -eq $User.id }).lastLogin - if (!$LastLoginTime) { - $LastLoginTime = 'Unknown' - } - - # Set Compliance Status - if ($UserDevice.Compliance -eq 'compliant') { - $ComplianceIcon = '' - } else { - $ComplianceIcon = '' - } - - # OS Icon - $OSIcon = Switch ($UserDevice.OS) { - 'Windows' { '' } - 'iOS' { '' } - 'Android' { '' } - 'macOS' { '' } - } - - '
  • ' + "$ComplianceIcon $OSIcon $($ParsedDeviceName) ($LastLoginTime)
  • " - - } - - - $userLicenses = ($user.AssignedLicenses.SkuID | ForEach-Object { - $UserLic = $_ - try { - $SkuPartNumber = ($Licenses | Where-Object { $_.SkuId -eq $UserLic }).SkuPartNumber - '
  • ' + "$((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $SkuPartNumber).Tolower()))
  • " - } catch {} - }) -join '' - - $UserMailboxStats = $MailboxStatsFull | where-object { $_.'User Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 - $UserMailUse = $UserMailboxStats.'Storage Used (Byte)' / 1GB - $UserMailTotal = $UserMailboxStats.'Prohibit Send/Receive Quota (Byte)' / 1GB - if ($UserMailTotal) { - $MailboxUse = [PSCustomObject]@{ - Enabled = $True - Used = $UserMailUse - Total = $UserMailTotal - Percent = ($UserMailUse / $UserMailTotal) * 100 - } - - $MailboxUseColor = if ($MailboxUse.Percent -ge 95) { - '#ec1c24' - } elseif ($MailboxUse.Percent -ge 85) { - '#FFA500' - } else { - '#008001' - } - - $MailboxParsed = '
    ' - - } else { - $MailboxUse = [PSCustomObject]@{ - Enabled = $False - Used = 0 - Total = 0 - Percent = 0 - } - - $MailboxParsed = 'Not Enabled' - } - - - $UserOneDriveStats = $OneDriveDetails | where-object { $_.'Owner Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 - $UserOneDriveUse = $UserOneDriveStats.'Storage Used (Byte)' / 1GB - $UserOneDriveTotal = $UserOneDriveStats.'Storage Allocated (Byte)' / 1GB - if ($UserOneDriveTotal) { - $OneDriveUse = [PSCustomObject]@{ - Enabled = $True - Used = $UserOneDriveUse - Total = $UserOneDriveTotal - Percent = ($UserOneDriveUse / $UserOneDriveTotal) * 100 - } - - $OneDriveUseColor = if ($OneDriveUse.Percent -ge 95) { - '#ec1c24' - } elseif ($MailboxUse.Percent -ge 85) { - '#FFA500' - } else { - '#008001' - } - - $OneDriveParsed = '
    ' - - } else { - $OneDriveUse = [PSCustomObject]@{ - Enabled = $False - Used = 0 - Total = 0 - Percent = 0 - } - - $OneDriveParsed = 'Not Enabled' - } - - # Actions - $ActionsHTML = @" -   -   -   -"@ - - [PSCustomObject]@{ - Name = $User.displayName - UPN = $User.userPrincipalName - Aliases = ($User.proxyAddresses -replace 'SMTP:', '') -join ', ' - Licenses = "
      $userLicenses
    " - Mailbox = $MailboxUse - MailboxParsed = $MailboxParsed - OneDrive = $OneDriveUse - OneDriveParsed = $OneDriveParsed - Devices = "
      $($UserDevices -join '')
    " - Actions = $ActionsHTML - } - - } - - + Write-Host "User Details Section" $UsersTableFornatted = $ParsedUsers | Select-Object Name, @{n = 'User Principal Name'; e = { $_.UPN } }, @@ -1114,7 +1516,7 @@ function Invoke-NinjaOneTenantSync { $UsersTableHTML = $UsersTableFornatted | ConvertTo-HTML -As Table -Fragment - $UsersTableHTML = ([System.Web.HttpUtility]::HtmlDecode($UsersTableHTML) -replace '
    ', '') -replace '', '' + $UsersTableHTML = ([System.Web.HttpUtility]::HtmlDecode($UsersTableHTML) -replace '', '') -replace '', '' $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.UsersSummary -NotePropertyValue @{'html' = $UsersTableHTML } @@ -1122,8 +1524,7 @@ function Invoke-NinjaOneTenantSync { - #Get available Ninja clients - + Write-Host "Posting Details" $Token = Get-NinjaOneToken -configuration $Configuration diff --git a/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 b/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 index a32f3a880399..58c4b5e2b981 100644 --- a/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 +++ b/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 @@ -1,5 +1,5 @@ function Get-NinjaOneTitle($Title, $Icon, $TitleLink, $TitleSize, $TitleClass) { - Return $(if ($TitleSize) { '<' + $TitleSize + ' style="font-family: sans-serif"' }else { '' + $(if ($Icon) { '  ' }) + $Title + $(if ($TitleLink) { '  ' }) + $(if ($TitleSize) { "" }else { '' }) + Return $(if ($TitleSize) { '<' + $TitleSize }else { '' + $(if ($Icon) { '  ' }) + $Title + $(if ($TitleLink) { '  ' }) + $(if ($TitleSize) { "" }else { '' }) } ### HTML Formatters ### @@ -24,7 +24,6 @@ function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string] Colour = '#8063BF' } ) - Get-NinjaInLineBarGraph -Title "Users" -Data $Data -KeyInLine #> @@ -40,7 +39,7 @@ function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string] $OutputHTML.add((Get-NinjaOneTitle -Icon $Icon -Title ($Title + $(if (!$NoCount) { " ($Total)" })) -TitleLink $TitleLink)) } - $OutputHTML.add('
    ') + $OutputHTML.add('
    ') foreach ($Item in $Data) { $OutputHTML.add(@" @@ -52,14 +51,14 @@ function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string] $OutputHTML.add('
    ') if ($KeyInline) { - $OutputHTML.add('
      ') + $OutputHTML.add('
        ') } else { - $OutputHTML.add('
          ') + $OutputHTML.add('
            ') } foreach ($Item in $Data) { $OutputHTML.add(@" -
          • $($Item.Label) ($($Item.Amount))
          • +
          • $($Item.Label) ($($Item.Amount))
          • "@) } @@ -96,19 +95,20 @@ $ManagementLinksData = @( [System.Collections.Generic.List[String]]$OutputHTML = @() - $OutputHTML.add('
            ') + $OutputHTML.add('
            ') if ($Title) { - $OutputHTML.add('
            ' + $(if ($Icon) { '  ' }) + $Title + '
            ') + $OutputHTML.add('
            ' + $(if ($Icon) { '  ' }) + $Title + '
            ') if ($TitleLink) { - $OutputHTML.add('') + $OutputHTML.add('') } $OutputHTML.add('
            ') } - $OutputHTML.add('
            ') + $OutputHTML.add('
            ') + $OutputHTML.add('
            ') + $OutputHTML.add('
    ') return $OutputHTML -join '' @@ -155,16 +155,16 @@ function Get-NinjaOneWidgetCard($Title, $Data, [string]$Icon, [string]$TitleLink [System.Collections.Generic.List[String]]$OutputHTML = @() - $OutputHTML.add('
    ') + $OutputHTML.add('
    ') foreach ($Item in $Data) { $HTML = @"
    - "@ @@ -201,20 +201,20 @@ function Get-NinjaOneCard($Title, $Body, [string]$Icon, [string]$TitleLink, [Str [System.Collections.Generic.List[String]]$OutputHTML = @() - $OutputHTML.add('
    ') + $OutputHTML.add('
    ') if ($Title) { - $OutputHTML.add('
    ' + $(if ($Icon) { '  ' }) + $Title + '
    ') + $OutputHTML.add('
    ' + $(if ($Icon) { '  ' }) + $Title + '
    ') if ($TitleLink) { - $OutputHTML.add('') + $OutputHTML.add('') } $OutputHTML.add('
    ') } - $OutputHTML.add('
    ') - $OutputHTML.add('

    ' + $Body + '

    ') + $OutputHTML.add('
    ') + $OutputHTML.add('

    ' + $Body + '

    ') $OutputHTML.add('
    ') @@ -239,7 +239,7 @@ function Get-NinjaOneInfoCard($Title, $Data, [string]$Icon, [string]$TitleLink) [System.Collections.Generic.List[String]]$ItemsHTML = @() foreach ($Item in $Data.PSObject.Properties) { - $ItemsHTML.add('

    ' + $Item.Name + '
    ' + $Item.Value + '

    ') + $ItemsHTML.add('

    ' + $Item.Name + '
    ' + $Item.Value + '

    ') } return Get-NinjaOneCard -Title $Title -Body ($ItemsHTML -join '') -Icon $Icon -TitleLink $TitleLink From 4d7bf54ee7e53e9d88bed60f5ad53b412d44b4c7 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:51:33 +0100 Subject: [PATCH 06/77] Add or update the Azure App Service build and deployment workflow config --- .github/workflows/ninjaone_cipp426ns.yml | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/ninjaone_cipp426ns.yml diff --git a/.github/workflows/ninjaone_cipp426ns.yml b/.github/workflows/ninjaone_cipp426ns.yml new file mode 100644 index 000000000000..5c3b5b928a17 --- /dev/null +++ b/.github/workflows/ninjaone_cipp426ns.yml @@ -0,0 +1,29 @@ +# 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 - cipp426ns + +on: + push: + branches: + - NinjaOne + workflow_dispatch: + +env: + AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root + +jobs: + build-and-deploy: + runs-on: windows-latest + steps: + - name: 'Checkout GitHub Action' + uses: actions/checkout@v2 + + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + id: fa + with: + app-name: 'cipp426ns' + slot-name: 'Production' + package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} + publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_D012FDCFEC3E4F2EA3C24F2B1333D7FC }} From beb0e145cfedad76de5e10dbeb6c7c63c09542d1 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:58:23 +0100 Subject: [PATCH 07/77] Add or update the Azure App Service build and deployment workflow config --- .github/workflows/ninjaone_cipp426ns.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ninjaone_cipp426ns.yml b/.github/workflows/ninjaone_cipp426ns.yml index 5c3b5b928a17..6df7b1bab0e9 100644 --- a/.github/workflows/ninjaone_cipp426ns.yml +++ b/.github/workflows/ninjaone_cipp426ns.yml @@ -26,4 +26,4 @@ jobs: app-name: 'cipp426ns' slot-name: 'Production' package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} - publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_D012FDCFEC3E4F2EA3C24F2B1333D7FC }} + publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_198DAF92160A4302B5AA0C145DE72796 }} From 6dbd2b42fde6120c8cd806437636fc568fbe92be Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:27:18 +0100 Subject: [PATCH 08/77] Fixes to error handling when not installed --- .../NinjaOne/Get-NinjaOneFieldMapping.ps1 | 101 ++++++++------- .../NinjaOne/Get-NinjaOneOrgMapping.ps1 | 47 ++++--- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 118 ++++++++++++------ 3 files changed, 161 insertions(+), 105 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 b/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 index e38b65ec34f5..36c98a21091c 100644 --- a/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 @@ -3,56 +3,63 @@ function Get-NinjaOneFieldMapping { param ( $CIPPMapping ) - #Get available mappings - $Mappings = [pscustomobject]@{} - $Filter = "PartitionKey eq 'NinjaFieldMapping'" - Get-AzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { - $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.NinjaOneName)"; value = "$($_.NinjaOne)" } - } - - [System.Collections.Generic.List[PSCustomObject]]$CIPPFields = @( - [PSCustomObject]@{ - InternalName = 'TenantLinks' - Description = 'Microsoft 365 Tenant Links - Field Used to Display Links to Microsoft 365 Portals and CIPP' - Scope = 'Organization' - Type = 'WYSIWYG' - }, - [PSCustomObject]@{ - InternalName = 'TenantSummary' - Description = 'Microsoft 365 Tenant Summary - Field Used to Display Tenant Summary Information' - Scope = 'Organization' - Type = 'WYSIWYG' - }, - [PSCustomObject]@{ - InternalName = 'UsersSummary' - Description = 'Microsoft 365 Users Summary - Field Used to Display User Summary Information' - Scope = 'Organization' - Type = 'WYSIWYG' - }, - [PSCustomObject]@{ - InternalName = 'DeviceCompliance' - Description = 'Intune Device Compliance Status - Field Used to Monitor Device Compliance' - Scope = 'Device' - Type = 'TEXT' + try { + #Get available mappings + $Mappings = [pscustomobject]@{} + $Filter = "PartitionKey eq 'NinjaFieldMapping'" + Get-AzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { + $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.NinjaOneName)"; value = "$($_.NinjaOne)" } } - ) + + [System.Collections.Generic.List[PSCustomObject]]$CIPPFields = @( + [PSCustomObject]@{ + InternalName = 'TenantLinks' + Description = 'Microsoft 365 Tenant Links - Field Used to Display Links to Microsoft 365 Portals and CIPP' + Scope = 'Organization' + Type = 'WYSIWYG' + }, + [PSCustomObject]@{ + InternalName = 'TenantSummary' + Description = 'Microsoft 365 Tenant Summary - Field Used to Display Tenant Summary Information' + Scope = 'Organization' + Type = 'WYSIWYG' + }, + [PSCustomObject]@{ + InternalName = 'UsersSummary' + Description = 'Microsoft 365 Users Summary - Field Used to Display User Summary Information' + Scope = 'Organization' + Type = 'WYSIWYG' + }, + [PSCustomObject]@{ + InternalName = 'DeviceCompliance' + Description = 'Intune Device Compliance Status - Field Used to Monitor Device Compliance' + Scope = 'Device' + Type = 'TEXT' + } + ) - $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json).NinjaOne + $Table = Get-CIPPTable -TableName Extensionsconfig + $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json -ea stop).NinjaOne - $Token = Get-NinjaOneToken -configuration $Configuration + + + $Token = Get-NinjaOneToken -configuration $Configuration - $NinjaCustomFieldsNodeRaw = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/device-custom-fields?scopes=node" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 - [System.Collections.Generic.List[PSCustomObject]]$NinjaCustomFieldsNode = $NinjaCustomFieldsNodeRaw | Where-Object {$_.technicianPermission -eq 'READ_ONLY' -and $_.type -in $CIPPFields.Type} | Select-Object @{n = 'name'; e = { $_.label }}, @{n = 'value'; e = { $_.name } }, type + $NinjaCustomFieldsNodeRaw = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/device-custom-fields?scopes=node" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + [System.Collections.Generic.List[PSCustomObject]]$NinjaCustomFieldsNode = $NinjaCustomFieldsNodeRaw | Where-Object { $_.technicianPermission -eq 'READ_ONLY' -and $_.type -in $CIPPFields.Type } | Select-Object @{n = 'name'; e = { $_.label } }, @{n = 'value'; e = { $_.name } }, type - $NinjaCustomFieldsOrgRaw = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/device-custom-fields?scopes=organization" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 - [System.Collections.Generic.List[PSCustomObject]]$NinjaCustomFieldsOrg = $NinjaCustomFieldsOrgRaw | Where-Object {$_.technicianPermission -eq 'READ_ONLY' -and $_.type -in $CIPPFields.Type} | Select-Object @{n = 'name'; e = { $_.label }}, @{n = 'value'; e = { $_.name } }, type + $NinjaCustomFieldsOrgRaw = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/device-custom-fields?scopes=organization" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + [System.Collections.Generic.List[PSCustomObject]]$NinjaCustomFieldsOrg = $NinjaCustomFieldsOrgRaw | Where-Object { $_.technicianPermission -eq 'READ_ONLY' -and $_.type -in $CIPPFields.Type } | Select-Object @{n = 'name'; e = { $_.label } }, @{n = 'value'; e = { $_.name } }, type - $DoNotSync = [PSCustomObject]@{ - name = '--- Do not synchronize ---' - value = $null - type = 'unset' + $DoNotSync = [PSCustomObject]@{ + name = '--- Do not synchronize ---' + value = $null + type = 'unset' + } + } catch { + [System.Collections.Generic.List[PSCustomObject]]$NinjaCustomFieldsNode = @() + [System.Collections.Generic.List[PSCustomObject]]$NinjaCustomFieldsOrg = @() } $NinjaCustomFieldsOrg.Insert(0, $DoNotSync) @@ -60,11 +67,11 @@ function Get-NinjaOneFieldMapping { $MappingObj = [PSCustomObject]@{ - CIPPOrgFields = $CIPPFields | Where-Object {$_.Scope -eq 'Organization'} - CIPPNodeFields = @($CIPPFields | Where-Object {$_.Scope -eq 'Device'}) - NinjaOrgFields = @($NinjaCustomFieldsOrg) + CIPPOrgFields = $CIPPFields | Where-Object { $_.Scope -eq 'Organization' } + CIPPNodeFields = @($CIPPFields | Where-Object { $_.Scope -eq 'Device' }) + NinjaOrgFields = @($NinjaCustomFieldsOrg) NinjaNodeFields = @($NinjaCustomFieldsNode) - Mappings = $Mappings + Mappings = $Mappings } return $MappingObj diff --git a/Modules/CippExtensions/NinjaOne/Get-NinjaOneOrgMapping.ps1 b/Modules/CippExtensions/NinjaOne/Get-NinjaOneOrgMapping.ps1 index e1dfbab345c7..2a7ab33227ae 100644 --- a/Modules/CippExtensions/NinjaOne/Get-NinjaOneOrgMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Get-NinjaOneOrgMapping.ps1 @@ -3,29 +3,36 @@ function Get-NinjaOneOrgMapping { param ( $CIPPMapping ) - #Get available mappings - $Mappings = [pscustomobject]@{} - $Filter = "PartitionKey eq 'NinjaOrgsMapping'" - Get-AzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { - $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.NinjaOneName)"; value = "$($_.NinjaOne)" } - } - #Get Available Tenants - $Tenants = Get-Tenants - #Get available Ninja clients - $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json).NinjaOne + try { + #Get available mappings + $Mappings = [pscustomobject]@{} + $Tenants = Get-Tenants + + $Filter = "PartitionKey eq 'NinjaOrgsMapping'" + Get-AzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { + $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.NinjaOneName)"; value = "$($_.NinjaOne)" } + } + #Get Available Tenants + + #Get available Ninja clients + $Table = Get-CIPPTable -TableName Extensionsconfig + $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json -ea stop).NinjaOne - $Token = Get-NinjaOneToken -configuration $Configuration - $After = 0 - $PageSize = 1000 - $NinjaOrgs = do { - $Result = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organizations?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 - $Result | Select-Object name, @{n = 'value'; e = { $_.id } } - $ResultCount = ($Result.id | Measure-Object -Maximum) - $After = $ResultCount.maximum + $Token = Get-NinjaOneToken -configuration $Configuration + + $After = 0 + $PageSize = 1000 + $NinjaOrgs = do { + $Result = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organizations?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + $Result | Select-Object name, @{n = 'value'; e = { $_.id } } + $ResultCount = ($Result.id | Measure-Object -Maximum) + $After = $ResultCount.maximum - } while ($ResultCount.count -eq $PageSize) + } while ($ResultCount.count -eq $PageSize) + } catch { + $NinjaOrgs = @() + } $MappingObj = [PSCustomObject]@{ Tenants = @($Tenants) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index ad9e2ff47c7a..4c7f3d095b80 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -154,8 +154,8 @@ function Invoke-NinjaOneTenantSync { # Create the update objects we will use to update NinjaOne $NinjaOrgUpdate = [PSCustomObject]@{} - $NinjaUserUpdates = [System.Collections.Generic.List[PSCustomObject]]@() - $NinjaUserCreation = [System.Collections.Generic.List[PSCustomObject]]@() + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserUpdates = @() + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCreation = @() # Build bulk requests array. [System.Collections.Generic.List[PSCustomObject]]$TenantRequests = @( @@ -504,21 +504,6 @@ function Invoke-NinjaOneTenantSync { $MailboxStatsFull = $null } - - - # Fetch Standards - $Table = Get-CippTable -tablename 'standards' - - $Filter = "PartitionKey eq 'standards'" - - try { - if ($Request.query.TenantFilter) { - $tenants = (Get-AzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -Depth 15 -ErrorAction Stop | Where-Object Tenant -EQ $Request.query.tenantFilter - } else { - $Tenants = (Get-AzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -Depth 15 -ErrorAction Stop - } - } catch {} - $FetchEnd = Get-Date @@ -663,20 +648,20 @@ function Invoke-NinjaOneTenantSync { $MailboxDetailedRequest = $MailboxDetailedFull | Where-Object { $_.ExternalDirectoryObjectId -eq $User.iD } $StatsRequest = $MailboxStatsFull | Where-Object { $_.'User Principal Name' -eq $User.UserPrincipalName } - try { - $PermsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($User.ID)')/MailboxPermission" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true -NoAuthCheck $True - } catch { - $PermsRequest = $null - } - - $ParsedPerms = foreach ($Perm in $PermsRequest) { - if ($Perm.User -ne 'NT AUTHORITY\SELF') { - [pscustomobject]@{ - User = $Perm.User - AccessRights = $Perm.PermissionList.AccessRights -join ', ' - } - } - } + #try { + # $PermsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($User.ID)')/MailboxPermission" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true -NoAuthCheck $True + #} catch { + # $PermsRequest = $null + #} + + #$ParsedPerms = foreach ($Perm in $PermsRequest) { + # if ($Perm.User -ne 'NT AUTHORITY\SELF') { + # [pscustomobject]@{ + # User = $Perm.User + # AccessRights = $Perm.PermissionList.AccessRights -join ', ' + # } + # } + #} try { $TotalItemSize = [math]::Round($StatsRequest.'Storage Used (Byte)' / 1Gb, 2) @@ -695,7 +680,7 @@ function Invoke-NinjaOneTenantSync { MailboxImapEnabled = $CASRequest.ImapEnabled MailboxPopEnabled = $CASRequest.PopEnabled MailboxActiveSyncEnabled = $CASRequest.ActiveSyncEnabled - Permissions = $ParsedPerms + #Permissions = $ParsedPerms ProhibitSendQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendQuota -split ' GB')[0], 2) ProhibitSendReceiveQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' GB')[0], 2) ItemCount = [math]::Round($StatsRequest.'Item Count', 2) @@ -831,7 +816,7 @@ function Invoke-NinjaOneTenantSync { if ($UserMailSettings) { $MailboxDetailsCardData = [PSCustomObject]@{ - 'Permissions' = "$($UserMailSettings.Permissions | ConvertTo-Html -Fragment | Out-String)" + #'Permissions' = "$($UserMailSettings.Permissions | ConvertTo-Html -Fragment | Out-String)" 'Prohibit Send Quota' = "$($UserMailSettings.ProhibitSendQuota)" 'Prohibit Send Receive Quota' = "$($UserMailSettings.ProhibitSendReceiveQuota)" 'Item Count' = "$($UserMailSettings.ProhibitSendReceiveQuota)" @@ -911,17 +896,17 @@ function Invoke-NinjaOneTenantSync { $CIPPUserLinksData = @( @{ Name = 'View User' - Link = "https://$($CIPPURL).auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/users/view?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/.auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/users/view?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" Icon = 'far fa-eye' }, @{ Name = 'Edit User' - Link = "https://$($CIPPURL).auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/users/edit?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/.auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/users/edit?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" Icon = 'fas fa-users-cog' }, @{ Name = 'Research Compromise' - Link = "https://$($CIPPURL).auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/ViewBec?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/.auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/ViewBec?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" Icon = 'fas fa-user-secret' } ) @@ -1022,7 +1007,7 @@ function Invoke-NinjaOneTenantSync { # Create New Users if (($NinjaUserCreation | Measure-Object).count -ge 1) { Write-Host "Creating NinjaOne Users" - $CreatedUsers = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserCreation | ConvertTo-Json -Depth 100) -EA Stop + $CreatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 } } Catch { Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" @@ -1032,13 +1017,70 @@ function Invoke-NinjaOneTenantSync { # Update Users if (($NinjaUserUpdates | Measure-Object).count -ge 1) { Write-Host "Updating NinjaOne Users" - $UpdatedUsers = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserUpdates | ConvertTo-Json -Depth 100) -EA Stop + $UpdatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 Write-Host "Completed Update" } } Catch { Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } + ### Relationship Mapping + # Parse out the NinjaOne ID to MS ID + [System.Collections.Generic.List[PSCustomObject]]$UsersMap = @() + + if (($UpdatedUsers | Measure-Object).count -ge 1) { + $UpdatedUsers | ForEach-Object { + $User = $_ + $Field = $User.updatedFields | Where-Object { $_.name -eq 'cippUserID' } + $UsersMap.Add([PSCustomObject]@{ + NinjaOneID = $User.documentId + M365ID = $Field.value + }) + } + } + + if (($CreatedUsers | Measure-Object).count -ge 1) { + $CreatedUsers | ForEach-Object { + $User = $_ + $Field = $User.fields | Where-Object { $_.name -eq 'cippUserID' } + $UsersMap.Add([PSCustomObject]@{ + NinjaOneID = $User.documentId + M365ID = $Field.value + }) + } + } + + + # Relate Users to Devices + Foreach ($LinkDevice in $ParsedDevices | Where-Object { $null -ne $_.NinjaDevice }) { + [System.Collections.Generic.List[PSCustomObject]]$Relations = @() + Foreach ($LinkUser in $LinkDevice.UserIDs) { + $MatchedUser = $UsersMap | Where-Object { $_.M365ID -eq $LinkUser } + if (($MatchedUser | Measure-Object).count -eq 1) { + $Relations.Add( + [PSCustomObject]@{ + relEntityType = "DOCUMENT" + relEntityId = $MatchedUser.NinjaOneID + } + ) + } + } + + $Relations | ConvertTo-Json -Depth 100 -AsArray | Out-File D:\Temp\Relations.json + Write-Host "URL: https://$($Configuration.Instance)/api/v2/related-items/entity/NODE/$($LinkDevice.NinjaDevice.id)/relations" + + try { + # Update Relations + if (($Relations | Measure-Object).count -ge 1) { + Write-Host "Updating Relations" + $RelationResult = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/NODE/$($LinkDevice.NinjaDevice.id)/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop + Write-Host "Completed Update" + } + } Catch { + Write-Host "Creating Relations Failed: $_" + } + } + ### M365 Links Section if ($MappedFields.TenantLinks) { From a27be5644ae372fd8181a5b8d52ab1201a1d4e4c Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:27:58 +0100 Subject: [PATCH 09/77] Fix to error handling --- Modules/CippExtensions/Private/Get-HaloMapping.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CippExtensions/Private/Get-HaloMapping.ps1 b/Modules/CippExtensions/Private/Get-HaloMapping.ps1 index dcf0fbf77d39..48eef261e0c0 100644 --- a/Modules/CippExtensions/Private/Get-HaloMapping.ps1 +++ b/Modules/CippExtensions/Private/Get-HaloMapping.ps1 @@ -14,7 +14,7 @@ function Get-HaloMapping { #Get available halo clients $Table = Get-CIPPTable -TableName Extensionsconfig try { - $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json).HaloPSA + $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json -ea stop).HaloPSA $Token = Get-HaloToken -configuration $Configuration $i = 1 $RawHaloClients = do { From ccbc544b4ae37ce0bc58ff4b8495d0c6eb0bbb68 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:37:39 +0100 Subject: [PATCH 10/77] Fixed not configured handling --- .../NinjaOne/Get-NinjaOneFieldMapping.ps1 | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 b/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 index 36c98a21091c..053c03a13bb5 100644 --- a/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 @@ -6,10 +6,6 @@ function Get-NinjaOneFieldMapping { try { #Get available mappings $Mappings = [pscustomobject]@{} - $Filter = "PartitionKey eq 'NinjaFieldMapping'" - Get-AzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { - $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.NinjaOneName)"; value = "$($_.NinjaOne)" } - } [System.Collections.Generic.List[PSCustomObject]]$CIPPFields = @( [PSCustomObject]@{ @@ -38,6 +34,11 @@ function Get-NinjaOneFieldMapping { } ) + $Filter = "PartitionKey eq 'NinjaFieldMapping'" + Get-AzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { + $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.NinjaOneName)"; value = "$($_.NinjaOne)" } + } + $Table = Get-CIPPTable -TableName Extensionsconfig $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json -ea stop).NinjaOne @@ -52,16 +53,18 @@ function Get-NinjaOneFieldMapping { $NinjaCustomFieldsOrgRaw = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/device-custom-fields?scopes=organization" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 [System.Collections.Generic.List[PSCustomObject]]$NinjaCustomFieldsOrg = $NinjaCustomFieldsOrgRaw | Where-Object { $_.technicianPermission -eq 'READ_ONLY' -and $_.type -in $CIPPFields.Type } | Select-Object @{n = 'name'; e = { $_.label } }, @{n = 'value'; e = { $_.name } }, type - $DoNotSync = [PSCustomObject]@{ - name = '--- Do not synchronize ---' - value = $null - type = 'unset' - } + } catch { [System.Collections.Generic.List[PSCustomObject]]$NinjaCustomFieldsNode = @() [System.Collections.Generic.List[PSCustomObject]]$NinjaCustomFieldsOrg = @() } + $DoNotSync = [PSCustomObject]@{ + name = '--- Do not synchronize ---' + value = $null + type = 'unset' + } + $NinjaCustomFieldsOrg.Insert(0, $DoNotSync) $NinjaCustomFieldsNode.Insert(0, $DoNotSync) From 106110f1300a66d4d77136f87322f2c6cbc266be Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Tue, 19 Sep 2023 09:03:53 +0100 Subject: [PATCH 11/77] Added NinjaOne Device Fields --- .../NinjaOne/Get-NinjaOneFieldMapping.ps1 | 12 ++ .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 126 +++++++++++++++++- 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 b/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 index 053c03a13bb5..62c7dbc858ba 100644 --- a/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Get-NinjaOneFieldMapping.ps1 @@ -26,6 +26,18 @@ function Get-NinjaOneFieldMapping { Scope = 'Organization' Type = 'WYSIWYG' }, + [PSCustomObject]@{ + InternalName = 'DeviceLinks' + Description = 'Microsoft 365 Device Links - Field Used to Display Links to Microsoft 365 Portals and CIPP' + Scope = 'Device' + Type = 'WYSIWYG' + }, + [PSCustomObject]@{ + InternalName = 'DeviceSummary' + Description = 'Microsoft 365 Device Summary - Field Used to Display Device Summary Information' + Scope = 'Device' + Type = 'WYSIWYG' + }, [PSCustomObject]@{ InternalName = 'DeviceCompliance' Description = 'Intune Device Compliance Status - Field Used to Monitor Device Compliance' diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 4c7f3d095b80..64e10461356d 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -605,8 +605,128 @@ function Invoke-NinjaOneTenantSync { Groups = $DeviceGroups NinjaDevice = $MatchedNinjaDevice DeviceLink = $ParsedDeviceName - } + } + + ### Update NinjaOne Device Fields + if ($MatchedNinjaDevice) { + $NinjaDeviceUpdate = [PSCustomObject]@{} + if ($MappedFields.DeviceLinks) { + $DeviceLinksData = @( + @{ + Name = 'Entra ID' + Link = "https://entra.microsoft.com/$($Customer.defaultDomainName)/#view/Microsoft_AAD_Devices/DeviceDetailsMenuBlade/~/Properties/deviceId/$($Device.azureADDeviceId)/deviceId/" + Icon = 'fab fa-microsoft' + }, + @{ + Name = 'Intune (Devices)' + Link = "https://intune.microsoft.com/$($Customer.defaultDomainName)/#view/Microsoft_Intune_Devices/DeviceSettingsMenuBlade/~/overview/mdmDeviceId/$($Device.id)/" + Icon = 'fas fa-laptop' + }, + @{ + Name = 'View Devices in CIPP' + Link = "https://$($CIPPURL)/.auth/login/aad?post_login_redirect_uri=$($CIPPURL)//endpoint/reports/devices?customerId=$($Customer.defaultDomainName)" + Icon = 'far fa-eye' + } + ) + + + + $DeviceLinksHTML = Get-NinjaOneLinks -Data $DeviceLinksData -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 + + $DeviceLinksHtml = '
    ' + $DeviceLinksHTML + '
    ' + + $NinjaDeviceUpdate | Add-Member -NotePropertyName $MappedFields.DeviceLinks -NotePropertyValue @{'html' = $DeviceLinksHtml } + + + } + + if ($MappedFields.DeviceSummary) { + + # Set Compliance Status + if ($Device.complianceState -eq 'compliant') { + $Compliance = '   Compliant' + } else { + $Compliance = '   Not Compliant' + } + # Device Details + $DeviceDetailsData = [PSCustomObject]@{ + 'Device Name' = $Device.deviceName + 'Primary User' = $Device.userDisplayName + 'Primary User Email' = $Device.userPrincipalName + 'Owner' = $Device.ownerType + 'Enrolled' = $Device.enrolledDateTime + 'Last Checkin' = $Device.lastSyncDateTime + 'Compliant' = $Compliance + 'Management Type' = $Device.managementAgent + } + + $DeviceDetailsCard = Get-NinjaOneInfoCard -Title "Device Details" -Data $DeviceDetailsData -Icon 'fas fa-laptop' + + # Device Hardware + $DeviceHardwareData = [PSCustomObject]@{ + 'Serial Number' = $Device.serialNumber + 'OS' = $Device.operatingSystem + 'OS Versions' = $Device.osVersion + 'Chassis' = $Device.chassisType + 'Model' = $Device.model + 'Manufacturer' = $Device.manufacturer + } + + $DeviceHardwareCard = Get-NinjaOneInfoCard -Title "Device Details" -Data $DeviceHardwareData -Icon 'fas fa-microchip' + + # Device Enrollment + $DeviceEnrollmentData = [PSCustomObject]@{ + 'Enrollment Type' = $Device.deviceEnrollmentType + 'Join Type' = $Device.joinType + 'Registration State' = $Device.deviceRegistrationState + 'Autopilot Enrolled' = $Device.autopilotEnrolled + 'Device Guard Requirements' = $Device.hardwareinformation.deviceGuardVirtualizationBasedSecurityHardwareRequirementState + 'Virtualistation Based Security' = $Device.hardwareinformation.deviceGuardVirtualizationBasedSecurityState + 'Credential Guard' = $Device.hardwareinformation.deviceGuardLocalSystemAuthorityCredentialGuardState + } + + $DeviceEnrollmentCard = Get-NinjaOneInfoCard -Title "Device Enrollment" -Data $DeviceEnrollmentData -Icon 'fas fa-table-list' + + + # Compliance Policies + $DevicePoliciesFormatted = $DevicePolcies | ConvertTo-Html -As Table -Fragment + $DevicePoliciesHTML = ([System.Web.HttpUtility]::HtmlDecode($DevicePoliciesFormatted) -replace '
    ', '') -replace '', '' + $TitleLink = "https://intune.microsoft.com/$($Customer.defaultDomainName)/#view/Microsoft_Intune_Devices/DeviceSettingsMenuBlade/~/compliance/mdmDeviceId/$($Device.id)/primaryUserId/" + $DeviceCompliancePoliciesCard = Get-NinjaOneCard -Title 'Device Compliance Policies' -Body $DevicePoliciesHTML -Icon 'fas fa-list-check' -TitleLink $TitleLink + + # Device Groups + $DeviceGroupsTable = foreach ($Group in $Groups) { + if ($device.azureADDeviceId -in $Group.members.deviceId) { + [PSCustomObject]@{ + Name = $Group.displayName + } + } + } + $DeviceGroupsFormatted = $DeviceGroupsTable | ConvertTo-Html -Fragment + $DeviceGroupsHTML = ([System.Web.HttpUtility]::HtmlDecode($DeviceGroupsFormatted) -replace '', '') -replace '', '' + $DeviceGroupsCard = Get-NinjaOneCard -Title 'Device Groups' -Body $DeviceGroupsHTML -Icon 'fas fa-layer-group' + + $DeviceSummaryHTML = '
    ' + + '
    ' + $DeviceDetailsCard + + '
    ' + $DeviceHardwareCard + + '
    ' + $DeviceEnrollmentCard + + '
    ' + $DeviceCompliancePoliciesCard + + '
    ' + $DeviceGroupsCard + '
    ' + + $NinjaDeviceUpdate | Add-Member -NotePropertyName $MappedFields.DeviceSummary -NotePropertyValue @{'html' = $DeviceSummaryHTML } + } + } + + if ($MappedFields.DeviceCompliance){ + $NinjaDeviceUpdate | Add-Member -NotePropertyName $MappedFields.DeviceCompliance -NotePropertyValue $Device.complianceState + } + + # Update Device + if ($MappedFields.DeviceSummary -or $MappedFields.DeviceLinks -or $MappedFields.DeviceCompliance) { + $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/device/$($MatchedNinjaDevice.id)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaDeviceUpdate | ConvertTo-Json -Depth 100) + } } ########## Create / Update User Objects @@ -639,7 +759,7 @@ function Invoke-NinjaOneTenantSync { } } - $PermsRequest = '' + #$PermsRequest = '' $StatsRequest = '' $MailboxDetailedRequest = '' $CASRequest = '' @@ -1073,7 +1193,7 @@ function Invoke-NinjaOneTenantSync { # Update Relations if (($Relations | Measure-Object).count -ge 1) { Write-Host "Updating Relations" - $RelationResult = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/NODE/$($LinkDevice.NinjaDevice.id)/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop + $RelationResult = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/NODE/$($LinkDevice.NinjaDevice.id)/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop Write-Host "Completed Update" } } Catch { From bb9844d6156137a5337e0dc90f898225cb573473 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Tue, 19 Sep 2023 09:23:13 +0100 Subject: [PATCH 12/77] Fixed Duplicate Relations --- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 64e10461356d..aa650114df7c 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -152,6 +152,9 @@ function Invoke-NinjaOneTenantSync { [System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = $NinjaOneOrgDocs | Where-Object { $_.documentTemplateId -eq $NinjaOneUsersTemplate.id } + # Get NinjaOne Related Items + $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + # Create the update objects we will use to update NinjaOne $NinjaOrgUpdate = [PSCustomObject]@{} [System.Collections.Generic.List[PSCustomObject]]$NinjaUserUpdates = @() @@ -719,7 +722,7 @@ function Invoke-NinjaOneTenantSync { } } - if ($MappedFields.DeviceCompliance){ + if ($MappedFields.DeviceCompliance) { $NinjaDeviceUpdate | Add-Member -NotePropertyName $MappedFields.DeviceCompliance -NotePropertyValue $Device.complianceState } @@ -1173,16 +1176,20 @@ function Invoke-NinjaOneTenantSync { # Relate Users to Devices Foreach ($LinkDevice in $ParsedDevices | Where-Object { $null -ne $_.NinjaDevice }) { + $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/with-entity/NODE/$($LinkDevice.NinjaDevice.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 [System.Collections.Generic.List[PSCustomObject]]$Relations = @() Foreach ($LinkUser in $LinkDevice.UserIDs) { $MatchedUser = $UsersMap | Where-Object { $_.M365ID -eq $LinkUser } if (($MatchedUser | Measure-Object).count -eq 1) { - $Relations.Add( - [PSCustomObject]@{ - relEntityType = "DOCUMENT" - relEntityId = $MatchedUser.NinjaOneID - } - ) + $ExistingRelation = $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -eq $MatchedUser.NinjaOneID } + if (!$ExistingRelation) { + $Relations.Add( + [PSCustomObject]@{ + relEntityType = "DOCUMENT" + relEntityId = $MatchedUser.NinjaOneID + } + ) + } } } From a26637d68809b5398a87165b33a5c910270d6cdf Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Fri, 3 Nov 2023 01:00:10 +0000 Subject: [PATCH 13/77] Subscription Details v1 --- Modules/CippExtensions/CippExtensions.psd1 | Bin 0 -> 11220 bytes .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 209 +++++++++++++++++- PublicWebhooks/run.ps1 | 14 +- PublicWebhooksProcess/run.ps1 | 2 +- 4 files changed, 211 insertions(+), 14 deletions(-) diff --git a/Modules/CippExtensions/CippExtensions.psd1 b/Modules/CippExtensions/CippExtensions.psd1 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ac0cf361c2c3aee2295537ff3c939ed259b467d6 100644 GIT binary patch literal 11220 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 TG)8u;F(2u9Ct>zSw7|asZC literal 0 HcmV?d00001 diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index aa650114df7c..c7cfdeee5769 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -132,11 +132,11 @@ function Invoke-NinjaOneTenantSync { $NinjaOneUsersTemplate = Invoke-NinjaOneDocumentTemplate -Template $UserDocTemplate -Token $Token + # Get NinjaOne Users - #[System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents?organizationIDs=$($NinjaOneOrg)&templateIds=$($NinjaOneUsersTemplate.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100)."$NinjaOneOrg" - [System.Collections.Generic.List[PSCustomObject]]$NinjaOneOrgDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($NinjaOneOrg)/documents" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100) + [System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents?organizationIds=$($NinjaOneOrg)&templateIds=$($NinjaOneUsersTemplate.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100) - foreach ($NinjaDoc in $NinjaOneOrgDocs) { + foreach ($NinjaDoc in $NinjaOneUserDocs) { $ParsedFields = [pscustomobject]@{} foreach ($Field in $NinjaDoc.Fields) { if ($Field.value.text) { @@ -150,15 +150,82 @@ function Invoke-NinjaOneTenantSync { } - [System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = $NinjaOneOrgDocs | Where-Object { $_.documentTemplateId -eq $NinjaOneUsersTemplate.id } - # Get NinjaOne Related Items $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + + # NinjaOne License Documents + $LicenseDocTemplate = [PSCustomObject]@{ + name = 'CIPP - Microsoft 365 Subscriptions' + allowMultiple = $true + fields = @( + [PSCustomObject]@{ + fieldLabel = 'Subscription Summary' + fieldName = 'cippSubscriptionSummary' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } + } + }, + [PSCustomObject]@{ + fieldLabel = 'Subscription Users' + fieldName = 'cippSubscriptionUsers' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } + } + }, + [PSCustomObject]@{ + fieldLabel = 'Subscription ID' + fieldName = 'cippSubscriptionID' + fieldType = 'TEXT' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + } + } + ) + } + + $NinjaOneLicenseTemplate = Invoke-NinjaOneDocumentTemplate -Template $LicenseDocTemplate -Token $Token + + # Get NinjaOne Licenses + [System.Collections.Generic.List[PSCustomObject]]$NinjaOneLicenseDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents?organizationIds=$($NinjaOneOrg)&templateIds=$($NinjaOneLicenseTemplate.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100) + + foreach ($NinjaLic in $NinjaOneLicenseDocs) { + $ParsedFields = [pscustomobject]@{} + foreach ($Field in $NinjaLic.Fields) { + if ($Field.value.text) { + $FieldVal = $Field.value.text + } else { + $FieldVal = $Field.value + } + $ParsedFields | Add-Member -NotePropertyName $Field.name -NotePropertyValue $FieldVal + } + $NinjaLic | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force + } + + # Create the update objects we will use to update NinjaOne $NinjaOrgUpdate = [PSCustomObject]@{} [System.Collections.Generic.List[PSCustomObject]]$NinjaUserUpdates = @() [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCreation = @() + [System.Collections.Generic.List[PSCustomObject]]$NinjaSubscriptionUpdates = @() + [System.Collections.Generic.List[PSCustomObject]]$NinjaSubscriptionCreation = @() # Build bulk requests array. [System.Collections.Generic.List[PSCustomObject]]$TenantRequests = @( @@ -221,6 +288,11 @@ function Invoke-NinjaOneTenantSync { id = 'SecureScoreControlProfiles' method = 'GET' url = '/security/secureScoreControlProfiles' + }, + @{ + id = 'Subscriptions' + method = 'GET' + url = '/directory/subscriptions' } ) @@ -235,6 +307,8 @@ function Invoke-NinjaOneTenantSync { $Users = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Users' $SecureScore = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'SecureScore' + + $Subscriptions = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Subscriptions' [System.Collections.Generic.List[PSCustomObject]]$SecureScoreProfiles = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'SecureScoreControlProfiles' @@ -1193,8 +1267,6 @@ function Invoke-NinjaOneTenantSync { } } - $Relations | ConvertTo-Json -Depth 100 -AsArray | Out-File D:\Temp\Relations.json - Write-Host "URL: https://$($Configuration.Instance)/api/v2/related-items/entity/NODE/$($LinkDevice.NinjaDevice.id)/relations" try { # Update Relations @@ -1209,6 +1281,129 @@ function Invoke-NinjaOneTenantSync { } + ### License Document Details + #NinjaOneLicenseTemplate + + foreach ($Subscription in $Subscriptions) { + $MatchedLicenseInfo = $Licenses | Where-Object -Property skuid -EQ $Subscription.skuId + + if (($MatchedLicenseInfo | Measure-Object).count -eq 0) { + Write-Host "Failed to match license: $($Subscription | ConvertTo-Json -Depth 100)" + Continue + } + + Write-Host "License Matched" + + $FriendlyLicenseName = $((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $Subscription.SkuPartNumber).Tolower())) + + Write-Host "Friendly Generated: $FriendlyLicenseName" + + $SubscriptionUsers = $Users | foreach-Object { + $SubUser = $_ + $MatchedPlans = $SubUser.AssignedPlans | Where-Object { $_.servicePlanId -in $Subscription.ServiceStatus.ServicePlanID } + if (($MatchedPlans | Measure-Object).count -gt 0 ) { + $SubRelUserID = ($UsersMap | Where-Object { $_.M365ID -eq $SubUser.id }).NinjaOneID + [PSCustomObject]@{ + Name = '' + $SubUser.displayName + '' + UPN = $SubUser.userPrincipalName + 'License Assigned' = $(Get-Date(($MatchedPlans | Group-Object assignedDateTime | Sort-Object Count -Desc | Select-Object -First 1).name) -Format u) + NinjaUserDocID = $SubRelUserID + } + } + } + + Write-Host "Looping Users" + + $SubscriptionUsersHTML = $SubscriptionUsers | Select-Object -ExcludeProperty NinjaUserDocID | ConvertTo-Html -As Table -Fragment + $SubscriptionUsersHTML = ([System.Web.HttpUtility]::HtmlDecode($SubscriptionUsersHTML) -replace '
    ', '') -replace '', '' + + $SubscriptionSummary = [PSCustomObject]@{ + 'Subscription Name' = $FriendlyLicenseName + Created = $Subscription.createdDateTime + Renewal = $Subscription.nextLifecycleDateTime + Trial = $Subscription.isTrial + Status = $Subscription.Status + 'Subscription Licenses' = $Subscription.totalLicenses + 'Tenant Used' = $MatchedLicenseInfo.consumedUnits + 'Tenant Total' = $MatchedLicenseInfo.prepaidUnits.enabled + 'Owner Tenant ID' = $Subscription.ownerTenantId + 'Owner ID' = $Subscription.ownerId + 'Owner Type' = $Subscription.ownerType + 'Subscription ID' = $Subscription.id + } + $SubscriptionOverviewCardHTML = Get-NinjaOneInfoCard -Title "Subscription Details" -Data $SubscriptionSummary -Icon 'fas fa-file-invoice' + + Write-Host "Summary Card Generated" + + $SubscriptionItemsTable = $Subscription.serviceStatus | Select-Object @{n = 'Plan Name'; e = { convert-skuname -skuname $_.servicePlanName } }, @{n = 'Applies To'; e = { $_.appliesTo } }, @{n = 'Provisioning Status'; e = { $_.provisioningStatus } } + $SubscriptionItemsHTML = $SubscriptionItemsTable | ConvertTo-Html -As Table -Fragment + $SubscriptionItemsHTML = ([System.Web.HttpUtility]::HtmlDecode($SubscriptionItemsHTML) -replace '', '') -replace '', '' + + $SubscriptionItemsCardHTML = Get-NinjaOneCard -Title 'Subscription Items' -Body $SubscriptionItemsHTML -Icon 'fas fa-chart-bar' + + Write-Host "Items Card Generated" + + $SubscriptionSummaryHTML = '
    ' + + '
    ' + $SubscriptionOverviewCardHTML + + '
    ' + $SubscriptionItemsCardHTML + + '
    ' + + $NinjaOneSubscription = $NinjaOneLicenseDocs | Where-Object { $_.ParsedFields.cippSubscriptionID -eq $Subscription.ID } + + $SubscriptionFields = @{ + cippSubscriptionSummary = @{'html' = $SubscriptionSummaryHTML } + cippSubscriptionUsers = @{'html' = $SubscriptionUsersHTML } + cippSubscriptionID = $Subscription.id + } + + Write-Host "Fields Mapped" + + if ($NinjaOneSubscription) { + $UpdateObject = [PSCustomObject]@{ + documentId = $NinjaOneSubscription.documentId + documentName = "$FriendlyLicenseName ($($Subscription.id))" + fields = $SubscriptionFields + } + $NinjaSubscriptionUpdates.Add($UpdateObject) + Write-Host "Upadate Object Added" + } else { + $CreateObject = [PSCustomObject]@{ + documentName = "$FriendlyLicenseName ($($Subscription.id))" + documentTemplateId = [int]($NinjaOneLicenseTemplate.id) + organizationId = [int]$NinjaOneOrg + fields = $SubscriptionFields + } + $NinjaSubscriptionCreation.Add($CreateObject) + Write-Host "Create Object Added" + } + + } + $NinjaSubscriptionCreation | ConvertTo-Json -Depth 100 | Out-File D:\Temp\SubscriptionCreation.json + $NinjaSubscriptionUpdates | ConvertTo-Json -Depth 100 | Out-File D:\Temp\SubscriptionUpdates.json + + try { + # Create New Subscriptions + if (($NinjaSubscriptionCreation | Measure-Object).count -ge 1) { + Write-Host "Creating NinjaOne Subscriptions" + $CreatedSubscriptions = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaSubscriptionCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + } + } Catch { + Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" + } + + try { + # Update Subscriptions + if (($NinjaSubscriptionUpdates | Measure-Object).count -ge 1) { + Write-Host "Updating NinjaOne Subscriptions" + $UpdatedSubscriptions = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaSubscriptionUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + Write-Host "Completed Update" + } + } Catch { + Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" + } + + + ### M365 Links Section if ($MappedFields.TenantLinks) { Write-Host "Tenant Links" diff --git a/PublicWebhooks/run.ps1 b/PublicWebhooks/run.ps1 index 16dc2319608d..708ba8c4314a 100644 --- a/PublicWebhooks/run.ps1 +++ b/PublicWebhooks/run.ps1 @@ -13,14 +13,16 @@ Write-Host $url if ($Request.CIPPID -in $Webhooks.CIPPID) { Write-Host "Found matching CIPPID" + Push-OutputBinding -Name QueueWebhook -Value $Request -Push-OutputBinding -Name QueueWebhook -Value $Request - -if ($Request.query.ValidationToken -or $Request.body.validationCode) { - Write-Host "Validation token received" - $body = $request.query.ValidationToken + if ($Request.query.ValidationToken -or $Request.body.validationCode) { + Write-Host "Validation token received" + $body = $request.query.ValidationToken + } else { + $Body = 'Webhook Recieved' + } } else { - $Body = 'Webhook Recieved' + $body = "This webhook is not authorized." } # Associate values to output bindings by calling 'Push-OutputBinding'. diff --git a/PublicWebhooksProcess/run.ps1 b/PublicWebhooksProcess/run.ps1 index 5a26becf3416..572879bd134e 100644 --- a/PublicWebhooksProcess/run.ps1 +++ b/PublicWebhooksProcess/run.ps1 @@ -11,7 +11,7 @@ Write-Host "Received request" Write-Host "CIPPID: $($request.Query.CIPPID)" $url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 Write-Host $url -if ($Request.query.CIPPID-in $Webhooks.CIPPID) { +if ($Request.query.CIPPID -in $Webhooks.CIPPID) { Write-Host "Found matching CIPPID" $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID From 820014473317bd97f5c7767a9491ca64f739af8d Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Wed, 15 Nov 2023 08:20:49 +0000 Subject: [PATCH 14/77] Subscription Related Items --- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index c7cfdeee5769..2af25f4648dc 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -1246,7 +1246,6 @@ function Invoke-NinjaOneTenantSync { }) } } - # Relate Users to Devices Foreach ($LinkDevice in $ParsedDevices | Where-Object { $null -ne $_.NinjaDevice }) { @@ -1284,7 +1283,7 @@ function Invoke-NinjaOneTenantSync { ### License Document Details #NinjaOneLicenseTemplate - foreach ($Subscription in $Subscriptions) { + $SubscriptionDetails = foreach ($Subscription in $Subscriptions) { $MatchedLicenseInfo = $Licenses | Where-Object -Property skuid -EQ $Subscription.skuId if (($MatchedLicenseInfo | Measure-Object).count -eq 0) { @@ -1292,11 +1291,9 @@ function Invoke-NinjaOneTenantSync { Continue } - Write-Host "License Matched" $FriendlyLicenseName = $((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $Subscription.SkuPartNumber).Tolower())) - Write-Host "Friendly Generated: $FriendlyLicenseName" $SubscriptionUsers = $Users | foreach-Object { $SubUser = $_ @@ -1312,8 +1309,6 @@ function Invoke-NinjaOneTenantSync { } } - Write-Host "Looping Users" - $SubscriptionUsersHTML = $SubscriptionUsers | Select-Object -ExcludeProperty NinjaUserDocID | ConvertTo-Html -As Table -Fragment $SubscriptionUsersHTML = ([System.Web.HttpUtility]::HtmlDecode($SubscriptionUsersHTML) -replace '
    ', '') -replace '', '' @@ -1333,7 +1328,6 @@ function Invoke-NinjaOneTenantSync { } $SubscriptionOverviewCardHTML = Get-NinjaOneInfoCard -Title "Subscription Details" -Data $SubscriptionSummary -Icon 'fas fa-file-invoice' - Write-Host "Summary Card Generated" $SubscriptionItemsTable = $Subscription.serviceStatus | Select-Object @{n = 'Plan Name'; e = { convert-skuname -skuname $_.servicePlanName } }, @{n = 'Applies To'; e = { $_.appliesTo } }, @{n = 'Provisioning Status'; e = { $_.provisioningStatus } } $SubscriptionItemsHTML = $SubscriptionItemsTable | ConvertTo-Html -As Table -Fragment @@ -1341,7 +1335,6 @@ function Invoke-NinjaOneTenantSync { $SubscriptionItemsCardHTML = Get-NinjaOneCard -Title 'Subscription Items' -Body $SubscriptionItemsHTML -Icon 'fas fa-chart-bar' - Write-Host "Items Card Generated" $SubscriptionSummaryHTML = '
    ' + '
    ' + $SubscriptionOverviewCardHTML + @@ -1356,7 +1349,6 @@ function Invoke-NinjaOneTenantSync { cippSubscriptionID = $Subscription.id } - Write-Host "Fields Mapped" if ($NinjaOneSubscription) { $UpdateObject = [PSCustomObject]@{ @@ -1365,7 +1357,6 @@ function Invoke-NinjaOneTenantSync { fields = $SubscriptionFields } $NinjaSubscriptionUpdates.Add($UpdateObject) - Write-Host "Upadate Object Added" } else { $CreateObject = [PSCustomObject]@{ documentName = "$FriendlyLicenseName ($($Subscription.id))" @@ -1374,12 +1365,14 @@ function Invoke-NinjaOneTenantSync { fields = $SubscriptionFields } $NinjaSubscriptionCreation.Add($CreateObject) - Write-Host "Create Object Added" + } + + [PSCustomObject]@{ + Name = "$FriendlyLicenseName ($($Subscription.id))" + Users = $SubscriptionUsers.NinjaUserDocID } } - $NinjaSubscriptionCreation | ConvertTo-Json -Depth 100 | Out-File D:\Temp\SubscriptionCreation.json - $NinjaSubscriptionUpdates | ConvertTo-Json -Depth 100 | Out-File D:\Temp\SubscriptionUpdates.json try { # Create New Subscriptions @@ -1402,6 +1395,39 @@ function Invoke-NinjaOneTenantSync { Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } + $SubscriptionDocs = $CreatedSubscriptions + $UpdatedSubscriptions + + # Relate Subscriptions to Users + Foreach ($LinkSub in $SubscriptionDetails) { + $MatchedSubDoc = $SubscriptionDocs | Where-Object { $_.documentName -eq $LinkSub.name } + if (($MatchedSubDoc | Measure-Object).count -eq 1) { + $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/with-entity/DOCUMENT/$($MatchedSubDoc.documentId)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + [System.Collections.Generic.List[PSCustomObject]]$Relations = @() + Foreach ($LinkUser in $LinkSub.Users) { + $ExistingRelation = $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -eq $LinkUser } + if (!$ExistingRelation) { + $Relations.Add( + [PSCustomObject]@{ + relEntityType = "DOCUMENT" + relEntityId = $LinkUser + } + ) + } + } + + try { + # Update Relations + if (($Relations | Measure-Object).count -ge 1) { + Write-Host "Updating Relations" + $RelationResult = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/DOCUMENT/$($($MatchedSubDoc.documentId))/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop + Write-Host "Completed Update" + } + } Catch { + Write-Host "Creating Relations Failed: $_" + } + } + } + ### M365 Links Section From 5d54550cafa2e97572907c43a6ba4fbe64315444 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:25:10 +0000 Subject: [PATCH 15/77] Syncing Licenses rather than subscriptions + others fixes --- ExecExtensionNinjaOneSync/function.json | 22 --- ExecExtensionNinjaOneSync/run.ps1 | 38 ---- ExecExtensionSync/function.json | 6 + ExecExtensionSync/run.ps1 | 63 ++++-- ExecExtensionTest/run.ps1 | 4 + .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 179 ++++++++++-------- .../function.json | 0 Scheduler_Extensions/run.ps1 | 52 +++++ Scheduler_NinjaOne/run.ps1 | 44 ----- 9 files changed, 214 insertions(+), 194 deletions(-) delete mode 100644 ExecExtensionNinjaOneSync/function.json delete mode 100644 ExecExtensionNinjaOneSync/run.ps1 rename {Scheduler_NinjaOne => Scheduler_Extensions}/function.json (100%) create mode 100644 Scheduler_Extensions/run.ps1 delete mode 100644 Scheduler_NinjaOne/run.ps1 diff --git a/ExecExtensionNinjaOneSync/function.json b/ExecExtensionNinjaOneSync/function.json deleted file mode 100644 index 9626c8400cc1..000000000000 --- a/ExecExtensionNinjaOneSync/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "NinjaProcess", - "queueName": "NinjaOneQueue" - } - ] -} diff --git a/ExecExtensionNinjaOneSync/run.ps1 b/ExecExtensionNinjaOneSync/run.ps1 deleted file mode 100644 index 564767a2346d..000000000000 --- a/ExecExtensionNinjaOneSync/run.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$Table = Get-CIPPTable -TableName NinjaOneSettings - -$CIPPMapping = Get-CIPPTable -TableName CippMapping -$Filter = "PartitionKey eq 'NinjaOrgsMapping'" -$TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } - -foreach ($Tenant in $TenantsToProcess) { - Push-OutputBinding -Name NinjaProcess -Value @{ - 'NinjaAction' = 'SyncTenant' - 'MappedTenant' = $Tenant - } - -} - -$AddObject = @{ - PartitionKey = 'NinjaConfig' - RowKey = 'NinjaLastRunTime' - 'SettingValue' = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffK") -} - -Add-AzDataTableEntity @Table -Entity $AddObject -Force - -Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' - -$Results = [pscustomobject]@{"Results" = "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" } - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) -clobber \ No newline at end of file diff --git a/ExecExtensionSync/function.json b/ExecExtensionSync/function.json index c0b29a163b2a..6fc0a8c6d4a3 100644 --- a/ExecExtensionSync/function.json +++ b/ExecExtensionSync/function.json @@ -17,6 +17,12 @@ "direction": "out", "name": "Msg", "queueName": "billqueue" + }, + { + "type": "queue", + "direction": "out", + "name": "NinjaProcess", + "queueName": "NinjaOneQueue" } ] } diff --git a/ExecExtensionSync/run.ps1 b/ExecExtensionSync/run.ps1 index 215298a5d488..477662fa6cf4 100644 --- a/ExecExtensionSync/run.ps1 +++ b/ExecExtensionSync/run.ps1 @@ -6,28 +6,63 @@ param($Request, $TriggerMetadata) $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -try { - Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Starting billing processing." -sev Info - $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 - foreach ($ConfigItem in $Configuration.psobject.properties.name) { - switch ($ConfigItem) { - "Gradient" { - If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { - Push-OutputBinding -Name msg -Value "LetsGo" - $Results = [pscustomobject]@{"Results" = "Succesfully started Gradient Sync" } +if ($Request.Query.Extension -eq 'Gradient') { + try { + Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Starting billing processing." -sev Info + $Table = Get-CIPPTable -TableName Extensionsconfig + $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 + foreach ($ConfigItem in $Configuration.psobject.properties.name) { + switch ($ConfigItem) { + "Gradient" { + If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { + Push-OutputBinding -Name msg -Value "LetsGo" + $Results = [pscustomobject]@{"Results" = "Succesfully started Gradient Sync" } + } } } } + } catch { + $Results = [pscustomobject]@{"Results" = "Could not start Gradient Sync: $($_.Exception.Message)" } + + Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Could not start billing processing $($_.Exception.Message)" -sev Error } } -catch { - $Results = [pscustomobject]@{"Results" = "Could not start Gradient Sync: $($_.Exception.Message)" } - Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Could not start billing processing $($_.Exception.Message)" -sev Error +if ($Request.Query.Extension -eq 'NinjaOne') { + try { + $Table = Get-CIPPTable -TableName NinjaOneSettings + + $CIPPMapping = Get-CIPPTable -TableName CippMapping + $Filter = "PartitionKey eq 'NinjaOrgsMapping'" + $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } + + foreach ($Tenant in $TenantsToProcess) { + Push-OutputBinding -Name NinjaProcess -Value @{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + } + + } + + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaLastRunTime' + 'SettingValue' = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffK") + } + + Add-AzDataTableEntity @Table -Entity $AddObject -Force + + Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' + + $Results = [pscustomobject]@{"Results" = "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" } + } catch { + $Results = [pscustomobject]@{"Results" = "Could not start NinjaOne Sync: $($_.Exception.Message)" } + Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Could not start NinjaOne Sync $($_.Exception.Message)" -sev Error + } + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Results diff --git a/ExecExtensionTest/run.ps1 b/ExecExtensionTest/run.ps1 index 32ed9300eb52..03ca59ae18bf 100644 --- a/ExecExtensionTest/run.ps1 +++ b/ExecExtensionTest/run.ps1 @@ -26,6 +26,10 @@ try { "CIPP-API" { $Results = [pscustomobject]@{"Results" = "You cannot test the CIPP-API from CIPP. Please check the documentation on how to test the CIPP-API." } } + "NinjaOne" { + $token = Get-NinjaOneToken -configuration $Configuration.NinjaOne + $Results = [pscustomobject]@{"Results" = "Succesfully Connected to NinjaOne" } + } } } catch { diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 2af25f4648dc..6fe11d0f4268 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -156,12 +156,12 @@ function Invoke-NinjaOneTenantSync { # NinjaOne License Documents $LicenseDocTemplate = [PSCustomObject]@{ - name = 'CIPP - Microsoft 365 Subscriptions' + name = 'CIPP - Microsoft 365 Licenses' allowMultiple = $true fields = @( [PSCustomObject]@{ - fieldLabel = 'Subscription Summary' - fieldName = 'cippSubscriptionSummary' + fieldLabel = 'License Summary' + fieldName = 'cippLicenseSummary' fieldType = 'WYSIWYG' fieldTechnicianPermission = 'READ_ONLY' fieldScriptPermission = 'NONE' @@ -174,8 +174,8 @@ function Invoke-NinjaOneTenantSync { } }, [PSCustomObject]@{ - fieldLabel = 'Subscription Users' - fieldName = 'cippSubscriptionUsers' + fieldLabel = 'License Users' + fieldName = 'cippLicenseUsers' fieldType = 'WYSIWYG' fieldTechnicianPermission = 'READ_ONLY' fieldScriptPermission = 'NONE' @@ -188,8 +188,8 @@ function Invoke-NinjaOneTenantSync { } }, [PSCustomObject]@{ - fieldLabel = 'Subscription ID' - fieldName = 'cippSubscriptionID' + fieldLabel = 'License ID' + fieldName = 'cippLicenseID' fieldType = 'TEXT' fieldTechnicianPermission = 'READ_ONLY' fieldScriptPermission = 'NONE' @@ -224,8 +224,8 @@ function Invoke-NinjaOneTenantSync { $NinjaOrgUpdate = [PSCustomObject]@{} [System.Collections.Generic.List[PSCustomObject]]$NinjaUserUpdates = @() [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCreation = @() - [System.Collections.Generic.List[PSCustomObject]]$NinjaSubscriptionUpdates = @() - [System.Collections.Generic.List[PSCustomObject]]$NinjaSubscriptionCreation = @() + [System.Collections.Generic.List[PSCustomObject]]$NinjaLicenseUpdates = @() + [System.Collections.Generic.List[PSCustomObject]]$NinjaLicenseCreation = @() # Build bulk requests array. [System.Collections.Generic.List[PSCustomObject]]$TenantRequests = @( @@ -447,6 +447,7 @@ function Invoke-NinjaOneTenantSync { } catch { $InstalledAppDetailsReturn = $null } + $DeviceAppInstallDetails = foreach ($Result in $InstalledAppDetailsReturn) { [pscustomobject]@{ ID = $Result.id @@ -1281,104 +1282,103 @@ function Invoke-NinjaOneTenantSync { ### License Document Details - #NinjaOneLicenseTemplate - - $SubscriptionDetails = foreach ($Subscription in $Subscriptions) { - $MatchedLicenseInfo = $Licenses | Where-Object -Property skuid -EQ $Subscription.skuId - if (($MatchedLicenseInfo | Measure-Object).count -eq 0) { - Write-Host "Failed to match license: $($Subscription | ConvertTo-Json -Depth 100)" - Continue - } + $LicenseDetails = foreach ($License in $Licenses) { + $MatchedSubscriptions = $Subscriptions | Where-Object -Property skuid -EQ $License.skuId - - $FriendlyLicenseName = $((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $Subscription.SkuPartNumber).Tolower())) + try { + $FriendlyLicenseName = $((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $License.SkuPartNumber).Tolower())) + } catch { + $FriendlyLicenseName = $License.SkuPartNumber + } - $SubscriptionUsers = $Users | foreach-Object { - $SubUser = $_ - $MatchedPlans = $SubUser.AssignedPlans | Where-Object { $_.servicePlanId -in $Subscription.ServiceStatus.ServicePlanID } - if (($MatchedPlans | Measure-Object).count -gt 0 ) { + $LicenseUsers = foreach ($SubUser in $Users) { + $MatchedLicense = $SubUser.assignedLicenses | Where-Object { $License.skuId -in $_.skuId } + $MatchedPlans = $SubUser.AssignedPlans | Where-Object { $_.servicePlanId -in $License.servicePlans.servicePlanID } + if (($MatchedLicense | Measure-Object).count -gt 0 ) { $SubRelUserID = ($UsersMap | Where-Object { $_.M365ID -eq $SubUser.id }).NinjaOneID [PSCustomObject]@{ Name = '' + $SubUser.displayName + '' UPN = $SubUser.userPrincipalName - 'License Assigned' = $(Get-Date(($MatchedPlans | Group-Object assignedDateTime | Sort-Object Count -Desc | Select-Object -First 1).name) -Format u) + 'License Assigned' = $(try { $(Get-Date(($MatchedPlans | Group-Object assignedDateTime | Sort-Object Count -Desc | Select-Object -First 1).name) -Format u) } catch { 'Unknown' }) NinjaUserDocID = $SubRelUserID } } } - $SubscriptionUsersHTML = $SubscriptionUsers | Select-Object -ExcludeProperty NinjaUserDocID | ConvertTo-Html -As Table -Fragment - $SubscriptionUsersHTML = ([System.Web.HttpUtility]::HtmlDecode($SubscriptionUsersHTML) -replace '
    ', '') -replace '', '' - - $SubscriptionSummary = [PSCustomObject]@{ - 'Subscription Name' = $FriendlyLicenseName - Created = $Subscription.createdDateTime - Renewal = $Subscription.nextLifecycleDateTime - Trial = $Subscription.isTrial - Status = $Subscription.Status - 'Subscription Licenses' = $Subscription.totalLicenses - 'Tenant Used' = $MatchedLicenseInfo.consumedUnits - 'Tenant Total' = $MatchedLicenseInfo.prepaidUnits.enabled - 'Owner Tenant ID' = $Subscription.ownerTenantId - 'Owner ID' = $Subscription.ownerId - 'Owner Type' = $Subscription.ownerType - 'Subscription ID' = $Subscription.id + $LicenseUsersHTML = $LicenseUsers | Select-Object -ExcludeProperty NinjaUserDocID | ConvertTo-Html -As Table -Fragment + $LicenseUsersHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseUsersHTML) -replace '', '') -replace '', '' + + $LicenseSummary = [PSCustomObject]@{ + 'License Name' = $FriendlyLicenseName + 'Tenant Used' = $License.consumedUnits + 'Tenant Total' = $License.prepaidUnits.enabled + 'SKU ID' = $License.skuId } - $SubscriptionOverviewCardHTML = Get-NinjaOneInfoCard -Title "Subscription Details" -Data $SubscriptionSummary -Icon 'fas fa-file-invoice' + $LicenseOverviewCardHTML = Get-NinjaOneInfoCard -Title "License Details" -Data $LicenseSummary -Icon 'fas fa-file-invoice' + + $SubscriptionsHTML = $MatchedSubscriptions | Select-Object @{'n' = 'Subscription Licenses'; 'e' = { $_.totalLicenses } }, + @{'n' = 'Created'; 'e' = { $_.createdDateTime } }, + @{'n' = 'Renewal'; 'e' = { $_.nextLifecycleDateTime } }, + @{'n' = 'Trial'; 'e' = { $_.isTrial } }, + @{'n' = 'Status'; 'e' = { $_.Status } } | ConvertTo-Html -As Table -Fragment + + $SubscriptionsHTML = ([System.Web.HttpUtility]::HtmlDecode($SubscriptionsHTML) -replace '', '') -replace '', '' + $SubscriptionCardHTML = Get-NinjaOneCard -Title "Subscriptions" -Body $SubscriptionsHTML -Icon 'fas fa-file-invoice' - $SubscriptionItemsTable = $Subscription.serviceStatus | Select-Object @{n = 'Plan Name'; e = { convert-skuname -skuname $_.servicePlanName } }, @{n = 'Applies To'; e = { $_.appliesTo } }, @{n = 'Provisioning Status'; e = { $_.provisioningStatus } } - $SubscriptionItemsHTML = $SubscriptionItemsTable | ConvertTo-Html -As Table -Fragment - $SubscriptionItemsHTML = ([System.Web.HttpUtility]::HtmlDecode($SubscriptionItemsHTML) -replace '', '') -replace '', '' + $LicenseItemsTable = $License.servicePlans | Select-Object @{n = 'Plan Name'; e = { convert-skuname -skuname $_.servicePlanName } }, @{n = 'Applies To'; e = { $_.appliesTo } }, @{n = 'Provisioning Status'; e = { $_.provisioningStatus } } + $LicenseItemsHTML = $LicenseItemsTable | ConvertTo-Html -As Table -Fragment + $LicenseItemsHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseItemsHTML) -replace '', '') -replace '', '' - $SubscriptionItemsCardHTML = Get-NinjaOneCard -Title 'Subscription Items' -Body $SubscriptionItemsHTML -Icon 'fas fa-chart-bar' + $LicenseItemsCardHTML = Get-NinjaOneCard -Title 'License Items' -Body $LicenseItemsHTML -Icon 'fas fa-chart-bar' - $SubscriptionSummaryHTML = '
    ' + - '
    ' + $SubscriptionOverviewCardHTML + - '
    ' + $SubscriptionItemsCardHTML + + $LicenseSummaryHTML = '
    ' + + '
    ' + $LicenseOverviewCardHTML + + '
    ' + $SubscriptionCardHTML + + '
    ' + $LicenseItemsCardHTML + '
    ' - $NinjaOneSubscription = $NinjaOneLicenseDocs | Where-Object { $_.ParsedFields.cippSubscriptionID -eq $Subscription.ID } + $NinjaOneLicense = $NinjaOneLicenseDocs | Where-Object { $_.ParsedFields.cippLicenseID -eq $License.ID } - $SubscriptionFields = @{ - cippSubscriptionSummary = @{'html' = $SubscriptionSummaryHTML } - cippSubscriptionUsers = @{'html' = $SubscriptionUsersHTML } - cippSubscriptionID = $Subscription.id + $LicenseFields = @{ + cippLicenseSummary = @{'html' = $LicenseSummaryHTML } + cippLicenseUsers = @{'html' = $LicenseUsersHTML } + cippLicenseID = $License.id } - if ($NinjaOneSubscription) { + if ($NinjaOneLicense) { $UpdateObject = [PSCustomObject]@{ - documentId = $NinjaOneSubscription.documentId - documentName = "$FriendlyLicenseName ($($Subscription.id))" - fields = $SubscriptionFields + documentId = $NinjaOneLicense.documentId + documentName = "$FriendlyLicenseName" + fields = $LicenseFields } - $NinjaSubscriptionUpdates.Add($UpdateObject) + $NinjaLicenseUpdates.Add($UpdateObject) } else { $CreateObject = [PSCustomObject]@{ - documentName = "$FriendlyLicenseName ($($Subscription.id))" + documentName = "$FriendlyLicenseName" documentTemplateId = [int]($NinjaOneLicenseTemplate.id) organizationId = [int]$NinjaOneOrg - fields = $SubscriptionFields + fields = $LicenseFields } - $NinjaSubscriptionCreation.Add($CreateObject) + $NinjaLicenseCreation.Add($CreateObject) } [PSCustomObject]@{ - Name = "$FriendlyLicenseName ($($Subscription.id))" - Users = $SubscriptionUsers.NinjaUserDocID + Name = "$FriendlyLicenseName" + Users = $LicenseUsers.NinjaUserDocID } } try { # Create New Subscriptions - if (($NinjaSubscriptionCreation | Measure-Object).count -ge 1) { - Write-Host "Creating NinjaOne Subscriptions" - $CreatedSubscriptions = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaSubscriptionCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + if (($NinjaLicenseCreation | Measure-Object).count -ge 1) { + Write-Host "Creating NinjaOne Licenses" + $CreatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 } } Catch { Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" @@ -1386,24 +1386,25 @@ function Invoke-NinjaOneTenantSync { try { # Update Subscriptions - if (($NinjaSubscriptionUpdates | Measure-Object).count -ge 1) { - Write-Host "Updating NinjaOne Subscriptions" - $UpdatedSubscriptions = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaSubscriptionUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + if (($NinjaLicenseUpdates | Measure-Object).count -ge 1) { + Write-Host "Updating NinjaOne Licenses" + $UpdatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 Write-Host "Completed Update" } } Catch { Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } - $SubscriptionDocs = $CreatedSubscriptions + $UpdatedSubscriptions + $LicenseDocs = $CreatedLicenses + $UpdatedLicenses # Relate Subscriptions to Users - Foreach ($LinkSub in $SubscriptionDetails) { - $MatchedSubDoc = $SubscriptionDocs | Where-Object { $_.documentName -eq $LinkSub.name } - if (($MatchedSubDoc | Measure-Object).count -eq 1) { - $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/with-entity/DOCUMENT/$($MatchedSubDoc.documentId)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + Foreach ($LinkLic in $LicenseDetails) { + $MatchedLicDoc = $LicenseDocs | Where-Object { $_.documentName -eq $LinkLic.name } + if (($MatchedLicDoc | Measure-Object).count -eq 1) { + # Remove existing relations + $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/with-entity/DOCUMENT/$($MatchedLicDoc.documentId)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 [System.Collections.Generic.List[PSCustomObject]]$Relations = @() - Foreach ($LinkUser in $LinkSub.Users) { + Foreach ($LinkUser in $LinkLic.Users) { $ExistingRelation = $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -eq $LinkUser } if (!$ExistingRelation) { $Relations.Add( @@ -1419,16 +1420,28 @@ function Invoke-NinjaOneTenantSync { # Update Relations if (($Relations | Measure-Object).count -ge 1) { Write-Host "Updating Relations" - $RelationResult = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/DOCUMENT/$($($MatchedSubDoc.documentId))/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop + $RelationResult = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/DOCUMENT/$($($MatchedLicDoc.documentId))/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop Write-Host "Completed Update" } } Catch { Write-Host "Creating Relations Failed: $_" } + + #Remove relations + foreach ($DelUser in $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -notin $LinkLic.Users }) { + try { + $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/$($DelUser.id)" -Method Delete -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + } catch { + Write-Host "Failed to remove relation $($DelUser.id) from $($LinkLic.name)" + } + } } } + ####################################################################### + + ### M365 Links Section if ($MappedFields.TenantLinks) { @@ -1767,6 +1780,20 @@ function Invoke-NinjaOneTenantSync { ### Summary Stats Write-Host "Widget Details" + ### Fetch BPA Data + $Table = get-cipptable 'cachebpav2' + $Tenants = Get-Tenants -IncludeErrors + $Data = (Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$NAME'") | ForEach-Object { + $row = $_ + $JSONFields | ForEach-Object { + $jsonContent = $row.$_ + if ($jsonContent -ne $null -and $jsonContent -ne 'FAILED') { + $row.$_ = $jsonContent | ConvertFrom-Json -Depth 15 + } + } + $row | Where-Object -Property PartitionKey -In $Tenants.customerId + } + [System.Collections.Generic.List[PSCustomObject]]$WidgetData = @() # Licensed Users diff --git a/Scheduler_NinjaOne/function.json b/Scheduler_Extensions/function.json similarity index 100% rename from Scheduler_NinjaOne/function.json rename to Scheduler_Extensions/function.json diff --git a/Scheduler_Extensions/run.ps1 b/Scheduler_Extensions/run.ps1 new file mode 100644 index 000000000000..bf2ddde5f665 --- /dev/null +++ b/Scheduler_Extensions/run.ps1 @@ -0,0 +1,52 @@ +using namespace System.Net + +param($Timer) + +$Table = Get-CIPPTable -TableName Extensionsconfig + +$Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json) + +# NinjaOne Extension +if ($Configuration.NinjaOne.Enabled -eq $True) { + + $Table = Get-CIPPTable -TableName NinjaOneSettings + $Settings = (Get-AzDataTableEntity @Table) + $TimeSetting = ($Settings | Where-Object { $_.RowKey -eq 'NinjaSyncTime' }).SettingValue + + if (($TimeSetting | Measure-Object).count -ne 1) { + [int]$TimeSetting = Get-Random -Minimum 0 -Maximum 96 + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaSyncTime' + 'SettingValue' = $TimeSetting + } + Add-AzDataTableEntity @Table -Entity $AddObject -Force + } + + $LastRunTime = Get-Date(($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue) + $CurrentInterval = ($currentHour * 4) + [math]::Floor($currentMinute / 15) + + if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $TimeSetting -eq $CurrentInterval) { + $CIPPMapping = Get-CIPPTable -TableName CippMapping + $Filter = "PartitionKey eq 'NinjaOrgsMapping'" + $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } + + foreach ($Tenant in $TenantsToProcess) { + Push-OutputBinding -Name NinjaProcess -Value @{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + } + + } + + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaLastRunTime' + 'SettingValue' = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffK") + } + Add-AzDataTableEntity @Table -Entity $AddObject -Force + + Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' + + } +} \ No newline at end of file diff --git a/Scheduler_NinjaOne/run.ps1 b/Scheduler_NinjaOne/run.ps1 deleted file mode 100644 index ed5514142559..000000000000 --- a/Scheduler_NinjaOne/run.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -using namespace System.Net - -param($Timer) - -$Table = Get-CIPPTable -TableName NinjaOneSettings -$Settings = (Get-AzDataTableEntity @Table) -$TimeSetting = ($Settings | Where-Object { $_.RowKey -eq 'NinjaSyncTime' }).SettingValue - -if (($TimeSetting | Measure-Object).count -ne 1) { - [int]$TimeSetting = Get-Random -Minimum 0 -Maximum 96 - $AddObject = @{ - PartitionKey = 'NinjaConfig' - RowKey = 'NinjaSyncTime' - 'SettingValue' = $TimeSetting - } - Add-AzDataTableEntity @Table -Entity $AddObject -Force -} - -$LastRunTime = Get-Date(($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue) -$CurrentInterval = ($currentHour * 4) + [math]::Floor($currentMinute / 15) - -if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $TimeSetting -eq $CurrentInterval) { - $CIPPMapping = Get-CIPPTable -TableName CippMapping - $Filter = "PartitionKey eq 'NinjaOrgsMapping'" - $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } - - foreach ($Tenant in $TenantsToProcess) { - Push-OutputBinding -Name NinjaProcess -Value @{ - 'NinjaAction' = 'SyncTenant' - 'MappedTenant' = $Tenant - } - - } - - $AddObject = @{ - PartitionKey = 'NinjaConfig' - RowKey = 'NinjaLastRunTime' - 'SettingValue' = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffK") - } - Add-AzDataTableEntity @Table -Entity $AddObject -Force - - Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' - -} \ No newline at end of file From 1544587ad0421495fe40377c91d7ca9585d5426a Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:14:53 +0000 Subject: [PATCH 16/77] BPA Widgets and Bug Fixes --- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 183 ++++++++++++------ .../NinjaOne/NinjaOneHelper.ps1 | 2 +- 2 files changed, 125 insertions(+), 60 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 6fe11d0f4268..849f95b6e5ea 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -6,7 +6,7 @@ function Invoke-NinjaOneTenantSync { try { $StartTime = Get-Date - write-host "$(Get-Date) - Starting NinjaOne Sync $($customer.DisplayName)" + write-host "$(Get-Date) - Starting NinjaOne Sync" # Fetch Custom NinjaOne Settings $Table = Get-CIPPTable -TableName NinjaOneSettings @@ -16,6 +16,7 @@ function Invoke-NinjaOneTenantSync { # Parse out the Tenant we are processing $MappedTenant = $QueueItem.MappedTenant $Customer = Get-Tenants | where-object { $_.customerId -eq $MappedTenant.RowKey } + Write-Host "Processing: $($Customer.displayName)" if (($Customer | Measure-Object).count -ne 1) { Throw "Unable to match the recieved ID to a tenant QueueItem: $($QueueItem | ConvertTo-Json -Depth 100 | Out-String) Matched Customer: $($Customer| ConvertTo-Json -Depth 100 | Out-String)" @@ -702,7 +703,7 @@ function Invoke-NinjaOneTenantSync { }, @{ Name = 'View Devices in CIPP' - Link = "https://$($CIPPURL)/.auth/login/aad?post_login_redirect_uri=$($CIPPURL)//endpoint/reports/devices?customerId=$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/endpoint/reports/devices?customerId=$($Customer.defaultDomainName)" Icon = 'far fa-eye' } ) @@ -722,9 +723,9 @@ function Invoke-NinjaOneTenantSync { # Set Compliance Status if ($Device.complianceState -eq 'compliant') { - $Compliance = '   Compliant' + $Compliance = '   Compliant' } else { - $Compliance = '   Not Compliant' + $Compliance = '   Not Compliant' } # Device Details @@ -901,9 +902,9 @@ function Invoke-NinjaOneTenantSync { # Set Compliance Status if ($UserDevice.Compliance -eq 'compliant') { - $ComplianceIcon = '' + $ComplianceIcon = '' } else { - $ComplianceIcon = '' + $ComplianceIcon = '' } # OS Icon @@ -941,14 +942,14 @@ function Invoke-NinjaOneTenantSync { } $OneDriveUseColor = if ($OneDriveUse.Percent -ge 95) { - '#ec1c24' + '#D53948' } elseif ($MailboxUse.Percent -ge 85) { '#FFA500' } else { - '#008001' + '#26A644' } - $OneDriveParsed = '
    ' + $OneDriveParsed = '
    ' } else { $OneDriveUse = [PSCustomObject]@{ @@ -992,14 +993,14 @@ function Invoke-NinjaOneTenantSync { } $MailboxUseColor = if ($MailboxUse.Percent -ge 95) { - '#ec1c24' + '#D53948' } elseif ($MailboxUse.Percent -ge 85) { '#FFA500' } else { - '#008001' + '#26A644' } - $MailboxParsed = '
    ' + $MailboxParsed = '
    ' } else { $MailboxUse = [PSCustomObject]@{ @@ -1094,17 +1095,17 @@ function Invoke-NinjaOneTenantSync { $CIPPUserLinksData = @( @{ Name = 'View User' - Link = "https://$($CIPPURL)/.auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/users/view?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/identity/administration/users/view?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" Icon = 'far fa-eye' }, @{ Name = 'Edit User' - Link = "https://$($CIPPURL)/.auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/users/edit?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/identity/administration/users/edit?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" Icon = 'fas fa-users-cog' }, @{ Name = 'Research Compromise' - Link = "https://$($CIPPURL)/.auth/login/aad?post_login_redirect_uri=$($CIPPURL)identity/administration/ViewBec?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/identity/administration/ViewBec?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" Icon = 'fas fa-user-secret' } ) @@ -1581,12 +1582,12 @@ function Invoke-NinjaOneTenantSync { @{ Label = 'Sign-In Enabled' Amount = $UsersEnabledCount - Colour = '#008001' + Colour = '#26A644' }, @{ Label = 'Sign-In Blocked' Amount = $TotalUsersCount - $UsersEnabledCount - Colour = '#ec1c24' + Colour = '#D53948' } ) @@ -1641,12 +1642,12 @@ function Invoke-NinjaOneTenantSync { @{ Label = 'Compliant' Amount = $ComplianceDevicesCount - Colour = '#008001' + Colour = '#26A644' }, @{ Label = 'Non Compliant' Amount = $TotalDeviceswCount - $ComplianceDevicesCount - Colour = '#ec1c24' + Colour = '#D53948' } ) @@ -1686,7 +1687,7 @@ function Invoke-NinjaOneTenantSync { @{ Label = 'Online in last 30 days' Amount = $OnlineInLast30Days - Colour = '#008001' + Colour = '#26A644' }, @{ Label = 'Not seen for 30+ days' @@ -1714,7 +1715,7 @@ function Invoke-NinjaOneTenantSync { @{ Label = 'Current Score' Amount = $CurrentSecureScore.currentScore - Colour = '#008001' + Colour = '#26A644' }, @{ Label = 'Points to Obtain' @@ -1776,25 +1777,111 @@ function Invoke-NinjaOneTenantSync { $TitleLink = "https://$CIPPUrl/tenant/administration/list-licenses?customerId=$($Customer.customerId)" $LicensesSummaryCardHTML = Get-NinjaOneCard -Title 'Licenses' -Body $LicenseTableHTML -Icon 'fas fa-chart-bar' -TitleLink $TitleLink - + ### Summary Stats Write-Host "Widget Details" + [System.Collections.Generic.List[PSCustomObject]]$WidgetData = @() + ### Fetch BPA Data $Table = get-cipptable 'cachebpav2' - $Tenants = Get-Tenants -IncludeErrors - $Data = (Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$NAME'") | ForEach-Object { - $row = $_ - $JSONFields | ForEach-Object { - $jsonContent = $row.$_ - if ($jsonContent -ne $null -and $jsonContent -ne 'FAILED') { - $row.$_ = $jsonContent | ConvertFrom-Json -Depth 15 - } - } - $row | Where-Object -Property PartitionKey -In $Tenants.customerId + $BPAData = (Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq '$($Customer.customerId)' and RowKey eq 'CIPP Best Practices v1.0 - Table view'") + + if ($Null -ne $BPAData.Timestamp) { + ## BPA Data Widgets + # Shared Mailboxes with Enabled Users + #$WidgetData.add([PSCustomObject]@{ + # Value = $( + # $SharedSendMailboxCount = ($BpaData.SharedMailboxeswithenabledusers | ConvertFrom-Json | Measure-Object).count + # if ($SharedSendMailboxCount -ne 0) { + # $ResultColour = '#D53948' + # } else { + # $ResultColour = '#26A644' + # } + # $SharedSendMailboxCount + # ) + # Description = 'Shared Mailboxes with enabled users' + # Colour = $ResultColour + # Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" + # }) + + # Unused Licenses + $WidgetData.add([PSCustomObject]@{ + Value = $( + $BPAUnusedLicenses = (($BpaData.Unusedlicenses | ConvertFrom-Json).availableUnits | Measure-Object -sum).sum + if ($BPAUnusedLicenses -ne 0) { + $ResultColour = '#D53948' + } else { + $ResultColour = '#26A644' + } + $BPAUnusedLicenses + ) + Description = 'Unused Licenses' + Colour = $ResultColour + Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" + }) + + + # Anonymous Reports + $WidgetData.add([PSCustomObject]@{ + Value = $(if ($BPAData.AnonymousPrivacyReports -eq $True) { + $ResultColour = '#D53948' + '' + } else { + $ResultColour = '#26A644' + '' + } + ) + Description = 'Anonymous Privacy Reports' + Colour = $ResultColour + Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" + }) + + # Unified Audit Log + $WidgetData.add([PSCustomObject]@{ + Value = $(if ($BPAData.UnifiedAuditLog -eq $True) { + $ResultColour = '#26A644' + '' + } else { + $ResultColour = '#D53948' + '' + } + ) + Description = 'Unified Audit Log' + Colour = $ResultColour + Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" + }) + + # oAuth App Consent + $WidgetData.add([PSCustomObject]@{ + Value = $(if ($BPAData.OAuthAppConsent -eq $True) { + $ResultColour = '#26A644' + '' + } else { + $ResultColour = '#D53948' + '' + } + ) + Description = 'OAuth App Consent' + Colour = $ResultColour + Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" + }) + } - [System.Collections.Generic.List[PSCustomObject]]$WidgetData = @() + # Blocked Senders + $BlockedSenderCount = ($BlockedSenders | Measure-Object).count + if ($BlockedSenderCount -eq 0) { + $BlockedSenderColour = '#26A644' + } else { + $BlockedSenderColour = '#D53948' + } + $WidgetData.add([PSCustomObject]@{ + Value = $BlockedSenderCount + Description = 'Blocked Senders' + Colour = $BlockedSenderColour + Link = "https://security.microsoft.com/restrictedentities?tid=$($Customer.customerId)" + }) # Licensed Users $WidgetData.add([PSCustomObject]@{ @@ -1828,18 +1915,6 @@ function Invoke-NinjaOneTenantSync { Link = "https://$CIPPUrl/identity/administration/roles?customerId=$($Customer.customerId)" }) - # Exchange - if ( 'exchange' -in $TenantDetails.assignedPlans.service) { - $ExchangeStatus = '' - } else { - $ExchangeStatus = '' - } - $WidgetData.add([PSCustomObject]@{ - Value = $ExchangeStatus - Description = 'Exchange' - Colour = '#CCCCCC' - Link = "https://admin.exchange.microsoft.com/?landingpage=homepage&form=mac_sidebar&delegatedOrg=$($Customer.DefaultDomainName)#" - }) # AAD Premium if ( 'AADPremiumService' -in $TenantDetails.assignedPlans.service) { @@ -1882,20 +1957,10 @@ function Invoke-NinjaOneTenantSync { - # Blocked Senders - $BlockedSenderCount = ($BlockedSenders | Measure-Object).count - if ($BlockedSenderCount -eq 0) { - $BlockedSenderColour = '#008001' - } else { - $BlockedSenderColour = '#ec1c24' - } - $WidgetData.add([PSCustomObject]@{ - Value = $BlockedSenderCount - Description = 'Blocked Senders' - Colour = $BlockedSenderColour - Link = "https://security.microsoft.com/restrictedentities?tid=$($Customer.customerId)" - }) - + + + + Write-Host 'Summary Details' $SummaryDetailsCardHTML = Get-NinjaOneWidgetCard -Title 'Summary Details' -Data $WidgetData -Icon 'fas fa-building' -TitleLink 'http://example.com' -SmallCols 2 -MedCols 3 -LargeCols 4 -XLCols 6 -NoCard diff --git a/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 b/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 index 58c4b5e2b981..af30f8a36b03 100644 --- a/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 +++ b/Modules/CippExtensions/NinjaOne/NinjaOneHelper.ps1 @@ -39,7 +39,7 @@ function Get-NinjaInLineBarGraph ($Data, [string]$Title, [string]$Icon, [string] $OutputHTML.add((Get-NinjaOneTitle -Icon $Icon -Title ($Title + $(if (!$NoCount) { " ($Total)" })) -TitleLink $TitleLink)) } - $OutputHTML.add('
    ') + $OutputHTML.add('
    ') foreach ($Item in $Data) { $OutputHTML.add(@" From 5c5ca5e1f175449232ce66de5cb4a6535f6e21bc Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:43:19 +0000 Subject: [PATCH 17/77] Initial Logging --- Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 849f95b6e5ea..cd915c82b16b 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -18,6 +18,8 @@ function Invoke-NinjaOneTenantSync { $Customer = Get-Tenants | where-object { $_.customerId -eq $MappedTenant.RowKey } Write-Host "Processing: $($Customer.displayName)" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Processing NinjaOne Synchronization for $($Customer.displayName)" -Sev 'Info' + if (($Customer | Measure-Object).count -ne 1) { Throw "Unable to match the recieved ID to a tenant QueueItem: $($QueueItem | ConvertTo-Json -Depth 100 | Out-String) Matched Customer: $($Customer| ConvertTo-Json -Depth 100 | Out-String)" } @@ -2015,6 +2017,6 @@ function Invoke-NinjaOneTenantSync { Write-Host "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" } catch { - Write-Host "FATAL ERROR: $_" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Failed Processing for $($Customer.displayName) Error: $_" -Sev 'Error' } } \ No newline at end of file From 5da67be6edaf1fff60aa4bf85163255c4b1b8560 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:47:16 +0000 Subject: [PATCH 18/77] Fixed File Output --- Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index cd915c82b16b..a9f4fa1daa9e 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -338,8 +338,6 @@ function Invoke-NinjaOneTenantSync { } } - $SecureScoreParsed | ConvertTo-Json | Out-File D:\Temp\ParsedScore.json - $TenantDetails = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'TenantDetails' write-verbose "$(Get-Date) - Parsing Users" From c13f0721939a526af337eb6ba63d8b598004bdc0 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:50:20 +0000 Subject: [PATCH 19/77] Additional Logging Improvements --- Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index a9f4fa1daa9e..001e0ea704df 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -2013,8 +2013,9 @@ function Invoke-NinjaOneTenantSync { $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($MappedTenant.NinjaOne)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaOrgUpdate | ConvertTo-Json -Depth 100) Write-Host "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Completed NinjaOne Sync for $($Customer.displayName) in $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds) seconds" -Sev 'info' } catch { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Failed Processing for $($Customer.displayName) Error: $_" -Sev 'Error' + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Failed NinjaOne Processing for $($Customer.displayName) Error: $_" -Sev 'Error' } } \ No newline at end of file From aee5052b43eacfd422ac3a6254316b8025f88ae2 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:06:10 +0000 Subject: [PATCH 20/77] Fix Host Detection --- ExecExtensionMapping/run.ps1 | 2 +- Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ExecExtensionMapping/run.ps1 b/ExecExtensionMapping/run.ps1 index 6804924416e5..99015d39bdea 100644 --- a/ExecExtensionMapping/run.ps1 +++ b/ExecExtensionMapping/run.ps1 @@ -40,7 +40,7 @@ try { } 'NinjaFields' { - $Body = Set-NinjaOneFieldMapping -CIPPMapping $Table -APIName $APIName -Request $Request + $Body = Set-NinjaOneFieldMapping -CIPPMapping $Table -APIName $APIName -Request $Request -TriggerMetadata $TriggerMetadata } } } diff --git a/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 b/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 index 115cbbefdc3a..4653d51ed13a 100644 --- a/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Set-NinjaOneFieldMapping.ps1 @@ -3,14 +3,15 @@ function Set-NinjaOneFieldMapping { param ( $CIPPMapping, $APIName, - $Request + $Request, + $TriggerMetadata ) $SettingsTable = Get-CIPPTable -TableName NinjaOneSettings $AddObject = @{ PartitionKey = 'NinjaConfig' RowKey = 'CIPPURL' - 'SettingValue' = ($Request.Url -split '/')[2] + 'SettingValue' = ([System.Uri]$TriggerMetadata.Headers.referer).Host } Add-AzDataTableEntity @SettingsTable -Entity $AddObject -Force From 08d6f2038751074d43be9d2645c50c838875ea61 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:23:19 +0000 Subject: [PATCH 21/77] Move dashboard link --- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 001e0ea704df..82088857b00e 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -1493,11 +1493,6 @@ function Invoke-NinjaOneTenantSync { Name = 'Azure Portal' Link = "https://portal.azure.com/$($customer.DefaultDomainName)" Icon = 'fas fa-server' - }, - @{ - Name = 'CIPP Tenant Admin' - Link = "https://$CIPPUrl/home?customerId=$($Customer.CustomerId)" - Icon = 'fas fa-shield-halved' } ) @@ -1505,6 +1500,12 @@ function Invoke-NinjaOneTenantSync { $M365LinksHTML = Get-NinjaOneLinks -Data $ManagementLinksData -Title 'Portals' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 $CIPPLinksData = @( + + @{ + Name = 'CIPP Tenant Dashboard' + Link = "https://$CIPPUrl/home?customerId=$($Customer.CustomerId)" + Icon = 'fas fa-shield-halved' + }, @{ Name = 'Edit Tenant' Link = "https://$CIPPUrl/tenant/administration/tenants/Edit?customerId=$($Customer.customerId)&tenantFilter=$($Customer.defaultDomainName)" From 45f2bf495502e988b4be34eedca305223dd45723 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sat, 18 Nov 2023 10:16:54 +0000 Subject: [PATCH 22/77] Implemented Togglable Settings + Bug Fixes --- GraphHelper.psm1 | 2 +- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 851 +++++++++--------- 2 files changed, 424 insertions(+), 429 deletions(-) diff --git a/GraphHelper.psm1 b/GraphHelper.psm1 index 5bf3231267fb..62b8f0d0b5a5 100644 --- a/GraphHelper.psm1 +++ b/GraphHelper.psm1 @@ -794,7 +794,7 @@ function New-GraphBulkRequest { foreach ($MoreData in $ReturnedData.Responses | Where-Object { $_.body.'@odata.nextLink' }) { Write-Host 'Getting more' - $AdditionalValues = New-GraphGetRequest -ComplexFilter -uri $MoreData.body.'@odata.nextLink' -tenantid $TenantFilter -NoAuthCheck:$NoAuthCheck + $AdditionalValues = New-GraphGetRequest -ComplexFilter -uri $MoreData.body.'@odata.nextLink' -tenantid $tenantid -NoAuthCheck:$NoAuthCheck $NewValues = [System.Collections.Generic.List[PSCustomObject]]$MoreData.body.value $AdditionalValues | ForEach-Object { $NewValues.add($_) } $MoreData.body.value = $NewValues diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 82088857b00e..ed84ab229749 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -51,175 +51,176 @@ function Invoke-NinjaOneTenantSync { $After = $ResultCount.maximum } while ($ResultCount.count -eq $PageSize) - - # Get NinjaOne User Documents - $UserDocTemplate = [PSCustomObject]@{ - name = 'CIPP - Microsoft 365 Users' - allowMultiple = $true - fields = @( - [PSCustomObject]@{ - fieldLabel = 'User Links' - fieldName = 'cippUserLinks' - fieldType = 'WYSIWYG' - fieldTechnicianPermission = 'READ_ONLY' - fieldScriptPermission = 'NONE' - fieldApiPermission = 'READ_WRITE' - fieldContent = @{ - required = $False - advancedSettings = @{ - expandLargeValueOnRender = $True + [System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = @() + + if ($Configuration.UserDocumentsEnabled -eq $True) { + # Get NinjaOne User Documents + $UserDocTemplate = [PSCustomObject]@{ + name = 'CIPP - Microsoft 365 Users' + allowMultiple = $true + fields = @( + [PSCustomObject]@{ + fieldLabel = 'User Links' + fieldName = 'cippUserLinks' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } } - } - }, - [PSCustomObject]@{ - fieldLabel = 'User Summary' - fieldName = 'cippUserSummary' - fieldType = 'WYSIWYG' - fieldTechnicianPermission = 'READ_ONLY' - fieldScriptPermission = 'NONE' - fieldApiPermission = 'READ_WRITE' - fieldContent = @{ - required = $False - advancedSettings = @{ - expandLargeValueOnRender = $True + }, + [PSCustomObject]@{ + fieldLabel = 'User Summary' + fieldName = 'cippUserSummary' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } } - } - }, - [PSCustomObject]@{ - fieldLabel = 'User Devices' - fieldName = 'cippUserDevices' - fieldType = 'WYSIWYG' - fieldTechnicianPermission = 'READ_ONLY' - fieldScriptPermission = 'NONE' - fieldApiPermission = 'READ_WRITE' - fieldContent = @{ - required = $False - advancedSettings = @{ - expandLargeValueOnRender = $True + }, + [PSCustomObject]@{ + fieldLabel = 'User Devices' + fieldName = 'cippUserDevices' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } } - } - }, - [PSCustomObject]@{ - fieldLabel = 'User Groups' - fieldName = 'cippUserGroups' - fieldType = 'WYSIWYG' - fieldTechnicianPermission = 'READ_ONLY' - fieldScriptPermission = 'NONE' - fieldApiPermission = 'READ_WRITE' - fieldContent = @{ - required = $False - advancedSettings = @{ - expandLargeValueOnRender = $True + }, + [PSCustomObject]@{ + fieldLabel = 'User Groups' + fieldName = 'cippUserGroups' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } } + }, + [PSCustomObject]@{ + fieldLabel = 'User ID' + fieldName = 'cippUserID' + fieldType = 'TEXT' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + }, + [PSCustomObject]@{ + fieldLabel = 'User UPN' + fieldName = 'cippUserUPN' + fieldType = 'TEXT' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' } - }, - [PSCustomObject]@{ - fieldLabel = 'User ID' - fieldName = 'cippUserID' - fieldType = 'TEXT' - fieldTechnicianPermission = 'READ_ONLY' - fieldScriptPermission = 'NONE' - fieldApiPermission = 'READ_WRITE' - }, - [PSCustomObject]@{ - fieldLabel = 'User UPN' - fieldName = 'cippUserUPN' - fieldType = 'TEXT' - fieldTechnicianPermission = 'READ_ONLY' - fieldScriptPermission = 'NONE' - fieldApiPermission = 'READ_WRITE' - } - ) - } + ) + } - $NinjaOneUsersTemplate = Invoke-NinjaOneDocumentTemplate -Template $UserDocTemplate -Token $Token + $NinjaOneUsersTemplate = Invoke-NinjaOneDocumentTemplate -Template $UserDocTemplate -Token $Token - # Get NinjaOne Users - [System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents?organizationIds=$($NinjaOneOrg)&templateIds=$($NinjaOneUsersTemplate.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100) + # Get NinjaOne Users + [System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents?organizationIds=$($NinjaOneOrg)&templateIds=$($NinjaOneUsersTemplate.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100) - foreach ($NinjaDoc in $NinjaOneUserDocs) { - $ParsedFields = [pscustomobject]@{} - foreach ($Field in $NinjaDoc.Fields) { - if ($Field.value.text) { - $FieldVal = $Field.value.text - } else { - $FieldVal = $Field.value + foreach ($NinjaDoc in $NinjaOneUserDocs) { + $ParsedFields = [pscustomobject]@{} + foreach ($Field in $NinjaDoc.Fields) { + if ($Field.value.text) { + $FieldVal = $Field.value.text + } else { + $FieldVal = $Field.value + } + $ParsedFields | Add-Member -NotePropertyName $Field.name -NotePropertyValue $FieldVal } - $ParsedFields | Add-Member -NotePropertyName $Field.name -NotePropertyValue $FieldVal + $NinjaDoc | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force } - $NinjaDoc | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force } - - # Get NinjaOne Related Items - $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 - - - # NinjaOne License Documents - $LicenseDocTemplate = [PSCustomObject]@{ - name = 'CIPP - Microsoft 365 Licenses' - allowMultiple = $true - fields = @( - [PSCustomObject]@{ - fieldLabel = 'License Summary' - fieldName = 'cippLicenseSummary' - fieldType = 'WYSIWYG' - fieldTechnicianPermission = 'READ_ONLY' - fieldScriptPermission = 'NONE' - fieldApiPermission = 'READ_WRITE' - fieldContent = @{ - required = $False - advancedSettings = @{ - expandLargeValueOnRender = $True + [System.Collections.Generic.List[PSCustomObject]]$NinjaOneLicenseDocs = @() + if ($Configuration.LicenseDocumentsEnabled) { + # NinjaOne License Documents + $LicenseDocTemplate = [PSCustomObject]@{ + name = 'CIPP - Microsoft 365 Licenses' + allowMultiple = $true + fields = @( + [PSCustomObject]@{ + fieldLabel = 'License Summary' + fieldName = 'cippLicenseSummary' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } } - } - }, - [PSCustomObject]@{ - fieldLabel = 'License Users' - fieldName = 'cippLicenseUsers' - fieldType = 'WYSIWYG' - fieldTechnicianPermission = 'READ_ONLY' - fieldScriptPermission = 'NONE' - fieldApiPermission = 'READ_WRITE' - fieldContent = @{ - required = $False - advancedSettings = @{ - expandLargeValueOnRender = $True + }, + [PSCustomObject]@{ + fieldLabel = 'License Users' + fieldName = 'cippLicenseUsers' + fieldType = 'WYSIWYG' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False + advancedSettings = @{ + expandLargeValueOnRender = $True + } + } + }, + [PSCustomObject]@{ + fieldLabel = 'License ID' + fieldName = 'cippLicenseID' + fieldType = 'TEXT' + fieldTechnicianPermission = 'READ_ONLY' + fieldScriptPermission = 'NONE' + fieldApiPermission = 'READ_WRITE' + fieldContent = @{ + required = $False } } - }, - [PSCustomObject]@{ - fieldLabel = 'License ID' - fieldName = 'cippLicenseID' - fieldType = 'TEXT' - fieldTechnicianPermission = 'READ_ONLY' - fieldScriptPermission = 'NONE' - fieldApiPermission = 'READ_WRITE' - fieldContent = @{ - required = $False - } - } - ) - } + ) + } - $NinjaOneLicenseTemplate = Invoke-NinjaOneDocumentTemplate -Template $LicenseDocTemplate -Token $Token + $NinjaOneLicenseTemplate = Invoke-NinjaOneDocumentTemplate -Template $LicenseDocTemplate -Token $Token - # Get NinjaOne Licenses - [System.Collections.Generic.List[PSCustomObject]]$NinjaOneLicenseDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents?organizationIds=$($NinjaOneOrg)&templateIds=$($NinjaOneLicenseTemplate.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100) + # Get NinjaOne Licenses + [System.Collections.Generic.List[PSCustomObject]]$NinjaOneLicenseDocs = ((Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents?organizationIds=$($NinjaOneOrg)&templateIds=$($NinjaOneLicenseTemplate.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100) - foreach ($NinjaLic in $NinjaOneLicenseDocs) { - $ParsedFields = [pscustomobject]@{} - foreach ($Field in $NinjaLic.Fields) { - if ($Field.value.text) { - $FieldVal = $Field.value.text - } else { - $FieldVal = $Field.value + foreach ($NinjaLic in $NinjaOneLicenseDocs) { + $ParsedFields = [pscustomobject]@{} + foreach ($Field in $NinjaLic.Fields) { + if ($Field.value.text) { + $FieldVal = $Field.value.text + } else { + $FieldVal = $Field.value + } + $ParsedFields | Add-Member -NotePropertyName $Field.name -NotePropertyValue $FieldVal } - $ParsedFields | Add-Member -NotePropertyName $Field.name -NotePropertyValue $FieldVal + $NinjaLic | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force } - $NinjaLic | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force } @@ -429,34 +430,6 @@ function Invoke-NinjaOneTenantSync { } } - write-verbose "$(Get-Date) - Parsing Apps" - # Fetch Apps - $DeviceApps = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'DeviceApps' - - # Fetch the App status for each device - [System.Collections.Generic.List[PSCustomObject]]$RequestArray = @() - foreach ($InstalledApp in $DeviceApps | where-object { $_.isAssigned -eq $True }) { - $RequestArray.add(@{ - id = $InstalledApp.id - method = 'GET' - url = "/deviceAppManagement/mobileApps/$($InstalledApp.id)/deviceStatuses" - }) - } - - try { - $InstalledAppDetailsReturn = New-GraphBulkRequest -Requests $RequestArray -tenantid $TenantFilter -NoAuthCheck $True - } catch { - $InstalledAppDetailsReturn = $null - } - - $DeviceAppInstallDetails = foreach ($Result in $InstalledAppDetailsReturn) { - [pscustomobject]@{ - ID = $Result.id - DisplayName = ($DeviceApps | where-object { $_.id -eq $Result.id }).DisplayName - InstalledAppDetails = $result.body.value - } - } - write-verbose "$(Get-Date) - Parsing Groups" # Fetch Groups $AllGroups = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Groups' @@ -587,9 +560,6 @@ function Invoke-NinjaOneTenantSync { $FetchEnd = Get-Date - Write-Host "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" - - ############################ Format and Synchronize to NinjaOne ############################ @@ -698,7 +668,7 @@ function Invoke-NinjaOneTenantSync { }, @{ Name = 'Intune (Devices)' - Link = "https://intune.microsoft.com/$($Customer.defaultDomainName)/#view/Microsoft_Intune_Devices/DeviceSettingsMenuBlade/~/overview/mdmDeviceId/$($Device.id)/" + Link = "https://intune.microsoft.com/$($Customer.defaultDomainName)/#view/Microsoft_Intune_Devices/DeviceSettingsMenuBlade/~/overview/mdmDeviceId/$($Device.id)" Icon = 'fas fa-laptop' }, @{ @@ -809,7 +779,14 @@ function Invoke-NinjaOneTenantSync { } ########## Create / Update User Objects - $ParsedUsers = foreach ($user in $licensedUsers) { + + if ($Configuration.LicensedOnly -eq $True) { + $SyncUsers = $licensedUsers + } else { + $SyncUsers = $Users + } + + $ParsedUsers = foreach ($user in $SyncUsers) { try { $NinjaOneUser = $NinjaOneUserDocs | Where-Object { $_.ParsedFields.cippUserID -eq $User.ID } if (($NinjaOneUser | Measure-Object).count -gt 1) { @@ -1013,7 +990,7 @@ function Invoke-NinjaOneTenantSync { $MailboxParsed = 'Not Enabled' } - if ($UserMailSettings) { + if ($UserMailSettings.ProhibitSendQuota) { $MailboxDetailsCardData = [PSCustomObject]@{ #'Permissions' = "$($UserMailSettings.Permissions | ConvertTo-Html -Fragment | Out-String)" 'Prohibit Send Quota' = "$($UserMailSettings.ProhibitSendQuota)" @@ -1095,17 +1072,17 @@ function Invoke-NinjaOneTenantSync { $CIPPUserLinksData = @( @{ Name = 'View User' - Link = "https://$($CIPPURL)/identity/administration/users/view?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/identity/administration/users/view?userId=$($User.id)&tenantDomain=$($Customer.defaultDomainName)" Icon = 'far fa-eye' }, @{ Name = 'Edit User' - Link = "https://$($CIPPURL)/identity/administration/users/edit?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/identity/administration/users/edit?userId=$($User.id)&tenantDomain=$($Customer.defaultDomainName)" Icon = 'fas fa-users-cog' }, @{ Name = 'Research Compromise' - Link = "https://$($CIPPURL)/identity/administration/ViewBec?userId=$($User.id)%26tenantDomain%3D$($Customer.defaultDomainName)" + Link = "https://$($CIPPURL)/identity/administration/ViewBec?userId=$($User.id)&tenantDomain=$($Customer.defaultDomainName)" Icon = 'fas fa-user-secret' } ) @@ -1114,7 +1091,6 @@ function Invoke-NinjaOneTenantSync { $ActionsHTML = @"     -   "@ @@ -1133,68 +1109,71 @@ function Invoke-NinjaOneTenantSync { Actions = $ActionsHTML } - - # Format into Ninja HTML - # Links - $M365UserLinksHTML = Get-NinjaOneLinks -Data $Microsoft365UserLinksData -Title 'Portals' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 - $CIPPUserLinksHTML = Get-NinjaOneLinks -Data $CIPPUserLinksData -Title 'CIPP Links' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 - $UserLinksHTML = '
    ' + $M365UserLinksHTML + '
    ' + $CIPPUserLinksHTML + '
    ' - - # UsersSummaryCards: - $UserOverviewCardHTML = Get-NinjaOneInfoCard -Title "User Details" -Data $UserOverviewCard -Icon 'fas fa-user' - $MailboxDetailsCardHTML = Get-NinjaOneInfoCard -Title "Mailbox Details" -Data $MailboxDetailsCardData -Icon 'fas fa-envelope' - $MailboxSettingsCardHTML = Get-NinjaOneInfoCard -Title "Mailbox Settings" -Data $MailboxSettingsCard -Icon 'fas fa-envelope' - $OneDriveCardHTML = Get-NinjaOneInfoCard -Title "OneDrive Details" -Data $OneDriveCardData -Icon 'fas fa-envelope' - $UserPolciesCard = Get-NinjaOneCard -Title "Assigned Conditional Access Policies" -Body $UserPoliciesFormatted - - - $UserSummaryHTML = '
    ' + - '
    ' + $UserOverviewCardHTML + - '
    ' + $MailboxDetailsCardHTML + - '
    ' + $MailboxSettingsCardHTML + - '
    ' + $OneDriveCardHTML + - '
    ' + $UserPolciesCard + - '
    ' + $DeviceSummaryCardHTML + - '
    ' - - - $UserDeviceDetailsTable = $UserDevicesDetailsRaw | Select-Object @{N = 'Name'; E = { $_.DeviceLink } }, - @{n = 'Enrolled'; e = { $_.Enrolled } }, - @{n = 'Last Sync'; e = { $_.LastSync } }, - @{n = 'OS'; e = { $_.OS } }, - @{n = 'OS Version'; e = { $_.OSVersion } }, - @{n = 'State'; e = { $_.Compliance } }, - @{n = 'Model'; e = { $_.Model } }, - @{n = 'Manufacturer'; e = { $_.Make } } + if ($Configuration.UserDocumentsEnabled -eq $True) { + + # Format into Ninja HTML + # Links + $M365UserLinksHTML = Get-NinjaOneLinks -Data $Microsoft365UserLinksData -Title 'Portals' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 + $CIPPUserLinksHTML = Get-NinjaOneLinks -Data $CIPPUserLinksData -Title 'CIPP Links' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 + $UserLinksHTML = '
    ' + $M365UserLinksHTML + '
    ' + $CIPPUserLinksHTML + '
    ' + + # UsersSummaryCards: + $UserOverviewCardHTML = Get-NinjaOneInfoCard -Title "User Details" -Data $UserOverviewCard -Icon 'fas fa-user' + $MailboxDetailsCardHTML = Get-NinjaOneInfoCard -Title "Mailbox Details" -Data $MailboxDetailsCardData -Icon 'fas fa-envelope' + $MailboxSettingsCardHTML = Get-NinjaOneInfoCard -Title "Mailbox Settings" -Data $MailboxSettingsCard -Icon 'fas fa-envelope' + $OneDriveCardHTML = Get-NinjaOneInfoCard -Title "OneDrive Details" -Data $OneDriveCardData -Icon 'fas fa-envelope' + $UserPolciesCard = Get-NinjaOneCard -Title "Assigned Conditional Access Policies" -Body $UserPoliciesFormatted + + + $UserSummaryHTML = '
    ' + + '
    ' + $UserOverviewCardHTML + + '
    ' + $MailboxDetailsCardHTML + + '
    ' + $MailboxSettingsCardHTML + + '
    ' + $OneDriveCardHTML + + '
    ' + $UserPolciesCard + + '
    ' + $DeviceSummaryCardHTML + + '
    ' + + + $UserDeviceDetailsTable = $UserDevicesDetailsRaw | Select-Object @{N = 'Name'; E = { $_.DeviceLink } }, + @{n = 'Enrolled'; e = { $_.Enrolled } }, + @{n = 'Last Sync'; e = { $_.LastSync } }, + @{n = 'OS'; e = { $_.OS } }, + @{n = 'OS Version'; e = { $_.OSVersion } }, + @{n = 'State'; e = { $_.Compliance } }, + @{n = 'Model'; e = { $_.Model } }, + @{n = 'Manufacturer'; e = { $_.Make } } - $UserDeviceDetailHTML = $UserDeviceDetailsTable | ConvertTo-Html -As Table -Fragment - $UserDeviceDetailHTML = ([System.Web.HttpUtility]::HtmlDecode($UserDeviceDetailHTML) -replace '
    ', '') -replace '', '' + $UserDeviceDetailHTML = $UserDeviceDetailsTable | ConvertTo-Html -As Table -Fragment + $UserDeviceDetailHTML = ([System.Web.HttpUtility]::HtmlDecode($UserDeviceDetailHTML) -replace '', '') -replace '', '' - $UserFields = @{ - cippUserLinks = @{'html' = $UserLinksHTML } - cippUserSummary = @{'html' = $UserSummaryHTML } - cippUserGroups = @{'html' = "$($UserGroups | ConvertTo-HTML -As Table -Fragment)" } - cippUserDevices = @{'html' = $UserDeviceDetailHTML } - cippUserID = $User.id - cippUserUPN = $User.userPrincipalName - } - if ($NinjaOneUser) { - $UpdateObject = [PSCustomObject]@{ - documentId = $NinjaOneUser.documentId - documentName = "$($User.displayName) ($($User.userPrincipalName))" - fields = $UserFields + $UserFields = @{ + cippUserLinks = @{'html' = $UserLinksHTML } + cippUserSummary = @{'html' = $UserSummaryHTML } + cippUserGroups = @{'html' = "$($UserGroups | ConvertTo-HTML -As Table -Fragment)" } + cippUserDevices = @{'html' = $UserDeviceDetailHTML } + cippUserID = $User.id + cippUserUPN = $User.userPrincipalName } - $NinjaUserUpdates.Add($UpdateObject) - } else { - $CreateObject = [PSCustomObject]@{ - documentName = "$($User.displayName) ($($User.userPrincipalName))" - documentTemplateId = ($NinjaOneUsersTemplate.id) - organizationId = [int]$NinjaOneOrg - fields = $UserFields + + if ($NinjaOneUser) { + $UpdateObject = [PSCustomObject]@{ + documentId = $NinjaOneUser.documentId + documentName = "$($User.displayName) ($($User.userPrincipalName))" + fields = $UserFields + } + $NinjaUserUpdates.Add($UpdateObject) + } else { + $CreateObject = [PSCustomObject]@{ + documentName = "$($User.displayName) ($($User.userPrincipalName))" + documentTemplateId = ($NinjaOneUsersTemplate.id) + organizationId = [int]$NinjaOneOrg + fields = $UserFields + } + $NinjaUserCreation.Add($CreateObject) } - $NinjaUserCreation.Add($CreateObject) } } catch { @@ -1202,243 +1181,256 @@ function Invoke-NinjaOneTenantSync { } } - try { - # Create New Users - if (($NinjaUserCreation | Measure-Object).count -ge 1) { - Write-Host "Creating NinjaOne Users" - $CreatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + [System.Collections.Generic.List[PSCustomObject]]$UsersMap = @() + + if ($Configuration.UserDocumentsEnabled -eq $True) { + try { + # Create New Users + if (($NinjaUserCreation | Measure-Object).count -ge 1) { + Write-Host "Creating NinjaOne Users" + $CreatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + } + } Catch { + Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" } - } Catch { - Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" - } - try { - # Update Users - if (($NinjaUserUpdates | Measure-Object).count -ge 1) { - Write-Host "Updating NinjaOne Users" - $UpdatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 - Write-Host "Completed Update" + try { + # Update Users + if (($NinjaUserUpdates | Measure-Object).count -ge 1) { + Write-Host "Updating NinjaOne Users" + $UpdatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + Write-Host "Completed Update" + } + } Catch { + Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } - } Catch { - Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" - } - ### Relationship Mapping - # Parse out the NinjaOne ID to MS ID - [System.Collections.Generic.List[PSCustomObject]]$UsersMap = @() + ### Relationship Mapping + # Parse out the NinjaOne ID to MS ID + - if (($UpdatedUsers | Measure-Object).count -ge 1) { - $UpdatedUsers | ForEach-Object { - $User = $_ - $Field = $User.updatedFields | Where-Object { $_.name -eq 'cippUserID' } - $UsersMap.Add([PSCustomObject]@{ - NinjaOneID = $User.documentId - M365ID = $Field.value - }) + if (($UpdatedUsers | Measure-Object).count -ge 1) { + $UpdatedUsers | ForEach-Object { + $User = $_ + $Field = $User.updatedFields | Where-Object { $_.name -eq 'cippUserID' } + $UsersMap.Add([PSCustomObject]@{ + NinjaOneID = $User.documentId + M365ID = $Field.value + }) + } } - } - if (($CreatedUsers | Measure-Object).count -ge 1) { - $CreatedUsers | ForEach-Object { - $User = $_ - $Field = $User.fields | Where-Object { $_.name -eq 'cippUserID' } - $UsersMap.Add([PSCustomObject]@{ - NinjaOneID = $User.documentId - M365ID = $Field.value - }) + if (($CreatedUsers | Measure-Object).count -ge 1) { + $CreatedUsers | ForEach-Object { + $User = $_ + $Field = $User.fields | Where-Object { $_.name -eq 'cippUserID' } + $UsersMap.Add([PSCustomObject]@{ + NinjaOneID = $User.documentId + M365ID = $Field.value + }) + } } - } + - # Relate Users to Devices - Foreach ($LinkDevice in $ParsedDevices | Where-Object { $null -ne $_.NinjaDevice }) { - $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/with-entity/NODE/$($LinkDevice.NinjaDevice.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 - [System.Collections.Generic.List[PSCustomObject]]$Relations = @() - Foreach ($LinkUser in $LinkDevice.UserIDs) { - $MatchedUser = $UsersMap | Where-Object { $_.M365ID -eq $LinkUser } - if (($MatchedUser | Measure-Object).count -eq 1) { - $ExistingRelation = $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -eq $MatchedUser.NinjaOneID } - if (!$ExistingRelation) { - $Relations.Add( - [PSCustomObject]@{ - relEntityType = "DOCUMENT" - relEntityId = $MatchedUser.NinjaOneID - } - ) + # Relate Users to Devices + Foreach ($LinkDevice in $ParsedDevices | Where-Object { $null -ne $_.NinjaDevice }) { + $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/with-entity/NODE/$($LinkDevice.NinjaDevice.id)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + [System.Collections.Generic.List[PSCustomObject]]$Relations = @() + Foreach ($LinkUser in $LinkDevice.UserIDs) { + $MatchedUser = $UsersMap | Where-Object { $_.M365ID -eq $LinkUser } + if (($MatchedUser | Measure-Object).count -eq 1) { + $ExistingRelation = $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -eq $MatchedUser.NinjaOneID } + if (!$ExistingRelation) { + $Relations.Add( + [PSCustomObject]@{ + relEntityType = "DOCUMENT" + relEntityId = $MatchedUser.NinjaOneID + } + ) + } } } - } - try { - # Update Relations - if (($Relations | Measure-Object).count -ge 1) { - Write-Host "Updating Relations" - $RelationResult = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/NODE/$($LinkDevice.NinjaDevice.id)/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop - Write-Host "Completed Update" + try { + # Update Relations + if (($Relations | Measure-Object).count -ge 1) { + Write-Host "Updating Relations" + $Null = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/NODE/$($LinkDevice.NinjaDevice.id)/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop + Write-Host "Completed Update" + } + } Catch { + Write-Host "Creating Relations Failed: $_" } - } Catch { - Write-Host "Creating Relations Failed: $_" } } - ### License Document Details + if ($Configuration.LicenseDocumentsEnabled -eq $True) { - $LicenseDetails = foreach ($License in $Licenses) { - $MatchedSubscriptions = $Subscriptions | Where-Object -Property skuid -EQ $License.skuId + $LicenseDetails = foreach ($License in $Licenses) { + $MatchedSubscriptions = $Subscriptions | Where-Object -Property skuid -EQ $License.skuId - try { - $FriendlyLicenseName = $((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $License.SkuPartNumber).Tolower())) - } catch { - $FriendlyLicenseName = $License.SkuPartNumber - } + try { + $FriendlyLicenseName = $((Get-Culture).TextInfo.ToTitleCase((convert-skuname -skuname $License.SkuPartNumber).Tolower())) + } catch { + $FriendlyLicenseName = $License.SkuPartNumber + } - $LicenseUsers = foreach ($SubUser in $Users) { - $MatchedLicense = $SubUser.assignedLicenses | Where-Object { $License.skuId -in $_.skuId } - $MatchedPlans = $SubUser.AssignedPlans | Where-Object { $_.servicePlanId -in $License.servicePlans.servicePlanID } - if (($MatchedLicense | Measure-Object).count -gt 0 ) { - $SubRelUserID = ($UsersMap | Where-Object { $_.M365ID -eq $SubUser.id }).NinjaOneID - [PSCustomObject]@{ - Name = '' + $SubUser.displayName + '' - UPN = $SubUser.userPrincipalName - 'License Assigned' = $(try { $(Get-Date(($MatchedPlans | Group-Object assignedDateTime | Sort-Object Count -Desc | Select-Object -First 1).name) -Format u) } catch { 'Unknown' }) - NinjaUserDocID = $SubRelUserID - } - } - } + $LicenseUsers = foreach ($SubUser in $Users) { + $MatchedLicense = $SubUser.assignedLicenses | Where-Object { $License.skuId -in $_.skuId } + $MatchedPlans = $SubUser.AssignedPlans | Where-Object { $_.servicePlanId -in $License.servicePlans.servicePlanID } + if (($MatchedLicense | Measure-Object).count -gt 0 ) { + $SubRelUserID = ($UsersMap | Where-Object { $_.M365ID -eq $SubUser.id }).NinjaOneID + if ($SubRelUserID){ + $LicUserName = '' + $SubUser.displayName + '' + } else { + $LicUserName = $SubUser.displayName + } + [PSCustomObject]@{ + Name = $LicUserName + UPN = $SubUser.userPrincipalName + 'License Assigned' = $(try { $(Get-Date(($MatchedPlans | Group-Object assignedDateTime | Sort-Object Count -Desc | Select-Object -First 1).name) -Format u) } catch { 'Unknown' }) + NinjaUserDocID = $SubRelUserID + } + } + } - $LicenseUsersHTML = $LicenseUsers | Select-Object -ExcludeProperty NinjaUserDocID | ConvertTo-Html -As Table -Fragment - $LicenseUsersHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseUsersHTML) -replace '', '') -replace '', '' + $LicenseUsersHTML = $LicenseUsers | Select-Object -ExcludeProperty NinjaUserDocID | ConvertTo-Html -As Table -Fragment + $LicenseUsersHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseUsersHTML) -replace '', '') -replace '', '' - $LicenseSummary = [PSCustomObject]@{ - 'License Name' = $FriendlyLicenseName - 'Tenant Used' = $License.consumedUnits - 'Tenant Total' = $License.prepaidUnits.enabled - 'SKU ID' = $License.skuId - } - $LicenseOverviewCardHTML = Get-NinjaOneInfoCard -Title "License Details" -Data $LicenseSummary -Icon 'fas fa-file-invoice' + $LicenseSummary = [PSCustomObject]@{ + 'License Name' = $FriendlyLicenseName + 'Tenant Used' = $License.consumedUnits + 'Tenant Total' = $License.prepaidUnits.enabled + 'SKU ID' = $License.skuId + } + $LicenseOverviewCardHTML = Get-NinjaOneInfoCard -Title "License Details" -Data $LicenseSummary -Icon 'fas fa-file-invoice' - $SubscriptionsHTML = $MatchedSubscriptions | Select-Object @{'n' = 'Subscription Licenses'; 'e' = { $_.totalLicenses } }, - @{'n' = 'Created'; 'e' = { $_.createdDateTime } }, - @{'n' = 'Renewal'; 'e' = { $_.nextLifecycleDateTime } }, - @{'n' = 'Trial'; 'e' = { $_.isTrial } }, - @{'n' = 'Status'; 'e' = { $_.Status } } | ConvertTo-Html -As Table -Fragment + $SubscriptionsHTML = $MatchedSubscriptions | Select-Object @{'n' = 'Subscription Licenses'; 'e' = { $_.totalLicenses } }, + @{'n' = 'Created'; 'e' = { $_.createdDateTime } }, + @{'n' = 'Renewal'; 'e' = { $_.nextLifecycleDateTime } }, + @{'n' = 'Trial'; 'e' = { $_.isTrial } }, + @{'n' = 'Status'; 'e' = { $_.Status } } | ConvertTo-Html -As Table -Fragment - $SubscriptionsHTML = ([System.Web.HttpUtility]::HtmlDecode($SubscriptionsHTML) -replace '', '') -replace '', '' - $SubscriptionCardHTML = Get-NinjaOneCard -Title "Subscriptions" -Body $SubscriptionsHTML -Icon 'fas fa-file-invoice' + $SubscriptionsHTML = ([System.Web.HttpUtility]::HtmlDecode($SubscriptionsHTML) -replace '', '') -replace '', '' + $SubscriptionCardHTML = Get-NinjaOneCard -Title "Subscriptions" -Body $SubscriptionsHTML -Icon 'fas fa-file-invoice' - $LicenseItemsTable = $License.servicePlans | Select-Object @{n = 'Plan Name'; e = { convert-skuname -skuname $_.servicePlanName } }, @{n = 'Applies To'; e = { $_.appliesTo } }, @{n = 'Provisioning Status'; e = { $_.provisioningStatus } } - $LicenseItemsHTML = $LicenseItemsTable | ConvertTo-Html -As Table -Fragment - $LicenseItemsHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseItemsHTML) -replace '', '') -replace '', '' + $LicenseItemsTable = $License.servicePlans | Select-Object @{n = 'Plan Name'; e = { convert-skuname -skuname $_.servicePlanName } }, @{n = 'Applies To'; e = { $_.appliesTo } }, @{n = 'Provisioning Status'; e = { $_.provisioningStatus } } + $LicenseItemsHTML = $LicenseItemsTable | ConvertTo-Html -As Table -Fragment + $LicenseItemsHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseItemsHTML) -replace '', '') -replace '', '' - $LicenseItemsCardHTML = Get-NinjaOneCard -Title 'License Items' -Body $LicenseItemsHTML -Icon 'fas fa-chart-bar' + $LicenseItemsCardHTML = Get-NinjaOneCard -Title 'License Items' -Body $LicenseItemsHTML -Icon 'fas fa-chart-bar' - $LicenseSummaryHTML = '
    ' + - '
    ' + $LicenseOverviewCardHTML + - '
    ' + $SubscriptionCardHTML + - '
    ' + $LicenseItemsCardHTML + - '
    ' + $LicenseSummaryHTML = '
    ' + + '
    ' + $LicenseOverviewCardHTML + + '
    ' + $SubscriptionCardHTML + + '
    ' + $LicenseItemsCardHTML + + '
    ' - $NinjaOneLicense = $NinjaOneLicenseDocs | Where-Object { $_.ParsedFields.cippLicenseID -eq $License.ID } + $NinjaOneLicense = $NinjaOneLicenseDocs | Where-Object { $_.ParsedFields.cippLicenseID -eq $License.ID } - $LicenseFields = @{ - cippLicenseSummary = @{'html' = $LicenseSummaryHTML } - cippLicenseUsers = @{'html' = $LicenseUsersHTML } - cippLicenseID = $License.id - } + $LicenseFields = @{ + cippLicenseSummary = @{'html' = $LicenseSummaryHTML } + cippLicenseUsers = @{'html' = $LicenseUsersHTML } + cippLicenseID = $License.id + } - if ($NinjaOneLicense) { - $UpdateObject = [PSCustomObject]@{ - documentId = $NinjaOneLicense.documentId - documentName = "$FriendlyLicenseName" - fields = $LicenseFields + if ($NinjaOneLicense) { + $UpdateObject = [PSCustomObject]@{ + documentId = $NinjaOneLicense.documentId + documentName = "$FriendlyLicenseName" + fields = $LicenseFields + } + $NinjaLicenseUpdates.Add($UpdateObject) + } else { + $CreateObject = [PSCustomObject]@{ + documentName = "$FriendlyLicenseName" + documentTemplateId = [int]($NinjaOneLicenseTemplate.id) + organizationId = [int]$NinjaOneOrg + fields = $LicenseFields + } + $NinjaLicenseCreation.Add($CreateObject) } - $NinjaLicenseUpdates.Add($UpdateObject) - } else { - $CreateObject = [PSCustomObject]@{ - documentName = "$FriendlyLicenseName" - documentTemplateId = [int]($NinjaOneLicenseTemplate.id) - organizationId = [int]$NinjaOneOrg - fields = $LicenseFields + + [PSCustomObject]@{ + Name = "$FriendlyLicenseName" + Users = $LicenseUsers.NinjaUserDocID } - $NinjaLicenseCreation.Add($CreateObject) - } - [PSCustomObject]@{ - Name = "$FriendlyLicenseName" - Users = $LicenseUsers.NinjaUserDocID } - } - - try { - # Create New Subscriptions - if (($NinjaLicenseCreation | Measure-Object).count -ge 1) { - Write-Host "Creating NinjaOne Licenses" - $CreatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + try { + # Create New Subscriptions + if (($NinjaLicenseCreation | Measure-Object).count -ge 1) { + Write-Host "Creating NinjaOne Licenses" + $CreatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + } + } Catch { + Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" } - } Catch { - Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" - } - try { - # Update Subscriptions - if (($NinjaLicenseUpdates | Measure-Object).count -ge 1) { - Write-Host "Updating NinjaOne Licenses" - $UpdatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 - Write-Host "Completed Update" + try { + # Update Subscriptions + if (($NinjaLicenseUpdates | Measure-Object).count -ge 1) { + Write-Host "Updating NinjaOne Licenses" + $UpdatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + Write-Host "Completed Update" + } + } Catch { + Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } - } Catch { - Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" - } - $LicenseDocs = $CreatedLicenses + $UpdatedLicenses - - # Relate Subscriptions to Users - Foreach ($LinkLic in $LicenseDetails) { - $MatchedLicDoc = $LicenseDocs | Where-Object { $_.documentName -eq $LinkLic.name } - if (($MatchedLicDoc | Measure-Object).count -eq 1) { - # Remove existing relations - $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/with-entity/DOCUMENT/$($MatchedLicDoc.documentId)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 - [System.Collections.Generic.List[PSCustomObject]]$Relations = @() - Foreach ($LinkUser in $LinkLic.Users) { - $ExistingRelation = $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -eq $LinkUser } - if (!$ExistingRelation) { - $Relations.Add( - [PSCustomObject]@{ - relEntityType = "DOCUMENT" - relEntityId = $LinkUser + $LicenseDocs = $CreatedLicenses + $UpdatedLicenses + + if ($Configuration.LicenseDocumentsEnabled -eq $True -and $Configuration.UserDocumentsEnabled -eq $True) { + # Relate Subscriptions to Users + Foreach ($LinkLic in $LicenseDetails) { + $MatchedLicDoc = $LicenseDocs | Where-Object { $_.documentName -eq $LinkLic.name } + if (($MatchedLicDoc | Measure-Object).count -eq 1) { + # Remove existing relations + $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/with-entity/DOCUMENT/$($MatchedLicDoc.documentId)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + [System.Collections.Generic.List[PSCustomObject]]$Relations = @() + Foreach ($LinkUser in $LinkLic.Users) { + $ExistingRelation = $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -eq $LinkUser } + if (!$ExistingRelation) { + $Relations.Add( + [PSCustomObject]@{ + relEntityType = "DOCUMENT" + relEntityId = $LinkUser + } + ) } - ) - } - } + } - try { - # Update Relations - if (($Relations | Measure-Object).count -ge 1) { - Write-Host "Updating Relations" - $RelationResult = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/DOCUMENT/$($($MatchedLicDoc.documentId))/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop - Write-Host "Completed Update" - } - } Catch { - Write-Host "Creating Relations Failed: $_" - } + try { + # Update Relations + if (($Relations | Measure-Object).count -ge 1) { + Write-Host "Updating Relations" + $Null = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/DOCUMENT/$($($MatchedLicDoc.documentId))/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop + Write-Host "Completed Update" + } + } Catch { + Write-Host "Creating Relations Failed: $_" + } - #Remove relations - foreach ($DelUser in $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -notin $LinkLic.Users }) { - try { - $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/$($DelUser.id)" -Method Delete -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 - } catch { - Write-Host "Failed to remove relation $($DelUser.id) from $($LinkLic.name)" + #Remove relations + foreach ($DelUser in $RelatedItems | Where-Object { $_.relEntityType -eq 'DOCUMENT' -and $_.relEntityId -notin $LinkLic.Users }) { + try { + $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/$($DelUser.id)" -Method Delete -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + } catch { + Write-Host "Failed to remove relation $($DelUser.id) from $($LinkLic.name)" + } + } } } } - } + } ####################################################################### @@ -2013,8 +2005,11 @@ function Invoke-NinjaOneTenantSync { $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($MappedTenant.NinjaOne)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaOrgUpdate | ConvertTo-Json -Depth 100) + Write-Host "Result: $($Result | convertto-json -depth 100)" + + Write-Host "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds)" Write-Host "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Completed NinjaOne Sync for $($Customer.displayName) in $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds) seconds" -Sev 'info' + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Completed NinjaOne Sync for $($Customer.displayName). Data fetched in $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds) seconds. Total time $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds) seconds" -Sev 'info' } catch { Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Failed NinjaOne Processing for $($Customer.displayName) Error: $_" -Sev 'Error' From 34c435dc1cb85ba4eedda88f66f4503737d783b9 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sat, 18 Nov 2023 13:24:23 +0000 Subject: [PATCH 23/77] Full Logging --- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 67 +++++++++---------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index ed84ab229749..d8c85de23872 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -6,7 +6,7 @@ function Invoke-NinjaOneTenantSync { try { $StartTime = Get-Date - write-host "$(Get-Date) - Starting NinjaOne Sync" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "$(Get-Date) - Starting NinjaOne Sync" # Fetch Custom NinjaOne Settings $Table = Get-CIPPTable -TableName NinjaOneSettings @@ -16,7 +16,7 @@ function Invoke-NinjaOneTenantSync { # Parse out the Tenant we are processing $MappedTenant = $QueueItem.MappedTenant $Customer = Get-Tenants | where-object { $_.customerId -eq $MappedTenant.RowKey } - Write-Host "Processing: $($Customer.displayName)" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Processing: $($Customer.displayName)" Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Processing NinjaOne Synchronization for $($Customer.displayName)" -Sev 'Info' @@ -1187,22 +1187,22 @@ function Invoke-NinjaOneTenantSync { try { # Create New Users if (($NinjaUserCreation | Measure-Object).count -ge 1) { - Write-Host "Creating NinjaOne Users" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Creating NinjaOne Users" $CreatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 } } Catch { - Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" } try { # Update Users if (($NinjaUserUpdates | Measure-Object).count -ge 1) { - Write-Host "Updating NinjaOne Users" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Updating NinjaOne Users" $UpdatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 - Write-Host "Completed Update" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Update" } } Catch { - Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } ### Relationship Mapping @@ -1255,12 +1255,12 @@ function Invoke-NinjaOneTenantSync { try { # Update Relations if (($Relations | Measure-Object).count -ge 1) { - Write-Host "Updating Relations" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Updating Relations" $Null = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/NODE/$($LinkDevice.NinjaDevice.id)/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop - Write-Host "Completed Update" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Update" } } Catch { - Write-Host "Creating Relations Failed: $_" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Creating Relations Failed: $_" } } } @@ -1367,22 +1367,22 @@ function Invoke-NinjaOneTenantSync { try { # Create New Subscriptions if (($NinjaLicenseCreation | Measure-Object).count -ge 1) { - Write-Host "Creating NinjaOne Licenses" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Creating NinjaOne Licenses" $CreatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 } } Catch { - Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" } try { # Update Subscriptions if (($NinjaLicenseUpdates | Measure-Object).count -ge 1) { - Write-Host "Updating NinjaOne Licenses" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Updating NinjaOne Licenses" $UpdatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 - Write-Host "Completed Update" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Update" } } Catch { - Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } $LicenseDocs = $CreatedLicenses + $UpdatedLicenses @@ -1410,12 +1410,12 @@ function Invoke-NinjaOneTenantSync { try { # Update Relations if (($Relations | Measure-Object).count -ge 1) { - Write-Host "Updating Relations" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Updating Relations" $Null = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/DOCUMENT/$($($MatchedLicDoc.documentId))/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop - Write-Host "Completed Update" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Update" } } Catch { - Write-Host "Creating Relations Failed: $_" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Creating Relations Failed: $_" } #Remove relations @@ -1423,7 +1423,7 @@ function Invoke-NinjaOneTenantSync { try { $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/$($DelUser.id)" -Method Delete -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 } catch { - Write-Host "Failed to remove relation $($DelUser.id) from $($LinkLic.name)" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Failed to remove relation $($DelUser.id) from $($LinkLic.name)" } } } @@ -1438,7 +1438,7 @@ function Invoke-NinjaOneTenantSync { ### M365 Links Section if ($MappedFields.TenantLinks) { - Write-Host "Tenant Links" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Tenant Links" $ManagementLinksData = @( @{ @@ -1540,7 +1540,7 @@ function Invoke-NinjaOneTenantSync { if ($MappedFields.TenantSummary) { - Write-Host "Tenant Summary" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Tenant Summary" ### Tenant Overview Card $ParsedAdmins = [PSCustomObject]@{} @@ -1562,7 +1562,7 @@ function Invoke-NinjaOneTenantSync { $TenantSummaryCard = Get-NinjaOneInfoCard -Title "Tenant Details" -Data $TenantDetailsItems -Icon 'fas fa-building' ### Users details card - Write-Host "User Details" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "User Details" $TotalUsersCount = ($Users | measure-object).count $GuestUsersCount = ($Users | where-object { $_.UserType -eq 'Guest' } | measure-object).count $LicensedUsersCount = ($licensedUsers | measure-object).count @@ -1620,7 +1620,7 @@ function Invoke-NinjaOneTenantSync { ### Device Details Card - Write-Host "Device Details" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Device Details" $TotalDeviceswCount = ($Devices | Measure-Object).count $ComplianceDevicesCount = ($Devices | Where-Object { $_.complianceState -eq 'compliant' } | Measure-Object).count $WindowsCount = ($Devices | Where-Object { $_.operatingSystem -eq 'Windows' } | Measure-Object).count @@ -1700,7 +1700,7 @@ function Invoke-NinjaOneTenantSync { $DeviceSummaryCardHTML = Get-NinjaOneCard -Title 'Device Details' -Body $DeviceCardBodyHTML -Icon 'fas fa-network-wired' -TitleLink $TitleLink #### Secure Score Card - Write-Host "Secure Score Details" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Secure Score Details" $Top5Actions = ($SecureScoreParsed | Where-Object { $_.scoreInPercentage -ne 100 } | Sort-Object 'Score Impact', adjustedRank -Descending) | Select-Object -First 5 # Score Chart @@ -1731,7 +1731,7 @@ function Invoke-NinjaOneTenantSync { ### CIPP Applied Standards Cards - Write-Host "Applied Standards" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Applied Standards" $StandardsDefinitions = Get-Content 'config/standards.json' | ConvertFrom-Json -Depth 100 $Table = Get-CippTable -tablename 'standards' @@ -1763,7 +1763,7 @@ function Invoke-NinjaOneTenantSync { $CIPPStandardsSummaryCardHTML = Get-NinjaOneCard -Title 'CIPP Applied Standards' -Body $CIPPStandardsBodyHTML -Icon 'fas fa-shield-halved' -TitleLink $TitleLink ### License Card - Write-Host "License Details" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "License Details" $LicenseTableHTML = $LicensesParsed | Sort-Object 'License Name' | ConvertTo-HTML -As Table -Fragment $LicenseTableHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseTableHTML) -replace '
    ', '') -replace '', '' @@ -1772,7 +1772,7 @@ function Invoke-NinjaOneTenantSync { ### Summary Stats - Write-Host "Widget Details" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Widget Details" [System.Collections.Generic.List[PSCustomObject]]$WidgetData = @() @@ -1954,12 +1954,12 @@ function Invoke-NinjaOneTenantSync { - Write-Host 'Summary Details' + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message 'Summary Details' $SummaryDetailsCardHTML = Get-NinjaOneWidgetCard -Title 'Summary Details' -Data $WidgetData -Icon 'fas fa-building' -TitleLink 'http://example.com' -SmallCols 2 -MedCols 3 -LargeCols 4 -XLCols 6 -NoCard # Create the Tenant Summary Field - Write-Host "Complete Tenant Summary" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Complete Tenant Summary" $TenantSummaryHTML = '
    ' + $SummaryDetailsCardHTML + '
    ' + '
    ' + '
    ' + $TenantSummaryCard + @@ -1977,7 +1977,7 @@ function Invoke-NinjaOneTenantSync { } if ($MappedFields.UsersSummary) { - Write-Host "User Details Section" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "User Details Section" $UsersTableFornatted = $ParsedUsers | Select-Object Name, @{n = 'User Principal Name'; e = { $_.UPN } }, @@ -1999,16 +1999,15 @@ function Invoke-NinjaOneTenantSync { - Write-Host "Posting Details" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Posting Details" $Token = Get-NinjaOneToken -configuration $Configuration $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($MappedTenant.NinjaOne)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaOrgUpdate | ConvertTo-Json -Depth 100) - Write-Host "Result: $($Result | convertto-json -depth 100)" - Write-Host "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds)" - Write-Host "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds)" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Completed NinjaOne Sync for $($Customer.displayName). Data fetched in $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds) seconds. Total time $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds) seconds" -Sev 'info' } catch { From 3c9f74693fffed8c40e29cf290704d69ff762d7a Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sat, 18 Nov 2023 13:42:43 +0000 Subject: [PATCH 24/77] Additional Logging --- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index d8c85de23872..6e82a5386aba 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -51,6 +51,8 @@ function Invoke-NinjaOneTenantSync { $After = $ResultCount.maximum } while ($ResultCount.count -eq $PageSize) + + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched NinjaOne Devices" [System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = @() @@ -153,6 +155,8 @@ function Invoke-NinjaOneTenantSync { } $NinjaDoc | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force } + + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched NinjaOne User Docs" } [System.Collections.Generic.List[PSCustomObject]]$NinjaOneLicenseDocs = @() @@ -221,6 +225,8 @@ function Invoke-NinjaOneTenantSync { } $NinjaLic | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force } + + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched NinjaOne License Docs" } @@ -308,6 +314,8 @@ function Invoke-NinjaOneTenantSync { Throw "Failed to fetch bulk company data: $_" } + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched Bulk M365 Data" + $Users = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Users' $SecureScore = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'SecureScore' @@ -366,6 +374,8 @@ function Invoke-NinjaOneTenantSync { $MemberReturn = $null } + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched M365 Roles" + $Roles = foreach ($Result in $MemberReturn) { [PSCustomObject]@{ ID = $Result.id @@ -422,6 +432,8 @@ function Invoke-NinjaOneTenantSync { $PolicyReturn = $null } + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched M365 Device Compliance" + $DeviceComplianceDetails = foreach ($Result in $PolicyReturn) { [pscustomobject]@{ ID = ($DeviceCompliancePolicies | where-object { $_.id -eq $Result.id }).id @@ -449,6 +461,9 @@ function Invoke-NinjaOneTenantSync { } catch { $GroupMembersReturn = $null } + + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched M365 Group Membership" + $Groups = foreach ($Result in $GroupMembersReturn) { [pscustomobject]@{ ID = $Result.id @@ -556,6 +571,7 @@ function Invoke-NinjaOneTenantSync { $MailboxStatsFull = $null } + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched M365 Additional Data" $FetchEnd = Get-Date From 5e04837f261fb8486f66427f71b1c080203aecb1 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sat, 18 Nov 2023 14:06:28 +0000 Subject: [PATCH 25/77] Additional Logging --- Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 6e82a5386aba..2aa2a211fd2d 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -794,6 +794,9 @@ function Invoke-NinjaOneTenantSync { } } + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Processed Devices" + + ########## Create / Update User Objects if ($Configuration.LicensedOnly -eq $True) { From 83bb2fa1364c991eb325fd2eb2f8bd4ae94c6b32 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sat, 18 Nov 2023 14:13:08 +0000 Subject: [PATCH 26/77] Added user progress logging --- Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 2aa2a211fd2d..cc4957ec1580 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -805,7 +805,10 @@ function Invoke-NinjaOneTenantSync { $SyncUsers = $Users } + $Count = 1 $ParsedUsers = foreach ($user in $SyncUsers) { + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Processed $($Count) of $($SyncUsers.count) users" + $Count ++ try { $NinjaOneUser = $NinjaOneUserDocs | Where-Object { $_.ParsedFields.cippUserID -eq $User.ID } if (($NinjaOneUser | Measure-Object).count -gt 1) { From 54f0c9bf6bd793d4c0048aea98d975f37d8156e3 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sat, 18 Nov 2023 20:13:05 +0000 Subject: [PATCH 27/77] Added user caching so we can resume from where we left off. --- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 274 ++++++++++++------ 1 file changed, 192 insertions(+), 82 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index cc4957ec1580..0cf8c8e269f5 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -6,7 +6,7 @@ function Invoke-NinjaOneTenantSync { try { $StartTime = Get-Date - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "$(Get-Date) - Starting NinjaOne Sync" + Write-Host "$(Get-Date) - Starting NinjaOne Sync" # Fetch Custom NinjaOne Settings $Table = Get-CIPPTable -TableName NinjaOneSettings @@ -16,7 +16,7 @@ function Invoke-NinjaOneTenantSync { # Parse out the Tenant we are processing $MappedTenant = $QueueItem.MappedTenant $Customer = Get-Tenants | where-object { $_.customerId -eq $MappedTenant.RowKey } - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Processing: $($Customer.displayName)" + Write-Host "Processing: $($Customer.displayName)" Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Processing NinjaOne Synchronization for $($Customer.displayName)" -Sev 'Info' @@ -52,7 +52,7 @@ function Invoke-NinjaOneTenantSync { } while ($ResultCount.count -eq $PageSize) - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched NinjaOne Devices" + Write-Host "Fetched NinjaOne Devices" [System.Collections.Generic.List[PSCustomObject]]$NinjaOneUserDocs = @() @@ -156,7 +156,7 @@ function Invoke-NinjaOneTenantSync { $NinjaDoc | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force } - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched NinjaOne User Docs" + Write-Host "Fetched NinjaOne User Docs" } [System.Collections.Generic.List[PSCustomObject]]$NinjaOneLicenseDocs = @() @@ -226,14 +226,12 @@ function Invoke-NinjaOneTenantSync { $NinjaLic | Add-Member -NotePropertyName 'ParsedFields' -NotePropertyValue $ParsedFields -Force } - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched NinjaOne License Docs" + Write-Host "Fetched NinjaOne License Docs" } # Create the update objects we will use to update NinjaOne $NinjaOrgUpdate = [PSCustomObject]@{} - [System.Collections.Generic.List[PSCustomObject]]$NinjaUserUpdates = @() - [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCreation = @() [System.Collections.Generic.List[PSCustomObject]]$NinjaLicenseUpdates = @() [System.Collections.Generic.List[PSCustomObject]]$NinjaLicenseCreation = @() @@ -314,7 +312,7 @@ function Invoke-NinjaOneTenantSync { Throw "Failed to fetch bulk company data: $_" } - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched Bulk M365 Data" + Write-Host "Fetched Bulk M365 Data" $Users = Get-GraphBulkResultByID -value -Results $TenantResults -ID 'Users' @@ -374,7 +372,7 @@ function Invoke-NinjaOneTenantSync { $MemberReturn = $null } - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched M365 Roles" + Write-Host "Fetched M365 Roles" $Roles = foreach ($Result in $MemberReturn) { [PSCustomObject]@{ @@ -432,7 +430,7 @@ function Invoke-NinjaOneTenantSync { $PolicyReturn = $null } - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched M365 Device Compliance" + Write-Host "Fetched M365 Device Compliance" $DeviceComplianceDetails = foreach ($Result in $PolicyReturn) { [pscustomobject]@{ @@ -462,7 +460,7 @@ function Invoke-NinjaOneTenantSync { $GroupMembersReturn = $null } - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched M365 Group Membership" + Write-Host "Fetched M365 Group Membership" $Groups = foreach ($Result in $GroupMembersReturn) { [pscustomobject]@{ @@ -571,7 +569,7 @@ function Invoke-NinjaOneTenantSync { $MailboxStatsFull = $null } - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Fetched M365 Additional Data" + Write-Host "Fetched M365 Additional Data" $FetchEnd = Get-Date @@ -794,7 +792,7 @@ function Invoke-NinjaOneTenantSync { } } - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Processed Devices" + Write-Host "Processed Devices" ########## Create / Update User Objects @@ -805,11 +803,45 @@ function Invoke-NinjaOneTenantSync { $SyncUsers = $Users } + + $UsersTable = Get-CippTable -tablename 'CacheNinjaOneParsedUsers' + $UsersUpdateTable = Get-CippTable -tablename 'CacheNinjaOneUsersUpdate' + $UsersMapTable = Get-CippTable -tablename 'NinjaOneUserMap' + + + $UsersFilter = "PartitionKey eq '$($Customer.CustomerId)'" + [System.Collections.Generic.List[PSCustomObject]]$ParsedUsers = Get-CIPPAzDataTableEntity @UsersTable -Filter $UsersFilter + if (($ParsedUsers | Measure-Object).count -eq 0) { + [System.Collections.Generic.List[PSCustomObject]]$ParsedUsers = @() + } + + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCache = Get-CIPPAzDataTableEntity @UsersUpdateTable -Filter $UsersFilter + if (($NinjaUserCache | Measure-Object).count -eq 0) { + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCache = @() + } + + [System.Collections.Generic.List[PSCustomObject]]$UsersMap = Get-CIPPAzDataTableEntity @UsersMapTable -Filter $UsersFilter + if (($UsersMap | Measure-Object).count -eq 0) { + [System.Collections.Generic.List[PSCustomObject]]$UsersMap = @() + } + + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserUpdates = $NinjaUserCache | Where-Object { $_.action -eq 'Update' } + if (($NinjaUserUpdates | Measure-Object).count -eq 0) { + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserUpdates = @() + } + + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCreation = $NinjaUserCache | Where-Object { $_.action -eq 'Create' } + if (($NinjaUserCreation | Measure-Object).count -eq 0) { + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCreation = @() + } + $Count = 1 - $ParsedUsers = foreach ($user in $SyncUsers) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Processed $($Count) of $($SyncUsers.count) users" - $Count ++ + foreach ($user in $SyncUsers | where-object { $_.id -notin $ParsedUsers.RowKey }) { try { + + Write-Host "Processing $($User.displayName)" + $Count ++ + $NinjaOneUser = $NinjaOneUserDocs | Where-Object { $_.ParsedFields.cippUserID -eq $User.ID } if (($NinjaOneUser | Measure-Object).count -gt 1) { Throw "Multiple Users with the same ID found" @@ -1118,18 +1150,24 @@ function Invoke-NinjaOneTenantSync { # Return Data for Users Summary Table - [PSCustomObject]@{ - Name = $User.displayName - UPN = $User.userPrincipalName - Aliases = ($User.proxyAddresses -replace 'SMTP:', '') -join ', ' + $ParsedUser = [PSCustomObject]@{ + PartitionKey = "$($Customer.CustomerId)" + RowKey = "$($User.id)" + Name = "$($User.displayName)" + UPN = "$($User.userPrincipalName)" + Aliases = "$(($User.proxyAddresses -replace 'SMTP:', '') -join ', ')" Licenses = "
      $userLicenses
    " - Mailbox = $MailboxUse - MailboxParsed = $MailboxParsed - OneDrive = $OneDriveUse - OneDriveParsed = $OneDriveParsed + Mailbox = "$($MailboxUse)" + MailboxParsed = "$($MailboxParsed)" + OneDrive = "$($OneDriveUse)" + OneDriveParsed = "$($OneDriveParsed)" Devices = "
      $($UserDevices -join '')
    " - Actions = $ActionsHTML + Actions = "$($ActionsHTML)" } + + Add-CIPPAzDataTableEntity @UsersTable -Entity $ParsedUser + $ParsedUsers.add($ParsedUser) + if ($Configuration.UserDocumentsEnabled -eq $True) { @@ -1182,74 +1220,145 @@ function Invoke-NinjaOneTenantSync { if ($NinjaOneUser) { $UpdateObject = [PSCustomObject]@{ + PartitionKey = $Customer.CustomerId + RowKey = $User.id + Action = 'Update' + Body = "$(@{ documentId = $NinjaOneUser.documentId documentName = "$($User.displayName) ($($User.userPrincipalName))" fields = $UserFields + } | ConvertTo-Json -Depth 100)" } $NinjaUserUpdates.Add($UpdateObject) + Add-CIPPAzDataTableEntity @UsersUpdateTable -Entity $UpdateObject + } else { $CreateObject = [PSCustomObject]@{ + PartitionKey = $Customer.CustomerId + RowKey = $User.id + Action = 'Create' + Body = "$(@{ documentName = "$($User.displayName) ($($User.userPrincipalName))" documentTemplateId = ($NinjaOneUsersTemplate.id) organizationId = [int]$NinjaOneOrg fields = $UserFields + } | ConvertTo-Json -Depth 100)" } $NinjaUserCreation.Add($CreateObject) + Add-CIPPAzDataTableEntity @UsersUpdateTable -Entity $CreateObject + } + + + try { + # Create New Users + if (($NinjaUserCreation | Measure-Object).count -ge 100) { + Write-Host "Creating NinjaOne Users" + $CreatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ("[$($NinjaUserCreation.body -join ',')]") -EA Stop).content | ConvertFrom-Json -Depth 100 + Remove-AzDataTableEntity @UsersUpdateTable -Entity $NinjaUserCreation + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCreation = @() + } + } Catch { + Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" } - } + try { + # Update Users + if (($NinjaUserUpdates | Measure-Object).count -ge 100) { + Write-Host "Updating NinjaOne Users" + $UpdatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ("[$($NinjaUserUpdates.body -join ',')]") -EA Stop).content | ConvertFrom-Json -Depth 100 + Remove-AzDataTableEntity @UsersUpdateTable -Entity $NinjaUserUpdates + [System.Collections.Generic.List[PSCustomObject]]$NinjaUserUpdates = @() + } + } Catch { + Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" + } + + $UserDocResults = $UpdatedUsers + $CreatedUsers + + if (($UserDocResults | Measure-Object).count -ge 1) { + $UserDocResults | ForEach-Object { + $UserDoc = $_ + $Field = $UserDoc.updatedFields | Where-Object { $_.name -eq 'cippUserID' } + + $MappedUser = ($UsersMap | Where-Object { $_.M365ID -eq $Field.value }) + if (($MappedUser | Measure-Object).count -eq 0) { + $UserMapItem = [PSCustomObject]@{ + PartitionKey = $Customer.CustomerId + RowKey = $User.id + NinjaOneID = $UserDoc.documentId + M365ID = $Field.value + } + $UsersMap.Add($UserMapItem) + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem + + } elseif ($MappedUser.NinjaOneID -ne $UserDoc.documentId) { + $MappedUser.NinjaOneID = $UserDoc.documentId + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $MappedUser -Force + } + + } + } + + } } catch { Write-Error "User $($User.UserPrincipalName): A fatal error occured while processing user $_" } + } - [System.Collections.Generic.List[PSCustomObject]]$UsersMap = @() + if ($Configuration.UserDocumentsEnabled -eq $True) { try { # Create New Users if (($NinjaUserCreation | Measure-Object).count -ge 1) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Creating NinjaOne Users" - $CreatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 + Write-Host "Creating NinjaOne Users" + $CreatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ("[$($NinjaUserCreation.body -join ',')]") -EA Stop).content | ConvertFrom-Json -Depth 100 + Remove-AzDataTableEntity @UsersUpdateTable -Entity $NinjaUserCreation + } } Catch { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" + Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" } try { # Update Users if (($NinjaUserUpdates | Measure-Object).count -ge 1) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Updating NinjaOne Users" - $UpdatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaUserUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Update" + Write-Host "Updating NinjaOne Users" + $UpdatedUsers = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ("[$($NinjaUserUpdates.body -join ',')]") -EA Stop).content | ConvertFrom-Json -Depth 100 + Remove-AzDataTableEntity @UsersUpdateTable -Entity $NinjaUserUpdates } } Catch { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" + Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } ### Relationship Mapping # Parse out the NinjaOne ID to MS ID - if (($UpdatedUsers | Measure-Object).count -ge 1) { - $UpdatedUsers | ForEach-Object { - $User = $_ - $Field = $User.updatedFields | Where-Object { $_.name -eq 'cippUserID' } - $UsersMap.Add([PSCustomObject]@{ - NinjaOneID = $User.documentId - M365ID = $Field.value - }) - } - } + $UserDocResults = $UpdatedUsers + $CreatedUsers + + if (($UserDocResults | Measure-Object).count -ge 1) { + $UserDocResults | ForEach-Object { + $UserDoc = $_ + $Field = $UserDoc.updatedFields | Where-Object { $_.name -eq 'cippUserID' } + + $MappedUser = ($UsersMap | Where-Object { $_.M365ID -eq $Field.value }) + if (($MappedUser | Measure-Object).count -eq 0) { + $UserMapItem = [PSCustomObject]@{ + PartitionKey = $Customer.CustomerId + RowKey = $Field.value + NinjaOneID = $UserDoc.documentId + M365ID = $Field.value + } + $UsersMap.Add($UserMapItem) + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem + + } elseif ($MappedUser.NinjaOneID -ne $UserDoc.documentId) { + $MappedUser.NinjaOneID = $UserDoc.documentId + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $MappedUser -Force + } - if (($CreatedUsers | Measure-Object).count -ge 1) { - $CreatedUsers | ForEach-Object { - $User = $_ - $Field = $User.fields | Where-Object { $_.name -eq 'cippUserID' } - $UsersMap.Add([PSCustomObject]@{ - NinjaOneID = $User.documentId - M365ID = $Field.value - }) } } @@ -1277,12 +1386,12 @@ function Invoke-NinjaOneTenantSync { try { # Update Relations if (($Relations | Measure-Object).count -ge 1) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Updating Relations" + Write-Host "Updating Relations" $Null = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/NODE/$($LinkDevice.NinjaDevice.id)/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Update" + Write-Host "Completed Update" } } Catch { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Creating Relations Failed: $_" + Write-Host "Creating Relations Failed: $_" } } } @@ -1305,7 +1414,7 @@ function Invoke-NinjaOneTenantSync { $MatchedPlans = $SubUser.AssignedPlans | Where-Object { $_.servicePlanId -in $License.servicePlans.servicePlanID } if (($MatchedLicense | Measure-Object).count -gt 0 ) { $SubRelUserID = ($UsersMap | Where-Object { $_.M365ID -eq $SubUser.id }).NinjaOneID - if ($SubRelUserID){ + if ($SubRelUserID) { $LicUserName = '' + $SubUser.displayName + '' } else { $LicUserName = $SubUser.displayName @@ -1389,22 +1498,22 @@ function Invoke-NinjaOneTenantSync { try { # Create New Subscriptions if (($NinjaLicenseCreation | Measure-Object).count -ge 1) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Creating NinjaOne Licenses" + Write-Host "Creating NinjaOne Licenses" $CreatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseCreation | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 } } Catch { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" + Write-Host "Bulk Creation Error, but may have been successful as only 1 record with an issue could have been the cause: $_" } try { # Update Subscriptions if (($NinjaLicenseUpdates | Measure-Object).count -ge 1) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Updating NinjaOne Licenses" + Write-Host "Updating NinjaOne Licenses" $UpdatedLicenses = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/documents" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaLicenseUpdates | ConvertTo-Json -Depth 100 -AsArray) -EA Stop).content | ConvertFrom-Json -Depth 100 - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Update" + Write-Host "Completed Update" } } Catch { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" + Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } $LicenseDocs = $CreatedLicenses + $UpdatedLicenses @@ -1432,12 +1541,12 @@ function Invoke-NinjaOneTenantSync { try { # Update Relations if (($Relations | Measure-Object).count -ge 1) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Updating Relations" + Write-Host "Updating Relations" $Null = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/entity/DOCUMENT/$($($MatchedLicDoc.documentId))/relations" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($Relations | ConvertTo-Json -Depth 100 -AsArray) -EA Stop - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Update" + Write-Host "Completed Update" } } Catch { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Creating Relations Failed: $_" + Write-Host "Creating Relations Failed: $_" } #Remove relations @@ -1445,7 +1554,7 @@ function Invoke-NinjaOneTenantSync { try { $RelatedItems = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/related-items/$($DelUser.id)" -Method Delete -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 } catch { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Failed to remove relation $($DelUser.id) from $($LinkLic.name)" + Write-Host "Failed to remove relation $($DelUser.id) from $($LinkLic.name)" } } } @@ -1460,7 +1569,7 @@ function Invoke-NinjaOneTenantSync { ### M365 Links Section if ($MappedFields.TenantLinks) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Tenant Links" + Write-Host "Tenant Links" $ManagementLinksData = @( @{ @@ -1562,7 +1671,7 @@ function Invoke-NinjaOneTenantSync { if ($MappedFields.TenantSummary) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Tenant Summary" + Write-Host "Tenant Summary" ### Tenant Overview Card $ParsedAdmins = [PSCustomObject]@{} @@ -1584,7 +1693,7 @@ function Invoke-NinjaOneTenantSync { $TenantSummaryCard = Get-NinjaOneInfoCard -Title "Tenant Details" -Data $TenantDetailsItems -Icon 'fas fa-building' ### Users details card - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "User Details" + Write-Host "User Details" $TotalUsersCount = ($Users | measure-object).count $GuestUsersCount = ($Users | where-object { $_.UserType -eq 'Guest' } | measure-object).count $LicensedUsersCount = ($licensedUsers | measure-object).count @@ -1642,7 +1751,7 @@ function Invoke-NinjaOneTenantSync { ### Device Details Card - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Device Details" + Write-Host "Device Details" $TotalDeviceswCount = ($Devices | Measure-Object).count $ComplianceDevicesCount = ($Devices | Where-Object { $_.complianceState -eq 'compliant' } | Measure-Object).count $WindowsCount = ($Devices | Where-Object { $_.operatingSystem -eq 'Windows' } | Measure-Object).count @@ -1722,7 +1831,7 @@ function Invoke-NinjaOneTenantSync { $DeviceSummaryCardHTML = Get-NinjaOneCard -Title 'Device Details' -Body $DeviceCardBodyHTML -Icon 'fas fa-network-wired' -TitleLink $TitleLink #### Secure Score Card - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Secure Score Details" + Write-Host "Secure Score Details" $Top5Actions = ($SecureScoreParsed | Where-Object { $_.scoreInPercentage -ne 100 } | Sort-Object 'Score Impact', adjustedRank -Descending) | Select-Object -First 5 # Score Chart @@ -1753,7 +1862,7 @@ function Invoke-NinjaOneTenantSync { ### CIPP Applied Standards Cards - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Applied Standards" + Write-Host "Applied Standards" $StandardsDefinitions = Get-Content 'config/standards.json' | ConvertFrom-Json -Depth 100 $Table = Get-CippTable -tablename 'standards' @@ -1785,7 +1894,7 @@ function Invoke-NinjaOneTenantSync { $CIPPStandardsSummaryCardHTML = Get-NinjaOneCard -Title 'CIPP Applied Standards' -Body $CIPPStandardsBodyHTML -Icon 'fas fa-shield-halved' -TitleLink $TitleLink ### License Card - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "License Details" + Write-Host "License Details" $LicenseTableHTML = $LicensesParsed | Sort-Object 'License Name' | ConvertTo-HTML -As Table -Fragment $LicenseTableHTML = ([System.Web.HttpUtility]::HtmlDecode($LicenseTableHTML) -replace '
    ', '') -replace '', '' @@ -1794,7 +1903,7 @@ function Invoke-NinjaOneTenantSync { ### Summary Stats - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Widget Details" + Write-Host "Widget Details" [System.Collections.Generic.List[PSCustomObject]]$WidgetData = @() @@ -1972,16 +2081,15 @@ function Invoke-NinjaOneTenantSync { - - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message 'Summary Details' - $SummaryDetailsCardHTML = Get-NinjaOneWidgetCard -Title 'Summary Details' -Data $WidgetData -Icon 'fas fa-building' -TitleLink 'http://example.com' -SmallCols 2 -MedCols 3 -LargeCols 4 -XLCols 6 -NoCard + Write-Host 'Summary Details' + $SummaryDetailsCardHTML = Get-NinjaOneWidgetCard -Data $WidgetData -Icon 'fas fa-building' -SmallCols 2 -MedCols 3 -LargeCols 4 -XLCols 6 -NoCard # Create the Tenant Summary Field - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Complete Tenant Summary" + Write-Host "Complete Tenant Summary" $TenantSummaryHTML = '
    ' + $SummaryDetailsCardHTML + '
    ' + '
    ' + '
    ' + $TenantSummaryCard + @@ -1999,7 +2107,7 @@ function Invoke-NinjaOneTenantSync { } if ($MappedFields.UsersSummary) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "User Details Section" + Write-Host "User Details Section" $UsersTableFornatted = $ParsedUsers | Select-Object Name, @{n = 'User Principal Name'; e = { $_.UPN } }, @@ -2019,17 +2127,19 @@ function Invoke-NinjaOneTenantSync { } + - - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Posting Details" + Write-Host "Posting Details" $Token = Get-NinjaOneToken -configuration $Configuration $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($MappedTenant.NinjaOne)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaOrgUpdate | ConvertTo-Json -Depth 100) - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds)" - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -Sev 'info' -message "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" + Remove-AzDataTableEntity @UsersTable -Entity $ParsedUsers + + Write-Host "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds)" + Write-Host "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Completed NinjaOne Sync for $($Customer.displayName). Data fetched in $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds) seconds. Total time $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds) seconds" -Sev 'info' } catch { From 442eb380fb77a01ff9ab6320cc2312549457e82b Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:17:46 +0000 Subject: [PATCH 28/77] Speed improvements and bug fixes for large tenants --- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 140 +++++++++++++----- 1 file changed, 100 insertions(+), 40 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 0cf8c8e269f5..284db82d0596 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -835,18 +835,18 @@ function Invoke-NinjaOneTenantSync { [System.Collections.Generic.List[PSCustomObject]]$NinjaUserCreation = @() } - $Count = 1 + foreach ($user in $SyncUsers | where-object { $_.id -notin $ParsedUsers.RowKey }) { try { - + Write-Host "Processing $($User.displayName)" - $Count ++ $NinjaOneUser = $NinjaOneUserDocs | Where-Object { $_.ParsedFields.cippUserID -eq $User.ID } if (($NinjaOneUser | Measure-Object).count -gt 1) { Throw "Multiple Users with the same ID found" } + $UserGroups = foreach ($Group in $Groups) { if ($User.id -in $Group.Members.id) { $FoundGroup = $AllGroups | Where-Object { $_.id -eq $Group.id } @@ -860,6 +860,7 @@ function Invoke-NinjaOneTenantSync { } } + $UserPolicies = foreach ($cap in $ConditionalAccessMembers) { if ($User.id -in $Cap.Members) { $temp = [PSCustomObject]@{ @@ -869,6 +870,7 @@ function Invoke-NinjaOneTenantSync { } } + #$PermsRequest = '' $StatsRequest = '' $MailboxDetailedRequest = '' @@ -920,6 +922,7 @@ function Invoke-NinjaOneTenantSync { $UserDevicesDetailsRaw = $ParsedDevices | where-object { $User.id -in $_.UserIDS } + $UserDevices = foreach ($UserDevice in $ParsedDevices | where-object { $User.id -in $_.UserIDS }) { $MatchedNinjaDevice = $UserDevice.NinjaDevice @@ -950,8 +953,10 @@ function Invoke-NinjaOneTenantSync { } + $aliases = (($user.ProxyAddresses | Where-Object { $_ -cnotmatch 'SMTP' -and $_ -notmatch '.onmicrosoft.com' }) -replace 'SMTP:', ' ') -join ', ' + $userLicenses = ($user.AssignedLicenses.SkuID | ForEach-Object { $UserLic = $_ try { @@ -960,10 +965,12 @@ function Invoke-NinjaOneTenantSync { } catch {} }) -join '' + $UserOneDriveStats = $OneDriveDetails | where-object { $_.'Owner Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 $UserOneDriveUse = $UserOneDriveStats.'Storage Used (Byte)' / 1GB $UserOneDriveTotal = $UserOneDriveStats.'Storage Allocated (Byte)' / 1GB + if ($UserOneDriveTotal) { $OneDriveUse = [PSCustomObject]@{ Enabled = $True @@ -1011,10 +1018,13 @@ function Invoke-NinjaOneTenantSync { 'One Drive' = 'Disabled' } } + $UserMailboxStats = $MailboxStatsFull | where-object { $_.'User Principal Name' -eq $User.userPrincipalName } | Select-Object -First 1 $UserMailUse = $UserMailboxStats.'Storage Used (Byte)' / 1GB $UserMailTotal = $UserMailboxStats.'Prohibit Send/Receive Quota (Byte)' / 1GB + + if ($UserMailTotal) { $MailboxUse = [PSCustomObject]@{ Enabled = $True @@ -1044,6 +1054,7 @@ function Invoke-NinjaOneTenantSync { $MailboxParsed = 'Not Enabled' } + if ($UserMailSettings.ProhibitSendQuota) { $MailboxDetailsCardData = [PSCustomObject]@{ #'Permissions' = "$($UserMailSettings.Permissions | ConvertTo-Html -Fragment | Out-String)" @@ -1075,6 +1086,7 @@ function Invoke-NinjaOneTenantSync { } } + # Format Conditional Access Polcies $UserPoliciesFormatted = '
      ' foreach ($Policy in $UserPolicies) { @@ -1082,6 +1094,7 @@ function Invoke-NinjaOneTenantSync { } $UserPoliciesFormatted = $UserPoliciesFormatted + '
    ' + $UserOverviewCard = [PSCustomObject]@{ 'User Name' = "$($User.displayName)" 'User Principal Name' = "$($User.userPrincipalName)" @@ -1095,6 +1108,7 @@ function Invoke-NinjaOneTenantSync { 'Licenses' = "$($userLicenses)" } + $Microsoft365UserLinksData = @( @{ Name = 'Entra ID' @@ -1147,7 +1161,6 @@ function Invoke-NinjaOneTenantSync {   "@ - # Return Data for Users Summary Table $ParsedUser = [PSCustomObject]@{ @@ -1165,6 +1178,7 @@ function Invoke-NinjaOneTenantSync { Actions = "$($ActionsHTML)" } + Add-CIPPAzDataTableEntity @UsersTable -Entity $ParsedUser $ParsedUsers.add($ParsedUser) @@ -1177,6 +1191,7 @@ function Invoke-NinjaOneTenantSync { $CIPPUserLinksHTML = Get-NinjaOneLinks -Data $CIPPUserLinksData -Title 'CIPP Links' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 $UserLinksHTML = '
    ' + $M365UserLinksHTML + '
    ' + $CIPPUserLinksHTML + '
    ' + # UsersSummaryCards: $UserOverviewCardHTML = Get-NinjaOneInfoCard -Title "User Details" -Data $UserOverviewCard -Icon 'fas fa-user' $MailboxDetailsCardHTML = Get-NinjaOneInfoCard -Title "Mailbox Details" -Data $MailboxDetailsCardData -Icon 'fas fa-envelope' @@ -1208,7 +1223,6 @@ function Invoke-NinjaOneTenantSync { $UserDeviceDetailHTML = ([System.Web.HttpUtility]::HtmlDecode($UserDeviceDetailHTML) -replace '
    ', '') -replace '', '' - $UserFields = @{ cippUserLinks = @{'html' = $UserLinksHTML } cippUserSummary = @{'html' = $UserSummaryHTML } @@ -1218,6 +1232,7 @@ function Invoke-NinjaOneTenantSync { cippUserUPN = $User.userPrincipalName } + if ($NinjaOneUser) { $UpdateObject = [PSCustomObject]@{ PartitionKey = $Customer.CustomerId @@ -1249,6 +1264,9 @@ function Invoke-NinjaOneTenantSync { } + $CreatedUsers = $Null + $UpdatedUsers = $Null + try { # Create New Users if (($NinjaUserCreation | Measure-Object).count -ge 100) { @@ -1273,31 +1291,43 @@ function Invoke-NinjaOneTenantSync { Write-Host "Bulk Update Errored, but may have been successful as only 1 record with an issue could have been the cause: $_" } + $UserDocResults = $UpdatedUsers + $CreatedUsers - if (($UserDocResults | Measure-Object).count -ge 1) { - $UserDocResults | ForEach-Object { + if (($UserDocResults | Where-Object { $Null -ne $_ -and $_ -ne '' } | Measure-Object).count -ge 1) { + $UserDocResults | Where-Object { $Null -ne $_ -and $_ -ne '' } | ForEach-Object { $UserDoc = $_ - $Field = $UserDoc.updatedFields | Where-Object { $_.name -eq 'cippUserID' } - - $MappedUser = ($UsersMap | Where-Object { $_.M365ID -eq $Field.value }) - if (($MappedUser | Measure-Object).count -eq 0) { - $UserMapItem = [PSCustomObject]@{ - PartitionKey = $Customer.CustomerId - RowKey = $User.id - NinjaOneID = $UserDoc.documentId - M365ID = $Field.value - } - $UsersMap.Add($UserMapItem) - Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem + if ($UserDoc.updatedFields) { + $Field = $UserDoc.updatedFields | Where-Object { $_.name -eq 'cippUserID' } + } else { + $Field = $UserDoc.fields | Where-Object { $_.name -eq 'cippUserID' } + } + + if ($Null -ne $Field.value -and $Field.value -ne '') { + + $MappedUser = ($UsersMap | Where-Object { $_.M365ID -eq $Field.value }) + if (($MappedUser | Measure-Object).count -eq 0) { + $UserMapItem = [PSCustomObject]@{ + PartitionKey = $Customer.CustomerId + RowKey = $Field.value + NinjaOneID = $UserDoc.documentId + M365ID = $Field.value + } + $UsersMap.Add($UserMapItem) + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem - } elseif ($MappedUser.NinjaOneID -ne $UserDoc.documentId) { - $MappedUser.NinjaOneID = $UserDoc.documentId - Add-CIPPAzDataTableEntity @UsersMapTable -Entity $MappedUser -Force + } elseif ($MappedUser.NinjaOneID -ne $UserDoc.documentId) { + $MappedUser.NinjaOneID = $UserDoc.documentId + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $MappedUser -Force + } + } else { + Write-Error "Unmatched Doc: $($UserDoc | convertto-json -depth 100)" } } + } + } } catch { @@ -1308,6 +1338,9 @@ function Invoke-NinjaOneTenantSync { + $CreatedUsers = $Null + $UpdatedUsers = $Null + if ($Configuration.UserDocumentsEnabled -eq $True) { try { # Create New Users @@ -1338,25 +1371,34 @@ function Invoke-NinjaOneTenantSync { $UserDocResults = $UpdatedUsers + $CreatedUsers - if (($UserDocResults | Measure-Object).count -ge 1) { - $UserDocResults | ForEach-Object { + if (($UserDocResults | Where-Object { $Null -ne $_ -and $_ -ne '' } | Measure-Object).count -ge 1) { + $UserDocResults | Where-Object { $Null -ne $_ -and $_ -ne '' } | ForEach-Object { $UserDoc = $_ - $Field = $UserDoc.updatedFields | Where-Object { $_.name -eq 'cippUserID' } + if ($UserDoc.updatedFields) { + $Field = $UserDoc.updatedFields | Where-Object { $_.name -eq 'cippUserID' } + } else { + $Field = $UserDoc.fields | Where-Object { $_.name -eq 'cippUserID' } + } + + if ($Null -ne $Field.value -and $Field.value -ne '') { + + $MappedUser = ($UsersMap | Where-Object { $_.M365ID -eq $Field.value }) + if (($MappedUser | Measure-Object).count -eq 0) { + $UserMapItem = [PSCustomObject]@{ + PartitionKey = $Customer.CustomerId + RowKey = $Field.value + NinjaOneID = $UserDoc.documentId + M365ID = $Field.value + } + $UsersMap.Add($UserMapItem) + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem - $MappedUser = ($UsersMap | Where-Object { $_.M365ID -eq $Field.value }) - if (($MappedUser | Measure-Object).count -eq 0) { - $UserMapItem = [PSCustomObject]@{ - PartitionKey = $Customer.CustomerId - RowKey = $Field.value - NinjaOneID = $UserDoc.documentId - M365ID = $Field.value + } elseif ($MappedUser.NinjaOneID -ne $UserDoc.documentId) { + $MappedUser.NinjaOneID = $UserDoc.documentId + Add-CIPPAzDataTableEntity @UsersMapTable -Entity $MappedUser -Force } - $UsersMap.Add($UserMapItem) - Add-CIPPAzDataTableEntity @UsersMapTable -Entity $UserMapItem - - } elseif ($MappedUser.NinjaOneID -ne $UserDoc.documentId) { - $MappedUser.NinjaOneID = $UserDoc.documentId - Add-CIPPAzDataTableEntity @UsersMapTable -Entity $MappedUser -Force + } else { + Write-Error "Unmatched Doc: $($UserDoc | convertto-json -depth 100)" } } @@ -1382,6 +1424,7 @@ function Invoke-NinjaOneTenantSync { } } + try { # Update Relations @@ -1537,6 +1580,7 @@ function Invoke-NinjaOneTenantSync { ) } } + try { # Update Relations @@ -2109,7 +2153,7 @@ function Invoke-NinjaOneTenantSync { if ($MappedFields.UsersSummary) { Write-Host "User Details Section" - $UsersTableFornatted = $ParsedUsers | Select-Object Name, + $UsersTableFornatted = $ParsedUsers | sort-object name | Select-Object -First 100 Name, @{n = 'User Principal Name'; e = { $_.UPN } }, #Aliases, Licenses, @@ -2122,8 +2166,24 @@ function Invoke-NinjaOneTenantSync { $UsersTableHTML = $UsersTableFornatted | ConvertTo-HTML -As Table -Fragment $UsersTableHTML = ([System.Web.HttpUtility]::HtmlDecode($UsersTableHTML) -replace '', '') -replace '', '' + + if ($ParsedUsers.count -gt 100) { + $Overflow = @" +
    + +
    +
    $($ParsedUsers.count) users found in Tenant
    +
    + Only the first 100 users are displayed here. To see all users please view users in CIPP. +
    +
    +
    +"@ + } else { + $Overflow = '' + } - $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.UsersSummary -NotePropertyValue @{'html' = $UsersTableHTML } + $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.UsersSummary -NotePropertyValue @{'html' = $Overflow + $UsersTableHTML } } From cc9362ff6f036da7349ae20b49422d28e5d0db07 Mon Sep 17 00:00:00 2001 From: Jr7468 Date: Tue, 21 Nov 2023 08:09:48 +0000 Subject: [PATCH 29/77] Accounting for empty starttime or endtime variable --- Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 b/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 index 9d0eb4f40120..99ea809558b1 100644 --- a/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 @@ -13,6 +13,13 @@ function Set-CIPPOutOfOffice { ) try { + if (-not $StartTime) { + $State = "Enabled" + $StartTime = (Get-Date).ToString("yyyy-MM-dd HH:mm") + } + if (-not $EndTime) { + $EndTime = (Get-Date $StartTime).AddDays(7) + } if ($State -ne "Scheduled") { $OutOfOffice = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-MailboxAutoReplyConfiguration" -cmdParams @{Identity = $userid; AutoReplyState = $State; InternalMessage = $InternalMessage; ExternalMessage = $ExternalMessage } -Anchor $userid Write-LogMessage -user $ExecutingUser -API $APIName -message "Set Out-of-office for $($userid) to $state" -Sev "Info" -tenant $TenantFilter From 5112e670fe722c7dad108245fc8f4f67cb9708a3 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 23 Nov 2023 17:31:00 +0100 Subject: [PATCH 30/77] add pagination --- GraphHelper.psm1 | 12 ++++++++++-- ListMailboxes/run.ps1 | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/GraphHelper.psm1 b/GraphHelper.psm1 index 7ce9c97e513c..59f55ba80fd2 100644 --- a/GraphHelper.psm1 +++ b/GraphHelper.psm1 @@ -598,8 +598,16 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc } try { - $ReturnedData = Invoke-RestMethod "https://outlook.office365.com/adminapi/beta/$($tenant)/InvokeCommand" -Method POST -Body $ExoBody -Headers $Headers -ContentType 'application/json; charset=utf-8' - if ($ReturnedData.'@adminapi.warnings') { + $URL = "https://outlook.office365.com/adminapi/beta/$($tenant)/InvokeCommand" + + $ReturnedData = + do { + $Return = Invoke-RestMethod $URL -Method POST -Body $ExoBody -Headers $Headers -ContentType 'application/json; charset=utf-8' + $URL = $Return.'@odata.nextLink' + $Return + } until ($null -eq $URL) + + if ($ReturnedData.'@adminapi.warnings' -and $ReturnedData.value -eq $null) { $ReturnedData.value = $ReturnedData.'@adminapi.warnings' } } diff --git a/ListMailboxes/run.ps1 b/ListMailboxes/run.ps1 index 544a716a761b..3e4be8447b6e 100644 --- a/ListMailboxes/run.ps1 +++ b/ListMailboxes/run.ps1 @@ -22,7 +22,7 @@ try { $ExoRequest = @{ tenantid = $TenantFilter cmdlet = 'Get-Mailbox' - cmdParams = @{} + cmdParams = @{resultsize = 'unlimited'} } $AllowedParameters = @( From afd38d8d80b21a8ad907ffabcddf0cb043661147 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 23 Nov 2023 17:41:52 +0100 Subject: [PATCH 31/77] Add select option for exchange --- GraphHelper.psm1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/GraphHelper.psm1 b/GraphHelper.psm1 index 59f55ba80fd2..88c7296ec8af 100644 --- a/GraphHelper.psm1 +++ b/GraphHelper.psm1 @@ -562,7 +562,8 @@ function Remove-CIPPCache { } } -function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anchor, $NoAuthCheck) { +function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anchor, $NoAuthCheck, $Select) { + if ((Get-AuthorisedRequest -TenantID $tenantid) -or $NoAuthCheck -eq $True) { $token = Get-ClassicAPIToken -resource 'https://outlook.office365.com' -Tenantid $tenantid $tenant = (get-tenants -IncludeErrors | Where-Object { $_.defaultDomainName -eq $tenantid -or $_.customerId -eq $tenantid }).customerId @@ -598,7 +599,8 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc } try { - $URL = "https://outlook.office365.com/adminapi/beta/$($tenant)/InvokeCommand" + if ($Select) { $Select = "`$select=$Select" } + $URL = "https://outlook.office365.com/adminapi/beta/$($tenant)/InvokeCommand?$Select" $ReturnedData = do { From ac8dc2e87ceeb60aa53292cbf6199f53b4d8d2b4 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 23 Nov 2023 17:46:11 +0100 Subject: [PATCH 32/77] Add select to list mailboxes --- ListMailboxes/run.ps1 | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ListMailboxes/run.ps1 b/ListMailboxes/run.ps1 index 3e4be8447b6e..1d2a02ff1112 100644 --- a/ListMailboxes/run.ps1 +++ b/ListMailboxes/run.ps1 @@ -15,14 +15,16 @@ $TenantFilter = $Request.Query.TenantFilter try { if ([bool]$Request.Query.SkipLicense -ne $true) { $users = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/?`$top=999&`$select=id,userPrincipalName,assignedLicenses" -Tenantid $tenantfilter - } else { + } + else { $users = @() } - + $Select = "id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses" $ExoRequest = @{ tenantid = $TenantFilter cmdlet = 'Get-Mailbox' - cmdParams = @{resultsize = 'unlimited'} + cmdParams = @{resultsize = 'unlimited' } + Select = $select } $AllowedParameters = @( @@ -54,7 +56,6 @@ try { } Write-Host ($ExoRequest | ConvertTo-Json) - $GraphRequest = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, @{ Name = 'displayName'; Expression = { $_.'DisplayName' } }, @@ -70,7 +71,8 @@ try { @{ Name = 'recipientTypeDetails'; Expression = { $_.'RecipientTypeDetails' } }, @{ Name = 'AdditionalEmailAddresses'; Expression = { ($_.'EmailAddresses' | Where-Object { $_ -clike 'smtp:*' }).Replace('smtp:', '') -join ', ' } } $StatusCode = [HttpStatusCode]::OK -} catch { +} +catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message $StatusCode = [HttpStatusCode]::Forbidden $GraphRequest = $ErrorMessage From 2bc6e9fcc894b01676f78e64e6583ac2a73f1858 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:12:00 +0000 Subject: [PATCH 33/77] Bug Fixes and Improvements Device Caching Device Mapping Table Version Check Webhook Subscriptions Compliance Status updates --- ExecExtensionsConfig/run.ps1 | 19 ++- .../Invoke-CIPPGraphWebhookProcessing.ps1 | 16 ++- .../Public/New-CIPPGraphSubscription.ps1 | 53 ++++--- Modules/CippExtensions/CippExtensions.psd1 | Bin 11220 -> 11302 bytes .../NinjaOne/Get-NinjaOneFieldMapping.ps1 | 11 +- .../NinjaOne/Invoke-NinjaOneDeviceWebhook.ps1 | 58 ++++++++ .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 131 +++++++++++++----- PublicWebhooks/run.ps1 | 3 +- Scheduler_Extensions/run.ps1 | 17 ++- 9 files changed, 238 insertions(+), 70 deletions(-) create mode 100644 Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDeviceWebhook.ps1 diff --git a/ExecExtensionsConfig/run.ps1 b/ExecExtensionsConfig/run.ps1 index 3e2291063751..7b127bbedcfe 100644 --- a/ExecExtensionsConfig/run.ps1 +++ b/ExecExtensionsConfig/run.ps1 @@ -14,13 +14,25 @@ $results = try { $APIConfig = New-CIPPAPIConfig -ExecutingUser $request.headers.'x-ms-client-principal' -resetpassword $request.body.CIPPAPI.ResetPassword $AddedText = $APIConfig.Results } + + # Check if NinjaOne URL is set correctly and the intance has at least version 5.6 + if ($request.body.NinjaOne) { + try { + [version]$Version = (Invoke-WebRequest -Method GET -Uri "https://$($request.body.NinjaOne.Instance -replace '/ws','')/app-version.txt" -ea stop).content + } catch { + throw "Failed to connect to NinjaOne check your Instance is set correctly eg 'app.ninjarmmm.com'" + } + if ($Version -lt [version]'5.6.0.0') { + throw "NinjaOne 5.6.0.0 is required. This will be rolling out regionally between the end of November and mid-December. Please try again at a later date." + } + } + $Table = Get-CIPPTable -TableName Extensionsconfig foreach ($APIKey in ([pscustomobject]$request.body).psobject.properties.name) { Write-Host "Working on $apikey" if ($request.body.$APIKey.APIKey -eq "SentToKeyVault" -or $request.body.$APIKey.APIKey -eq "") { Write-Host "Not sending to keyvault. Key previously set or left blank." - } - else { + } else { Write-Host "writing API Key to keyvault, and clearing." Write-Host "$ENV:WEBSITE_DEPLOYMENT_ID" if ($request.body.$APIKey.APIKey) { @@ -38,8 +50,7 @@ $results = try { Add-CIPPAzDataTableEntity @Table -Entity $Config -Force | Out-Null "Successfully set the configuration. $AddedText" -} -catch { +} catch { "Failed to set configuration: $($_.Exception.message) Linenumber: $($_.InvocationInfo.ScriptLineNumber)" } diff --git a/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookProcessing.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookProcessing.ps1 index ad04c364c432..d147b1ca0989 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookProcessing.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPGraphWebhookProcessing.ps1 @@ -5,7 +5,19 @@ function Invoke-CippGraphWebhookProcessing { $CIPPID, $WebhookInfo ) - # To do # + $Table = Get-CIPPTable -TableName Extensionsconfig -} + $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json) + + Switch ($WebhookInfo.Resource) { + 'devices' { + # NinjaOne Extension + if ($Configuration.NinjaOne.Enabled -eq $True) { + Invoke-NinjaOneDeviceWebhook -Data $Data -Configuration $Configuration.NinjaOne + } + } + } + + + } diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 010e81d422d2..610c2ddb5a72 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -35,34 +35,41 @@ function New-CIPPGraphSubscription { WebhookNotificationUrl = [string]$Auditlog.webhook.address } - $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow + $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow } else { - $expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") - $params = @{ - changeType = $TypeofSubscription - notificationUrl = "$BaseURL/API/PublicWebhooks?EventType=$EventType&CIPPID=$CIPPID?Type=GraphSubscription" - resource = $Resource - expirationDateTime = $expiredate - } | ConvertTo-Json + # First check if there is an exsiting Webhook in place + $WebhookFilter = "PartitionKey eq '$($TenantFilter)'" + $ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter + $MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource } + if (($MatchedWebhook | Measure-Object).count -eq 0) { + + $expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") + $params = @{ + changeType = $TypeofSubscription + notificationUrl = "https://$BaseURL/API/PublicWebhooks?EventType=$EventType&CIPPID=$CIPPID?Type=GraphSubscription" + resource = $Resource + expirationDateTime = $expiredate + } | ConvertTo-Json - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/subscriptions" -tenantid $TenantFilter -type POST -body $params -verbose - #If creation is succesfull, we store the GUID in the storage table webhookTable to make sure we can check against this later on. - #We store the GUID as rowkey, the event type, the resource, and the expiration date as properties, we also add the Tenant name so we can easily find this later on. - #We don't store the return, because Ms decided that a renewal or re-authenticate does not change the url, but does change the id... - $WebhookRow = @{ - PartitionKey = [string]$TenantFilter - RowKey = [string]$CIPPID - EventType = [string]$EventType - Resource = [string]$Resource - Expiration = [string]$expiredate - SubscriptionID = [string]$GraphRequest.id - WebhookNotificationUrl = [string]$GraphRequest.notificationUrl + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/subscriptions" -tenantid $TenantFilter -type POST -body $params -verbose + #If creation is succesfull, we store the GUID in the storage table webhookTable to make sure we can check against this later on. + #We store the GUID as rowkey, the event type, the resource, and the expiration date as properties, we also add the Tenant name so we can easily find this later on. + #We don't store the return, because Ms decided that a renewal or re-authenticate does not change the url, but does change the id... + $WebhookRow = @{ + PartitionKey = [string]$TenantFilter + RowKey = [string]$CIPPID + EventType = [string]$EventType + Resource = [string]$Resource + Expiration = [string]$expiredate + SubscriptionID = [string]$GraphRequest.id + WebhookNotificationUrl = [string]$GraphRequest.notificationUrl + } + $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow + #todo: add remove webhook function, add check webhook function, add list webhooks function + #add refresh webhook function based on table. } - $null = Add-CIPPAzDataTableEntity @WebhookTable -Entity $WebhookRow - #todo: add remove webhook function, add check webhook function, add list webhooks function - #add refresh webhook function based on table. } Write-LogMessage -user $ExecutingUser -API $APIName -message "Created Webhook subscription for $($TenantFilter)" -Sev "Info" -tenant $TenantFilter return "Created Webhook subscription for $($TenantFilter)" diff --git a/Modules/CippExtensions/CippExtensions.psd1 b/Modules/CippExtensions/CippExtensions.psd1 index ac0cf361c2c3aee2295537ff3c939ed259b467d6..edf5fbcddae74ba8496593f6193daa3c83243cd6 100644 GIT binary patch delta 44 xcmcZ-zAR#cmGtB*ViJ>oNhye@GL$i7G9)vkGK2$h5<><' + $Device.deviceName + '' + } else { + continue + } + # Match Users [System.Collections.Generic.List[String]]$DeviceUsers = @() [System.Collections.Generic.List[String]]$DeviceUserIDs = @() [System.Collections.Generic.List[PSCustomObject]]$DeviceUsersDetail = @() + + $MappedDevice = ($DeviceMap | Where-Object { $_.M365ID -eq $device.id }) + if (($MappedDevice | Measure-Object).count -eq 0) { + $DeviceMapItem = [PSCustomObject]@{ + PartitionKey = $Customer.CustomerId + RowKey = $device.AzureADDeviceId + NinjaOneID = $MatchedNinjaDevice.id + M365ID = $device.id + } + $DeviceMap.Add($DeviceMapItem) + Add-CIPPAzDataTableEntity @DeviceMapTable -Entity $DeviceMapItem + + } elseif ($MappedDevice.NinjaOneID -ne $MatchedNinjaDevice.id) { + $MappedDevice.NinjaOneID = $MatchedNinjaDevice.id + Add-CIPPAzDataTableEntity @DeviceMapTable -Entity $MappedDevice -Force + } + + + + Foreach ($DeviceUser in $Device.usersloggedon) { $FoundUser = ($Users | Where-Object { $_.id -eq $DeviceUser.userid }) $DeviceUsers.add($FoundUser.DisplayName) @@ -625,22 +676,10 @@ function Invoke-NinjaOneTenantSync { } } - # First lets match on serial - $MatchedNinjaDevice = $NinjaDevices | Where-Object { $_.system.biosSerialNumber -eq $Device.SerialNumber -or $_.system.serialNumber -eq $Device.SerialNumber } - - # See if we found just one device, if not match on name - if (($MatchedNinjaDevice | Measure-Object).count -ne 1) { - $MatchedNinjaDevice = $NinjaDevices | Where-Object { $_.systemName -eq $Device.Name -or $_.dnsName -eq $Device.Name } - } - - # Check on a match again and set name - if (($MatchedNinjaDevice | Measure-Object).count -eq 1) { - $ParsedDeviceName = '' + $Device.deviceName + '' - } else { - $ParsedDeviceName = $Device.deviceName - } - - [PSCustomObject]@{ + $ParsedDevice = [PSCustomObject]@{ + PartitionKey = $Customer.CustomerId + RowKey = $device.AzureADDeviceId + id = $Device.id Name = $Device.deviceName SerialNumber = $Device.serialNumber OS = $Device.operatingSystem @@ -669,6 +708,14 @@ function Invoke-NinjaOneTenantSync { NinjaDevice = $MatchedNinjaDevice DeviceLink = $ParsedDeviceName } + + Add-CIPPAzDataTableEntity @DeviceTable -Entity @{ + PartitionKey = $Customer.CustomerId + RowKey = $device.AzureADDeviceId + RawDevice = "$($ParsedDevice | ConvertTo-Json -Depth 100 -compress)" + } + + $ParsedDevices.add($ParsedDevice) ### Update NinjaOne Device Fields if ($MatchedNinjaDevice) { @@ -775,7 +822,7 @@ function Invoke-NinjaOneTenantSync { '
    ' + $DeviceHardwareCard + '
    ' + $DeviceEnrollmentCard + '
    ' + $DeviceCompliancePoliciesCard + - '
    ' + $DeviceGroupsCard + '
    ' + $DeviceGroupsCard + '
    ' $NinjaDeviceUpdate | Add-Member -NotePropertyName $MappedFields.DeviceSummary -NotePropertyValue @{'html' = $DeviceSummaryHTML } @@ -783,7 +830,13 @@ function Invoke-NinjaOneTenantSync { } if ($MappedFields.DeviceCompliance) { - $NinjaDeviceUpdate | Add-Member -NotePropertyName $MappedFields.DeviceCompliance -NotePropertyValue $Device.complianceState + if ($Device.complianceState -eq 'compliant') { + $Compliant = 'Compliant' + } else { + $Compliant = 'Non-Compliant' + } + $NinjaDeviceUpdate | Add-Member -NotePropertyName $MappedFields.DeviceCompliance -NotePropertyValue $Compliant + } # Update Device @@ -792,6 +845,11 @@ function Invoke-NinjaOneTenantSync { } } + # Enable Device Updates Subscription if needed. + if ($MappedFields.DeviceCompliance) { + New-CIPPGraphSubscription -TenantFilter $TenantFilter -TypeofSubscription 'updated' -BaseURL $CIPPUrl -Resource 'devices' -EventType 'DeviceUpdate' -ExecutingUser 'NinjaOneSync' + } + Write-Host "Processed Devices" @@ -839,8 +897,6 @@ function Invoke-NinjaOneTenantSync { foreach ($user in $SyncUsers | where-object { $_.id -notin $ParsedUsers.RowKey }) { try { - Write-Host "Processing $($User.displayName)" - $NinjaOneUser = $NinjaOneUserDocs | Where-Object { $_.ParsedFields.cippUserID -eq $User.ID } if (($NinjaOneUser | Measure-Object).count -gt 1) { Throw "Multiple Users with the same ID found" @@ -1189,7 +1245,7 @@ function Invoke-NinjaOneTenantSync { # Links $M365UserLinksHTML = Get-NinjaOneLinks -Data $Microsoft365UserLinksData -Title 'Portals' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 $CIPPUserLinksHTML = Get-NinjaOneLinks -Data $CIPPUserLinksData -Title 'CIPP Links' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 - $UserLinksHTML = '
    ' + $M365UserLinksHTML + '
    ' + $CIPPUserLinksHTML + '
    ' + $UserLinksHTML = '
    ' + $M365UserLinksHTML + '
    ' + $CIPPUserLinksHTML + '
    ' # UsersSummaryCards: @@ -1707,7 +1763,7 @@ function Invoke-NinjaOneTenantSync { $CIPPLinksHTML = Get-NinjaOneLinks -Data $CIPPLinksData -Title 'CIPP Actions' -SmallCols 2 -MedCols 3 -LargeCols 3 -XLCols 3 - $LinksHtml = '
    ' + $CIPPLinksHTML + '
    ' + $LinksHtml = '
    ' + $CIPPLinksHTML + '
    ' $NinjaOrgUpdate | Add-Member -NotePropertyName $MappedFields.TenantLinks -NotePropertyValue @{'html' = $LinksHtml } @@ -1990,24 +2046,24 @@ function Invoke-NinjaOneTenantSync { }) - # Anonymous Reports + # Unified Audit Log $WidgetData.add([PSCustomObject]@{ - Value = $(if ($BPAData.AnonymousPrivacyReports -eq $True) { - $ResultColour = '#D53948' + Value = $(if ($BPAData.UnifiedAuditLog -eq $True) { + $ResultColour = '#26A644' '' } else { - $ResultColour = '#26A644' + $ResultColour = '#D53948' '' } ) - Description = 'Anonymous Privacy Reports' + Description = 'Unified Audit Log' Colour = $ResultColour - Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" + Link = "https://security.microsoft.com/auditlogsearch?viewid=Async%20Search&tid=$($Customer.customerId)" }) - # Unified Audit Log + # Passwords Never Expire $WidgetData.add([PSCustomObject]@{ - Value = $(if ($BPAData.UnifiedAuditLog -eq $True) { + Value = $(if ($BPAData.PasswordNeverExpires -eq $True) { $ResultColour = '#26A644' '' } else { @@ -2015,7 +2071,7 @@ function Invoke-NinjaOneTenantSync { '' } ) - Description = 'Unified Audit Log' + Description = 'Password Never Expires' Colour = $ResultColour Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" }) @@ -2032,7 +2088,7 @@ function Invoke-NinjaOneTenantSync { ) Description = 'OAuth App Consent' Colour = $ResultColour - Link = "https://$CIPPUrl/tenant/standards/bpa-report?SearchNow=true&Report=CIPP+Best+Practices+v1.0+-+Tenant+view&tenantFilter=$($Customer.customerId)" + Link = "https://entra.microsoft.com/$($Customer.customerId)/#view/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/~/UserSettings" }) } @@ -2196,13 +2252,18 @@ function Invoke-NinjaOneTenantSync { $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($MappedTenant.NinjaOne)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaOrgUpdate | ConvertTo-Json -Depth 100) - Remove-AzDataTableEntity @UsersTable -Entity $ParsedUsers + Write-Host "Cleaning Users Cache" + Remove-AzDataTableEntity @UsersTable -Entity ($ParsedUsers | Select-Object PartitionKey, RowKey) + Write-Host "Cleaning Device Cache" + $ParsedDevices | ConvertTo-Json -Depth 100 | Out-File D:\Temp\Parseddevices.json + Remove-AzDataTableEntity @DeviceTable -Entity ($ParsedDevices | Select-Object PartitionKey, RowKey) Write-Host "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds)" Write-Host "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Completed NinjaOne Sync for $($Customer.displayName). Data fetched in $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds) seconds. Total time $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds) seconds" -Sev 'info' } catch { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Failed NinjaOne Processing for $($Customer.displayName) Error: $_" -Sev 'Error' + Write-Error "Failed NinjaOne Processing for $($Customer.displayName) Linenumber: $($_.InvocationInfo.ScriptLineNumber) Error: $($_.Exception.message)" + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "Failed NinjaOne Processing for $($Customer.displayName) Linenumber: $($_.InvocationInfo.ScriptLineNumber) Error: $($_.Exception.message)" -Sev 'Error' } } \ No newline at end of file diff --git a/PublicWebhooks/run.ps1 b/PublicWebhooks/run.ps1 index 708ba8c4314a..232c44458572 100644 --- a/PublicWebhooks/run.ps1 +++ b/PublicWebhooks/run.ps1 @@ -13,12 +13,11 @@ Write-Host $url if ($Request.CIPPID -in $Webhooks.CIPPID) { Write-Host "Found matching CIPPID" - Push-OutputBinding -Name QueueWebhook -Value $Request - if ($Request.query.ValidationToken -or $Request.body.validationCode) { Write-Host "Validation token received" $body = $request.query.ValidationToken } else { + Push-OutputBinding -Name QueueWebhook -Value $Request $Body = 'Webhook Recieved' } } else { diff --git a/Scheduler_Extensions/run.ps1 b/Scheduler_Extensions/run.ps1 index bf2ddde5f665..55a545dc3f9e 100644 --- a/Scheduler_Extensions/run.ps1 +++ b/Scheduler_Extensions/run.ps1 @@ -6,6 +6,8 @@ $Table = Get-CIPPTable -TableName Extensionsconfig $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json) +Write-Host "Started Schedular" + # NinjaOne Extension if ($Configuration.NinjaOne.Enabled -eq $True) { @@ -13,8 +15,10 @@ if ($Configuration.NinjaOne.Enabled -eq $True) { $Settings = (Get-AzDataTableEntity @Table) $TimeSetting = ($Settings | Where-Object { $_.RowKey -eq 'NinjaSyncTime' }).SettingValue + + if (($TimeSetting | Measure-Object).count -ne 1) { - [int]$TimeSetting = Get-Random -Minimum 0 -Maximum 96 + [int]$TimeSetting = Get-Random -Minimum 1 -Maximum 95 $AddObject = @{ PartitionKey = 'NinjaConfig' RowKey = 'NinjaSyncTime' @@ -23,10 +27,19 @@ if ($Configuration.NinjaOne.Enabled -eq $True) { Add-AzDataTableEntity @Table -Entity $AddObject -Force } + Write-Host "Ninja Time Setting: $TimeSetting" + $LastRunTime = Get-Date(($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue) - $CurrentInterval = ($currentHour * 4) + [math]::Floor($currentMinute / 15) + + Write-Host "Last Run: $LastRunTime" + + $CurrentTime = Get-Date + $CurrentInterval = ($CurrentTime.Hour * 4) + [math]::Floor($CurrentTime.Minute / 15) + + Write-Host "Current Interval: $CurrentInterval" if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $TimeSetting -eq $CurrentInterval) { + Write-Host "Executing" $CIPPMapping = Get-CIPPTable -TableName CippMapping $Filter = "PartitionKey eq 'NinjaOrgsMapping'" $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } From cade61b329afb5de9c678b250f6731c268460220 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:24:18 +0000 Subject: [PATCH 34/77] Bug Fix --- Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 57023f6e866f..997d7ba3553d 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -2255,7 +2255,6 @@ function Invoke-NinjaOneTenantSync { Write-Host "Cleaning Users Cache" Remove-AzDataTableEntity @UsersTable -Entity ($ParsedUsers | Select-Object PartitionKey, RowKey) Write-Host "Cleaning Device Cache" - $ParsedDevices | ConvertTo-Json -Depth 100 | Out-File D:\Temp\Parseddevices.json Remove-AzDataTableEntity @DeviceTable -Entity ($ParsedDevices | Select-Object PartitionKey, RowKey) Write-Host "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds)" From b5253054d5ebd2cecd3d88fdbd1ea56d77df557f Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 23 Nov 2023 23:07:03 +0000 Subject: [PATCH 35/77] Graph Subscription Bug Fixes --- Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 | 2 +- .../NinjaOne/Invoke-NinjaOneDeviceWebhook.ps1 | 9 ++++----- PublicWebhooksProcess/run.ps1 | 10 +--------- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 index 610c2ddb5a72..18a58945002e 100644 --- a/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPGraphSubscription.ps1 @@ -47,7 +47,7 @@ function New-CIPPGraphSubscription { $expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") $params = @{ changeType = $TypeofSubscription - notificationUrl = "https://$BaseURL/API/PublicWebhooks?EventType=$EventType&CIPPID=$CIPPID?Type=GraphSubscription" + notificationUrl = "https://$BaseURL/API/PublicWebhooks?EventType=$EventType&CIPPID=$($CIPPID)&Type=GraphSubscription" resource = $Resource expirationDateTime = $expiredate } | ConvertTo-Json diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDeviceWebhook.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDeviceWebhook.ps1 index 0ee98dbc1663..508f96eb75b8 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDeviceWebhook.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDeviceWebhook.ps1 @@ -5,7 +5,7 @@ function Invoke-NinjaOneDeviceWebhook { $Configuration ) try { - + Write-LogMessage -user $ExecutingUser -API $APIName -message "Webhook Recieved - Updating NinjaOne Device compliance for $($Data.resourceData.id) in $($Data.tenantId)" -Sev "Info" -tenant $TenantFilter $MappedFields = [pscustomobject]@{} $CIPPMapping = Get-CIPPTable -TableName CippMapping $Filter = "PartitionKey eq 'NinjaFieldMapping'" @@ -14,9 +14,9 @@ function Invoke-NinjaOneDeviceWebhook { } if ($MappedFields.DeviceCompliance) { + $tenantfilter = $Data.tenantId + $M365DeviceID = $Data.resourceData.id - $tenantfilter = $($Data.value.tenantId) - $M365DeviceID = $Data.value.resourceData.id $DeviceM365 = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/devices/$($M365DeviceID)" -Tenantid $tenantfilter $DeviceFilter = "PartitionKey eq '$($tenantfilter)' and RowKey eq '$($DeviceM365.deviceID)'" @@ -36,9 +36,8 @@ function Invoke-NinjaOneDeviceWebhook { "$($MappedFields.DeviceCompliance)" = $Compliant } | ConvertTo-Json - Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/device/$($Device.NinjaOneID)" -Method PATCH -Body $ComplianceBody -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' + $Null = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/device/$($Device.NinjaOneID)/custom-fields" -Method PATCH -Body $ComplianceBody -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' - $NinjaDeviceUpdate | Add-Member -NotePropertyName $MappedFields.DeviceCompliance -NotePropertyValue $Device.complianceState Write-Host "Updated NinjaOne Device Compliance" diff --git a/PublicWebhooksProcess/run.ps1 b/PublicWebhooksProcess/run.ps1 index 572879bd134e..8c5f91227ceb 100644 --- a/PublicWebhooksProcess/run.ps1 +++ b/PublicWebhooksProcess/run.ps1 @@ -11,7 +11,7 @@ Write-Host "Received request" Write-Host "CIPPID: $($request.Query.CIPPID)" $url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 Write-Host $url -if ($Request.query.CIPPID -in $Webhooks.CIPPID) { +if ($Request.query.CIPPID -in $Webhooks.RowKey) { Write-Host "Found matching CIPPID" $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID @@ -42,7 +42,6 @@ if ($Request.query.CIPPID -in $Webhooks.CIPPID) { if ($item.operation -eq "UserLoggedIn" -and "AdminLoggedIn" -in $operations) { Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations } - $body = "OK" } } } @@ -50,10 +49,3 @@ if ($Request.query.CIPPID -in $Webhooks.CIPPID) { } else { Write-Host 'Unauthorised Webhook' } - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) From e2d578a9539cf62a1ef4d1d0ec203e41161faedb Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:57:44 +0000 Subject: [PATCH 36/77] Text tweaks --- ExecExtensionMapping/run.ps1 | 2 +- ExecExtensionTest/run.ps1 | 2 +- .../CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 | 13 +++---------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/ExecExtensionMapping/run.ps1 b/ExecExtensionMapping/run.ps1 index 99015d39bdea..2709f64de294 100644 --- a/ExecExtensionMapping/run.ps1 +++ b/ExecExtensionMapping/run.ps1 @@ -55,7 +55,7 @@ try { switch ($Request.Query.AutoMapping) { 'NinjaOrgs' { Push-OutputBinding -Name NinjaProcess -Value @{'NinjaAction' = 'StartAutoMapping' } - $Body = [pscustomobject]@{'Results' = 'Automapping Request has been queued. Exact name matches will appear first and matches on device names and serials will take longer.' } + $Body = [pscustomobject]@{'Results' = 'Automapping Request has been queued. Exact name matches will appear first and matches on device names and serials will take longer. Please check the CIPP Logbook and refresh the page once complete.' } } diff --git a/ExecExtensionTest/run.ps1 b/ExecExtensionTest/run.ps1 index 03ca59ae18bf..bbac07c87fb2 100644 --- a/ExecExtensionTest/run.ps1 +++ b/ExecExtensionTest/run.ps1 @@ -12,7 +12,7 @@ try { switch ($Request.query.extensionName) { "HaloPSA" { $token = Get-HaloToken -configuration $Configuration.HaloPSA - $Results = [pscustomobject]@{"Results" = "Succesfully Connected to HaloPSA" } + $Results = [pscustomobject]@{"Results" = "Successfully Connected to HaloPSA" } } "Gradient" { $GradientToken = Get-GradientToken -Configuration $Configuration.Gradient diff --git a/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 b/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 index 85ca41b2d7da..096bbe1c702b 100644 --- a/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 +++ b/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 @@ -3,20 +3,13 @@ function Get-NinjaOneToken { param ( $Configuration ) - ################## TO DO CONVERT TO KEY VAULT BEFORE COMMIT ################################### - #$null = Connect-AzAccount -Identity - #$body = @{ - # grant_type = 'client_credentials' - # client_id = $Configuration.ClientId - # client_secret = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "NinjaOne" -AsPlainText) - # scope = 'monioring management' - #} + $null = Connect-AzAccount -Identity $body = @{ grant_type = 'client_credentials' client_id = $Configuration.ClientId - client_secret = $Configuration.TEMPSECRETCHANGEME - scope = 'monitoring management' + client_secret = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "NinjaOne" -AsPlainText) + scope = 'monioring management' } $token = Invoke-RestMethod -Uri "https://$($Configuration.Instance -replace '/ws','')/ws/oauth/token" -Method Post -Body $body -ContentType 'application/x-www-form-urlencoded' From a212da32d8ae7e9cce4ec7b35d58eb3cc7392c2f Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:34:06 +0000 Subject: [PATCH 37/77] Fixed scope --- Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 b/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 index 096bbe1c702b..08dc7004ffb2 100644 --- a/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 +++ b/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 @@ -3,13 +3,13 @@ function Get-NinjaOneToken { param ( $Configuration ) - $null = Connect-AzAccount -Identity + $body = @{ grant_type = 'client_credentials' client_id = $Configuration.ClientId client_secret = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "NinjaOne" -AsPlainText) - scope = 'monioring management' + scope = 'monitoring management' } $token = Invoke-RestMethod -Uri "https://$($Configuration.Instance -replace '/ws','')/ws/oauth/token" -Method Post -Body $body -ContentType 'application/x-www-form-urlencoded' From a983ea909671efd87c1640bb76c5dfd5c92c2920 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:21:40 +0000 Subject: [PATCH 38/77] Bug Fixes and Local NinjaOne Credential support --- .../NinjaOne/Get-NinjaOneOrgMapping.ps1 | 5 +++-- .../CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 | 12 ++++++++++-- .../NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 9 +++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Get-NinjaOneOrgMapping.ps1 b/Modules/CippExtensions/NinjaOne/Get-NinjaOneOrgMapping.ps1 index 2a7ab33227ae..822ed28b42b4 100644 --- a/Modules/CippExtensions/NinjaOne/Get-NinjaOneOrgMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Get-NinjaOneOrgMapping.ps1 @@ -29,14 +29,15 @@ function Get-NinjaOneOrgMapping { $ResultCount = ($Result.id | Measure-Object -Maximum) $After = $ResultCount.maximum - } while ($ResultCount.count -eq $PageSize) + } while ($ResultCount.count -eq $PageSize) + } catch { $NinjaOrgs = @() } $MappingObj = [PSCustomObject]@{ Tenants = @($Tenants) - NinjaOrgs = @($NinjaOrgs) + NinjaOrgs = @($NinjaOrgs | Sort-Object name) Mappings = $Mappings } diff --git a/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 b/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 index 08dc7004ffb2..4c4d8bdc3e77 100644 --- a/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 +++ b/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 @@ -3,12 +3,20 @@ function Get-NinjaOneToken { param ( $Configuration ) - $null = Connect-AzAccount -Identity + + + if (!$ENV:SetFromProfile) { + $null = Connect-AzAccount -Identity + $ClientSecret = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "NinjaOne" -AsPlainText) + } else { + $ClientSecret = $ENV:NinjaClientSecret + } + $body = @{ grant_type = 'client_credentials' client_id = $Configuration.ClientId - client_secret = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "NinjaOne" -AsPlainText) + client_secret = $ClientSecret scope = 'monitoring management' } diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index 997d7ba3553d..d84c6aaee35e 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -2253,9 +2253,14 @@ function Invoke-NinjaOneTenantSync { $Result = Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organization/$($MappedTenant.NinjaOne)/custom-fields" -Method PATCH -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body ($NinjaOrgUpdate | ConvertTo-Json -Depth 100) Write-Host "Cleaning Users Cache" - Remove-AzDataTableEntity @UsersTable -Entity ($ParsedUsers | Select-Object PartitionKey, RowKey) + if (($ParsedUsers | Measure-Object).count -gt 0) { + Remove-AzDataTableEntity @UsersTable -Entity ($ParsedUsers | Select-Object PartitionKey, RowKey) + } + Write-Host "Cleaning Device Cache" - Remove-AzDataTableEntity @DeviceTable -Entity ($ParsedDevices | Select-Object PartitionKey, RowKey) + if (($ParsedDevices | Measure-Object).count -gt 0) { + Remove-AzDataTableEntity @DeviceTable -Entity ($ParsedDevices | Select-Object PartitionKey, RowKey) + } Write-Host "Total Fetch Time: $((New-TimeSpan -Start $StartTime -End $FetchEnd).TotalSeconds)" Write-Host "Completed Total Time: $((New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds)" From f3561f5f8c2dbecfc27e3510bbd3fc0f14a35a3a Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:29:32 +0000 Subject: [PATCH 39/77] Authentication Fix --- Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 b/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 index 4c4d8bdc3e77..17ebad207a2d 100644 --- a/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 +++ b/Modules/CippExtensions/NinjaOne/Get-NinjaOneToken.ps1 @@ -5,7 +5,7 @@ function Get-NinjaOneToken { ) - if (!$ENV:SetFromProfile) { + if (!$ENV:NinjaClientSecret) { $null = Connect-AzAccount -Identity $ClientSecret = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "NinjaOne" -AsPlainText) } else { From 5c2d52b2849fe712bd6264f896275e3e8fe8564b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 24 Nov 2023 17:09:12 +0100 Subject: [PATCH 40/77] removed sharedmailboxes query. --- ListMailboxes/run.ps1 | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ListMailboxes/run.ps1 b/ListMailboxes/run.ps1 index 1d2a02ff1112..532c02da36c2 100644 --- a/ListMailboxes/run.ps1 +++ b/ListMailboxes/run.ps1 @@ -13,12 +13,6 @@ Write-Host 'PowerShell HTTP trigger function processed a request.' # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.TenantFilter try { - if ([bool]$Request.Query.SkipLicense -ne $true) { - $users = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/?`$top=999&`$select=id,userPrincipalName,assignedLicenses" -Tenantid $tenantfilter - } - else { - $users = @() - } $Select = "id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses" $ExoRequest = @{ tenantid = $TenantFilter @@ -59,13 +53,6 @@ try { $GraphRequest = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, @{ Name = 'displayName'; Expression = { $_.'DisplayName' } }, - @{ Name = 'SharedMailboxWithLicense'; Expression = { - $ID = $_.id - $Shared = if ($_.'RecipientTypeDetails' -eq 'SharedMailbox') { $true } else { $false } - if (($users | Where-Object -Property ID -EQ $ID).assignedLicenses.skuid -and $Shared) { $true } else { $false } - } - }, - @{ Name = 'primarySmtpAddress'; Expression = { $_.'PrimarySMTPAddress' } }, @{ Name = 'recipientType'; Expression = { $_.'RecipientType' } }, @{ Name = 'recipientTypeDetails'; Expression = { $_.'RecipientTypeDetails' } }, From e7bf44fa22560469f902dd2c508af99937c56a18 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 24 Nov 2023 17:11:50 +0100 Subject: [PATCH 41/77] typo --- Scheduler_Extensions/run.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scheduler_Extensions/run.ps1 b/Scheduler_Extensions/run.ps1 index 55a545dc3f9e..b8d80be6a5bf 100644 --- a/Scheduler_Extensions/run.ps1 +++ b/Scheduler_Extensions/run.ps1 @@ -6,7 +6,7 @@ $Table = Get-CIPPTable -TableName Extensionsconfig $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json) -Write-Host "Started Schedular" +Write-Host "Started Scheduler for Extensions" # NinjaOne Extension if ($Configuration.NinjaOne.Enabled -eq $True) { From 9f90ad69d1313e8a4d0f53208b7ee59c311ce77e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 24 Nov 2023 17:12:52 +0100 Subject: [PATCH 42/77] typo --- .../Invoke-NinjaOneDocumentTemplate.ps1 | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDocumentTemplate.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDocumentTemplate.ps1 index d2b9bc3ace7e..e816c66d4000 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDocumentTemplate.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneDocumentTemplate.ps1 @@ -13,23 +13,26 @@ function Invoke-NinjaOneDocumentTemplate { } if (!$ID) { - $DocumentTemplates = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/document-templates/" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + $DocumentTemplates = (Invoke-WebRequest -Uri "https://$($Configuration.Instance)/api/v2/document-templates/" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -Depth 100 $DocumentTemplate = $DocumentTemplates | Where-Object { $_.name -eq $Template.name } - } else { - $DocumentTemplate = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/document-templates/$($ID)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + } + else { + $DocumentTemplate = (Invoke-WebRequest -Uri "https://$($Configuration.Instance)/api/v2/document-templates/$($ID)" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -Depth 100 } $MatchedCount = ($DocumentTemplate | Measure-Object).count if ($MatchedCount -eq 1) { # Matched a single document template $NinjaDocumentTemplate = $DocumentTemplate - } elseif ($MatchedCount -eq 0) { + } + elseif ($MatchedCount -eq 0) { # Create a new Document Template - $Body = $Template | ConvertTo-Json -depth 100 - $NinjaDocumentTemplate = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/document-templates/" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body $Body).content | ConvertFrom-Json -depth 100 - } else { + $Body = $Template | ConvertTo-Json -Depth 100 + $NinjaDocumentTemplate = (Invoke-WebRequest -Uri "https://$($Configuration.Instance)/api/v2/document-templates/" -Method POST -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -Body $Body).content | ConvertFrom-Json -Depth 100 + } + else { # Matched multiple templates. Should be impossible but lets check anyway :D - Throw "Multiiple Documents Matched the Provided Criteria" + Throw "Multiple Documents Matched the Provided Criteria" } return $NinjaDocumentTemplate From a94ece7c6363228269cfdc3a2b7681def0e4ead5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 24 Nov 2023 22:33:48 +0100 Subject: [PATCH 43/77] Update logging messages --- Standards_NudgeMFA/run.ps1 | 4 ++-- Standards_calDefault/run.ps1 | 2 +- Standards_fwdAdminAlerts/run.ps1 | 10 ++++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Standards_NudgeMFA/run.ps1 b/Standards_NudgeMFA/run.ps1 index 29fb5b21227e..0ec7b80ef78d 100644 --- a/Standards_NudgeMFA/run.ps1 +++ b/Standards_NudgeMFA/run.ps1 @@ -17,8 +17,8 @@ try { $body.registrationEnforcement.authenticationMethodsRegistrationCampaign.state = $status $body = ConvertTo-Json -Depth 10 -InputObject ($body | Select-Object registrationEnforcement) New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy" -Type patch -Body $body -ContentType "application/json" - Write-LogMessage -API "Standards" -tenant $tenant -message "$status Authenticator App Nudge" -sev Info + Write-LogMessage -API "Standards" -tenant $tenant -message "Authenticator App Nudge/Registration campaign $status." -sev Info } catch { - Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to $status Authenticator App Nudge: $($_.exception.message)" -sev Error + Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to $status Authenticator App Nudge/Registration campaign: $($_.exception.message)" -sev Error } \ No newline at end of file diff --git a/Standards_calDefault/run.ps1 b/Standards_calDefault/run.ps1 index 49c2b9702549..fa5add33f029 100644 --- a/Standards_calDefault/run.ps1 +++ b/Standards_calDefault/run.ps1 @@ -12,7 +12,7 @@ foreach ($Mailbox in $Mailboxes) { try { New-ExoRequest -tenantid $Tenant -cmdlet "Get-MailboxFolderStatistics" -cmdParams @{identity = $Mailbox.UserPrincipalName; FolderScope = 'Calendar' } -Anchor $Mailbox.UserPrincipalName | ForEach-Object { New-ExoRequest -tenantid $Tenant -cmdlet "Set-MailboxFolderPermission" -cmdparams @{Identity = "$($Mailbox.UserPrincipalName):$($_.FolderId)"; User = 'Default'; AccessRights = $setting.permissionlevel } -Anchor $Mailbox.UserPrincipalName - Write-LogMessage -API "Standards" -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($setting.permissionlevel)" -sev Error + Write-LogMessage -API "Standards" -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($setting.permissionlevel)" -sev Info } } catch { diff --git a/Standards_fwdAdminAlerts/run.ps1 b/Standards_fwdAdminAlerts/run.ps1 index b3160913ad5b..fa5add33f029 100644 --- a/Standards_fwdAdminAlerts/run.ps1 +++ b/Standards_fwdAdminAlerts/run.ps1 @@ -11,15 +11,13 @@ $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet "get-mailbox" foreach ($Mailbox in $Mailboxes) { try { New-ExoRequest -tenantid $Tenant -cmdlet "Get-MailboxFolderStatistics" -cmdParams @{identity = $Mailbox.UserPrincipalName; FolderScope = 'Calendar' } -Anchor $Mailbox.UserPrincipalName | ForEach-Object { - New-ExoRequest -tenantid $Tenant -cmdlet "Set-MailboxFolderPermission" -cmdparams @{Identity = ($_.identity).replace('\', ':\'); User = 'Default'; AccessRights = $setting.permissionlevel } -Anchor $Mailbox.UserPrincipalName - Write-LogMessage -API "Standards" -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName) to $($setting.permissionlevel)" -sev Error - + New-ExoRequest -tenantid $Tenant -cmdlet "Set-MailboxFolderPermission" -cmdparams @{Identity = "$($Mailbox.UserPrincipalName):$($_.FolderId)"; User = 'Default'; AccessRights = $setting.permissionlevel } -Anchor $Mailbox.UserPrincipalName + Write-LogMessage -API "Standards" -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($setting.permissionlevel)" -sev Info } } catch { - Write-LogMessage -API "Standards" -tenant $tenant -message "Could not set default calendar permissions. Error: $($_.exception.message)" -sev Error + Write-LogMessage -API "Standards" -tenant $tenant -message "Could not set default calendar permissions for $($Mailbox.UserPrincipalName). Error: $($_.exception.message)" -sev Error } } -Write-LogMessage -API "Standards" -tenant $tenant -message "Done setting default calendar permissions." -sev Info - +Write-LogMessage -API "Standards" -tenant $tenant -message "Done setting default calendar permissions." -sev Info \ No newline at end of file From 00470dd9c5e4b7145238ebfb377ddafa7fa6f88b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 24 Nov 2023 23:17:35 +0100 Subject: [PATCH 44/77] Change License Buy Self Service to follow logging standards --- Standards_DisableSelfServiceLicenses/run.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Standards_DisableSelfServiceLicenses/run.ps1 b/Standards_DisableSelfServiceLicenses/run.ps1 index 50f3be239b63..3a6960cdd678 100644 --- a/Standards_DisableSelfServiceLicenses/run.ps1 +++ b/Standards_DisableSelfServiceLicenses/run.ps1 @@ -1,9 +1,9 @@ param($tenant) try { - Write-LogMessage "Standards API: $($tenant) failed to disable License Buy Self Service: $($exception.message)" -sev Error - + Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable License Buy Self Service: $($_.exception.message)" -sev Error + } catch { - Write-LogMessage "Standards API: $($tenant) failed to disable License Buy Self Service: $($exception.message)" -sev Error + Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable License Buy Self Service: $($_.exception.message)" -sev Error } \ No newline at end of file From 26dd4ac1947fe7b9e0b5c5d57513c454e0d980a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 25 Nov 2023 00:51:01 +0100 Subject: [PATCH 45/77] Logging and caldefaluts change --- Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 | 10 ++++++---- Standards_calDefault/run.ps1 | 2 +- UpdatePermissionsQueue/run.ps1 | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 index 8b3c1bbd0f82..d4c06d0e904f 100644 --- a/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPCPVConsent.ps1 @@ -15,7 +15,8 @@ function Set-CIPPCPVConsent { try { $DeleteSP = New-GraphpostRequest -Type DELETE -noauthcheck $true -uri "https://api.partnercenter.microsoft.com/v1/customers/$($TenantFilter)/applicationconsents/$($ENV:applicationId)" -scope 'https://api.partnercenter.microsoft.com/.default' -tenantid $env:TenantID $Results.add("Deleted Service Principal from $TenantName") - } catch { + } + catch { $Results.add("Error deleting SP - $($_.Exception.Message)") } } @@ -39,13 +40,14 @@ function Set-CIPPCPVConsent { } Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force $Results.add("Successfully added CPV Application to tenant $($TenantName)") | Out-Null - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Added our Service Principal to $($TenantName): $($_.Exception.message)" -Sev 'Info' -tenant $($Tenantfilter) + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Added our Service Principal to $($TenantName): $($_.Exception.message)" -Sev 'Info' -tenant $TenantName -tenantId $TenantFilter - } catch { + } + catch { $ErrorMessage = $_.Exception.Message if ($ErrorMessage -like '*409 (Conflict)*') { return @("We've already added our Service Principal to $($TenantName)") } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add our Service Principal to the client tenant $($TenantName): $($_.Exception.message)" -Sev 'Error' -tenant $($Tenantfilter) + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add our Service Principal to the client tenant $($TenantName): $($_.Exception.message)" -Sev 'Error' -tenant $TenantName -tenantId $TenantFilter return @("Could not add our Service Principal to the client tenant $($TenantName): $($_.Exception.message)") } return $Results diff --git a/Standards_calDefault/run.ps1 b/Standards_calDefault/run.ps1 index 49c2b9702549..83bcb5b78e39 100644 --- a/Standards_calDefault/run.ps1 +++ b/Standards_calDefault/run.ps1 @@ -10,7 +10,7 @@ if (!$Setting) { $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet "get-mailbox" foreach ($Mailbox in $Mailboxes) { try { - New-ExoRequest -tenantid $Tenant -cmdlet "Get-MailboxFolderStatistics" -cmdParams @{identity = $Mailbox.UserPrincipalName; FolderScope = 'Calendar' } -Anchor $Mailbox.UserPrincipalName | ForEach-Object { + New-ExoRequest -tenantid $Tenant -cmdlet "Get-MailboxFolderStatistics" -cmdParams @{identity = $Mailbox.UserPrincipalName; FolderScope = 'Calendar' } -Anchor $Mailbox.UserPrincipalName | Where-Object { $_.FolderType -eq 'Calendar' } | ForEach-Object { New-ExoRequest -tenantid $Tenant -cmdlet "Set-MailboxFolderPermission" -cmdparams @{Identity = "$($Mailbox.UserPrincipalName):$($_.FolderId)"; User = 'Default'; AccessRights = $setting.permissionlevel } -Anchor $Mailbox.UserPrincipalName Write-LogMessage -API "Standards" -tenant $tenant -message "Set default folder permission for $($Mailbox.UserPrincipalName):\$($_.Name) to $($setting.permissionlevel)" -sev Error } diff --git a/UpdatePermissionsQueue/run.ps1 b/UpdatePermissionsQueue/run.ps1 index 8756f33019be..9b147478e274 100644 --- a/UpdatePermissionsQueue/run.ps1 +++ b/UpdatePermissionsQueue/run.ps1 @@ -4,7 +4,7 @@ Write-Host "Applying permissions for $($QueueItem.defaultDomainName)" $Table = Get-CIPPTable -TableName cpvtenants $CPVRows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Tenant -EQ $QueueItem.customerId if (!$CPVRows -or $ENV:ApplicationID -notin $CPVRows.applicationId) { - Write-LogMessage -message "A New tenant has been added, or a new CIPP-SAM Application is in use: $($queueitem.defaultDomainName) with id $($queueitem.customerId)" -Sev "Warn" -API "NewTenant" + Write-LogMessage -tenant $queueitem.defaultDomainName -tenantId $queueitem.customerId -message "A New tenant has been added, or a new CIPP-SAM Application is in use" -Sev "Warn" -API "NewTenant" Write-Host "Adding CPV permissions" Set-CIPPCPVConsent -Tenantfilter $QueueItem.defaultDomainName } @@ -12,4 +12,4 @@ if (!$CPVRows -or $ENV:ApplicationID -notin $CPVRows.applicationId) { Add-CIPPApplicationPermission -RequiredResourceAccess "CippDefaults" -ApplicationId $ENV:ApplicationID -tenantfilter $QueueItem.defaultDomainName Add-CIPPDelegatedPermission -RequiredResourceAccess "CippDefaults" -ApplicationId $ENV:ApplicationID -tenantfilter $QueueItem.defaultDomainName -Write-LogMessage -message "Updated permissions for $QueueItem" -Sev "Info" -tenant $QueueItem.defaultDomainName -API "UpdatePermissionsQueue" +Write-LogMessage -tenant $QueueItem.defaultDomainName -tenantId $queueitem.customerId -message "Updated permissions for $($QueueItem.defaultDomainName)" -Sev "Info" -API "UpdatePermissionsQueue" \ No newline at end of file From dde512b7d49864944bd0a63026d6391a2abac12b Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Sat, 25 Nov 2023 21:26:07 +0100 Subject: [PATCH 46/77] Add app consent requests page --- .../Invoke-ListAppConsentRequests.ps1 | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 new file mode 100644 index 000000000000..89ecf70bd9ba --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 @@ -0,0 +1,58 @@ +using namespace System.Net + +function Invoke-ListAppConsentRequests { + <# + .FUNCTIONALITY + Entrypoint + #> + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + $TenantFilter = $Request.Query.TenantFilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + try { + if ($Request.Query.TenantFilter -eq "AllTenants") { + throw "AllTenants is not yet supported" + } else { + $TenantFilter = $Request.Query.TenantFilter + } + + $appConsentRequests = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/identityGovernance/appConsent/appConsentRequests" -tenantid $TenantFilter # Need the beta endpoint to get consentType + $Results = foreach ($app in $appConsentRequests) { + $userConsentRequests = New-GraphGetRequest -Uri "https://graph.microsoft.com/v1.0/identityGovernance/appConsent/appConsentRequests/$($app.id)/userConsentRequests" -tenantid $TenantFilter + $userConsentRequests | ForEach-Object { + [pscustomobject]@{ + appId = $app.appId + appDisplayName = $app.appDisplayName + requestUser = $_.createdBy.user.userPrincipalName + requestReason = $_.reason + requestDate = $_.createdDateTime + requestStatus = $_.status + reviewedBy = $_.approval.stages.reviewedBy.userPrincipalName + reviewedJustification = $_.approval.stages.justification + reviewedDate = $_.approval.stages.reviewedDateTime + reviewedStatus = $_.approval.stages.status + scopes = $app.pendingScopes.displayName + 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" + } + } + } + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $StatusCode = [HttpStatusCode]::OK + Write-LogMessage -user $ExecutingUser -API $APIName -message "app consent request list failed" -Sev "Error" -tenant $TenantFilter + $Results = @{ appDisplayName = "Error: $($_.Exception.Message)" } + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($Results) + }) +} \ No newline at end of file From 224beb4cc0f0b6d120f8f5cf43027b8012e555a5 Mon Sep 17 00:00:00 2001 From: Jr7468 Date: Sat, 25 Nov 2023 21:33:03 +0000 Subject: [PATCH 47/77] Adjusted output to not be an array. --- ExecSetOoO/run.ps1 | 4 ++-- Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ExecSetOoO/run.ps1 b/ExecSetOoO/run.ps1 index bb34c7069454..2382c99b661c 100644 --- a/ExecSetOoO/run.ps1 +++ b/ExecSetOoO/run.ps1 @@ -30,10 +30,10 @@ try { "Could not add out of office message for $($username). Error: $($_.Exception.Message)" } - $body = [pscustomobject]@{"Results" = @($results) } + $body = [pscustomobject]@{"Results" = $($results) } } catch { - $body = [pscustomobject]@{"Results" = @("Could not set Out of Office user: $($_.Exception.message)") } + $body = [pscustomobject]@{"Results" = "Could not set Out of Office user: $($_.Exception.message)"} } # Associate values to output bindings by calling 'Push-OutputBinding'. diff --git a/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 b/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 index 99ea809558b1..c9b4f66240d3 100644 --- a/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 @@ -23,7 +23,7 @@ function Set-CIPPOutOfOffice { if ($State -ne "Scheduled") { $OutOfOffice = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-MailboxAutoReplyConfiguration" -cmdParams @{Identity = $userid; AutoReplyState = $State; InternalMessage = $InternalMessage; ExternalMessage = $ExternalMessage } -Anchor $userid Write-LogMessage -user $ExecutingUser -API $APIName -message "Set Out-of-office for $($userid) to $state" -Sev "Info" -tenant $TenantFilter - return "Set Out-of-office for $($userid) to $state. Message is $InternalMessage" + return "Set Out-of-office for $($userid) to $state. Internal Message is $InternalMessage | External Message is $ExternalMessage" } else { $OutOfOffice = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-MailboxAutoReplyConfiguration" -cmdParams @{Identity = $userid; AutoReplyState = $State; InternalMessage = $InternalMessage; ExternalMessage = $ExternalMessage; StartTime = $StartTime; EndTime = $EndTime } -Anchor $userid From 97158576b80ba66b35165df9b9c1b9ef0cffe08e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 00:08:07 +0100 Subject: [PATCH 48/77] REplaced all Add functions with standarized trigger --- AddAPDevice/function.json | 19 ---- AddAlert/function.json | 22 ---- AddAlert/run.ps1 | 94 ----------------- AddAutopilotConfig/function.json | 19 ---- AddCAPolicy/function.json | 19 ---- AddCATemplate/function.json | 19 ---- AddChocoApp/Choco.App.xml | 15 --- AddChocoApp/Choco.app.json | 65 ------------ AddChocoApp/IntunePackage.intunewin | Bin 28368 -> 0 bytes AddChocoApp/IntunePackage/Install.ps1 | 61 ----------- .../IntunePackage/IntuneWinAppUtil.exe | Bin 54160 -> 0 bytes AddChocoApp/IntunePackage/Uninstall.ps1 | 9 -- AddChocoApp/function.json | 24 ----- AddContact/function.json | 19 ---- AddDefenderDeployment/function.json | 19 ---- AddEnrollment/function.json | 19 ---- AddExConnector/function.json | 19 ---- AddExConnectorTemplate/function.json | 19 ---- AddGroup/function.json | 19 ---- AddGroupTemplate/function.json | 19 ---- AddGuest/function.json | 19 ---- AddIntuneTemplate/function.json | 19 ---- AddMSPApp/Immybot.app.json | 65 ------------ AddMSPApp/automate.app.json | 64 ------------ AddMSPApp/automate.app.xml | 16 --- AddMSPApp/automate.detection.ps1 | 29 ------ AddMSPApp/automate.intunewin | Bin 2176 -> 0 bytes AddMSPApp/cwcommand.app.json | 65 ------------ AddMSPApp/cwcommand.app.xml | 16 --- AddMSPApp/cwcommand.intunewin | Bin 2768 -> 0 bytes AddMSPApp/datto.app.json | 65 ------------ AddMSPApp/datto.app.xml | 15 --- AddMSPApp/datto.intunewin | Bin 768 -> 0 bytes AddMSPApp/function.json | 24 ----- AddMSPApp/huntress.app.json | 65 ------------ AddMSPApp/huntress.app.xml | 15 --- AddMSPApp/huntress.intunewin | Bin 8912 -> 0 bytes AddMSPApp/immy.app.xml | 15 --- AddMSPApp/immy.intunewin | Bin 752 -> 0 bytes AddMSPApp/ninjarmm.app.json | 64 ------------ AddMSPApp/ninjarmm.app.xml | 15 --- AddMSPApp/syncro.app.json | 65 ------------ AddMSPApp/syncro.app.xml | 15 --- AddMSPApp/syncro.intunewin | Bin 784 -> 0 bytes AddNamedLocation/function.json | 19 ---- AddOfficeApp/function.json | 19 ---- AddPolicy/function.json | 19 ---- AddScheduledItem/function.json | 19 ---- AddScheduledItem/run.ps1 | 9 -- AddSharedMailbox/function.json | 19 ---- AddSpamFilter/function.json | 19 ---- AddSpamFilterTemplate/function.json | 19 ---- AddStandardsDeploy/function.json | 19 ---- AddTeam/function.json | 19 ---- AddTransportRule/function.json | 19 ---- AddTransportTemplate/function.json | 19 ---- AddUser/function.json | 19 ---- AddWinGetApp/WinGetBody.json | 14 --- AddWinGetApp/function.json | 24 ----- .../Invoke-Activity_AddOrUpdateTableRows.ps1 | 20 ++++ .../Invoke-Activity_GetAllTableRows.ps1 | 13 +++ .../Public/Entrypoints/Invoke-AddAPDevice.ps1 | 17 ++- .../Public/Entrypoints/Invoke-AddAlert.ps1 | 97 ++++++++++++++++++ .../Entrypoints/Invoke-AddAutopilotConfig.ps1 | 15 ++- .../Public/Entrypoints/Invoke-AddCAPolicy.ps1 | 15 ++- .../Entrypoints/Invoke-AddCATemplate.ps1 | 15 ++- .../Public/Entrypoints/Invoke-AddChocoApp.ps1 | 15 ++- ...nvoke-AddChocoApp_OrchestrationStarter.ps1 | 28 +++++ ...-AddChocoApp_OrchestrationStarterTimer.ps1 | 26 +++++ .../Public/Entrypoints/Invoke-AddContact.ps1 | 15 ++- .../Invoke-AddDefenderDeployment.ps1 | 15 ++- .../Entrypoints/Invoke-AddEnrollment.ps1 | 15 ++- .../Entrypoints/Invoke-AddExConnector.ps1 | 15 ++- .../Invoke-AddExConnectorTemplate.ps1 | 15 ++- .../Public/Entrypoints/Invoke-AddGroup.ps1 | 17 ++- .../Entrypoints/Invoke-AddGroupTemplate.ps1 | 15 ++- .../Public/Entrypoints/Invoke-AddGuest.ps1 | 15 ++- .../Entrypoints/Invoke-AddIntuneTemplate.ps1 | 15 ++- .../Public/Entrypoints/Invoke-AddMSPApp.ps1 | 15 ++- .../Entrypoints/Invoke-AddNamedLocation.ps1 | 15 ++- .../Entrypoints/Invoke-AddOfficeApp.ps1 | 15 ++- .../Public/Entrypoints/Invoke-AddPolicy.ps1 | 15 ++- .../Entrypoints/Invoke-AddScheduledItem.ps1 | 16 +++ .../Entrypoints/Invoke-AddSharedMailbox.ps1 | 15 ++- .../Entrypoints/Invoke-AddSpamFilter.ps1 | 15 ++- .../Invoke-AddSpamFilterTemplate.ps1 | 15 ++- .../Entrypoints/Invoke-AddStandardsDeploy.ps1 | 15 ++- .../Public/Entrypoints/Invoke-AddTeam.ps1 | 15 ++- .../Entrypoints/Invoke-AddTransportRule.ps1 | 15 ++- .../Invoke-AddTransportTemplate.ps1 | 15 ++- .../Public/Entrypoints/Invoke-AddUser.ps1 | 15 ++- .../Entrypoints/Invoke-AddWinGetApp.ps1 | 15 ++- .../Invoke-BestPracticeAnalyser_List.ps1 | 33 ++++++ Z_CIPPHttpTrigger/function.json | 6 ++ 94 files changed, 538 insertions(+), 1535 deletions(-) delete mode 100644 AddAPDevice/function.json delete mode 100644 AddAlert/function.json delete mode 100644 AddAlert/run.ps1 delete mode 100644 AddAutopilotConfig/function.json delete mode 100644 AddCAPolicy/function.json delete mode 100644 AddCATemplate/function.json delete mode 100644 AddChocoApp/Choco.App.xml delete mode 100644 AddChocoApp/Choco.app.json delete mode 100644 AddChocoApp/IntunePackage.intunewin delete mode 100644 AddChocoApp/IntunePackage/Install.ps1 delete mode 100644 AddChocoApp/IntunePackage/IntuneWinAppUtil.exe delete mode 100644 AddChocoApp/IntunePackage/Uninstall.ps1 delete mode 100644 AddChocoApp/function.json delete mode 100644 AddContact/function.json delete mode 100644 AddDefenderDeployment/function.json delete mode 100644 AddEnrollment/function.json delete mode 100644 AddExConnector/function.json delete mode 100644 AddExConnectorTemplate/function.json delete mode 100644 AddGroup/function.json delete mode 100644 AddGroupTemplate/function.json delete mode 100644 AddGuest/function.json delete mode 100644 AddIntuneTemplate/function.json delete mode 100644 AddMSPApp/Immybot.app.json delete mode 100644 AddMSPApp/automate.app.json delete mode 100644 AddMSPApp/automate.app.xml delete mode 100644 AddMSPApp/automate.detection.ps1 delete mode 100644 AddMSPApp/automate.intunewin delete mode 100644 AddMSPApp/cwcommand.app.json delete mode 100644 AddMSPApp/cwcommand.app.xml delete mode 100644 AddMSPApp/cwcommand.intunewin delete mode 100644 AddMSPApp/datto.app.json delete mode 100644 AddMSPApp/datto.app.xml delete mode 100644 AddMSPApp/datto.intunewin delete mode 100644 AddMSPApp/function.json delete mode 100644 AddMSPApp/huntress.app.json delete mode 100644 AddMSPApp/huntress.app.xml delete mode 100644 AddMSPApp/huntress.intunewin delete mode 100644 AddMSPApp/immy.app.xml delete mode 100644 AddMSPApp/immy.intunewin delete mode 100644 AddMSPApp/ninjarmm.app.json delete mode 100644 AddMSPApp/ninjarmm.app.xml delete mode 100644 AddMSPApp/syncro.app.json delete mode 100644 AddMSPApp/syncro.app.xml delete mode 100644 AddMSPApp/syncro.intunewin delete mode 100644 AddNamedLocation/function.json delete mode 100644 AddOfficeApp/function.json delete mode 100644 AddPolicy/function.json delete mode 100644 AddScheduledItem/function.json delete mode 100644 AddScheduledItem/run.ps1 delete mode 100644 AddSharedMailbox/function.json delete mode 100644 AddSpamFilter/function.json delete mode 100644 AddSpamFilterTemplate/function.json delete mode 100644 AddStandardsDeploy/function.json delete mode 100644 AddTeam/function.json delete mode 100644 AddTransportRule/function.json delete mode 100644 AddTransportTemplate/function.json delete mode 100644 AddUser/function.json delete mode 100644 AddWinGetApp/WinGetBody.json delete mode 100644 AddWinGetApp/function.json create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_AddOrUpdateTableRows.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_GetAllTableRows.ps1 rename AddAPDevice/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddAPDevice.ps1 (91%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 rename AddAutopilotConfig/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddAutopilotConfig.ps1 (94%) rename AddCAPolicy/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddCAPolicy.ps1 (85%) rename AddCATemplate/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddCATemplate.ps1 (93%) rename AddChocoApp/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 (92%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarterTimer.ps1 rename AddContact/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddContact.ps1 (88%) rename AddDefenderDeployment/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddDefenderDeployment.ps1 (99%) rename AddEnrollment/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddEnrollment.ps1 (93%) rename AddExConnector/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 (83%) rename AddExConnectorTemplate/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnectorTemplate.ps1 (91%) rename AddGroup/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroup.ps1 (94%) rename AddGroupTemplate/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroupTemplate.ps1 (87%) rename AddGuest/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddGuest.ps1 (91%) rename AddIntuneTemplate/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddIntuneTemplate.ps1 (96%) rename AddMSPApp/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 (95%) rename AddNamedLocation/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddNamedLocation.ps1 (91%) rename AddOfficeApp/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddOfficeApp.ps1 (97%) rename AddPolicy/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddPolicy.ps1 (94%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 rename AddSharedMailbox/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddSharedMailbox.ps1 (86%) rename AddSpamFilter/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilter.ps1 (89%) rename AddSpamFilterTemplate/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilterTemplate.ps1 (92%) rename AddStandardsDeploy/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddStandardsDeploy.ps1 (86%) rename AddTeam/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddTeam.ps1 (90%) rename AddTransportRule/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportRule.ps1 (89%) rename AddTransportTemplate/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportTemplate.ps1 (96%) rename AddUser/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 (96%) rename AddWinGetApp/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 (91%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-BestPracticeAnalyser_List.ps1 diff --git a/AddAPDevice/function.json b/AddAPDevice/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddAPDevice/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddAlert/function.json b/AddAlert/function.json deleted file mode 100644 index 1de186148741..000000000000 --- a/AddAlert/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Subscription", - "queueName": "AlertSubscriptions" - } - ] -} diff --git a/AddAlert/run.ps1 b/AddAlert/run.ps1 deleted file mode 100644 index 13e890b456d7..000000000000 --- a/AddAlert/run.ps1 +++ /dev/null @@ -1,94 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value -$Results = foreach ($Tenant in $tenants) { - try { - $TenantID = if ($tenant -ne 'AllTenants') { - (get-tenants | Where-Object -Property defaultDomainName -EQ $Tenant).customerId - } - else { - 'AllTenants' - } - if ($Request.body.SetAlerts) { - $CompleteObject = @{ - tenant = $tenant - tenantid = $TenantID - AdminPassword = [bool]$Request.body.AdminPassword - DefenderMalware = [bool]$Request.body.DefenderMalware - DefenderStatus = [bool]$Request.body.DefenderStatus - MFAAdmins = [bool]$Request.body.MFAAdmins - MFAAlertUsers = [bool]$Request.body.MFAAlertUsers - NewGA = [bool]$Request.body.NewGA - NewRole = [bool]$Request.body.NewRole - QuotaUsed = [bool]$Request.body.QuotaUsed - UnusedLicenses = [bool]$Request.body.UnusedLicenses - OverusedLicenses = [bool]$Request.body.OverusedLicenses - AppSecretExpiry = [bool]$Request.body.AppSecretExpiry - ApnCertExpiry = [bool]$Request.body.ApnCertExpiry - VppTokenExpiry = [bool]$Request.body.VppTokenExpiry - DepTokenExpiry = [bool]$Request.body.DepTokenExpiry - NoCAConfig = [bool]$Request.body.NoCAConfig - SecDefaultsUpsell = [bool]$Request.body.SecDefaultsUpsell - SharePointQuota = [bool]$Request.body.SharePointQuota - ExpiringLicenses = [bool]$Request.body.ExpiringLicenses - type = 'Alert' - RowKey = $TenantID - PartitionKey = 'Alert' - } - - $Table = get-cipptable -TableName 'SchedulerConfig' - Add-CIPPAzDataTableEntity @Table -Entity $CompleteObject -Force - } - $URL = ($request.headers.'x-ms-original-url').split('/api') | Select-Object -First 1 - if ($Tenant -eq 'AllTenants') { - Get-Tenants | ForEach-Object { - foreach ($eventType in $Request.body.EventTypes.value) { - $params = @{ - TenantFilter = $_.defaultDomainName - auditLogAPI = $true - operations = ($Request.body.Operations.value -join ',') - allowedLocations = ($Request.body.AllowedLocations.value -join ',') - BaseURL = $URL - EventType = $eventType - ExecutingUser = $Request.headers.'x-ms-client-principal' - } - Push-OutputBinding -Name Subscription -Value $Params - } - } - } - else { - foreach ($eventType in $Request.body.EventTypes.value) { - $params = @{ - TenantFilter = $tenant - auditLogAPI = $true - operations = ($Request.body.Operations.value -join ',') - allowedLocations = ($Request.body.AllowedLocations.value -join ',') - BaseURL = $URL - EventType = $eventType - ExecutingUser = $Request.headers.'x-ms-client-principal' - } - New-CIPPGraphSubscription @params - } - } - "Successfully added Alert for $($Tenant) to queue." - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Successfully added Alert for $($Tenant) to queue." -Sev 'Info' - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Failed to add Alert for for $($Tenant) to queue" -Sev 'Error' - "Failed to add Alert for for $($Tenant) to queue $($_.Exception.message)" - } -} - -$body = [pscustomobject]@{'Results' = @($results) } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/AddAutopilotConfig/function.json b/AddAutopilotConfig/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddAutopilotConfig/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddCAPolicy/function.json b/AddCAPolicy/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddCAPolicy/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddCATemplate/function.json b/AddCATemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddCATemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddChocoApp/Choco.App.xml b/AddChocoApp/Choco.App.xml deleted file mode 100644 index fb0aac775d73..000000000000 --- a/AddChocoApp/Choco.App.xml +++ /dev/null @@ -1,15 +0,0 @@ - - Install.ps1 - 28319 - IntunePackage.intunewin - Install.ps1 - - bmoyHXFtIws7JrnXNDV4rjzap+Be+4ZJEDJkTfbVIL8= - xNh8ZUZ6TLsAtihUEAU/NHiRfutDzz+eSgEdpaXUo9Q= - 3aQFPhO8ywEC4Ojby1lR0w== - PXX+hj3DXEpzMEMYBDXmAIlSyDIGuAwmAHIQpZIt8hU= - ProfileVersion1 - fx41h3rGZYZO3Jux7JnPgatlmpMc2ZFIZS8ipF5VDDw= - SHA256 - - \ No newline at end of file diff --git a/AddChocoApp/Choco.app.json b/AddChocoApp/Choco.app.json deleted file mode 100644 index 7ef3f82d640f..000000000000 --- a/AddChocoApp/Choco.app.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "displayName": "", - "installCommandLine": "", - "uninstallCommandLine": "", - "description": "", - "developer": " ", - "owner": " ", - "informationUrl": " ", - "privacyInformationUrl": " ", - "fileName": "IntunePackage.intunewin", - "@odata.type": "#microsoft.graph.win32LobApp", - "applicableArchitectures": "x86, x64", - - "installExperience": { - "runAsAccount": "user", - "deviceRestartBehavior": "allow", - "@odata.type": "microsoft.graph.win32LobAppInstallExperience" - }, - "detectionRules": [ - { - "@odata.type": "#microsoft.graph.win32LobAppFileSystemDetection", - "path": "%programfiles%\\7-zip", - "fileOrFolderName": "7z.exe", - "check32BitOn64System": false, - "detectionType": "exists" - } - ], - "returncode": [ - { - "returnCode": 0, - "type": "success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1707, - "type": "Success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1641, - "type": "hardReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1618, - "type": "retry", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 3010, - "type": "softReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - } - ], - "minimumNumberOfProcessors": "1", - "minimumFreeDiskSpaceInMB": "8", - "minimumCpuSpeedInMHz": "4", - "minimumSupportedOperatingSystem": { - "@odata.type": "microsoft.graph.windowsMinimumOperatingSystem", - "v10_1607": true - }, - "notes": "CIPP Uploaded application", - "minimumMemoryInMB": "1", - "setupFilePath": "install.ps1" -} diff --git a/AddChocoApp/IntunePackage.intunewin b/AddChocoApp/IntunePackage.intunewin deleted file mode 100644 index f388884134ed6934bbe64c6593dcda5236d9ad09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28368 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�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 - install.ps1 - 2117 - automate.intunewin - install.ps1 - - TL5w2kSbhW0+Vb/ngucj1fIa7YfAnFG/d+U3o/qGG24= - NGPnJKKQIPM4yD4dCJ0GVCF0pqFsLX2TCb040bjLBBg= - QGvxYMYrgYovA6uo9XQ60w== - Q8PF4sGPbuxDyoQpmJUGVLvZw9hGhOBX0IhQNeeQEHk= - ProfileVersion1 - 49a2kb03OrNyDt0eZHSpSARq9HzvQL0IrBkcPffwC4M= - SHA256 - - \ No newline at end of file diff --git a/AddMSPApp/automate.detection.ps1 b/AddMSPApp/automate.detection.ps1 deleted file mode 100644 index be2de760777f..000000000000 --- a/AddMSPApp/automate.detection.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -$ServerAddress = '##SERVER##' -if (Get-Module -ListAvailable -Name ConnectWiseAutomateAgent) { - Import-Module ConnectWiseAutomateAgent -} -else { - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force - Install-Module -Name PowerShellGet -Force -AllowClobber - Update-Module -Name PowerShellGet - Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted - Install-Module ConnectWiseAutomateAgent -MinimumVersion 0.1.2.0 -Confirm:$false -Force -} - -Invoke-CWAACommand -Command 'Send Status' -Start-Sleep -Seconds 20 - -$AgentInfo = Get-CWAAInfo -$ServerPassword = ConvertFrom-CWAASecurity $AgentInfo.ServerPassword -$LastContact = try { Get-Date $AgentInfo.LastSuccessStatus } catch { $null } - - -if ($AgentInfo.ID -gt 0 -and $LastContact -gt (Get-Date).AddDays(-30) -and $AgentInfo.Server -contains $ServerAddress -and $ServerPassword -ne 'Enter the server password here.') { - Write-Output 'SUCCESS: Agent is healthy' - exit 0 -} -else { - Write-Output 'ERROR: Agent is not healthy' - Write-Output ($AgentInfo | Select-Object ID, LocationID, LastSuccessStatus, Server | ConvertTo-Json) - exit 1 -} \ No newline at end of file diff --git a/AddMSPApp/automate.intunewin b/AddMSPApp/automate.intunewin deleted file mode 100644 index 62ef0e2259630d2d7a509b188f6b660ed9e103fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2176 zcmV-`2!HoO!^Pskk8bQk%7iJHl?GJ1*~8dIgy2`uh)^}>kPvx5Yw=*lD}jnH1FNX@ zbUM?Fcyz>$=Ebc0euX$J&))Ap2^5~>IeOR|!Mhp<+fjvHKz?nb_=cuQ}) zAi=`zX+-IM4Q%7+GACg-K}yPyD}QsOwp66I#Fx@8xtAg2+AYgAJ} z^zef1^ZJUnQ6TSrfVV?^8)O*|8_p6m=jLy~MN1lRkd`N5IfTLgIY%Ij*7(SxdgcHB zhctPCl)`>W#h}C2IMeDz5*?^D_XSb7Dthag92OWE?(bptxAxZm4A^qh2p8IrygI;$ z7TX>+$s5}SNM7g`|3k-toqnsvc=5S==v~#3D_}_)2X%VFH<1%Q)+|6q4m0Wt_2?%i%iG(tW|vwr zP;J47Uk-U;?h|*m=S%7+bQ?N=1Qfc&(-;)PlxiLW^KBcg_i;NsM}R9Ir+}X@v@b;ded{mAINv zS3H422bLh_#F>dAcx1V2TQ*+U{h*uDQ#)Y%A}*ZHW3Ih=7$xuZ2+({p-+X*K{V{E- zRkuWj#(3(#v~}^r>#7wny$CWEU(0WAm7mTLBOH`alZNVU!+XRJV!hQ0p>Zgt|en_yw7TErmZ1{lQisCo__B;Mc zeJ)%1(+@RFu+H~xZ8-<0RC;4ov0J#bLo7+!tf z{pvNefOKS)G?mFQ4AF?^e3g{3aJTM0*(S{KOwBPxkBoW=-4;C}*Ro?yN`I^=xuhTq z*U^5!w#RGKd$ao_0|q=1G!I*=dPy%)qvxn_QB=;ToU3F{;fT10nj|!ut_0VN{kXAY zINZ7y6B+99N45%k+y|#vx)c2ih1;V4H|F-tfYLyEG`O7bT8r8wsTK|!HW0c*H{-U|YOI{ionR}fIsg&7dZ z_A@qDtZ2e#NkrX!X5NXCl+!Ps3)T-^7B9>`jUOj5-Ax7`%9eJp*126V?_v&8`dW*8 z4r=)IGup@WgoiP->wN&kpVt~6sA2oxA9w!tK9^4JO^&?nwVWJd zv=($KI;QaanJG^}MCp)&!uXIDPUGD>LK>_(LNzYoj=G{;_ItU9{|9;eVliB~q{mh2Hd?27e z!gNdkyla<9#An)=u1yX!bZU&Tp5&~lmx{TtKVnJru*#N1q)9qDOi$Fu+cgCRXK<)y z+j~+p-ni4E$+fu;Gl&)uDCnD77pbQLJ!;d>S)6vjGrD05!aLOFWx_*6B*%i#Ju0pu z4Wmx!%RrvIwIMg(+*P$~2Z>T;YW?qJaL>0-Wj+AlaIq9@^syx59CLr<1awP5VqOl| zV)rrA(=s$9*NUHfZuK!r%lqwi!e`bm-leVE)CY$!skxI6FOe#zREz~v9Cu=b4=*Rx CKQ(Fq diff --git a/AddMSPApp/cwcommand.app.json b/AddMSPApp/cwcommand.app.json deleted file mode 100644 index 2d032177b625..000000000000 --- a/AddMSPApp/cwcommand.app.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "displayName": "", - "installCommandLine": "", - "uninstallCommandLine": "", - "description": " ", - "developer": " ", - "owner": " ", - "informationUrl": " ", - "privacyInformationUrl": " ", - "fileName": "cwcommand.intunewin", - "@odata.type": "#microsoft.graph.win32LobApp", - "applicableArchitectures": "x86, x64", - - "installExperience": { - "runAsAccount": "system", - "deviceRestartBehavior": "suppress", - "@odata.type": "microsoft.graph.win32LobAppInstallExperience" - }, - "detectionRules": [ - { - "@odata.type": "#microsoft.graph.win32LobAppFileSystemDetection", - "path": "%ProgramFiles(x86)%\\ITSPlatform\\agentcore\\", - "fileOrFolderName": "platform-agent-core.exe", - "check32BitOn64System": false, - "detectionType": "exists" - } - ], - "returncode": [ - { - "returnCode": 0, - "type": "success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1707, - "type": "Success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1641, - "type": "hardReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1618, - "type": "retry", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 3010, - "type": "softReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - } - ], - "minimumNumberOfProcessors": "1", - "minimumFreeDiskSpaceInMB": "8", - "minimumCpuSpeedInMHz": "4", - "minimumSupportedOperatingSystem": { - "@odata.type": "microsoft.graph.windowsMinimumOperatingSystem", - "v10_1607": true - }, - "notes": "CIPP Uploaded application", - "minimumMemoryInMB": "1", - "setupFilePath": "install.ps1" -} diff --git a/AddMSPApp/cwcommand.app.xml b/AddMSPApp/cwcommand.app.xml deleted file mode 100644 index cc23edaad6fa..000000000000 --- a/AddMSPApp/cwcommand.app.xml +++ /dev/null @@ -1,16 +0,0 @@ - - install.ps1 - 2717 - cwcommand.intunewin - install.ps1 - - 6bilNd8M34xxoOl/marQi04r0PjYRD0YUuVf5hR/cVY= - QIeCE2WnKhg/yyyN2Vd7WBqy/9Vo22oOY+jN+o7NsM8= - fPjQqWF6INy3aAXKeGIlig== - qWx8/p2CoO2vuP/Dkr7KJw7JoxhmcA4XSj2ictbcC7M= - ProfileVersion1 - fslGge4BZ0F//6xgGCNIIVY5VPr/B2Ms1sGI7RiA9Bo= - SHA256 - - \ No newline at end of file diff --git a/AddMSPApp/cwcommand.intunewin b/AddMSPApp/cwcommand.intunewin deleted file mode 100644 index 3f93872597085254880ee5496d9491cb4dd37d95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2768 zcmV;>3NQ7kY<&Kmf}riMxc|eFzRD*K$)gx%a1IwrJ)&~f+zYdO_|U0gdLZ1lXa&l6 zVkL?R2%e6f8$RNFRUm)WYfBG!B0tiJLyi?}09haF({-8cR^AMHtGN~%8DKPk& zTb+}$+r2T4R^z)jF*ncpcM7QOyzyA$W6!JzAG18V0xs0 zl!EWFkC_BVXSyIkE<)HsrsVYW3I?r^Twk9^HT74W905!UMR8(U;#8R>f7BO4Ak<=(JWTX9;O}+<5woSK?f= za+&yG5P+wP5iYQ$s+Ll)-h;%m^6GUdRZVFqDLD#^-(HYZH`}41)_AQ<_)w|2x1QTw zJRoWw_qw_5hTQY0!l?8?7C=7{YzEhg)*C;At!2ZnF_(>cK$r1^noN288AYpjJ!Rb*{nneVWXSyrXtkM-p>dt&nmRs zmbJcE70t1?H#7>wH8#Gm2jKU$WhB6c%LOCTO;>1#DGwMY6%xlW7)B`kTdcT`(} zIevRiNWB+2NQ&nap|23`lKKG}wL&!1-lB>7(`qj8N)+*5;J925Fov--C7}!;Ugy?k z!5OT9UZye0+530*rtssr`6xz|Q+q4c=I|dt% z&mzju`nuC=6J|i4ELz zOl!HS+G(4*?gQ@j$K~H@)H#(DO9UgsMrA_J-m49(YhGKXL@a-W!6j9(#+D;eoEEur z^I5;cRze1s3boqIjEW7w^3>|z+Supb*Delz)<=9>Qj)H6%C=(JVPyl7RgAYtx&@sn9c@^%`rlfS{uR2C9;4DYlyya@JPoxMp3y~J_OM^ zxPPqLfFF`b@QzBY%S?V7-e9;(!siI`$>DD1ED4yS(ijA^E>w`qi+~E)L+ap~`p5zJ ziUHVX5I?#wU+ljrKLX5;I>ty>6T=U32e}%iv1`D zp)3WqjLTm-|M)mq-IDV)VSVI&CdF@&o{D+q%fTuhfzZ^Y3ypMuA?X zmA2uXR4kjh65z8Lu3iS+V(~=*ist*1PWb_Xo9}rDTKh_c`n1?=$Mmw^6*86M%OK&1J>KI9*yb~TI11$jLUXps_D_AZZVdCyJ zcck6@DsfaMiESzdjHIeaEzPaSg9*Mtw<|gsY!K)r76Ora!mJ-+!{N`+U}iS)SVHAG zA_?Q+q*8MBCXscu!UsGk=QhfpfdSd?uu)1Ip^_)S7*gwDC>ogPgsh1r=pTc5O`cO+ z1Ezt^R+%JX_9gHA+OWfKEv83a8!mw&TL??j_LdYI#%A@78E0ZEhpm|UY$%Aq4yhBC zT|(5!npCx)5|k74_g&ysT7+e7AiQl18FZV` z9N6Luq-?znSdbT#3eNh2RyZ#ruywR3ymj^1pZjETuWJpT+AGIV91+of2MXP?Hh=K^ z#QROCVJ-2C*K4>}#1uS7)Tucv496zp_VR*aXdnmtY4WbCZ3Hc{UP~~fuYfa2z6wis z?%fGVn27wCi&@AES~R|;*I{{!8XTka9}={0-${Dz5Cy(>;gC1y;i`eQY}pRKOw}?w zLWPdFH|fAL8T}R?p#0-Fo=~~j(@6IizhkfJC>}7qz?-O(ko=&~`OcroSMpUyiTJNH$*DVSGZ{*B0U8~Q%ukho+yo8|%4(;o&Un?ROwHs+F? zS$Ls`vIycxO#VPMnzkL@6n@S^8ULstd{DUB$8~7ixw$p~H4Z&;YI8kY13j$MneU=T*2pQ!iM& zrhl}Y7hpFs54waD<3by8xqU2-U8hE{R>d3haESEkEHya6cjpp%hsEQx4t7MhYfNqG zIPqMzccQ?OEgq1!{=9!HF#J1Vg9=0eyY^5>(u||UHnbuqXe4^2t#(T1^KGiOll zo&L7U_?9f>wW~wIipGD}=&ZBeI>CZs2dgxMzGRq^_{-}9S4t4!b}{u{RBM)ny=7h$ zXe=#1^WknvIHijx>99LxTh)x}@DOwtGRe$yqboP9%4H0z;SO;!jr87s4Z4X~x9Z@5 WKbhY*0o9bA1j{3i*36HUE9HiG-dt}0 diff --git a/AddMSPApp/datto.app.json b/AddMSPApp/datto.app.json deleted file mode 100644 index cdd10dfd8aeb..000000000000 --- a/AddMSPApp/datto.app.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "displayName": "", - "installCommandLine": "", - "uninstallCommandLine": "", - "description": " ", - "developer": " ", - "owner": " ", - "informationUrl": " ", - "privacyInformationUrl": " ", - "fileName": "DattoRMM.intunewin", - "@odata.type": "#microsoft.graph.win32LobApp", - "applicableArchitectures": "x86, x64", - - "installExperience": { - "runAsAccount": "system", - "deviceRestartBehavior": "suppress", - "@odata.type": "microsoft.graph.win32LobAppInstallExperience" - }, - "detectionRules": [ - { - "@odata.type": "#microsoft.graph.win32LobAppFileSystemDetection", - "path": "%programfiles(x86)%\\CentraStage", - "fileOrFolderName": "CagService.exe.config", - "check32BitOn64System": false, - "detectionType": "exists" - } - ], - "returncode": [ - { - "returnCode": 0, - "type": "success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1707, - "type": "Success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1641, - "type": "hardReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1618, - "type": "retry", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 3010, - "type": "softReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - } - ], - "minimumNumberOfProcessors": "1", - "minimumFreeDiskSpaceInMB": "8", - "minimumCpuSpeedInMHz": "4", - "minimumSupportedOperatingSystem": { - "@odata.type": "microsoft.graph.windowsMinimumOperatingSystem", - "v10_1607": true - }, - "notes": "CIPP Uploaded application", - "minimumMemoryInMB": "1", - "setupFilePath": "install.ps1" -} diff --git a/AddMSPApp/datto.app.xml b/AddMSPApp/datto.app.xml deleted file mode 100644 index 145d910d8ef3..000000000000 --- a/AddMSPApp/datto.app.xml +++ /dev/null @@ -1,15 +0,0 @@ - - install.ps1 - 705 - datto.intunewin - install.ps1 - - sL/LP/JZ4F4cBSykm6usgJoV1PMoqd62C6JUwuo2z24= - PEpeqeoX7jAWxb0xHGfCkKFxh4/YRfoMTVXrP+uZWzM= - ulFPA+vYjaxX0pvq0BMAKQ== - 28ZFU4AT1OznwF8pfqO8i+WFUNSf9024H4Jw2H7UJWs= - ProfileVersion1 - YEb+QNQCko/uZyedA+JfcP/RDm+nZOIjFN04CfhwN4c= - SHA256 - - \ No newline at end of file diff --git a/AddMSPApp/datto.intunewin b/AddMSPApp/datto.intunewin deleted file mode 100644 index 607b0725032b7f93ba2226ea9af588c77620ed96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmV+b1ONQn#zj+r6V&YIz+WkTqr8jdg;3O=_f5DTf^gV=)Fo@WQBMQw*o~}L(wpkg z696gpMu|)NYJLjr%TdhbceLGQvAA|Rw?=D7s?71sb!hj>flu)7fRS&2F#ScOxJBP!Ach7YBiqM<;i#tw_eo-=kh}kQ~7HEBBUW*6TN~)a*d~XIKqN zI}Fw48A9=bkVcfnrD5f>r6YDW+R|__*Y9#{(GcwYmMoR;SdJseBPMSl+|dTDlP3fQ zGBAIuG20Ts_UV3QL_B*H2Dv8X{SJ(|J1`MS+M4n!30whDzUi_Yvq$)8%{tNr6LwEe zn&`BaO7@BVL1YqYYr#;Gvx5HWX!-R-v0fz1mj+7L9~)OvS7Y&K8gOY0`{WmfK0ReU z#cKd|L*JRtaIq}eMQtFKaUF?ks15>w)nQiT&9_^efEakNs6*|3XdB0VlJmD)B4($fYMQO?TX<88h~DG zeV5ZVvlz;S(ix&I7D(iq(-sg#z|oLS%SFR!nU8UW`1KK^DScagjah5hiYk3PZxIeW yot}Zx9?2Er1qjM_Nz>yn$v&pkqwTBb(stS~#sFe?T0*@_dBZ?}Vr3U(_4eNPW`&6W diff --git a/AddMSPApp/function.json b/AddMSPApp/function.json deleted file mode 100644 index 3bd167116eae..000000000000 --- a/AddMSPApp/function.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "name": "starter", - "direction": "in", - "type": "durableClient" - } - ] -} \ No newline at end of file diff --git a/AddMSPApp/huntress.app.json b/AddMSPApp/huntress.app.json deleted file mode 100644 index 46c8a22057ae..000000000000 --- a/AddMSPApp/huntress.app.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "displayName": "", - "installCommandLine": "", - "uninstallCommandLine": "", - "description": " ", - "developer": " ", - "owner": " ", - "informationUrl": " ", - "privacyInformationUrl": " ", - "fileName": "huntress.intunewin", - "@odata.type": "#microsoft.graph.win32LobApp", - "applicableArchitectures": "x86, x64", - - "installExperience": { - "runAsAccount": "system", - "deviceRestartBehavior": "suppress", - "@odata.type": "microsoft.graph.win32LobAppInstallExperience" - }, - "detectionRules": [ - { - "@odata.type": "#microsoft.graph.win32LobAppFileSystemDetection", - "path": "%ProgramFiles%\\Huntress", - "fileOrFolderName": "HuntressAgent.exe", - "check32BitOn64System": false, - "detectionType": "exists" - } - ], - "returncode": [ - { - "returnCode": 0, - "type": "success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1707, - "type": "Success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1641, - "type": "hardReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1618, - "type": "retry", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 3010, - "type": "softReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - } - ], - "minimumNumberOfProcessors": "1", - "minimumFreeDiskSpaceInMB": "8", - "minimumCpuSpeedInMHz": "4", - "minimumSupportedOperatingSystem": { - "@odata.type": "microsoft.graph.windowsMinimumOperatingSystem", - "v10_1607": true - }, - "notes": "CIPP Uploaded application", - "minimumMemoryInMB": "1", - "setupFilePath": "install.ps1" -} diff --git a/AddMSPApp/huntress.app.xml b/AddMSPApp/huntress.app.xml deleted file mode 100644 index ad4f731a67ff..000000000000 --- a/AddMSPApp/huntress.app.xml +++ /dev/null @@ -1,15 +0,0 @@ - - install.ps1 - 8859 - huntress.intunewin - install.ps1 - - 0wrFiLHex//63XQZEbX535qvhQE5+MiZmfPho1CMrT4= - UOlXFsrh+Pq6ZZNmg2+gzuTCSDAxQNUDVkc6oR5SVAY= - x0cPnMjK6AZARRPhOfC5pg== - z+N/v0mfq8T871kS07/QZ1Lgay2hRabSxwDWRKz3fG4= - ProfileVersion1 - z8JuA/5iCrLM1cRkhL3di5eDysNsab62E812KGsrkbY= - SHA256 - - \ No newline at end of file diff --git a/AddMSPApp/huntress.intunewin b/AddMSPApp/huntress.intunewin deleted file mode 100644 index 12c077f56145007ead1c3def1496abd5fd0fdcae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8912 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 diff --git a/AddMSPApp/immy.app.xml b/AddMSPApp/immy.app.xml deleted file mode 100644 index 33c2b026cf06..000000000000 --- a/AddMSPApp/immy.app.xml +++ /dev/null @@ -1,15 +0,0 @@ - - install.ps1 - 701 - IntunePackage.intunewin - install.ps1 - - doCFlu6eR0FygwmKEt64S1Yd8DBfok8/l0ophT7m2R4= - 5ai/8f238719rpGrOigf7mjx5u+gF6cBTOLWMBx5lrY= - klnIk9zH1fNeekrILt3tLw== - 9vyw/gMTsEsx3o3TVapXLxUQooNlVMRQj5/cXTo77x0= - ProfileVersion1 - 1eS5ExzbAFPOfI8x9REbwff0RAy5gvjTxYNZdAKUDcc= - SHA256 - - \ No newline at end of file diff --git a/AddMSPApp/immy.intunewin b/AddMSPApp/immy.intunewin deleted file mode 100644 index 5102fd736f7565e0d5786f6065da7e74cf40b081..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 752 zcmVMH z-R&4UTZ^)p&{3Ct-0mb;bz-7e zC93!SN_vMjmVdi61hH;iz4)J-ahd=_QL-4S;F`rbB~_1}Xo|cGABKpw|9}B15+49F zFSUolGHlzB*#(D2v6&|LU#X}MMe)7*mpNA7&vK0pXX)o#3hVb&5DOyYUgTpp3nKxo zlyu((V$)@C7eZ@PtL!jIFqF)P5C`15L%ly;cxZ}m^Z7Fip0#kfhr@Ft+e;kPdSct# zVbU2WSzBIBAz0kjZVf93ZUOy^b~kHA1%w72sQ>DiIc9U*D<{kM3udq)2mC{z;jv*< z|88UrY%TLK-P~t&u2lk35T3=p7)g%(xZ8yMpjXtjF4Z7ot!=F1(=MH6MXzi^*StL; zWNYqpuzj@YW=-=-LIxFZ|f>1HzE}W_(j{{1upDD3iT2;Q5?o+f;CsEyt#^u9J`26L`s~O$71&*v6t0!w_ zc1XT1+b7yj*17J<^_}wmt;4lbD8L3zsX78`*-8-$BSJF-@pt%A+_A#E>(@Y8KR5H! i(ON7ANGQ#Q_PYN7BTz~6PSnktbtC5+o);rImGo)`d~uWj diff --git a/AddMSPApp/ninjarmm.app.json b/AddMSPApp/ninjarmm.app.json deleted file mode 100644 index 5cd567eaf210..000000000000 --- a/AddMSPApp/ninjarmm.app.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "displayName": "", - "installCommandLine": "", - "uninstallCommandLine": "", - "description": " ", - "developer": " ", - "owner": " ", - "informationUrl": " ", - "privacyInformationUrl": " ", - "fileName": "ninjarmm.intunewin", - "@odata.type": "#microsoft.graph.win32LobApp", - "applicableArchitectures": "x86, x64", - - "installExperience": { - "runAsAccount": "system", - "deviceRestartBehavior": "suppress", - "@odata.type": "microsoft.graph.win32LobAppInstallExperience" - }, - "detectionRules": [ - { - "@odata.type": "#microsoft.graph.win32LobAppFileSystemDetection", - "path": "%ProgramData%\\Syncro\\Bin", - "fileOrFolderName": "Syncro.Overmind.Service.exe", - "check32BitOn64System": false, - "detectionType": "exists" - } - ], - "returncode": [ - { - "returnCode": 0, - "type": "success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1707, - "type": "Success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1641, - "type": "hardReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1618, - "type": "retry", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 3010, - "type": "softReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - } - ], - "minimumNumberOfProcessors": "1", - "minimumFreeDiskSpaceInMB": "8", - "minimumCpuSpeedInMHz": "4", - "minimumSupportedOperatingSystem": { - "@odata.type": "microsoft.graph.windowsMinimumOperatingSystem", - "v10_1607": true - }, - "notes": "CIPP Uploaded application", - "minimumMemoryInMB": "1" -} diff --git a/AddMSPApp/ninjarmm.app.xml b/AddMSPApp/ninjarmm.app.xml deleted file mode 100644 index 647d26712e8b..000000000000 --- a/AddMSPApp/ninjarmm.app.xml +++ /dev/null @@ -1,15 +0,0 @@ - - install.ps1 - 728 - syncro.intunewin - install.ps1 - - XsBprXNg7cPsNS7YfRarT1zcSoL6/EF+c2dAjkhfuwk= - c4xkRYZg/r/h1xUD1172Tmr877nDXIoa3wuQnj0dpLQ= - OE7D78wJtXIpVESqaZAiWw== - 2m0nbXMdYK9dQgtjZraz3VsSxQlbO0jabpTaUjPg8I8= - ProfileVersion1 - gPccLH2ZAerHl8aYGYTxWmC8mnpJncTKBw5BX4IpH+g= - SHA256 - - \ No newline at end of file diff --git a/AddMSPApp/syncro.app.json b/AddMSPApp/syncro.app.json deleted file mode 100644 index 1d5f0e01fb4b..000000000000 --- a/AddMSPApp/syncro.app.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "displayName": "", - "installCommandLine": "", - "uninstallCommandLine": "", - "description": " ", - "developer": " ", - "owner": " ", - "informationUrl": " ", - "privacyInformationUrl": " ", - "fileName": "syncro.intunewin", - "@odata.type": "#microsoft.graph.win32LobApp", - "applicableArchitectures": "x86, x64", - - "installExperience": { - "runAsAccount": "system", - "deviceRestartBehavior": "suppress", - "@odata.type": "microsoft.graph.win32LobAppInstallExperience" - }, - "detectionRules": [ - { - "@odata.type": "#microsoft.graph.win32LobAppFileSystemDetection", - "path": "%ProgramData%\\Syncro\\Bin", - "fileOrFolderName": "Syncro.Overmind.Service.exe", - "check32BitOn64System": false, - "detectionType": "exists" - } - ], - "returncode": [ - { - "returnCode": 0, - "type": "success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1707, - "type": "Success", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1641, - "type": "hardReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 1618, - "type": "retry", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - }, - { - "returnCode": 3010, - "type": "softReboot", - "@odata.type": "#microsoft.graph.win32LobAppReturnCode" - } - ], - "minimumNumberOfProcessors": "1", - "minimumFreeDiskSpaceInMB": "8", - "minimumCpuSpeedInMHz": "4", - "minimumSupportedOperatingSystem": { - "@odata.type": "microsoft.graph.windowsMinimumOperatingSystem", - "v10_1607": true - }, - "notes": "CIPP Uploaded application", - "minimumMemoryInMB": "1", - "setupFilePath": "install.ps1" -} diff --git a/AddMSPApp/syncro.app.xml b/AddMSPApp/syncro.app.xml deleted file mode 100644 index 647d26712e8b..000000000000 --- a/AddMSPApp/syncro.app.xml +++ /dev/null @@ -1,15 +0,0 @@ - - install.ps1 - 728 - syncro.intunewin - install.ps1 - - XsBprXNg7cPsNS7YfRarT1zcSoL6/EF+c2dAjkhfuwk= - c4xkRYZg/r/h1xUD1172Tmr877nDXIoa3wuQnj0dpLQ= - OE7D78wJtXIpVESqaZAiWw== - 2m0nbXMdYK9dQgtjZraz3VsSxQlbO0jabpTaUjPg8I8= - ProfileVersion1 - gPccLH2ZAerHl8aYGYTxWmC8mnpJncTKBw5BX4IpH+g= - SHA256 - - \ No newline at end of file diff --git a/AddMSPApp/syncro.intunewin b/AddMSPApp/syncro.intunewin deleted file mode 100644 index 1cf9f0ef8c66400b340846306c7324c537f70946..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 784 zcmV+r1MmFWZ6|GW9bm6rLJMPNwzJ(^62%EyJ4o7Yl-g1=;P8()PQ&la3AJ)5R79$2 zkRn@R!GFs*LSB|kkZF+_kLfesgajT?{A)s+D!)wW?O@Enes$38oebJR*IFyJ?*rvIO?Q3m9fKxK$lDl_Z{Tn0;J< zAr7VMw5rWr<4mbt1eA0Bt$4E_l~VLe4knQMkLs1?{&nlSk%B*ALNm~na)`llNhDL8 zv)9`zh^j{(BO#*k0V=X}Ar2l^AyYtUFiq5?b%;jkyqf>AQxx#?#Iu&W_?(jIdP@8P zhZ-(QGII?VQJusVHMJf1o+MoD54s@%)x zd3HQT&nyXxRPca57c{ArrGS@0An@Q^+JD1jEAKezRu87619Jk2-nLC`@0!tRWJM?- z%bDKeymJ0fQVegx(T-1SJ3#i?WCV@k4$ni?F>X2HGhUj*Bfc#>k67DRF}#^+D+?;H ziKg1eBsr_;9PQE7)-FUlki$Df^y1tEj?D$MPEgwCnE8P|t32-bc2kv(fr7&+*x?sQ zDvD5wH$mmPwKqjoSO0GlQK_TigHV*a-ZEJaRWveeE^rRz-ihx2#mm_Kn|EM=yonqb z_q9jLx+_+4Cmv>B_gP-&bUPKRld1uu*q7sGImb!RYFkYhq2*{@3(2yBA?K}|)%DWaUc6TY OuT%rHS!sGZ@5QMjzlw|i diff --git a/AddNamedLocation/function.json b/AddNamedLocation/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddNamedLocation/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddOfficeApp/function.json b/AddOfficeApp/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddOfficeApp/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddPolicy/function.json b/AddPolicy/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddPolicy/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddScheduledItem/function.json b/AddScheduledItem/function.json deleted file mode 100644 index b0ca1676cc0b..000000000000 --- a/AddScheduledItem/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] - } \ No newline at end of file diff --git a/AddScheduledItem/run.ps1 b/AddScheduledItem/run.ps1 deleted file mode 100644 index 6f64a255a725..000000000000 --- a/AddScheduledItem/run.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -using namespace System.Net -param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -$Result = Add-CIPPScheduledTask -Task $Request.body -hidden $false -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{ Results = $Result } - }) \ No newline at end of file diff --git a/AddSharedMailbox/function.json b/AddSharedMailbox/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddSharedMailbox/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddSpamFilter/function.json b/AddSpamFilter/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/AddSpamFilter/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddSpamFilterTemplate/function.json b/AddSpamFilterTemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddSpamFilterTemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddStandardsDeploy/function.json b/AddStandardsDeploy/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddStandardsDeploy/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddTeam/function.json b/AddTeam/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddTeam/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddTransportRule/function.json b/AddTransportRule/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/AddTransportRule/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddTransportTemplate/function.json b/AddTransportTemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddTransportTemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddUser/function.json b/AddUser/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/AddUser/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/AddWinGetApp/WinGetBody.json b/AddWinGetApp/WinGetBody.json deleted file mode 100644 index bc0552931ac6..000000000000 --- a/AddWinGetApp/WinGetBody.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "@odata.type": "#microsoft.graph.winGetApp", - "displayName": "Display Name value", - "description": "Description value", - "publisher": "Publisher value", - "isFeatured": true, - "uploadState": 11, - "publishingState": "processing", - "packageIdentifier": "", - "installExperience": { - "@odata.type": "microsoft.graph.winGetAppInstallExperience", - "runAsAccount": "user" - } -} diff --git a/AddWinGetApp/function.json b/AddWinGetApp/function.json deleted file mode 100644 index 3bd167116eae..000000000000 --- a/AddWinGetApp/function.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "name": "starter", - "direction": "in", - "type": "durableClient" - } - ] -} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_AddOrUpdateTableRows.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_AddOrUpdateTableRows.ps1 new file mode 100644 index 000000000000..4b4fc1e66d8d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_AddOrUpdateTableRows.ps1 @@ -0,0 +1,20 @@ + using namespace System.Net + + Function Invoke-Activity_AddOrUpdateTableRows { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + try { + #Sending each item indivually, if it fails, log an error. + Add-CIPPAzDataTableEntity @Table -Entity $param -Force + } + catch { + Write-LogMessage -API 'Activity_AddOrUpdateTableRows' -message "Unable to write to '$($TableParams.TableName)' Using RowKey $($param.RowKey) table: $($_.Exception.Message)" -sev error + } +} + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_GetAllTableRows.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_GetAllTableRows.ps1 new file mode 100644 index 000000000000..493de3147ccc --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_GetAllTableRows.ps1 @@ -0,0 +1,13 @@ + using namespace System.Net + + Function Invoke-Activity_GetAllTableRows { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Write-Output $Rows + + } diff --git a/AddAPDevice/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAPDevice.ps1 similarity index 91% rename from AddAPDevice/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddAPDevice.ps1 index 7f689191c4c5..be59d3c44b89 100644 --- a/AddAPDevice/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAPDevice.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddAPDevice { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" @@ -40,4 +45,6 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body - }) \ No newline at end of file + }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 new file mode 100644 index 000000000000..25740fc87531 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 @@ -0,0 +1,97 @@ +using namespace System.Net + +Function Invoke-AddAlert { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value + $Results = foreach ($Tenant in $tenants) { + try { + $TenantID = if ($tenant -ne 'AllTenants') { + (get-tenants | Where-Object -Property defaultDomainName -EQ $Tenant).customerId + } else { + 'AllTenants' + } + if ($Request.body.SetAlerts) { + $CompleteObject = @{ + tenant = $tenant + tenantid = $TenantID + AdminPassword = [bool]$Request.body.AdminPassword + DefenderMalware = [bool]$Request.body.DefenderMalware + DefenderStatus = [bool]$Request.body.DefenderStatus + MFAAdmins = [bool]$Request.body.MFAAdmins + MFAAlertUsers = [bool]$Request.body.MFAAlertUsers + NewGA = [bool]$Request.body.NewGA + NewRole = [bool]$Request.body.NewRole + QuotaUsed = [bool]$Request.body.QuotaUsed + UnusedLicenses = [bool]$Request.body.UnusedLicenses + OverusedLicenses = [bool]$Request.body.OverusedLicenses + AppSecretExpiry = [bool]$Request.body.AppSecretExpiry + ApnCertExpiry = [bool]$Request.body.ApnCertExpiry + VppTokenExpiry = [bool]$Request.body.VppTokenExpiry + DepTokenExpiry = [bool]$Request.body.DepTokenExpiry + NoCAConfig = [bool]$Request.body.NoCAConfig + SecDefaultsUpsell = [bool]$Request.body.SecDefaultsUpsell + SharePointQuota = [bool]$Request.body.SharePointQuota + ExpiringLicenses = [bool]$Request.body.ExpiringLicenses + type = 'Alert' + RowKey = $TenantID + PartitionKey = 'Alert' + } + + $Table = get-cipptable -TableName 'SchedulerConfig' + Add-CIPPAzDataTableEntity @Table -Entity $CompleteObject -Force + } + $URL = ($request.headers.'x-ms-original-url').split('/api') | Select-Object -First 1 + if ($Tenant -eq 'AllTenants') { + Get-Tenants | ForEach-Object { + foreach ($eventType in $Request.body.EventTypes.value) { + $params = @{ + TenantFilter = $_.defaultDomainName + auditLogAPI = $true + operations = ($Request.body.Operations.value -join ',') + allowedLocations = ($Request.body.AllowedLocations.value -join ',') + BaseURL = $URL + EventType = $eventType + ExecutingUser = $Request.headers.'x-ms-client-principal' + } + Push-OutputBinding -Name Subscription -Value $Params + } + } + } else { + foreach ($eventType in $Request.body.EventTypes.value) { + $params = @{ + TenantFilter = $tenant + auditLogAPI = $true + operations = ($Request.body.Operations.value -join ',') + allowedLocations = ($Request.body.AllowedLocations.value -join ',') + BaseURL = $URL + EventType = $eventType + ExecutingUser = $Request.headers.'x-ms-client-principal' + } + New-CIPPGraphSubscription @params + } + } + "Successfully added Alert for $($Tenant) to queue." + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Successfully added Alert for $($Tenant) to queue." -Sev 'Info' + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Failed to add Alert for for $($Tenant) to queue" -Sev 'Error' + "Failed to add Alert for for $($Tenant) to queue $($_.Exception.message)" + } + } + + $body = [pscustomobject]@{'Results' = @($results) } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/AddAutopilotConfig/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAutopilotConfig.ps1 similarity index 94% rename from AddAutopilotConfig/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddAutopilotConfig.ps1 index 4711ab7997d4..f850042fc7d2 100644 --- a/AddAutopilotConfig/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAutopilotConfig.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddAutopilotConfig { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" @@ -68,3 +73,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ }) + + } diff --git a/AddCAPolicy/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddCAPolicy.ps1 similarity index 85% rename from AddCAPolicy/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddCAPolicy.ps1 index ae7ec4a3584d..ab0fcfffb7d3 100644 --- a/AddCAPolicy/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddCAPolicy.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddCAPolicy { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value @@ -30,3 +35,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddCATemplate/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddCATemplate.ps1 similarity index 93% rename from AddCATemplate/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddCATemplate.ps1 index f5d1010676d5..9fcd305891ba 100644 --- a/AddCATemplate/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddCATemplate.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddCATemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $TenantFilter = $Request.Query.TenantFilter @@ -63,3 +68,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddChocoApp/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 similarity index 92% rename from AddChocoApp/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 index 13fced9492e5..d57a4710ced6 100644 --- a/AddChocoApp/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddChocoApp { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" Set-Location (Get-Item $PSScriptRoot).Parent.FullName @@ -56,3 +61,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 new file mode 100644 index 000000000000..41e391fa1e0b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 @@ -0,0 +1,28 @@ + using namespace System.Net + + Function Invoke-AddChocoApp_OrchestrationStarter { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Write-LogMessage -API "ChocoApps" -message "Attempted to start upload but an instance was already running." -sev Info +} +else { + $InstanceId = Start-NewOrchestration -FunctionName 'Applications_Orchestrator' + Write-Host "Started orchestration with ID = '$InstanceId'" + $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId + Write-LogMessage -API "ChocoApps" -message "Started uploading applications to tenants" -sev Info + $Results = [pscustomobject]@{"Results" = "Started application queue" } +} +Write-Host ($Orchestrator | ConvertTo-Json) + + +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $results + }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarterTimer.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarterTimer.ps1 new file mode 100644 index 000000000000..eeb6b13b1e17 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarterTimer.ps1 @@ -0,0 +1,26 @@ + using namespace System.Net + + Function Invoke-AddChocoApp_OrchestrationStarterTimer { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Write-LogMessage -API 'ChocoApps' -message 'Attempted to start upload but an instance was already running.' -sev Info + } + else { + $InstanceId = Start-NewOrchestration -FunctionName 'Applications_Orchestrator' + Write-Host "Started orchestration with ID = '$InstanceId'" + $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Timer -InstanceId $InstanceId + Write-LogMessage -API 'ChocoApps' -message 'Started uploading applications to tenants' -sev Info + $Results = [pscustomobject]@{'Results' = 'Started running analysis' } + } + Write-Host ($Orchestrator | ConvertTo-Json) +} +catch { + Write-Host "AddChocoApp_OrchestratorStarterTimer Exception: $($_.Exception.Message)" +} + + } diff --git a/AddContact/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddContact.ps1 similarity index 88% rename from AddContact/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddContact.ps1 index abc29a96ab10..1c4f2963635d 100644 --- a/AddContact/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddContact.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddContact { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $contactobj = $Request.body @@ -39,3 +44,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + + } diff --git a/AddDefenderDeployment/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddDefenderDeployment.ps1 similarity index 99% rename from AddDefenderDeployment/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddDefenderDeployment.ps1 index de00187282f9..8d2fcc9113b3 100644 --- a/AddDefenderDeployment/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddDefenderDeployment.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddDefenderDeployment { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Tenants = ($Request.body.selectedTenants).defaultDomainName @@ -235,3 +240,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddEnrollment/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddEnrollment.ps1 similarity index 93% rename from AddEnrollment/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddEnrollment.ps1 index e894e094a6f7..512c7ac30956 100644 --- a/AddEnrollment/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddEnrollment.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddEnrollment { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" @@ -55,3 +60,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddExConnector/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 similarity index 83% rename from AddExConnector/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 index 2b6170111c12..fb4e739bfed4 100644 --- a/AddExConnector/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddExConnector { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $ConnectorType = ($Request.body.PowerShellCommand | ConvertFrom-Json).cippConnectorType $RequestParams = $Request.Body.PowerShellCommand | ConvertFrom-Json | Select-Object -Property * -ExcludeProperty GUID, cippConnectorType, comments @@ -26,3 +31,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @{Results = @($Result) } }) + + } diff --git a/AddExConnectorTemplate/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnectorTemplate.ps1 similarity index 91% rename from AddExConnectorTemplate/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnectorTemplate.ps1 index 6f00ce655732..099d51c2a7c0 100644 --- a/AddExConnectorTemplate/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnectorTemplate.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddExConnectorTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" Write-Host ($request | ConvertTo-Json -Compress) @@ -47,3 +52,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddGroup/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroup.ps1 similarity index 94% rename from AddGroup/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroup.ps1 index 88e9d50debda..b52d4091b595 100644 --- a/AddGroup/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroup.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddGroup { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $groupobj = $Request.body @@ -65,4 +70,6 @@ $body = [pscustomobject]@{"Results" = @($results) } Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body - }) \ No newline at end of file + }) + + } diff --git a/AddGroupTemplate/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroupTemplate.ps1 similarity index 87% rename from AddGroupTemplate/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroupTemplate.ps1 index bf53245a4d57..19ec092ca49b 100644 --- a/AddGroupTemplate/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGroupTemplate.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddGroupTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $GUID = (New-Guid).GUID @@ -41,3 +46,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddGuest/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGuest.ps1 similarity index 91% rename from AddGuest/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddGuest.ps1 index 9e340718d370..4c94ec4740f9 100644 --- a/AddGuest/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddGuest.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddGuest { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Results = [System.Collections.ArrayList]@() $userobj = $Request.body @@ -47,3 +52,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + + } diff --git a/AddIntuneTemplate/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddIntuneTemplate.ps1 similarity index 96% rename from AddIntuneTemplate/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddIntuneTemplate.ps1 index 1d5810379230..281c482d4d42 100644 --- a/AddIntuneTemplate/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddIntuneTemplate.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddIntuneTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $GUID = (New-Guid).GUID @@ -120,3 +125,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddMSPApp/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 similarity index 95% rename from AddMSPApp/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 index 133a12ed7974..c920e5cfe32f 100644 --- a/AddMSPApp/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddMSPApp { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' Set-Location (Get-Item $PSScriptRoot).Parent.FullName @@ -89,3 +94,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddNamedLocation/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddNamedLocation.ps1 similarity index 91% rename from AddNamedLocation/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddNamedLocation.ps1 index 723b573c8bb3..48556531ca75 100644 --- a/AddNamedLocation/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddNamedLocation.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddNamedLocation { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" @@ -55,3 +60,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddOfficeApp/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddOfficeApp.ps1 similarity index 97% rename from AddOfficeApp/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddOfficeApp.ps1 index 7d87c7cca41f..732aa7dc231a 100644 --- a/AddOfficeApp/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddOfficeApp.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddOfficeApp { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" @@ -93,3 +98,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ }) + + } diff --git a/AddPolicy/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddPolicy.ps1 similarity index 94% rename from AddPolicy/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddPolicy.ps1 index e3fdeb1b12d6..4884caffb04b 100644 --- a/AddPolicy/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddPolicy.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddPolicy { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value @@ -73,3 +78,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 new file mode 100644 index 000000000000..3c7406d35503 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 @@ -0,0 +1,16 @@ + using namespace System.Net + + Function Invoke-AddScheduledItem { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ Results = $Result } + }) + + } diff --git a/AddSharedMailbox/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddSharedMailbox.ps1 similarity index 86% rename from AddSharedMailbox/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddSharedMailbox.ps1 index 35f242fb479c..32596817183b 100644 --- a/AddSharedMailbox/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddSharedMailbox.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddSharedMailbox { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $groupobj = $Request.body @@ -38,3 +43,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + + } diff --git a/AddSpamFilter/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilter.ps1 similarity index 89% rename from AddSpamFilter/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilter.ps1 index 5b4cff20e52f..e9be0668bd97 100644 --- a/AddSpamFilter/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilter.ps1 @@ -1,9 +1,14 @@ + using namespace System.Net -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddSpamFilter { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" @@ -35,3 +40,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @{Results = @($Result) } }) + + } diff --git a/AddSpamFilterTemplate/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilterTemplate.ps1 similarity index 92% rename from AddSpamFilterTemplate/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilterTemplate.ps1 index e8a6175b4cf8..45fae4ecbfc0 100644 --- a/AddSpamFilterTemplate/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddSpamFilterTemplate.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddSpamFilterTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" Write-Host ($request | ConvertTo-Json -Compress) @@ -42,3 +47,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddStandardsDeploy/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddStandardsDeploy.ps1 similarity index 86% rename from AddStandardsDeploy/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddStandardsDeploy.ps1 index a62ed9102df7..fda5dbc33053 100644 --- a/AddStandardsDeploy/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddStandardsDeploy.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddStandardsDeploy { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $user = $request.headers.'x-ms-client-principal' @@ -41,3 +46,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddTeam/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddTeam.ps1 similarity index 90% rename from AddTeam/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddTeam.ps1 index b7750696392c..d11998bc4260 100644 --- a/AddTeam/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddTeam.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddTeam { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $userobj = $Request.body @@ -49,3 +54,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + + } diff --git a/AddTransportRule/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportRule.ps1 similarity index 89% rename from AddTransportRule/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportRule.ps1 index 173f2662cc3f..4a5ddf22abbc 100644 --- a/AddTransportRule/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportRule.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddTransportRule { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $RequestParams = $Request.Body.PowerShellCommand | ConvertFrom-Json | Select-Object -Property * -ExcludeProperty GUID, Comments, HasSenderOverride, ExceptIfHasSenderOverride, ExceptIfMessageContainsDataClassifications, MessageContainsDataClassifications @@ -37,3 +42,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @{Results = @($Result) } }) + + } diff --git a/AddTransportTemplate/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportTemplate.ps1 similarity index 96% rename from AddTransportTemplate/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportTemplate.ps1 index 551074fd1761..43d8b28b13cd 100644 --- a/AddTransportTemplate/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddTransportTemplate.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddTransportTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" Write-Host ($request | ConvertTo-Json -Compress) @@ -42,3 +47,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/AddUser/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 similarity index 96% rename from AddUser/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 index 7de7b212c642..f9eb6ba28546 100644 --- a/AddUser/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddUser { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Results = [System.Collections.ArrayList]@() $userobj = $Request.body @@ -112,3 +117,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + + } diff --git a/AddWinGetApp/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 similarity index 91% rename from AddWinGetApp/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 index a84e5ac41b03..1dbc7e279c8c 100644 --- a/AddWinGetApp/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-AddWinGetApp { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" Set-Location (Get-Item $PSScriptRoot).Parent.FullName @@ -57,3 +62,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-BestPracticeAnalyser_List.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-BestPracticeAnalyser_List.ps1 new file mode 100644 index 000000000000..82f02778a5f2 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-BestPracticeAnalyser_List.ps1 @@ -0,0 +1,33 @@ + using namespace System.Net + + Function Invoke-BestPracticeAnalyser_List { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName +Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + +$Tenants = Get-Tenants +$Table = get-cipptable 'cachebpa' +$Results = (Get-CIPPAzDataTableEntity @Table) | ForEach-Object { + $_.UnusedLicenseList = @(ConvertFrom-Json -ErrorAction silentlycontinue -InputObject $_.UnusedLicenseList) + $_ +} + +if (!$Results) { + $Results = @{ + Tenant = "The BPA has not yet run." + } +} +Write-Host ($Tenants | ConvertTo-Json) +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @(($Results | Where-Object -Property RowKey -In $Tenants.customerId)) + }) + + } diff --git a/Z_CIPPHttpTrigger/function.json b/Z_CIPPHttpTrigger/function.json index 8bcb9e5eb526..9abea99584fd 100644 --- a/Z_CIPPHttpTrigger/function.json +++ b/Z_CIPPHttpTrigger/function.json @@ -21,6 +21,12 @@ "name": "QueueItem", "queueName": "CIPPGenericQueue" }, + { + "type": "queue", + "direction": "out", + "name": "Subscription", + "queueName": "AlertSubscriptions" + }, { "name": "starter", "type": "durableClient", From 5a0d44b1612dbf5426661157fd60c44f2e96bf08 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 00:36:17 +0100 Subject: [PATCH 49/77] fixes orch start --- ...nvoke-AddChocoApp_OrchestrationStarter.ps1 | 26 +++++++++---------- ...-AddChocoApp_OrchestrationStarterTimer.ps1 | 26 ------------------- 2 files changed, 12 insertions(+), 40 deletions(-) delete mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarterTimer.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 index 41e391fa1e0b..fbbcb29a1dce 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarter.ps1 @@ -1,6 +1,6 @@ - using namespace System.Net +using namespace System.Net - Function Invoke-AddChocoApp_OrchestrationStarter { +Function Invoke-AddChocoApp_OrchestrationStarter { <# .FUNCTIONALITY Entrypoint @@ -8,21 +8,19 @@ [CmdletBinding()] param($Request, $TriggerMetadata) - Write-LogMessage -API "ChocoApps" -message "Attempted to start upload but an instance was already running." -sev Info -} -else { + Write-LogMessage -API 'ChocoApps' -message 'Attempted to start upload but an instance was already running.' -sev Info $InstanceId = Start-NewOrchestration -FunctionName 'Applications_Orchestrator' Write-Host "Started orchestration with ID = '$InstanceId'" $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId - Write-LogMessage -API "ChocoApps" -message "Started uploading applications to tenants" -sev Info - $Results = [pscustomobject]@{"Results" = "Started application queue" } -} -Write-Host ($Orchestrator | ConvertTo-Json) + Write-LogMessage -API 'ChocoApps' -message 'Started uploading applications to tenants' -sev Info + $Results = [pscustomobject]@{'Results' = 'Started application queue' } + Write-Host ($Orchestrator | ConvertTo-Json) -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $results - }) - } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $results + }) + +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarterTimer.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarterTimer.ps1 deleted file mode 100644 index eeb6b13b1e17..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp_OrchestrationStarterTimer.ps1 +++ /dev/null @@ -1,26 +0,0 @@ - using namespace System.Net - - Function Invoke-AddChocoApp_OrchestrationStarterTimer { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - Write-LogMessage -API 'ChocoApps' -message 'Attempted to start upload but an instance was already running.' -sev Info - } - else { - $InstanceId = Start-NewOrchestration -FunctionName 'Applications_Orchestrator' - Write-Host "Started orchestration with ID = '$InstanceId'" - $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Timer -InstanceId $InstanceId - Write-LogMessage -API 'ChocoApps' -message 'Started uploading applications to tenants' -sev Info - $Results = [pscustomobject]@{'Results' = 'Started running analysis' } - } - Write-Host ($Orchestrator | ConvertTo-Json) -} -catch { - Write-Host "AddChocoApp_OrchestratorStarterTimer Exception: $($_.Exception.Message)" -} - - } From ed3dd0a73ba4411a22fbe73345beed957302ce36 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 01:10:55 +0100 Subject: [PATCH 50/77] Replace all Edit Functions --- EditCAPolicy/function.json | 19 ------------------- EditExConnector/function.json | 19 ------------------- EditGroup/function.json | 19 ------------------- EditPolicy/function.json | 19 ------------------- EditSpamFilter/function.json | 19 ------------------- EditTenant/function.json | 19 ------------------- EditTransportRule/function.json | 19 ------------------- EditUser/function.json | 19 ------------------- .../Entrypoints/Invoke-EditCAPolicy.ps1 | 15 +++++++++++---- .../Entrypoints/Invoke-EditExConnector.ps1 | 15 +++++++++++---- .../Public/Entrypoints/Invoke-EditGroup.ps1 | 15 +++++++++++---- .../Public/Entrypoints/Invoke-EditPolicy.ps1 | 15 +++++++++++---- .../Entrypoints/Invoke-EditSpamFilter.ps1 | 15 +++++++++++---- .../Public/Entrypoints/Invoke-EditTenant.ps1 | 15 +++++++++++---- .../Entrypoints/Invoke-EditTransportRule.ps1 | 15 +++++++++++---- .../Public/Entrypoints/Invoke-EditUser.ps1 | 15 +++++++++++---- 16 files changed, 88 insertions(+), 184 deletions(-) delete mode 100644 EditCAPolicy/function.json delete mode 100644 EditExConnector/function.json delete mode 100644 EditGroup/function.json delete mode 100644 EditPolicy/function.json delete mode 100644 EditSpamFilter/function.json delete mode 100644 EditTenant/function.json delete mode 100644 EditTransportRule/function.json delete mode 100644 EditUser/function.json rename EditCAPolicy/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 (83%) rename EditExConnector/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-EditExConnector.ps1 (82%) rename EditGroup/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-EditGroup.ps1 (97%) rename EditPolicy/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-EditPolicy.ps1 (90%) rename EditSpamFilter/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-EditSpamFilter.ps1 (81%) rename EditTenant/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 (92%) rename EditTransportRule/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 (81%) rename EditUser/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 (96%) diff --git a/EditCAPolicy/function.json b/EditCAPolicy/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/EditCAPolicy/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/EditExConnector/function.json b/EditExConnector/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/EditExConnector/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/EditGroup/function.json b/EditGroup/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/EditGroup/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/EditPolicy/function.json b/EditPolicy/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/EditPolicy/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/EditSpamFilter/function.json b/EditSpamFilter/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/EditSpamFilter/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/EditTenant/function.json b/EditTenant/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/EditTenant/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/EditTransportRule/function.json b/EditTransportRule/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/EditTransportRule/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/EditUser/function.json b/EditUser/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/EditUser/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/EditCAPolicy/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 similarity index 83% rename from EditCAPolicy/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 index 98856573fb0b..0820377f6ed1 100644 --- a/EditCAPolicy/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-EditCAPolicy { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Tenant = $request.query.tenantFilter @@ -27,3 +32,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/EditExConnector/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditExConnector.ps1 similarity index 82% rename from EditExConnector/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-EditExConnector.ps1 index 9c53f3e6e555..89a13dbb80bc 100644 --- a/EditExConnector/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditExConnector.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-EditExConnector { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Tenantfilter = $request.Query.tenantfilter @@ -28,3 +33,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @{Results = $Result } }) + + } diff --git a/EditGroup/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditGroup.ps1 similarity index 97% rename from EditGroup/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-EditGroup.ps1 index ed3f3afa4f7d..7cce727e5294 100644 --- a/EditGroup/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditGroup.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-EditGroup { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Results = [System.Collections.ArrayList]@() @@ -178,3 +183,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + + } diff --git a/EditPolicy/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditPolicy.ps1 similarity index 90% rename from EditPolicy/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-EditPolicy.ps1 index 5bf4dbd5f09a..076ecde86889 100644 --- a/EditPolicy/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditPolicy.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-EditPolicy { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $user = $request.headers.'x-ms-client-principal' @@ -36,3 +41,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/EditSpamFilter/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditSpamFilter.ps1 similarity index 81% rename from EditSpamFilter/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-EditSpamFilter.ps1 index 72ec5aa10967..7138186ae76b 100644 --- a/EditSpamFilter/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditSpamFilter.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-EditSpamFilter { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Tenantfilter = $request.Query.tenantfilter @@ -27,3 +32,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @{Results = $Result } }) + + } diff --git a/EditTenant/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 similarity index 92% rename from EditTenant/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 index 8a0427aaf662..5f9ab0b06682 100644 --- a/EditTenant/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-EditTenant { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $tenantDisplayName = $request.body.displayName @@ -55,3 +60,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/EditTransportRule/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 similarity index 81% rename from EditTransportRule/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 index 9228d09212bd..4798cfe16c0f 100644 --- a/EditTransportRule/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-EditTransportRule { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Tenantfilter = $request.Query.tenantfilter @@ -27,3 +32,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @{Results = $Result } }) + + } diff --git a/EditUser/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 similarity index 96% rename from EditUser/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 index 9c414e521610..676b8f9cd53e 100644 --- a/EditUser/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-EditUser { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $userobj = $Request.body $Results = [System.Collections.ArrayList]@() @@ -108,3 +113,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + + } From 4e60a18f3a7ed685089a84b2aa514801501bc1b9 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 01:11:32 +0100 Subject: [PATCH 51/77] layout --- .../Entrypoints/Invoke-EditCAPolicy.ps1 | 47 +++-- .../Public/Entrypoints/Invoke-EditTenant.ps1 | 92 +++++---- .../Entrypoints/Invoke-EditTransportRule.ps1 | 47 +++-- .../Public/Entrypoints/Invoke-EditUser.ps1 | 185 +++++++++--------- 4 files changed, 181 insertions(+), 190 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 index 0820377f6ed1..da71838797bc 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditCAPolicy.ps1 @@ -1,6 +1,6 @@ - using namespace System.Net +using namespace System.Net - Function Invoke-EditCAPolicy { +Function Invoke-EditCAPolicy { <# .FUNCTIONALITY Entrypoint @@ -8,29 +8,28 @@ [CmdletBinding()] param($Request, $TriggerMetadata) - $APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -$Tenant = $request.query.tenantFilter -$ID = $request.query.guid -$results = try { - $EditBody = "{`"state`": `"$($request.query.state)`"}" - $Request = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta//identity/conditionalAccess/policies/$($id)" -tenantid $tenant -type PATCH -body $EditBody - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Edited CA policy $($ID)" -Sev "Error" - "Successfully edited CA policy" -} -catch { - "Failed to add CA policy: $($_.Exception.Message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Failed editing CA policy $($ID). Error: $($_.Exception.Message)" -Sev "Error" - continue -} + $Tenant = $request.query.tenantFilter + $ID = $request.query.guid + $results = try { + $EditBody = "{`"state`": `"$($request.query.state)`"}" + $Request = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta//identity/conditionalAccess/policies/$($id)" -tenantid $tenant -type PATCH -body $EditBody + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Edited CA policy $($ID)" -Sev 'Error' + 'Successfully edited CA policy' + } catch { + "Failed to add CA policy: $($_.Exception.Message)" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Failed editing CA policy $($ID). Error: $($_.Exception.Message)" -Sev 'Error' + continue + } -$body = [pscustomobject]@{"Results" = $results } + $body = [pscustomobject]@{'Results' = $results } -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) - } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 index 5f9ab0b06682..0c10dc387b08 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTenant.ps1 @@ -1,6 +1,6 @@ - using namespace System.Net +using namespace System.Net - Function Invoke-EditTenant { +Function Invoke-EditTenant { <# .FUNCTIONALITY Entrypoint @@ -8,57 +8,53 @@ [CmdletBinding()] param($Request, $TriggerMetadata) - $APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$tenantDisplayName = $request.body.displayName -$tenantDefaultDomainName = $request.body.defaultDomainName -$Tenant = $request.body.tenantid -$customerContextId = $request.body.customerId - -$tokens = try { - $AADGraphtoken = (Get-GraphToken -scope 'https://graph.windows.net/.default') - $allTenantsDetails = (Invoke-RestMethod -Method GET -Uri 'https://graph.windows.net/myorganization/contracts?api-version=1.6' -ContentType 'application/json' -Headers $AADGraphtoken) - $tenantObjectId = $allTenantsDetails.value | Where-Object { $_.customerContextId -eq $customerContextId } | Select-Object 'objectId' -} -catch { - $Results = "Failed to retrieve list of tenants. Error: $($_.Exception.Message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantDisplayName) -message "Failed to retrieve list of tenants. Error: $($_.Exception.Message)" -Sev 'Error' -} + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $tenantDisplayName = $request.body.displayName + $tenantDefaultDomainName = $request.body.defaultDomainName + $Tenant = $request.body.tenantid + $customerContextId = $request.body.customerId + + $tokens = try { + $AADGraphtoken = (Get-GraphToken -scope 'https://graph.windows.net/.default') + $allTenantsDetails = (Invoke-RestMethod -Method GET -Uri 'https://graph.windows.net/myorganization/contracts?api-version=1.6' -ContentType 'application/json' -Headers $AADGraphtoken) + $tenantObjectId = $allTenantsDetails.value | Where-Object { $_.customerContextId -eq $customerContextId } | Select-Object 'objectId' + } catch { + $Results = "Failed to retrieve list of tenants. Error: $($_.Exception.Message)" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantDisplayName) -message "Failed to retrieve list of tenants. Error: $($_.Exception.Message)" -Sev 'Error' + } -if ($tenantObjectId) { - try { - $bodyToPatch = '{"displayName":"' + $tenantDisplayName + '","defaultDomainName":"' + $tenantDefaultDomainName + '"}' - $patchTenant = (Invoke-RestMethod -Method PATCH -Uri "https://graph.windows.net/myorganization/contracts/$($tenantObjectId.objectId)?api-version=1.6" -Body $bodyToPatch -ContentType 'application/json' -Headers $AADGraphtoken -ErrorAction Stop) - $Filter = "PartitionKey eq 'Tenants' and defaultDomainName eq '{0}'" -f $tenantDefaultDomainName + if ($tenantObjectId) { try { - $TenantsTable = Get-CippTable -tablename Tenants - $Tenant = Get-CIPPAzDataTableEntity @TenantsTable -Filter $Filter - $Tenant.displayName = $tenantDisplayName - Update-AzDataTableEntity @TenantsTable -Entity $Tenant - } - catch { - $AddedText = "but could not edit the tenant cache. Clear the tenant cache to display the updated details" + $bodyToPatch = '{"displayName":"' + $tenantDisplayName + '","defaultDomainName":"' + $tenantDefaultDomainName + '"}' + $patchTenant = (Invoke-RestMethod -Method PATCH -Uri "https://graph.windows.net/myorganization/contracts/$($tenantObjectId.objectId)?api-version=1.6" -Body $bodyToPatch -ContentType 'application/json' -Headers $AADGraphtoken -ErrorAction Stop) + $Filter = "PartitionKey eq 'Tenants' and defaultDomainName eq '{0}'" -f $tenantDefaultDomainName + try { + $TenantsTable = Get-CippTable -tablename Tenants + $Tenant = Get-CIPPAzDataTableEntity @TenantsTable -Filter $Filter + $Tenant.displayName = $tenantDisplayName + Update-AzDataTableEntity @TenantsTable -Entity $Tenant + } catch { + $AddedText = 'but could not edit the tenant cache. Clear the tenant cache to display the updated details' + } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenantDisplayName -message "Edited tenant $tenantDisplayName" -Sev 'Info' + $results = "Successfully amended details for $($Tenant.displayName) $AddedText" + } catch { + $results = "Failed to amend details for $tenantDisplayName : $($_.Exception.Message)" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenantDisplayName -message "Failed amending details $tenantDisplayName. Error: $($_.Exception.Message)" -Sev 'Error' } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenantDisplayName -message "Edited tenant $tenantDisplayName" -Sev 'Info' - $results = "Successfully amended details for $($Tenant.displayName) $AddedText" + } else { + $Results = 'Could not find the tenant to edit in the contract endpoint. Please ensure you have a reseller relationship with the tenant you are trying to edit.' } - catch { - $results = "Failed to amend details for $tenantDisplayName : $($_.Exception.Message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenantDisplayName -message "Failed amending details $tenantDisplayName. Error: $($_.Exception.Message)" -Sev 'Error' - } -} -else { - $Results = "Could not find the tenant to edit in the contract endpoint. Please ensure you have a reseller relationship with the tenant you are trying to edit." -} -$body = [pscustomobject]@{'Results' = $results } + $body = [pscustomobject]@{'Results' = $results } -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) - } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 index 4798cfe16c0f..11c669a95358 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditTransportRule.ps1 @@ -1,6 +1,6 @@ - using namespace System.Net +using namespace System.Net - Function Invoke-EditTransportRule { +Function Invoke-EditTransportRule { <# .FUNCTIONALITY Entrypoint @@ -8,29 +8,28 @@ [CmdletBinding()] param($Request, $TriggerMetadata) - $APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Tenantfilter = $request.Query.tenantfilter + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Tenantfilter = $request.Query.tenantfilter -$Params = @{ - Identity = $request.query.guid -} - -try { - $cmdlet = if ($request.query.state -eq "enable") { "Enable-TransportRule" } else { "Disable-TransportRule" } - $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params -UseSystemMailbox $true - $Result = "Set transport rule $($Request.query.guid) to $($request.query.State)" - Write-LogMessage -API "TransportRules" -tenant $tenantfilter -message "Set transport rule $($Request.query.guid) to $($request.query.State)" -sev Debug -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception - $Result = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{Results = $Result } - }) + $Params = @{ + Identity = $request.query.guid + } + try { + $cmdlet = if ($request.query.state -eq 'enable') { 'Enable-TransportRule' } else { 'Disable-TransportRule' } + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params -UseSystemMailbox $true + $Result = "Set transport rule $($Request.query.guid) to $($request.query.State)" + Write-LogMessage -API 'TransportRules' -tenant $tenantfilter -message "Set transport rule $($Request.query.guid) to $($request.query.State)" -sev Debug + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception + $Result = $ErrorMessage } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = $Result } + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 index 676b8f9cd53e..996352c449dd 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-EditUser.ps1 @@ -1,6 +1,6 @@ - using namespace System.Net +using namespace System.Net - Function Invoke-EditUser { +Function Invoke-EditUser { <# .FUNCTIONALITY Entrypoint @@ -8,110 +8,107 @@ [CmdletBinding()] param($Request, $TriggerMetadata) - $APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$userobj = $Request.body -$Results = [System.Collections.ArrayList]@() -$licenses = ($userobj | Select-Object "License_*").psobject.properties.value -$Aliases = if ($userobj.AddedAliases) { ($userobj.AddedAliases).Split([Environment]::NewLine) } + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $userobj = $Request.body + $Results = [System.Collections.ArrayList]@() + $licenses = ($userobj | Select-Object 'License_*').psobject.properties.value + $Aliases = if ($userobj.AddedAliases) { ($userobj.AddedAliases).Split([Environment]::NewLine) } -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." -#Edit the user -try { - Write-Host "$([boolean]$UserObj.mustchangepass)" - $Email = "$($UserObj.username)@$($UserObj.domain)" - $UserprincipalName = "$($UserObj.username)@$($UserObj.domain)" - $BodyToship = [pscustomobject] @{ - "givenName" = $userobj.firstname - "surname" = $userobj.lastname - "city" = $userobj.city - "country" = $userobj.country - "department" = $userobj.department - "displayName" = $UserObj.Displayname - "postalCode" = $userobj.postalCode - "companyName" = $userobj.companyName - "mailNickname" = $UserObj.username - "jobTitle" = $UserObj.JobTitle - "userPrincipalName" = $Email - "usageLocation" = $UserObj.usageLocation - "mobilePhone" = $userobj.mobilePhone - "streetAddress" = $userobj.streetAddress - "businessPhones" = @($userobj.businessPhone) - "passwordProfile" = @{ - "forceChangePasswordNextSignIn" = [boolean]$UserObj.mustchangepass + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + #Edit the user + try { + Write-Host "$([boolean]$UserObj.mustchangepass)" + $Email = "$($UserObj.username)@$($UserObj.domain)" + $UserprincipalName = "$($UserObj.username)@$($UserObj.domain)" + $BodyToship = [pscustomobject] @{ + 'givenName' = $userobj.firstname + 'surname' = $userobj.lastname + 'city' = $userobj.city + 'country' = $userobj.country + 'department' = $userobj.department + 'displayName' = $UserObj.Displayname + 'postalCode' = $userobj.postalCode + 'companyName' = $userobj.companyName + 'mailNickname' = $UserObj.username + 'jobTitle' = $UserObj.JobTitle + 'userPrincipalName' = $Email + 'usageLocation' = $UserObj.usageLocation + 'mobilePhone' = $userobj.mobilePhone + 'streetAddress' = $userobj.streetAddress + 'businessPhones' = @($userobj.businessPhone) + 'passwordProfile' = @{ + 'forceChangePasswordNextSignIn' = [boolean]$UserObj.mustchangepass + } + } | ForEach-Object { + $NonEmptyProperties = $_.psobject.Properties | Select-Object -ExpandProperty Name + $_ | Select-Object -Property $NonEmptyProperties | ConvertTo-Json } - } | ForEach-Object { - $NonEmptyProperties = $_.psobject.Properties | Select-Object -ExpandProperty Name - $_ | Select-Object -Property $NonEmptyProperties | ConvertTo-Json - } - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type PATCH -body $BodyToship -verbose - $results.add( "Success. The user has been edited." ) - Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Edited user $($userobj.displayname) with id $($userobj.Userid)" -Sev "Info" - if ($userobj.password) { - $passwordProfile = [pscustomobject]@{"passwordProfile" = @{ "password" = $userobj.password; "forceChangePasswordNextSignIn" = [boolean]$UserObj.mustchangepass } } | ConvertTo-Json - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type PATCH -body $PasswordProfile -verbose - $results.add("Success. The password has been set to $($userobj.password)") - Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Reset $($userobj.displayname)'s Password" -Sev "Info" + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type PATCH -body $BodyToship -verbose + $results.add( 'Success. The user has been edited.' ) + Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Edited user $($userobj.displayname) with id $($userobj.Userid)" -Sev 'Info' + if ($userobj.password) { + $passwordProfile = [pscustomobject]@{'passwordProfile' = @{ 'password' = $userobj.password; 'forceChangePasswordNextSignIn' = [boolean]$UserObj.mustchangepass } } | ConvertTo-Json + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type PATCH -body $PasswordProfile -verbose + $results.add("Success. The password has been set to $($userobj.password)") + Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Reset $($userobj.displayname)'s Password" -Sev 'Info' + } + } catch { + Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "User edit API failed. $($_.Exception.Message)" -Sev 'Error' + $results.add( "Failed to edit user. $($_.Exception.Message)") } -} -catch { - Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "User edit API failed. $($_.Exception.Message)" -Sev "Error" - $results.add( "Failed to edit user. $($_.Exception.Message)") -} -#Reassign the licenses -try { + #Reassign the licenses + try { - if ($licenses -or $userobj.RemoveAllLicenses) { - $licenses = (($userobj | Select-Object "License_*").psobject.properties | Where-Object { $_.value -EQ $true }).name -replace "License_", "" - $CurrentLicenses = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid - $RemovalList = ($CurrentLicenses.assignedLicenses | Where-Object -Property skuid -NotIn $licenses).skuid - $LicensesToRemove = if ($RemovalList) { ConvertTo-Json @( $RemovalList ) } else { "[]" } + if ($licenses -or $userobj.RemoveAllLicenses) { + $licenses = (($userobj | Select-Object 'License_*').psobject.properties | Where-Object { $_.value -EQ $true }).name -replace 'License_', '' + $CurrentLicenses = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid + $RemovalList = ($CurrentLicenses.assignedLicenses | Where-Object -Property skuid -NotIn $licenses).skuid + $LicensesToRemove = if ($RemovalList) { ConvertTo-Json @( $RemovalList ) } else { '[]' } - $liclist = foreach ($license in $Licenses) { '{"disabledPlans": [],"skuId": "' + $license + '" },' } - $LicenseBody = '{"addLicenses": [' + $LicList + '], "removeLicenses": ' + $LicensesToRemove + '}' - if ($userobj.RemoveAllLicenses) { $LicenseBody = '{"addLicenses": [], "removeLicenses": ' + $LicensesToRemove + '}' } - Write-Host $LicenseBody - $LicRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)/assignlicense" -tenantid $Userobj.tenantid -type POST -body $LicenseBody -verbose + $liclist = foreach ($license in $Licenses) { '{"disabledPlans": [],"skuId": "' + $license + '" },' } + $LicenseBody = '{"addLicenses": [' + $LicList + '], "removeLicenses": ' + $LicensesToRemove + '}' + if ($userobj.RemoveAllLicenses) { $LicenseBody = '{"addLicenses": [], "removeLicenses": ' + $LicensesToRemove + '}' } + Write-Host $LicenseBody + $LicRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)/assignlicense" -tenantid $Userobj.tenantid -type POST -body $LicenseBody -verbose - Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Changed user $($userobj.displayname) license. Sent info: $licensebody" -Sev "Info" - $results.add( "Success. User license has been edited." ) - } + Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Changed user $($userobj.displayname) license. Sent info: $licensebody" -Sev 'Info' + $results.add( 'Success. User license has been edited.' ) + } -} -catch { - Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "License assign API failed. $($_.Exception.Message)" -Sev "Error" - $results.add( "We've failed to assign the license. $($_.Exception.Message)") -} + } catch { + Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "License assign API failed. $($_.Exception.Message)" -Sev 'Error' + $results.add( "We've failed to assign the license. $($_.Exception.Message)") + } -#Add Aliases, removal currently not supported. -try { - if ($Aliases) { - foreach ($Alias in $Aliases) { - New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type "patch" -body "{`"mail`": `"$Alias`"}" -verbose + #Add Aliases, removal currently not supported. + try { + if ($Aliases) { + foreach ($Alias in $Aliases) { + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type 'patch' -body "{`"mail`": `"$Alias`"}" -verbose + } + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type 'patch' -body "{`"mail`": `"$UserprincipalName`"}" -verbose + Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Added Aliases to $($userobj.displayname)" -Sev 'Info' + $results.add( 'Success. added aliasses to user.') } - New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type "patch" -body "{`"mail`": `"$UserprincipalName`"}" -verbose - Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Added Aliases to $($userobj.displayname)" -Sev "Info" - $results.add( "Success. added aliasses to user.") + + } catch { + Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Alias API failed. $($_.Exception.Message)" -Sev 'Error' + $results.add( "Successfully edited user. The password is $password. We've failed to create the Aliases: $($_.Exception.Message)") } -} -catch { - Write-LogMessage -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Alias API failed. $($_.Exception.Message)" -Sev "Error" - $results.add( "Successfully edited user. The password is $password. We've failed to create the Aliases: $($_.Exception.Message)") -} + if ($Request.body.CopyFrom -ne '') { + $CopyFrom = Set-CIPPCopyGroupMembers -ExecutingUser $request.headers.'x-ms-client-principal' -tenantid $Userobj.tenantid -CopyFromId $Request.body.CopyFrom -UserID $UserprincipalName -TenantFilter $Userobj.tenantid + $results.AddRange($CopyFrom) + } + $body = @{'Results' = @($results) } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) -if ($Request.body.CopyFrom -ne "") { - $CopyFrom = Set-CIPPCopyGroupMembers -ExecutingUser $request.headers.'x-ms-client-principal' -tenantid $Userobj.tenantid -CopyFromId $Request.body.CopyFrom -UserID $UserprincipalName -TenantFilter $Userobj.tenantid - $results.AddRange($CopyFrom) } -$body = @{"Results" = @($results) } -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) - - } From 9e69ebc0aa2750a40b251b66b039ae6d8b5fe734 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 01:17:26 +0100 Subject: [PATCH 52/77] replace all removes with entrypoints --- .../CIPPCore/Public/Invoke-RemoveAPDevice.ps1 | 41 +++++++++++++++++++ Modules/CIPPCore/Public/Invoke-RemoveApp.ps1 | 37 +++++++++++++++++ .../CIPPCore/Public/Invoke-RemoveCAPolicy.ps1 | 35 ++++++++++++++++ .../Public/Invoke-RemoveCATemplate.ps1 | 36 ++++++++++++++++ .../CIPPCore/Public/Invoke-RemoveContact.ps1 | 36 ++++++++++++++++ .../Public/Invoke-RemoveExConnector.ps1 | 36 ++++++++++++++++ .../Invoke-RemoveExConnectorTemplate.ps1 | 35 ++++++++++++++++ .../Public/Invoke-RemoveGroupTemplate.ps1 | 38 +++++++++++++++++ .../Public/Invoke-RemoveIntuneTemplate.ps1 | 38 +++++++++++++++++ .../CIPPCore/Public/Invoke-RemovePolicy.ps1 | 39 ++++++++++++++++++ .../Public/Invoke-RemoveQueuedAlert.ps1 | 33 +++++++++++++++ .../Public/Invoke-RemoveQueuedApp.ps1 | 35 ++++++++++++++++ .../Public/Invoke-RemoveScheduledItem.ps1 | 19 ++++++--- .../Public/Invoke-RemoveSpamfilter.ps1 | 37 +++++++++++++++++ .../Invoke-RemoveSpamfilterTemplate.ps1 | 35 ++++++++++++++++ .../CIPPCore/Public/Invoke-RemoveStandard.ps1 | 37 +++++++++++++++++ .../Public/Invoke-RemoveTransportRule.ps1 | 35 ++++++++++++++++ .../Invoke-RemoveTransportRuleTemplate.ps1 | 35 ++++++++++++++++ Modules/CIPPCore/Public/Invoke-RemoveUser.ps1 | 37 +++++++++++++++++ .../Public/Invoke-RemoveWebhookAlert.ps1 | 30 ++++++++++++++ RemoveAPDevice/function.json | 19 --------- RemoveAPDevice/run.ps1 | 36 ---------------- RemoveApp/function.json | 19 --------- RemoveApp/run.ps1 | 31 -------------- RemoveCAPolicy/function.json | 19 --------- RemoveCAPolicy/run.ps1 | 29 ------------- RemoveCATemplate/function.json | 19 --------- RemoveCATemplate/run.ps1 | 29 ------------- RemoveCippQueue/function.json | 18 -------- RemoveContact/function.json | 19 --------- RemoveContact/run.ps1 | 30 -------------- RemoveExConnector/function.json | 19 --------- RemoveExConnector/run.ps1 | 30 -------------- RemoveExConnectorTemplate/function.json | 19 --------- RemoveExConnectorTemplate/run.ps1 | 28 ------------- RemoveGroupTemplate/function.json | 19 --------- RemoveGroupTemplate/run.ps1 | 31 -------------- RemoveIntuneTemplate/function.json | 19 --------- RemoveIntuneTemplate/run.ps1 | 31 -------------- RemovePolicy/function.json | 19 --------- RemovePolicy/run.ps1 | 33 --------------- RemoveQueuedAlert/function.json | 19 --------- RemoveQueuedAlert/run.ps1 | 26 ------------ RemoveQueuedApp/function.json | 19 --------- RemoveQueuedApp/run.ps1 | 28 ------------- RemoveScheduledItem/function.json | 19 --------- RemoveSpamfilter/function.json | 19 --------- RemoveSpamfilter/run.ps1 | 31 -------------- RemoveSpamfilterTemplate/function.json | 19 --------- RemoveSpamfilterTemplate/run.ps1 | 28 ------------- RemoveStandard/function.json | 19 --------- RemoveStandard/run.ps1 | 30 -------------- RemoveTransportRule/function.json | 19 --------- RemoveTransportRule/run.ps1 | 29 ------------- RemoveTransportRuleTemplate/function.json | 19 --------- RemoveTransportRuleTemplate/run.ps1 | 28 ------------- RemoveUser/function.json | 19 --------- RemoveUser/run.ps1 | 31 -------------- RemoveWebhookAlert/function.json | 19 --------- RemoveWebhookAlert/run.ps1 | 24 ----------- 60 files changed, 698 insertions(+), 967 deletions(-) create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveAPDevice.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveApp.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveCAPolicy.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveCATemplate.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveContact.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveExConnector.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveExConnectorTemplate.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveGroupTemplate.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveIntuneTemplate.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemovePolicy.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveQueuedAlert.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveQueuedApp.ps1 rename RemoveScheduledItem/run.ps1 => Modules/CIPPCore/Public/Invoke-RemoveScheduledItem.ps1 (57%) create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveSpamfilter.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveSpamfilterTemplate.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveStandard.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveTransportRule.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveTransportRuleTemplate.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveUser.ps1 create mode 100644 Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 delete mode 100644 RemoveAPDevice/function.json delete mode 100644 RemoveAPDevice/run.ps1 delete mode 100644 RemoveApp/function.json delete mode 100644 RemoveApp/run.ps1 delete mode 100644 RemoveCAPolicy/function.json delete mode 100644 RemoveCAPolicy/run.ps1 delete mode 100644 RemoveCATemplate/function.json delete mode 100644 RemoveCATemplate/run.ps1 delete mode 100644 RemoveCippQueue/function.json delete mode 100644 RemoveContact/function.json delete mode 100644 RemoveContact/run.ps1 delete mode 100644 RemoveExConnector/function.json delete mode 100644 RemoveExConnector/run.ps1 delete mode 100644 RemoveExConnectorTemplate/function.json delete mode 100644 RemoveExConnectorTemplate/run.ps1 delete mode 100644 RemoveGroupTemplate/function.json delete mode 100644 RemoveGroupTemplate/run.ps1 delete mode 100644 RemoveIntuneTemplate/function.json delete mode 100644 RemoveIntuneTemplate/run.ps1 delete mode 100644 RemovePolicy/function.json delete mode 100644 RemovePolicy/run.ps1 delete mode 100644 RemoveQueuedAlert/function.json delete mode 100644 RemoveQueuedAlert/run.ps1 delete mode 100644 RemoveQueuedApp/function.json delete mode 100644 RemoveQueuedApp/run.ps1 delete mode 100644 RemoveScheduledItem/function.json delete mode 100644 RemoveSpamfilter/function.json delete mode 100644 RemoveSpamfilter/run.ps1 delete mode 100644 RemoveSpamfilterTemplate/function.json delete mode 100644 RemoveSpamfilterTemplate/run.ps1 delete mode 100644 RemoveStandard/function.json delete mode 100644 RemoveStandard/run.ps1 delete mode 100644 RemoveTransportRule/function.json delete mode 100644 RemoveTransportRule/run.ps1 delete mode 100644 RemoveTransportRuleTemplate/function.json delete mode 100644 RemoveTransportRuleTemplate/run.ps1 delete mode 100644 RemoveUser/function.json delete mode 100644 RemoveUser/run.ps1 delete mode 100644 RemoveWebhookAlert/function.json delete mode 100644 RemoveWebhookAlert/run.ps1 diff --git a/Modules/CIPPCore/Public/Invoke-RemoveAPDevice.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveAPDevice.ps1 new file mode 100644 index 000000000000..8639d495321d --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveAPDevice.ps1 @@ -0,0 +1,41 @@ +using namespace System.Net + +Function Invoke-RemoveAPDevice { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $Deviceid = $Request.Query.ID + + try { + if ($TenantFilter -eq $null -or $TenantFilter -eq 'null') { + $GraphRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities/$Deviceid" -type DELETE + } else { + $GraphRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities/$Deviceid" -tenantid $TenantFilter -type DELETE + } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $TenantFilter -API $APINAME -message "Deleted autopilot device $Deviceid" -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully deleted the autopilot device' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $TenantFilter -API $APINAME -message "Autopilot Delete API failed for $deviceid. The error is: $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to delete device: $($_.Exception.Message)" } + } + #force a sync, this can give "too many requests" if deleleting a bunch of devices though. + $GraphRequest = New-GraphPOSTRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotSettings/sync' -tenantid $TenantFilter -type POST -body '{}' + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveApp.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveApp.ps1 new file mode 100644 index 000000000000..1a80a02c9e30 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveApp.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-RemoveApp { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $policyId = $Request.Query.ID + if (!$policyId) { exit } + try { + #$unAssignRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies('$($policyId)')/assign" -type POST -Body '{"assignments":[]}' -tenant $TenantFilter + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($policyId)" -type DELETE -tenant $TenantFilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted $policyId" -Sev 'Info' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = 'Successfully deleted the application' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete app $policyId. $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = "Could not delete this application: $($_.Exception.Message)" } + + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + #@{ Name = 'LicJoined'; Expression = { ($_.assignedLicenses | ForEach-Object { convert-skuname -skuID $_.skuid }) -join ", " } }, @{ Name = 'Aliases'; Expression = { $_.Proxyaddresses -join ", " } }, @{ Name = 'primDomain'; Expression = { $_.userPrincipalName -split "@" | Select-Object -Last 1 } } + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveCAPolicy.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveCAPolicy.ps1 new file mode 100644 index 000000000000..8cef68c26155 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveCAPolicy.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-RemoveCAPolicy { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $policyId = $Request.Query.GUID + if (!$policyId) { exit } + try { + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$($policyId)" -type DELETE -tenant $TenantFilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted CA Policy $policyId" -Sev 'Info' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = 'Successfully deleted the policy' } + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete CA policy $policyId. $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = "Could not delete policy: $($_.Exception.Message)" } + + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveCATemplate.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveCATemplate.ps1 new file mode 100644 index 000000000000..318184576061 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveCATemplate.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-RemoveCATemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $ID = $request.query.id + try { + $Table = Get-CippTable -tablename 'templates' + + $Filter = "PartitionKey eq 'CATemplate' and RowKey eq '$id'" + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $clearRow + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Conditional Access Template with ID $ID." -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully removed Conditional Access Template' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Conditional Access template $ID. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveContact.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveContact.ps1 new file mode 100644 index 000000000000..c4a47e9b182a --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveContact.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-RemoveContact { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Tenantfilter = $request.Query.tenantfilter + + + $Params = @{ + Identity = $request.query.guid + } + + try { + $Params = @{ Identity = $request.query.GUID } + + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Remove-MailContact' -cmdParams $params -UseSystemMailbox $true + $Result = "Deleted $($Request.query.guid)" + Write-LogMessage -API 'TransportRules' -tenant $tenantfilter -message "Deleted contact $($Request.query.guid)" -sev Debug + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception + $Result = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = $Result } + }) + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveExConnector.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveExConnector.ps1 new file mode 100644 index 000000000000..09c474712e18 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveExConnector.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-RemoveExConnector { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Tenantfilter = $request.Query.tenantfilter + + + $Params = @{ + Identity = $request.query.guid + } + + try { + $Params = @{ Identity = $request.query.GUID } + + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Remove-$($Request.query.Type)Connector" -cmdParams $params + $Result = "Deleted $($Request.query.guid)" + Write-LogMessage -API 'TransportRules' -tenant $tenantfilter -message "Deleted transport rule $($Request.query.guid)" -sev Debug + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception + $Result = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = $Result } + }) + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveExConnectorTemplate.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveExConnectorTemplate.ps1 new file mode 100644 index 000000000000..3e78057683d9 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveExConnectorTemplate.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-RemoveExConnectorTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $ID = $request.query.id + try { + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'ExConnectorTemplate' and RowKey eq '$id'" + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $clearRow + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Exchange Connector Template with ID $ID." -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully removed Exchange Connector Template' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Exchange Connector Template $ID. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveGroupTemplate.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveGroupTemplate.ps1 new file mode 100644 index 000000000000..2efd9d639307 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveGroupTemplate.ps1 @@ -0,0 +1,38 @@ +using namespace System.Net + +Function Invoke-RemoveGroupTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $ID = $request.query.id + try { + $Table = Get-CippTable -tablename 'templates' + Write-Host $id + + $Filter = "PartitionKey eq 'GroupTemplate' and RowKey eq '$id'" + Write-Host $Filter + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $clearRow + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Intune Template with ID $ID." -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully removed Template' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove intune template $ID. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveIntuneTemplate.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveIntuneTemplate.ps1 new file mode 100644 index 000000000000..4575cf0fe57a --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveIntuneTemplate.ps1 @@ -0,0 +1,38 @@ +using namespace System.Net + +Function Invoke-RemoveIntuneTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $ID = $request.query.id + try { + $Table = Get-CippTable -tablename 'templates' + Write-Host $id + + $Filter = "PartitionKey eq 'IntuneTemplate' and RowKey eq '$id'" + Write-Host $Filter + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $clearRow + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Intune Template with ID $ID." -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully removed Intune Template' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove intune template $ID. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemovePolicy.ps1 b/Modules/CIPPCore/Public/Invoke-RemovePolicy.ps1 new file mode 100644 index 000000000000..22ae4c5d7db5 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemovePolicy.ps1 @@ -0,0 +1,39 @@ +using namespace System.Net + +Function Invoke-RemovePolicy { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $policyId = $Request.Query.ID + if (!$policyId) { exit } + try { + + #$unAssignRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies('$($policyId)')/assign" -type POST -Body '{"assignments":[]}' -tenant $TenantFilter + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$($Request.Query.URLName)('$($policyId)')" -type DELETE -tenant $TenantFilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted $policyId" -Sev 'Info' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = 'Successfully deleted the policy' } + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete policy $policyId. $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = "Could not delete policy: $($_.Exception.Message)" } + + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + #@{ Name = 'LicJoined'; Expression = { ($_.assignedLicenses | ForEach-Object { convert-skuname -skuID $_.skuid }) -join ", " } }, @{ Name = 'Aliases'; Expression = { $_.Proxyaddresses -join ", " } }, @{ Name = 'primDomain'; Expression = { $_.userPrincipalName -split "@" | Select-Object -Last 1 } } + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveQueuedAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveQueuedAlert.ps1 new file mode 100644 index 000000000000..d69ed68c84e0 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveQueuedAlert.ps1 @@ -0,0 +1,33 @@ +using namespace System.Net + +Function Invoke-RemoveQueuedAlert { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Table = Get-CIPPTable -TableName 'SchedulerConfig' + $ID = $request.query.id + try { + $Filter = "RowKey eq '{0}' and PartitionKey eq 'Alert'" -f $ID + $Alert = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $Alert + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed application queue for $ID." -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully removed from queue.' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove from queue $ID. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to remove alert from queue $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveQueuedApp.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveQueuedApp.ps1 new file mode 100644 index 000000000000..add290577208 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveQueuedApp.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-RemoveQueuedApp { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $ID = $request.query.id + try { + $Table = Get-CippTable -tablename 'apps' + $Filter = "PartitionKey eq 'apps' and RowKey eq '$id'" + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $clearRow + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed application queue for $ID." -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully removed from queue.' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove application queue for $ID. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = 'Failed to remove standard)' } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/RemoveScheduledItem/run.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveScheduledItem.ps1 similarity index 57% rename from RemoveScheduledItem/run.ps1 rename to Modules/CIPPCore/Public/Invoke-RemoveScheduledItem.ps1 index 191c95570b0f..888e4a906980 100644 --- a/RemoveScheduledItem/run.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveScheduledItem.ps1 @@ -1,9 +1,14 @@ using namespace System.Net -param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -$task = @{ - RowKey = $Request.Query.ID + +Function Invoke-RemoveScheduledItem { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + RowKey = $Request.Query.ID PartitionKey = 'ScheduledTask' } $Table = Get-CIPPTable -TableName 'ScheduledTasks' @@ -11,4 +16,6 @@ Remove-AzDataTableEntity @Table -Entity $task Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @{ Results = 'Task removed successfully.' } - }) \ No newline at end of file + }) + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveSpamfilter.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveSpamfilter.ps1 new file mode 100644 index 000000000000..e962aab4a7a4 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveSpamfilter.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-RemoveSpamfilter { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Tenantfilter = $request.Query.tenantfilter + + + $Params = @{ + Identity = $request.query.name + } + + try { + $cmdlet = 'Remove-HostedContentFilterRule' + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params + $cmdlet = 'Remove-HostedContentFilterPolicy' + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params + $Result = "Deleted $($Request.query.name)" + Write-LogMessage -API 'TransportRules' -tenant $tenantfilter -message "Deleted transport rule $($Request.query.name)" -sev Debug + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception + $Result = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = $Result } + }) + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveSpamfilterTemplate.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveSpamfilterTemplate.ps1 new file mode 100644 index 000000000000..8008d3b11a18 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveSpamfilterTemplate.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-RemoveSpamfilterTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $ID = $request.query.id + try { + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'SpamfilterTemplate' and RowKey eq '$id'" + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $clearRow + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Transport Rule Template with ID $ID." -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully removed Transport Rule Template' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Transport Rule template $ID. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveStandard.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveStandard.ps1 new file mode 100644 index 000000000000..d3898d337a73 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveStandard.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-RemoveStandard { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $ID = $request.query.id + try { + $Table = Get-CippTable -tablename 'standards' + $Filter = "PartitionKey eq 'standards' and RowKey eq '$id'" + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $clearRow + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed standards for $ID." -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully removed standards deployment' } + + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove standard for $ID. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = 'Failed to remove standard)' } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveTransportRule.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveTransportRule.ps1 new file mode 100644 index 000000000000..b340f331903f --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveTransportRule.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-RemoveTransportRule { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Tenantfilter = $request.Query.tenantfilter + + + $Params = @{ + Identity = $request.query.guid + } + + try { + $cmdlet = 'Remove-TransportRule' + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params -UseSystemMailbox $true + $Result = "Deleted $($Request.query.guid)" + Write-LogMessage -API 'TransportRules' -tenant $tenantfilter -message "Deleted transport rule $($Request.query.guid)" -sev Debug + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception + $Result = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = $Result } + }) + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveTransportRuleTemplate.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveTransportRuleTemplate.ps1 new file mode 100644 index 000000000000..952796e819d8 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveTransportRuleTemplate.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-RemoveTransportRuleTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $ID = $request.query.id + try { + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'TransportTemplate' and RowKey eq '$id'" + $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $clearRow + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Transport Rule Template with ID $ID." -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Successfully removed Transport Rule Template' } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Transport Rule template $ID. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveUser.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveUser.ps1 new file mode 100644 index 000000000000..96b42b933ba6 --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveUser.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-RemoveUser { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $userid = $Request.Query.ID + if (!$userid) { exit } + try { + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)" -type DELETE -tenant $TenantFilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted $userid" -Sev 'Info' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = 'Successfully deleted the user.' } + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete user $userid. $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = "Could not delete user: $($_.Exception.Message)" } + + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + #@{ Name = 'LicJoined'; Expression = { ($_.assignedLicenses | ForEach-Object { convert-skuname -skuID $_.skuid }) -join ", " } }, @{ Name = 'Aliases'; Expression = { $_.Proxyaddresses -join ", " } }, @{ Name = 'primDomain'; Expression = { $_.userPrincipalName -split "@" | Select-Object -Last 1 } } + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 new file mode 100644 index 000000000000..e54f283123de --- /dev/null +++ b/Modules/CIPPCore/Public/Invoke-RemoveWebhookAlert.ps1 @@ -0,0 +1,30 @@ +using namespace System.Net + +Function Invoke-RemoveWebhookAlert { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + try { + $Results = Remove-CIPPGraphSubscription -TenantFilter $Request.query.TenantFilter -CIPPID $Request.query.CIPPID + $body = [pscustomobject]@{'Results' = $Results } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove webhook alert. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to remove webhook alert: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + + +} diff --git a/RemoveAPDevice/function.json b/RemoveAPDevice/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveAPDevice/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveAPDevice/run.ps1 b/RemoveAPDevice/run.ps1 deleted file mode 100644 index 945b97b0b071..000000000000 --- a/RemoveAPDevice/run.ps1 +++ /dev/null @@ -1,36 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$Deviceid = $Request.Query.ID - -try { - if ($TenantFilter -eq $null -or $TenantFilter -eq "null") { - $GraphRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities/$Deviceid" -type DELETE - } - else { - $GraphRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities/$Deviceid" -tenantid $TenantFilter -type DELETE - } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $TenantFilter -API $APINAME -message "Deleted autopilot device $Deviceid" -Sev "Info" - $body = [pscustomobject]@{"Results" = "Successfully deleted the autopilot device" } -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $TenantFilter -API $APINAME -message "Autopilot Delete API failed for $deviceid. The error is: $($_.Exception.Message)" -Sev "Error" - $body = [pscustomobject]@{"Results" = "Failed to delete device: $($_.Exception.Message)" } -} -#force a sync, this can give "too many requests" if deleleting a bunch of devices though. -$GraphRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotSettings/sync" -tenantid $TenantFilter -type POST -body "{}" - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) \ No newline at end of file diff --git a/RemoveApp/function.json b/RemoveApp/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveApp/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveApp/run.ps1 b/RemoveApp/run.ps1 deleted file mode 100644 index f6ec2f9c1e27..000000000000 --- a/RemoveApp/run.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$policyId = $Request.Query.ID -if (!$policyId) { exit } -try { - #$unAssignRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies('$($policyId)')/assign" -type POST -Body '{"assignments":[]}' -tenant $TenantFilter - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($policyId)" -type DELETE -tenant $TenantFilter - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted $policyId" -Sev "Info" -tenant $TenantFilter - $body = [pscustomobject]@{"Results" = "Successfully deleted the application" } -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete app $policyId. $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter - $body = [pscustomobject]@{"Results" = "Could not delete this application: $($_.Exception.Message)" } - -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - -#@{ Name = 'LicJoined'; Expression = { ($_.assignedLicenses | ForEach-Object { convert-skuname -skuID $_.skuid }) -join ", " } }, @{ Name = 'Aliases'; Expression = { $_.Proxyaddresses -join ", " } }, @{ Name = 'primDomain'; Expression = { $_.userPrincipalName -split "@" | Select-Object -Last 1 } } \ No newline at end of file diff --git a/RemoveCAPolicy/function.json b/RemoveCAPolicy/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveCAPolicy/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveCAPolicy/run.ps1 b/RemoveCAPolicy/run.ps1 deleted file mode 100644 index 75397a287758..000000000000 --- a/RemoveCAPolicy/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$policyId = $Request.Query.GUID -if (!$policyId) { exit } -try { - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$($policyId)" -type DELETE -tenant $TenantFilter - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted CA Policy $policyId" -Sev "Info" -tenant $TenantFilter - $body = [pscustomobject]@{"Results" = "Successfully deleted the policy" } - -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete CA policy $policyId. $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter - $body = [pscustomobject]@{"Results" = "Could not delete policy: $($_.Exception.Message)" } - -} - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemoveCATemplate/function.json b/RemoveCATemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveCATemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveCATemplate/run.ps1 b/RemoveCATemplate/run.ps1 deleted file mode 100644 index 4d682d715b92..000000000000 --- a/RemoveCATemplate/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$ID = $request.query.id -try { - $Table = Get-CippTable -tablename 'templates' - - $Filter = "PartitionKey eq 'CATemplate' and RowKey eq '$id'" - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $clearRow - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Conditional Access Template with ID $ID." -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Successfully removed Conditional Access Template' } -} catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Conditional Access template $ID. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemoveCippQueue/function.json b/RemoveCippQueue/function.json deleted file mode 100644 index f0af9f1ceba8..000000000000 --- a/RemoveCippQueue/function.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "scriptFile": "../Modules/CippQueue/CippQueue.psm1", - "entryPoint": "Remove-CippQueue", - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/RemoveContact/function.json b/RemoveContact/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/RemoveContact/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveContact/run.ps1 b/RemoveContact/run.ps1 deleted file mode 100644 index 4e28cb3d1f19..000000000000 --- a/RemoveContact/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Tenantfilter = $request.Query.tenantfilter - - -$Params = @{ - Identity = $request.query.guid -} - -try { - $Params = @{ Identity = $request.query.GUID } - - $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Remove-MailContact" -cmdParams $params -UseSystemMailbox $true - $Result = "Deleted $($Request.query.guid)" - Write-LogMessage -API "TransportRules" -tenant $tenantfilter -message "Deleted contact $($Request.query.guid)" -sev Debug -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception - $Result = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{Results = $Result } - }) diff --git a/RemoveExConnector/function.json b/RemoveExConnector/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/RemoveExConnector/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveExConnector/run.ps1 b/RemoveExConnector/run.ps1 deleted file mode 100644 index 0e2469eb9b46..000000000000 --- a/RemoveExConnector/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Tenantfilter = $request.Query.tenantfilter - - -$Params = @{ - Identity = $request.query.guid -} - -try { - $Params = @{ Identity = $request.query.GUID } - - $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Remove-$($Request.query.Type)Connector" -cmdParams $params - $Result = "Deleted $($Request.query.guid)" - Write-LogMessage -API "TransportRules" -tenant $tenantfilter -message "Deleted transport rule $($Request.query.guid)" -sev Debug -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception - $Result = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{Results = $Result } - }) diff --git a/RemoveExConnectorTemplate/function.json b/RemoveExConnectorTemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveExConnectorTemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveExConnectorTemplate/run.ps1 b/RemoveExConnectorTemplate/run.ps1 deleted file mode 100644 index 718e4e72e403..000000000000 --- a/RemoveExConnectorTemplate/run.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$ID = $request.query.id -try { - $Table = Get-CippTable -tablename 'templates' - $Filter = "PartitionKey eq 'ExConnectorTemplate' and RowKey eq '$id'" - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $clearRow - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Exchange Connector Template with ID $ID." -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Successfully removed Exchange Connector Template' } -} catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Exchange Connector Template $ID. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemoveGroupTemplate/function.json b/RemoveGroupTemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveGroupTemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveGroupTemplate/run.ps1 b/RemoveGroupTemplate/run.ps1 deleted file mode 100644 index d9069b9f7e3c..000000000000 --- a/RemoveGroupTemplate/run.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$ID = $request.query.id -try { - $Table = Get-CippTable -tablename 'templates' - Write-Host $id - - $Filter = "PartitionKey eq 'GroupTemplate' and RowKey eq '$id'" - Write-Host $Filter - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $clearRow - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Intune Template with ID $ID." -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Successfully removed Template' } -} catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove intune template $ID. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemoveIntuneTemplate/function.json b/RemoveIntuneTemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveIntuneTemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveIntuneTemplate/run.ps1 b/RemoveIntuneTemplate/run.ps1 deleted file mode 100644 index c4f678b4eef4..000000000000 --- a/RemoveIntuneTemplate/run.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$ID = $request.query.id -try { - $Table = Get-CippTable -tablename 'templates' - Write-Host $id - - $Filter = "PartitionKey eq 'IntuneTemplate' and RowKey eq '$id'" - Write-Host $Filter - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $clearRow - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Intune Template with ID $ID." -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Successfully removed Intune Template' } -} catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove intune template $ID. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemovePolicy/function.json b/RemovePolicy/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemovePolicy/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemovePolicy/run.ps1 b/RemovePolicy/run.ps1 deleted file mode 100644 index 8b0c01d8b698..000000000000 --- a/RemovePolicy/run.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$policyId = $Request.Query.ID -if (!$policyId) { exit } -try { - - #$unAssignRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies('$($policyId)')/assign" -type POST -Body '{"assignments":[]}' -tenant $TenantFilter - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$($Request.Query.URLName)('$($policyId)')" -type DELETE -tenant $TenantFilter - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted $policyId" -Sev "Info" -tenant $TenantFilter - $body = [pscustomobject]@{"Results" = "Successfully deleted the policy" } - -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete policy $policyId. $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter - $body = [pscustomobject]@{"Results" = "Could not delete policy: $($_.Exception.Message)" } - -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - -#@{ Name = 'LicJoined'; Expression = { ($_.assignedLicenses | ForEach-Object { convert-skuname -skuID $_.skuid }) -join ", " } }, @{ Name = 'Aliases'; Expression = { $_.Proxyaddresses -join ", " } }, @{ Name = 'primDomain'; Expression = { $_.userPrincipalName -split "@" | Select-Object -Last 1 } } \ No newline at end of file diff --git a/RemoveQueuedAlert/function.json b/RemoveQueuedAlert/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveQueuedAlert/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveQueuedAlert/run.ps1 b/RemoveQueuedAlert/run.ps1 deleted file mode 100644 index 81b7a3cb907b..000000000000 --- a/RemoveQueuedAlert/run.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -$Table = Get-CIPPTable -TableName 'SchedulerConfig' -$ID = $request.query.id -try { - $Filter = "RowKey eq '{0}' and PartitionKey eq 'Alert'" -f $ID - $Alert = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $Alert - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed application queue for $ID." -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Successfully removed from queue.' } -} catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove from queue $ID. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed to remove alert from queue $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemoveQueuedApp/function.json b/RemoveQueuedApp/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveQueuedApp/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveQueuedApp/run.ps1 b/RemoveQueuedApp/run.ps1 deleted file mode 100644 index 7ce40f7ca3fd..000000000000 --- a/RemoveQueuedApp/run.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$ID = $request.query.id -try { - $Table = Get-CippTable -tablename 'apps' - $Filter = "PartitionKey eq 'apps' and RowKey eq '$id'" - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $clearRow - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed application queue for $ID." -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Successfully removed from queue.' } -} catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove application queue for $ID. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = 'Failed to remove standard)' } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemoveScheduledItem/function.json b/RemoveScheduledItem/function.json deleted file mode 100644 index b0ca1676cc0b..000000000000 --- a/RemoveScheduledItem/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] - } \ No newline at end of file diff --git a/RemoveSpamfilter/function.json b/RemoveSpamfilter/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/RemoveSpamfilter/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveSpamfilter/run.ps1 b/RemoveSpamfilter/run.ps1 deleted file mode 100644 index 9d8b238e00dc..000000000000 --- a/RemoveSpamfilter/run.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Tenantfilter = $request.Query.tenantfilter - - -$Params = @{ - Identity = $request.query.name -} - -try { - $cmdlet = "Remove-HostedContentFilterRule" - $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params - $cmdlet = "Remove-HostedContentFilterPolicy" - $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params - $Result = "Deleted $($Request.query.name)" - Write-LogMessage -API "TransportRules" -tenant $tenantfilter -message "Deleted transport rule $($Request.query.name)" -sev Debug -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception - $Result = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{Results = $Result } - }) diff --git a/RemoveSpamfilterTemplate/function.json b/RemoveSpamfilterTemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveSpamfilterTemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveSpamfilterTemplate/run.ps1 b/RemoveSpamfilterTemplate/run.ps1 deleted file mode 100644 index a79169c0995a..000000000000 --- a/RemoveSpamfilterTemplate/run.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$ID = $request.query.id -try { - $Table = Get-CippTable -tablename 'templates' - $Filter = "PartitionKey eq 'SpamfilterTemplate' and RowKey eq '$id'" - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $clearRow - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Transport Rule Template with ID $ID." -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Successfully removed Transport Rule Template' } -} catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Transport Rule template $ID. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemoveStandard/function.json b/RemoveStandard/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveStandard/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveStandard/run.ps1 b/RemoveStandard/run.ps1 deleted file mode 100644 index c38d977111f2..000000000000 --- a/RemoveStandard/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$ID = $request.query.id -try { - $Table = Get-CippTable -tablename 'standards' - $Filter = "PartitionKey eq 'standards' and RowKey eq '$id'" - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $clearRow - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed standards for $ID." -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Successfully removed standards deployment' } - - -} catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove standard for $ID. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = 'Failed to remove standard)' } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemoveTransportRule/function.json b/RemoveTransportRule/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/RemoveTransportRule/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveTransportRule/run.ps1 b/RemoveTransportRule/run.ps1 deleted file mode 100644 index 67c0d92b449e..000000000000 --- a/RemoveTransportRule/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Tenantfilter = $request.Query.tenantfilter - - -$Params = @{ - Identity = $request.query.guid -} - -try { - $cmdlet = "Remove-TransportRule" - $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet $cmdlet -cmdParams $params -UseSystemMailbox $true - $Result = "Deleted $($Request.query.guid)" - Write-LogMessage -API "TransportRules" -tenant $tenantfilter -message "Deleted transport rule $($Request.query.guid)" -sev Debug -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception - $Result = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{Results = $Result } - }) diff --git a/RemoveTransportRuleTemplate/function.json b/RemoveTransportRuleTemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveTransportRuleTemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveTransportRuleTemplate/run.ps1 b/RemoveTransportRuleTemplate/run.ps1 deleted file mode 100644 index 7e4b7b6b6548..000000000000 --- a/RemoveTransportRuleTemplate/run.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$ID = $request.query.id -try { - $Table = Get-CippTable -tablename 'templates' - $Filter = "PartitionKey eq 'TransportTemplate' and RowKey eq '$id'" - $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $clearRow - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed Transport Rule Template with ID $ID." -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Successfully removed Transport Rule Template' } -} catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove Transport Rule template $ID. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed to remove template: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - diff --git a/RemoveUser/function.json b/RemoveUser/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveUser/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveUser/run.ps1 b/RemoveUser/run.ps1 deleted file mode 100644 index da7e1952b713..000000000000 --- a/RemoveUser/run.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$userid = $Request.Query.ID -if (!$userid) { exit } -try { - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)" -type DELETE -tenant $TenantFilter - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted $userid" -Sev "Info" -tenant $TenantFilter - $body = [pscustomobject]@{"Results" = "Successfully deleted the user." } - -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete user $userid. $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter - $body = [pscustomobject]@{"Results" = "Could not delete user: $($_.Exception.Message)" } - -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - -#@{ Name = 'LicJoined'; Expression = { ($_.assignedLicenses | ForEach-Object { convert-skuname -skuID $_.skuid }) -join ", " } }, @{ Name = 'Aliases'; Expression = { $_.Proxyaddresses -join ", " } }, @{ Name = 'primDomain'; Expression = { $_.userPrincipalName -split "@" | Select-Object -Last 1 } } \ No newline at end of file diff --git a/RemoveWebhookAlert/function.json b/RemoveWebhookAlert/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/RemoveWebhookAlert/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/RemoveWebhookAlert/run.ps1 b/RemoveWebhookAlert/run.ps1 deleted file mode 100644 index dbda93213351..000000000000 --- a/RemoveWebhookAlert/run.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -try { - $Results = Remove-CIPPGraphSubscription -TenantFilter $Request.query.TenantFilter -CIPPID $Request.query.CIPPID - $body = [pscustomobject]@{"Results" = $Results } -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to remove webhook alert. $($_.Exception.Message)" -Sev "Error" - $body = [pscustomobject]@{"Results" = "Failed to remove webhook alert: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) - From 9629f40230cfb2a9a185830b52c63ac63a424a0b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 01:29:15 +0100 Subject: [PATCH 53/77] replace "gets" and remove unused APIs --- GetCippAlerts/function.json | 19 -- GetCippAlerts/run.ps1 | 64 ----- GetDashboard/function.json | 19 -- GetDashboard/run.ps1 | 247 ------------------ GetVersion/function.json | 19 -- GetVersion/run.ps1 | 29 -- .../Entrypoints/Invoke-GetCippAlerts.ps1 | 71 +++++ .../Public/Entrypoints/Invoke-GetVersion.ps1 | 36 +++ 8 files changed, 107 insertions(+), 397 deletions(-) delete mode 100644 GetCippAlerts/function.json delete mode 100644 GetCippAlerts/run.ps1 delete mode 100644 GetDashboard/function.json delete mode 100644 GetDashboard/run.ps1 delete mode 100644 GetVersion/function.json delete mode 100644 GetVersion/run.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-GetVersion.ps1 diff --git a/GetCippAlerts/function.json b/GetCippAlerts/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/GetCippAlerts/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/GetCippAlerts/run.ps1 b/GetCippAlerts/run.ps1 deleted file mode 100644 index 4bb100fcff4d..000000000000 --- a/GetCippAlerts/run.ps1 +++ /dev/null @@ -1,64 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$Alerts = [System.Collections.ArrayList]@() -$Table = Get-CippTable -tablename CippAlerts -$PartitionKey = Get-Date -UFormat '%Y%m%d' -$Filter = "PartitionKey eq '{0}'" -f $PartitionKey -$Rows = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Sort-Object TableTimestamp -Descending | Select-Object -First 10 - - - -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$APIVersion = Get-Content "version_latest.txt" | Out-String -$CIPPVersion = $request.query.localversion - -$RemoteAPIVersion = Invoke-RestMethod -Uri "https://raw.githubusercontent.com/KelvinTegelaar/CIPP-API/master/version_latest.txt" -$RemoteCIPPVersion = Invoke-RestMethod -Uri "https://raw.githubusercontent.com/KelvinTegelaar/CIPP/master/public/version_latest.txt" - -$version = [PSCustomObject]@{ - LocalCIPPVersion = $CIPPVersion - RemoteCIPPVersion = $RemoteCIPPVersion - LocalCIPPAPIVersion = $APIVersion - RemoteCIPPAPIVersion = $RemoteAPIVersion - OutOfDateCIPP = ([version]$RemoteCIPPVersion -gt [version]$CIPPVersion) - OutOfDateCIPPAPI = ([version]$RemoteAPIVersion -gt [version]$APIVersion) -} -if ($version.outOfDateCIPP) { - $Alerts.add(@{Alert = 'Your CIPP Frontend is out of date. Please update to the latest version. Find more on the following '; link = "https://docs.cipp.app/setup/installation/updating"; type = "warning" }) - Write-LogMessage -message "Your CIPP Frontend is out of date. Please update to the latest version" -API 'Updates' -tenant "All Tenants" -sev Alert - -} -if ($version.outOfDateCIPPAPI) { - $Alerts.add(@{Alert = 'Your CIPP API is out of date. Please update to the latest version. Find more on the following'; link = "https://docs.cipp.app/setup/installation/updating"; type = "warning" }) - Write-LogMessage -message "Your CIPP API is out of date. Please update to the latest version" -API 'Updates' -tenant "All Tenants" -sev Alert -} - - -if ($env:ApplicationID -eq 'LongApplicationID' -or $null -eq $ENV:ApplicationID) { $Alerts.add(@{Alert = 'You have not yet setup your SAM Setup. Please go to the SAM Wizard in settings to finish setup'; link = "/cipp/setup"; type = "warning" }) } -if ($env:FUNCTIONS_EXTENSION_VERSION -ne '~4') { - $Alerts.add(@{Alert = 'Your Function App is running on a Runtime version lower than 4. This impacts performance. Go to Settings -> Backend -> Function App Configuration -> Function Runtime Settings and set this to 4 for maximum performance'; link = "/cipp/setup"; type = "warning" }) -} -if ($psversiontable.psversion.toString() -lt 7.2) { $Alerts.add(@{Alert = 'Your Function App is running on Powershell 7. This impacts performance. Go to Settings -> Backend -> Function App Configuration -> General Settings and set PowerShell Core Version to 7.2 for maximum performance'; link = "/cipp/setup"; type = "danger" }) } -if ($env:WEBSITE_RUN_FROM_PACKAGE -ne '1') { - $Alerts.add( - @{Alert = 'Your Function App is running in write mode. This will cause performance issues and increase cost. Please check this '; - link = "https://docs.cipp.app/setup/installation/runfrompackage"; - type = "warning" - }) -} -if ($Rows) { $Rows | ForEach-Object { $alerts.add($_) } } -$Alerts = @($Alerts) -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Alerts - }) diff --git a/GetDashboard/function.json b/GetDashboard/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/GetDashboard/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/GetDashboard/run.ps1 b/GetDashboard/run.ps1 deleted file mode 100644 index 8c9bafcebc28..000000000000 --- a/GetDashboard/run.ps1 +++ /dev/null @@ -1,247 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -Function Test-CronRange { - <# - .EXAMPLE - # * always passes - Test-CronRange -Range '*' -InputValue 10 -Verbose - # a min-max range - Test-CronRange -Range '1-15' -InputValue 10 -Verbose - # stepped value - Test-CronRange -Range '*/15' -InputValue 30 -verbose - # A specific value list - Test-CronRange -Range '2,5,8,9' -InputValue 10 -verbose - Test-CronRange -Range '*/4' -InputValue 60 -verbose - #> - [cmdletbinding()] - param( - [ValidatePattern('^[\d-*/,]*$')] - [string]$range - , - [int]$inputvalue - ) - Write-Verbose "Testing $range" - If ($range -eq '*') { - Return $true - } - If ($range -match '^\d+$') { - Write-Verbose 'Specific Value(int)' - Return ($inputvalue -eq [int]$range) - } - If ($range -match '[\d]+-[\d]+([/][\d])*') { - Write-Verbose 'min-max range' - [int]$min, [int]$max = $range -split '-' - Return ($inputvalue -ge $min -and $inputvalue -le $max) - } - If ($range -match ('([*]+|[\d]+-[\d]+)[/][\d]+')) { - Write-Verbose 'Step Value' - $list, $step = $range -split '/' - Write-Verbose "Using Step of $step" - $IsInStep = ( ($inputvalue / $step).GetType().Name -eq 'Int32' ) - Return ( $IsInStep ) - } - If ($range -match '(\d+)(,\s*\d+)*') { - Write-Verbose 'value list' - $list = @() - $list = $range -split ',' - Return ( $list -contains $InputValue ) - } - Write-Error "Could not process Range format: $Range" -} -Function ConvertFrom-DateTable { - Param ( - $DateTable - ) - $datestring = '{0}-{1:00}-{2:00} {3:00}:{4:00}' -f $DateTable.year, $DateTable.month, $DateTable.day, $DateTable.hour, $DateTable.Minute - $date = [datetime]::ParseExact($datestring, 'yyyy-MM-dd HH:mm', $null) - return $date -} -Function Invoke-CronIncrement { - param( - [psobject] - $DateTable - , - [ValidateSet('Minute', 'Hour', 'Day', 'Month')] - [string] - $Increment - ) - $date = ConvertFrom-DateTable -DateTable $DateTable - $date = switch ($Increment) { - 'Minute' { $date.AddMinutes(1) } - 'Hour' { $date.AddHours(1) } - 'Day' { $date.AddDays(1) } - 'Month' { $date.AddMonths(1) } - } - $output = [ordered]@{ - Minute = $date.Minute - Hour = $date.hour - Day = $date.day - Weekday = $date.DayOfWeek.value__ - Month = $date.month - Year = $date.year - } - Return $output -} -Function Get-CronNextExecutionTime { - <# - .SYNOPSIS - Currently only support * or digits - todo: add support for ',' '-' '/' ',' - .EXAMPLE - Get-CronNextExecutionTime -Expression '* * * * *' - Get-CronNextExecutionTime -Expression '5 * * * *' - Get-CronNextExecutionTime -Expression '* 13-21 * * *' - Get-CronNextExecutionTime -Expression '0 0 2 * *' - Get-CronNextExecutionTime -Expression '15 14 * 1-3 *' - Get-CronNextExecutionTime -Expression '15 14 * * 4' - Get-CronNextExecutionTime -Expression '15 14 * 2 *' - Get-CronNextExecutionTime -Expression '15 14-20 * * *' - Get-CronNextExecutionTime -Expression '15 14 * * 1' - #> - [cmdletbinding()] - param( - [string] - $Expression = '* * * * *' - , - $InputDate - ) - # Split Expression in variables and set to INT if possible - $cronMinute, $cronHour, $cronDay, $cronMonth, $cronWeekday = $Expression -Split ' ' - Get-Variable -Scope local | Where-Object { $_.name -like 'cron*' } | ForEach-Object { - If ($_.Value -ne '*') { - Try { - [int]$newValue = $_.Value - Set-Variable -Name $_.Name -Value $newValue -ErrorAction Ignore - } - Catch {} - } - } - # Get the next default Time (= next minute) - $nextdate = If ($InputDate) { $InputDate } Else { Get-Date } - $nextdate = $nextdate.addMinutes(1) - $next = [ordered]@{ - Minute = $nextdate.Minute - Hour = $nextdate.hour - Day = $nextdate.day - Weekday = $nextdate.DayOfWeek.value__ - Month = $nextdate.month - Year = $nextdate.year - } - # Increase Minutes until it is in the range. - # If Minutes passes the 60 mark, the hour is incremented - $done = $false - Do { - If ((Test-CronRange -InputValue $next.Minute -range $cronMinute) -eq $False) { - Do { - $next = Invoke-CronIncrement -DateTable $Next -Increment Minute - } While ( (Test-CronRange -InputValue $next.Minute -range $cronMinute) -eq $False ) - continue - } - # Check if the next Hour is in the desired range - # Add a Day because the desired Hour has already passed - If ((Test-CronRange -InputValue $next.Hour -range $cronHour) -eq $False) { - Do { - $next = Invoke-CronIncrement -DateTable $Next -Increment Hour - $next.Minute = 0 - } While ((Test-CronRange -InputValue $next.Hour -range $cronHour) -eq $False) - continue - } - # Increase Days until it is in the range. - # If Days passes the 30/31 mark, the Month is incremented - If ((Test-CronRange -InputValue $next.day -range $cronday) -eq $False) { - Do { - $next = Invoke-CronIncrement -DateTable $Next -Increment Day - $next.Hour = 0 - $next.Minute = 0 - } While ((Test-CronRange -InputValue $next.day -range $cronday) -eq $False) - continue - } - # Increase Months until it is in the range. - # If Months passes the 12 mark, the Year is incremented - If ((Test-CronRange -InputValue $next.Month -range $cronMonth) -eq $False) { - Do { - $next = Invoke-CronIncrement -DateTable $Next -Increment Month - $next.Hour = 0 - $next.Minute = 0 - } While ((Test-CronRange -InputValue $next.Month -range $cronMonth) -eq $False) - continue - } - If ((Test-CronRange -InputValue $Next.WeekDay -Range $cronWeekday) -eq $false) { - Do { - $next = Invoke-CronIncrement -DateTable $Next -Increment Day - $next.Hour = 0 - $next.Minute = 0 - } While ( (Test-CronRange -InputValue $Next.WeekDay -Range $cronWeekday) -eq $false ) - continue - } - $done = $true - } While ($done -eq $false) - $date = ConvertFrom-DateTable -DateTable $next - If (!$date) { Throw 'Could not create date' } - - # Add Days until weekday matches - - Return $Date -} - -$Table = Get-CippTable -tablename CippLogs -$PartitionKey = Get-Date -UFormat '%Y%m%d' -$Filter = "PartitionKey eq '{0}'" -f $PartitionKey -$Rows = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Sort-Object TableTimestamp -Descending | Select-Object -First 10 - -$Standards = Get-CippTable -tablename standards -$QueuedStandards = (Get-CIPPAzDataTableEntity @Standards -Property RowKey | Measure-Object).Count - -$Apps = Get-CippTable -tablename apps -$QueuedApps = (Get-CIPPAzDataTableEntity @Apps -Property RowKey | Measure-Object).Count - -$SlimRows = New-Object System.Collections.ArrayList -foreach ($Row in $Rows) { - $SlimRows.Add(@{ - Tenant = $Row.Tenant - Message = $Row.Message - }) -} -$Alerts = [System.Collections.ArrayList]@() -if ($env:ApplicationID -eq 'LongApplicationID' -or $null -eq $ENV:ApplicationID) { $Alerts.add('You have not yet setup your SAM Setup. Please go to the SAM Wizard in settings to finish setup') } -if ($env:FUNCTIONS_EXTENSION_VERSION -ne '~4') { $Alerts.add('Your Function App is running on a Runtime version lower than 4. This impacts performance. Go to Settings -> Backend -> Function App Configuration -> Function Runtime Settings and set this to 4 for maximum performance') } -if ($psversiontable.psversion.toString() -lt 7.2) { $Alerts.add('Your Function App is running on Powershell 7. This impacts performance. Go to Settings -> Backend -> Function App Configuration -> General Settings and set PowerShell Core Version to 7.2 for maximum performance') } -if ($env:WEBSITE_RUN_FROM_PACKAGE -ne '1') { $Alerts.add('Your Function App is running in write mode. Please check the release notes to enable Run from Package mode. (https://github.com/KelvinTegelaar/CIPP/releases/tag/v2.1.11.0)') } -try { - $TenantCount = (Get-Tenants -IncludeErrors | Measure-Object).Count - $TenantErrorCount = $TenantCount - (Get-Tenants | Measure-Object).Count -} -catch { - $TenantCount = 0 - $TenantErrorCount = 0 -} -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -if (!$env:WEBSITE_NAME) { - #Running locally, no alerts. :) - $Alerts = $null -} -else { - $Alerts = @($Alerts) -} -$dash = [PSCustomObject]@{ - NextStandardsRun = (Get-CronNextExecutionTime -Expression '0 */3 * * *').tostring('s') - NextBPARun = (Get-CronNextExecutionTime -Expression '0 3 * * *').tostring('s') - queuedApps = [int64]$QueuedApps - queuedStandards = [int64]$QueuedStandards - tenantCount = [int64]$TenantCount - tenantErrorCount = [int64]$TenantErrorCount - RefreshTokenDate = (Get-CronNextExecutionTime -Expression '0 0 * * 0').AddDays('-7').tostring('s') -split 'T' | Select-Object -First 1 - LastLog = @($SlimRows) - Alerts = $Alerts -} -# Write to the Azure Functions log stream. - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $dash - }) diff --git a/GetVersion/function.json b/GetVersion/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/GetVersion/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/GetVersion/run.ps1 b/GetVersion/run.ps1 deleted file mode 100644 index d0e092e20571..000000000000 --- a/GetVersion/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -$APIVersion = Get-Content "version_latest.txt" | Out-String -$CIPPVersion = $request.query.localversion - -$RemoteAPIVersion = Invoke-RestMethod -Uri "https://raw.githubusercontent.com/KelvinTegelaar/CIPP-API/master/version_latest.txt" -$RemoteCIPPVersion = Invoke-RestMethod -Uri "https://raw.githubusercontent.com/KelvinTegelaar/CIPP/master/public/version_latest.txt" - -$version = [PSCustomObject]@{ - LocalCIPPVersion = $CIPPVersion - RemoteCIPPVersion = $RemoteCIPPVersion - LocalCIPPAPIVersion = $APIVersion - RemoteCIPPAPIVersion = $RemoteAPIVersion - OutOfDateCIPP = ([version]$RemoteCIPPVersion -gt [version]$CIPPVersion) - OutOfDateCIPPAPI = ([version]$RemoteAPIVersion -gt [version]$APIVersion) -} -# Write to the Azure Functions log stream. - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Version - }) \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 new file mode 100644 index 000000000000..f10b68fc0b03 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 @@ -0,0 +1,71 @@ +using namespace System.Net + +Function Invoke-GetCippAlerts { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $Alerts = [System.Collections.ArrayList]@() + $Table = Get-CippTable -tablename CippAlerts + $PartitionKey = Get-Date -UFormat '%Y%m%d' + $Filter = "PartitionKey eq '{0}'" -f $PartitionKey + $Rows = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Sort-Object TableTimestamp -Descending | Select-Object -First 10 + + + + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + $APIVersion = Get-Content 'version_latest.txt' | Out-String + $CIPPVersion = $request.query.localversion + + $RemoteAPIVersion = Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/KelvinTegelaar/CIPP-API/master/version_latest.txt' + $RemoteCIPPVersion = Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/KelvinTegelaar/CIPP/master/public/version_latest.txt' + + $version = [PSCustomObject]@{ + LocalCIPPVersion = $CIPPVersion + RemoteCIPPVersion = $RemoteCIPPVersion + LocalCIPPAPIVersion = $APIVersion + RemoteCIPPAPIVersion = $RemoteAPIVersion + OutOfDateCIPP = ([version]$RemoteCIPPVersion -gt [version]$CIPPVersion) + OutOfDateCIPPAPI = ([version]$RemoteAPIVersion -gt [version]$APIVersion) + } + if ($version.outOfDateCIPP) { + $Alerts.add(@{Alert = 'Your CIPP Frontend is out of date. Please update to the latest version. Find more on the following '; link = 'https://docs.cipp.app/setup/installation/updating'; type = 'warning' }) + Write-LogMessage -message 'Your CIPP Frontend is out of date. Please update to the latest version' -API 'Updates' -tenant 'All Tenants' -sev Alert + + } + if ($version.outOfDateCIPPAPI) { + $Alerts.add(@{Alert = 'Your CIPP API is out of date. Please update to the latest version. Find more on the following'; link = 'https://docs.cipp.app/setup/installation/updating'; type = 'warning' }) + Write-LogMessage -message 'Your CIPP API is out of date. Please update to the latest version' -API 'Updates' -tenant 'All Tenants' -sev Alert + } + + + if ($env:ApplicationID -eq 'LongApplicationID' -or $null -eq $ENV:ApplicationID) { $Alerts.add(@{Alert = 'You have not yet setup your SAM Setup. Please go to the SAM Wizard in settings to finish setup'; link = '/cipp/setup'; type = 'warning' }) } + if ($env:FUNCTIONS_EXTENSION_VERSION -ne '~4') { + $Alerts.add(@{Alert = 'Your Function App is running on a Runtime version lower than 4. This impacts performance. Go to Settings -> Backend -> Function App Configuration -> Function Runtime Settings and set this to 4 for maximum performance'; link = '/cipp/setup'; type = 'warning' }) + } + if ($psversiontable.psversion.toString() -lt 7.2) { $Alerts.add(@{Alert = 'Your Function App is running on Powershell 7. This impacts performance. Go to Settings -> Backend -> Function App Configuration -> General Settings and set PowerShell Core Version to 7.2 for maximum performance'; link = '/cipp/setup'; type = 'danger' }) } + if ($env:WEBSITE_RUN_FROM_PACKAGE -ne '1') { + $Alerts.add( + @{Alert = 'Your Function App is running in write mode. This will cause performance issues and increase cost. Please check this ' + link = 'https://docs.cipp.app/setup/installation/runfrompackage' + type = 'warning' + }) + } + if ($Rows) { $Rows | ForEach-Object { $alerts.add($_) } } + $Alerts = @($Alerts) + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Alerts + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-GetVersion.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-GetVersion.ps1 new file mode 100644 index 000000000000..b92ecb48d6e6 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-GetVersion.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-GetVersion { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $APIVersion = Get-Content 'version_latest.txt' | Out-String + $CIPPVersion = $request.query.localversion + + $RemoteAPIVersion = Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/KelvinTegelaar/CIPP-API/master/version_latest.txt' + $RemoteCIPPVersion = Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/KelvinTegelaar/CIPP/master/public/version_latest.txt' + + $version = [PSCustomObject]@{ + LocalCIPPVersion = $CIPPVersion + RemoteCIPPVersion = $RemoteCIPPVersion + LocalCIPPAPIVersion = $APIVersion + RemoteCIPPAPIVersion = $RemoteAPIVersion + OutOfDateCIPP = ([version]$RemoteCIPPVersion -gt [version]$CIPPVersion) + OutOfDateCIPPAPI = ([version]$RemoteAPIVersion -gt [version]$APIVersion) + } + # Write to the Azure Functions log stream. + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Version + }) + +} From 439960ec04f2a4513ed48b851704d09cd7dcb9ad Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 01:32:49 +0100 Subject: [PATCH 54/77] removed non required functions, replaced access check --- ExecAccessChecks/function.json | 19 ---------- ExecAccessChecks/run.ps1 | 29 --------------- .../Invoke-Activity_AddOrUpdateTableRows.ps1 | 20 ----------- .../Invoke-Activity_GetAllTableRows.ps1 | 13 ------- .../Entrypoints/Invoke-ExecAccessChecks.ps1 | 36 +++++++++++++++++++ 5 files changed, 36 insertions(+), 81 deletions(-) delete mode 100644 ExecAccessChecks/function.json delete mode 100644 ExecAccessChecks/run.ps1 delete mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_AddOrUpdateTableRows.ps1 delete mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_GetAllTableRows.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAccessChecks.ps1 diff --git a/ExecAccessChecks/function.json b/ExecAccessChecks/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecAccessChecks/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecAccessChecks/run.ps1 b/ExecAccessChecks/run.ps1 deleted file mode 100644 index 0cc71645208e..000000000000 --- a/ExecAccessChecks/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -if ($Request.query.Permissions -eq 'true') { - $Results = Test-CIPPAccessPermissions -tenantfilter $ENV:tenantid -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -} - -if ($Request.query.Tenants -eq 'true') { - $Results = Test-CIPPAccessTenant -Tenantcsv $Request.body.TenantId -} -if ($Request.query.GDAP -eq 'true') { - $Results = Test-CIPPGDAPRelationships -} - -$body = [pscustomobject]@{'Results' = $Results } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_AddOrUpdateTableRows.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_AddOrUpdateTableRows.ps1 deleted file mode 100644 index 4b4fc1e66d8d..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_AddOrUpdateTableRows.ps1 +++ /dev/null @@ -1,20 +0,0 @@ - using namespace System.Net - - Function Invoke-Activity_AddOrUpdateTableRows { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - try { - #Sending each item indivually, if it fails, log an error. - Add-CIPPAzDataTableEntity @Table -Entity $param -Force - } - catch { - Write-LogMessage -API 'Activity_AddOrUpdateTableRows' -message "Unable to write to '$($TableParams.TableName)' Using RowKey $($param.RowKey) table: $($_.Exception.Message)" -sev error - } -} - - } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_GetAllTableRows.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_GetAllTableRows.ps1 deleted file mode 100644 index 493de3147ccc..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-Activity_GetAllTableRows.ps1 +++ /dev/null @@ -1,13 +0,0 @@ - using namespace System.Net - - Function Invoke-Activity_GetAllTableRows { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - Write-Output $Rows - - } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAccessChecks.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAccessChecks.ps1 new file mode 100644 index 000000000000..93a72e2e296d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAccessChecks.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-ExecAccessChecks { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + if ($Request.query.Permissions -eq 'true') { + $Results = Test-CIPPAccessPermissions -tenantfilter $ENV:tenantid -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + } + + if ($Request.query.Tenants -eq 'true') { + $Results = Test-CIPPAccessTenant -Tenantcsv $Request.body.TenantId + } + if ($Request.query.GDAP -eq 'true') { + $Results = Test-CIPPGDAPRelationships + } + + $body = [pscustomobject]@{'Results' = $Results } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} From b7a384a08c24000f720a287537d28ce6b5fbf424 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 01:59:33 +0100 Subject: [PATCH 55/77] replace all list functions --- ListAPDevices/function.json | 19 -- ListAPDevices/run.ps1 | 30 ---- ListAlertsQueue/function.json | 19 -- ListAlertsQueue/run.ps1 | 46 ----- ListAllTenantDeviceCompliance/function.json | 19 -- ListAllTenantDeviceCompliance/run.ps1 | 39 ----- ListAppStatus/function.json | 19 -- ListAppStatus/run.ps1 | 34 ---- ListApplicationQueue/function.json | 19 -- ListApplicationQueue/run.ps1 | 33 ---- ListApps/function.json | 19 -- ListApps/run.ps1 | 29 ---- ListAppsRepository/function.json | 16 -- ListAppsRepository/run.ps1 | 65 ------- ListAutopilotconfig/function.json | 19 -- ListAutopilotconfig/run.ps1 | 36 ---- ListAzureADConnectStatus/function.json | 19 -- ListAzureADConnectStatus/run.ps1 | 72 -------- ListBPA/function.json | 19 -- ListBPATemplates/function.json | 19 -- ListBPATemplates/run.ps1 | 33 ---- ListBasicAuth/function.json | 22 --- ListBasicAuth/run.ps1 | 59 ------- ListBasicAuthAllTenants/function.json | 10 -- ListBasicAuthAllTenants/run.ps1 | 39 ----- ListCAtemplates/function.json | 19 -- ListCAtemplates/run.ps1 | 42 ----- ListCalendarPermissions/function.json | 19 -- ListCalendarPermissions/run.ps1 | 29 ---- ListConditionalAccessPolicies/function.json | 19 -- ListContacts/function.json | 19 -- ListContacts/run.ps1 | 35 ---- ListDefenderState/function.json | 19 -- ListDefenderState/run.ps1 | 31 ---- ListDefenderTVM/function.json | 19 -- ListDefenderTVM/run.ps1 | 43 ----- ListDeletedItems/function.json | 19 -- ListDeletedItems/run.ps1 | 23 --- ListDeviceDetails/function.json | 22 --- ListDeviceDetails/run.ps1 | 93 ---------- ListDevices/function.json | 19 -- ListDevices/run.ps1 | 29 ---- ListDomainHealth/function.json | 19 -- ListDomainHealth/run.ps1 | 146 ---------------- ListDomains/function.json | 19 -- ListDomains/run.ps1 | 29 ---- ListExConnectorTemplates/function.json | 19 -- ListExConnectorTemplates/run.ps1 | 29 ---- ListExchangeConnectors/function.json | 19 -- ListExchangeConnectors/run.ps1 | 25 --- ListExtensionsConfig/function.json | 19 -- ListExtensionsConfig/run.ps1 | 19 -- ListExternalTenantInfo/function.json | 19 -- ListExternalTenantInfo/run.ps1 | 70 -------- ListGDAPInvite/function.json | 19 -- ListGDAPInvite/run.ps1 | 25 --- ListGDAPQueue/function.json | 19 -- ListGDAPQueue/run.ps1 | 28 --- ListGDAPRoles/function.json | 19 -- ListGDAPRoles/run.ps1 | 28 --- ListGenericTestFunction/function.json | 22 --- ListGenericTestFunction/run.ps1 | 13 -- ListGroupTemplates/function.json | 19 -- ListGroupTemplates/run.ps1 | 30 ---- ListGroups/function.json | 19 -- ListGroups/run.ps1 | 72 -------- ListHaloClients/function.json | 19 -- ListHaloClients/run.ps1 | 43 ----- ListInactiveAccounts/function.json | 19 -- ListInactiveAccounts/run.ps1 | 30 ---- ListIntuneIntents/function.json | 19 -- ListIntuneIntents/run.ps1 | 29 ---- ListIntunePolicy/function.json | 19 -- ListIntunePolicy/run.ps1 | 62 ------- ListIntuneTemplates/function.json | 19 -- ListIntuneTemplates/run.ps1 | 44 ----- ListKnownIPDb/function.json | 19 -- ListKnownIPDb/run.ps1 | 20 --- ListLicenses/function.json | 22 --- ListLicenses/run.ps1 | 36 ---- ListLogs/function.json | 19 -- ListLogs/run.ps1 | 54 ------ ListMFAUsers/function.json | 22 --- ListMFAUsers/run.ps1 | 36 ---- ListMailQuarantine/function.json | 19 -- ListMailQuarantine/run.ps1 | 24 --- ListMailboxCAS/function.json | 19 -- ListMailboxCAS/run.ps1 | 36 ---- ListMailboxMobileDevices/function.json | 19 -- ListMailboxMobileDevices/run.ps1 | 49 ------ ListMailboxRules/function.json | 22 --- ListMailboxRules/run.ps1 | 49 ------ ListMailboxStatistics/function.json | 22 --- ListMailboxStatistics/run.ps1 | 60 ------- ListMailboxes/function.json | 19 -- ListMailboxes/run.ps1 | 71 -------- ListMessageTrace/function.json | 19 -- ListMessageTrace/run.ps1 | 39 ----- ListNamedLocations/function.json | 19 -- ListNamedLocations/run.ps1 | 33 ---- ListNotificationConfig/function.json | 19 -- ListNotificationConfig/run.ps1 | 33 ---- ListOAuthApps/function.json | 19 -- ListOAuthApps/run.ps1 | 49 ------ ListOoO/function.json | 19 -- ListOoO/run.ps1 | 21 --- ListOrg/function.json | 19 -- ListOrg/run.ps1 | 26 --- ListPartnerRelationships/function.json | 22 --- ListPartnerRelationships/run.ps1 | 30 ---- ListPhishPolicies/function.json | 19 -- ListPhishPolicies/run.ps1 | 41 ----- ListPotentialApps/function.json | 19 -- ListPotentialApps/run.ps1 | 25 --- ListRoles/function.json | 19 -- ListRoles/run.ps1 | 33 ---- ListScheduledItems/function.json | 19 -- ListScheduledItems/run.ps1 | 15 -- ListServiceHealth/function.json | 16 -- ListServiceHealth/run.ps1 | 36 ---- ListSharedMailboxAccountEnabled/function.json | 19 -- ListSharedMailboxAccountEnabled/run.ps1 | 42 ----- ListSharedMailboxStatistics/function.json | 16 -- ListSharedMailboxStatistics/run.ps1 | 35 ---- ListSharepointQuota/function.json | 19 -- ListSharepointSettings/function.json | 19 -- ListSharepointSettings/run.ps1 | 25 --- ListSignIns/function.json | 22 --- ListSignIns/run.ps1 | 48 ----- ListSites/function.json | 19 -- ListSites/run.ps1 | 47 ----- ListSpamFilterTemplates/function.json | 19 -- ListSpamFilterTemplates/run.ps1 | 27 --- ListSpamfilter/function.json | 19 -- ListSpamfilter/run.ps1 | 26 --- ListStandards/function.json | 19 -- ListStandards/run.ps1 | 46 ----- ListTeams/function.json | 19 -- ListTeams/run.ps1 | 43 ----- ListTeamsActivity/function.json | 16 -- ListTeamsActivity/run.ps1 | 23 --- ListTeamsVoice/function.json | 19 -- ListTeamsVoice/run.ps1 | 42 ----- ListTenantDetails/function.json | 19 -- ListTenantDetails/run.ps1 | 43 ----- ListTenants/function.json | 19 -- ListTenants/run.ps1 | 70 -------- ListTransportRules/function.json | 19 -- ListTransportRules/run.ps1 | 24 --- ListTransportRulesTemplates/function.json | 19 -- ListTransportRulesTemplates/run.ps1 | 39 ----- .../function.json | 19 -- ListUserConditionalAccessPolicies/run.ps1 | 42 ----- ListUserCounts/function.json | 19 -- ListUserCounts/run.ps1 | 38 ---- ListUserDevices/function.json | 19 -- ListUserDevices/run.ps1 | 56 ------ ListUserGroups/function.json | 19 -- ListUserGroups/run.ps1 | 34 ---- ListUserMailboxDetails/function.json | 19 -- ListUserMailboxDetails/run.ps1 | 164 ------------------ ListUserMailboxRules/function.json | 19 -- ListUserMailboxRules/run.ps1 | 39 ----- ListUserPhoto/function.json | 19 -- ListUserPhoto/run.ps1 | 27 --- ListUserSigninLogs/function.json | 19 -- ListUserSigninLogs/run.ps1 | 51 ------ ListUsers/function.json | 22 --- ListUsers/run.ps1 | 83 --------- ListWebhookAlert/function.json | 19 -- ListWebhookAlert/run.ps1 | 16 -- ListmailboxPermissions/function.json | 19 -- ListmailboxPermissions/run.ps1 | 62 ------- .../Entrypoints/Invoke-ListAPDevices.ps1 | 36 ++++ .../Entrypoints/Invoke-ListAlertsQueue.ps1 | 53 ++++++ .../Invoke-ListAllTenantDeviceCompliance.ps1 | 44 +++++ .../Invoke-ListAppConsentRequests.ps1 | 33 ++-- .../Entrypoints/Invoke-ListAppStatus.ps1 | 40 +++++ .../Invoke-ListApplicationQueue.ps1 | 40 +++++ .../Public/Entrypoints/Invoke-ListApps.ps1 | 35 ++++ .../Entrypoints/Invoke-ListAppsRepository.ps1 | 69 ++++++++ .../Invoke-ListAutopilotconfig.ps1 | 42 +++++ .../Invoke-ListAzureADConnectStatus.ps1 | 77 ++++++++ .../Public/Entrypoints/Invoke-ListBPA.ps1 | 15 +- .../Entrypoints/Invoke-ListBPATemplates.ps1 | 40 +++++ .../Entrypoints/Invoke-ListBasicAuth.ps1 | 63 +++++++ .../Invoke-ListBasicAuthAllTenants.ps1 | 45 +++++ .../Entrypoints/Invoke-ListCAtemplates.ps1 | 49 ++++++ .../Invoke-ListCalendarPermissions.ps1 | 35 ++++ .../Invoke-ListConditionalAccessPolicies.ps1 | 17 +- .../Entrypoints/Invoke-ListContacts.ps1 | 41 +++++ .../Entrypoints/Invoke-ListDefenderState.ps1 | 37 ++++ .../Entrypoints/Invoke-ListDefenderTVM.ps1 | 49 ++++++ .../Entrypoints/Invoke-ListDeletedItems.ps1 | 30 ++++ .../Entrypoints/Invoke-ListDeviceDetails.ps1 | 100 +++++++++++ .../Public/Entrypoints/Invoke-ListDevices.ps1 | 35 ++++ .../Entrypoints/Invoke-ListDomainHealth.ps1 | 153 ++++++++++++++++ .../Public/Entrypoints/Invoke-ListDomains.ps1 | 35 ++++ .../Invoke-ListExConnectorTemplates.ps1 | 36 ++++ .../Invoke-ListExchangeConnectors.ps1 | 31 ++++ .../Invoke-ListExtensionsConfig.ps1 | 26 +++ .../Invoke-ListExternalTenantInfo.ps1 | 77 ++++++++ .../Entrypoints/Invoke-ListGDAPInvite.ps1 | 32 ++++ .../Entrypoints/Invoke-ListGDAPQueue.ps1 | 35 ++++ .../Entrypoints/Invoke-ListGDAPRoles.ps1 | 35 ++++ .../Invoke-ListGenericAllTenants.ps1 | 48 +++++ .../Invoke-ListGenericTestFunction.ps1 | 20 +++ .../Entrypoints/Invoke-ListGroupTemplates.ps1 | 37 ++++ .../Public/Entrypoints/Invoke-ListGroups.ps1 | 77 ++++++++ .../Entrypoints/Invoke-ListHaloClients.ps1 | 49 ++++++ .../Invoke-ListInactiveAccounts.ps1 | 36 ++++ .../Entrypoints/Invoke-ListIntuneIntents.ps1 | 35 ++++ .../Entrypoints/Invoke-ListIntunePolicy.ps1 | 67 +++++++ .../Invoke-ListIntuneTemplates.ps1 | 51 ++++++ .../Entrypoints/Invoke-ListKnownIPDb.ps1 | 27 +++ .../Entrypoints/Invoke-ListLicenses.ps1 | 41 +++++ .../Invoke-ListLicensesAllTenants.ps1 | 35 ++++ .../Public/Entrypoints/Invoke-ListLogs.ps1 | 59 +++++++ .../Entrypoints/Invoke-ListMFAUsers.ps1 | 41 +++++ .../Invoke-ListMFAUsersAllTenants.ps1 | 62 +++++++ .../Entrypoints/Invoke-ListMailQuarantine.ps1 | 30 ++++ .../Entrypoints/Invoke-ListMailboxCAS.ps1 | 42 +++++ .../Invoke-ListMailboxMobileDevices.ps1 | 55 ++++++ .../Entrypoints/Invoke-ListMailboxRules.ps1 | 54 ++++++ .../Invoke-ListMailboxRulesAllTenants.ps1 | 59 +++++++ .../Invoke-ListMailboxStatistics.ps1 | 64 +++++++ .../Entrypoints/Invoke-ListMailboxes.ps1 | 77 ++++++++ .../Entrypoints/Invoke-ListMessageTrace.ps1 | 44 +++++ .../Entrypoints/Invoke-ListNamedLocations.ps1 | 39 +++++ .../Invoke-ListNotificationConfig.ps1 | 40 +++++ .../Entrypoints/Invoke-ListOAuthApps.ps1 | 54 ++++++ .../Public/Entrypoints/Invoke-ListOoO.ps1 | 27 +++ .../Public/Entrypoints/Invoke-ListOrg.ps1 | 32 ++++ .../Invoke-ListPartnerRelationships.ps1 | 37 ++++ .../Entrypoints/Invoke-ListPhishPolicies.ps1 | 48 +++++ .../Entrypoints/Invoke-ListPotentialApps.ps1 | 32 ++++ .../Public/Entrypoints/Invoke-ListRoles.ps1 | 40 +++++ .../Entrypoints/Invoke-ListScheduledItems.ps1 | 22 +++ .../Entrypoints/Invoke-ListServiceHealth.ps1 | 43 +++++ ...Invoke-ListSharedMailboxAccountEnabled.ps1 | 48 +++++ .../Invoke-ListSharedMailboxStatistics.ps1 | 40 +++++ .../Invoke-ListSharepointQuota.ps1 | 93 +++++----- .../Invoke-ListSharepointSettings.ps1 | 32 ++++ .../Public/Entrypoints/Invoke-ListSignIns.ps1 | 53 ++++++ .../Public/Entrypoints/Invoke-ListSites.ps1 | 52 ++++++ .../Invoke-ListSpamFilterTemplates.ps1 | 34 ++++ .../Entrypoints/Invoke-ListSpamfilter.ps1 | 32 ++++ .../Entrypoints/Invoke-ListStandards.ps1 | 51 ++++++ .../Public/Entrypoints/Invoke-ListTeams.ps1 | 50 ++++++ .../Entrypoints/Invoke-ListTeamsActivity.ps1 | 30 ++++ .../Entrypoints/Invoke-ListTeamsVoice.ps1 | 47 +++++ .../Entrypoints/Invoke-ListTenantDetails.ps1 | 48 +++++ .../Public/Entrypoints/Invoke-ListTenants.ps1 | 73 ++++++++ .../Entrypoints/Invoke-ListTransportRules.ps1 | 30 ++++ .../Invoke-ListTransportRulesTemplates.ps1 | 46 +++++ ...voke-ListUserConditionalAccessPolicies.ps1 | 48 +++++ .../Entrypoints/Invoke-ListUserCounts.ps1 | 45 +++++ .../Entrypoints/Invoke-ListUserDevices.ps1 | 61 +++++++ .../Entrypoints/Invoke-ListUserGroups.ps1 | 41 +++++ .../Invoke-ListUserMailboxDetails.ps1 | 164 ++++++++++++++++++ .../Invoke-ListUserMailboxRules.ps1 | 45 +++++ .../Entrypoints/Invoke-ListUserPhoto.ps1 | 34 ++++ .../Entrypoints/Invoke-ListUserSettings.ps1 | 3 +- .../Entrypoints/Invoke-ListUserSigninLogs.ps1 | 57 ++++++ .../Public/Entrypoints/Invoke-ListUsers.ps1 | 88 ++++++++++ .../Entrypoints/Invoke-ListWebhookAlert.ps1 | 23 +++ .../Invoke-ListmailboxPermissions.ps1 | 68 ++++++++ .../Public/Invoke-RemoveScheduledItem.ps1 | 25 ++- Z_CIPPHttpTrigger/function.json | 30 ++++ 269 files changed, 4421 insertions(+), 5330 deletions(-) delete mode 100644 ListAPDevices/function.json delete mode 100644 ListAPDevices/run.ps1 delete mode 100644 ListAlertsQueue/function.json delete mode 100644 ListAlertsQueue/run.ps1 delete mode 100644 ListAllTenantDeviceCompliance/function.json delete mode 100644 ListAllTenantDeviceCompliance/run.ps1 delete mode 100644 ListAppStatus/function.json delete mode 100644 ListAppStatus/run.ps1 delete mode 100644 ListApplicationQueue/function.json delete mode 100644 ListApplicationQueue/run.ps1 delete mode 100644 ListApps/function.json delete mode 100644 ListApps/run.ps1 delete mode 100644 ListAppsRepository/function.json delete mode 100644 ListAppsRepository/run.ps1 delete mode 100644 ListAutopilotconfig/function.json delete mode 100644 ListAutopilotconfig/run.ps1 delete mode 100644 ListAzureADConnectStatus/function.json delete mode 100644 ListAzureADConnectStatus/run.ps1 delete mode 100644 ListBPA/function.json delete mode 100644 ListBPATemplates/function.json delete mode 100644 ListBPATemplates/run.ps1 delete mode 100644 ListBasicAuth/function.json delete mode 100644 ListBasicAuth/run.ps1 delete mode 100644 ListBasicAuthAllTenants/function.json delete mode 100644 ListBasicAuthAllTenants/run.ps1 delete mode 100644 ListCAtemplates/function.json delete mode 100644 ListCAtemplates/run.ps1 delete mode 100644 ListCalendarPermissions/function.json delete mode 100644 ListCalendarPermissions/run.ps1 delete mode 100644 ListConditionalAccessPolicies/function.json delete mode 100644 ListContacts/function.json delete mode 100644 ListContacts/run.ps1 delete mode 100644 ListDefenderState/function.json delete mode 100644 ListDefenderState/run.ps1 delete mode 100644 ListDefenderTVM/function.json delete mode 100644 ListDefenderTVM/run.ps1 delete mode 100644 ListDeletedItems/function.json delete mode 100644 ListDeletedItems/run.ps1 delete mode 100644 ListDeviceDetails/function.json delete mode 100644 ListDeviceDetails/run.ps1 delete mode 100644 ListDevices/function.json delete mode 100644 ListDevices/run.ps1 delete mode 100644 ListDomainHealth/function.json delete mode 100644 ListDomainHealth/run.ps1 delete mode 100644 ListDomains/function.json delete mode 100644 ListDomains/run.ps1 delete mode 100644 ListExConnectorTemplates/function.json delete mode 100644 ListExConnectorTemplates/run.ps1 delete mode 100644 ListExchangeConnectors/function.json delete mode 100644 ListExchangeConnectors/run.ps1 delete mode 100644 ListExtensionsConfig/function.json delete mode 100644 ListExtensionsConfig/run.ps1 delete mode 100644 ListExternalTenantInfo/function.json delete mode 100644 ListExternalTenantInfo/run.ps1 delete mode 100644 ListGDAPInvite/function.json delete mode 100644 ListGDAPInvite/run.ps1 delete mode 100644 ListGDAPQueue/function.json delete mode 100644 ListGDAPQueue/run.ps1 delete mode 100644 ListGDAPRoles/function.json delete mode 100644 ListGDAPRoles/run.ps1 delete mode 100644 ListGenericTestFunction/function.json delete mode 100644 ListGenericTestFunction/run.ps1 delete mode 100644 ListGroupTemplates/function.json delete mode 100644 ListGroupTemplates/run.ps1 delete mode 100644 ListGroups/function.json delete mode 100644 ListGroups/run.ps1 delete mode 100644 ListHaloClients/function.json delete mode 100644 ListHaloClients/run.ps1 delete mode 100644 ListInactiveAccounts/function.json delete mode 100644 ListInactiveAccounts/run.ps1 delete mode 100644 ListIntuneIntents/function.json delete mode 100644 ListIntuneIntents/run.ps1 delete mode 100644 ListIntunePolicy/function.json delete mode 100644 ListIntunePolicy/run.ps1 delete mode 100644 ListIntuneTemplates/function.json delete mode 100644 ListIntuneTemplates/run.ps1 delete mode 100644 ListKnownIPDb/function.json delete mode 100644 ListKnownIPDb/run.ps1 delete mode 100644 ListLicenses/function.json delete mode 100644 ListLicenses/run.ps1 delete mode 100644 ListLogs/function.json delete mode 100644 ListLogs/run.ps1 delete mode 100644 ListMFAUsers/function.json delete mode 100644 ListMFAUsers/run.ps1 delete mode 100644 ListMailQuarantine/function.json delete mode 100644 ListMailQuarantine/run.ps1 delete mode 100644 ListMailboxCAS/function.json delete mode 100644 ListMailboxCAS/run.ps1 delete mode 100644 ListMailboxMobileDevices/function.json delete mode 100644 ListMailboxMobileDevices/run.ps1 delete mode 100644 ListMailboxRules/function.json delete mode 100644 ListMailboxRules/run.ps1 delete mode 100644 ListMailboxStatistics/function.json delete mode 100644 ListMailboxStatistics/run.ps1 delete mode 100644 ListMailboxes/function.json delete mode 100644 ListMailboxes/run.ps1 delete mode 100644 ListMessageTrace/function.json delete mode 100644 ListMessageTrace/run.ps1 delete mode 100644 ListNamedLocations/function.json delete mode 100644 ListNamedLocations/run.ps1 delete mode 100644 ListNotificationConfig/function.json delete mode 100644 ListNotificationConfig/run.ps1 delete mode 100644 ListOAuthApps/function.json delete mode 100644 ListOAuthApps/run.ps1 delete mode 100644 ListOoO/function.json delete mode 100644 ListOoO/run.ps1 delete mode 100644 ListOrg/function.json delete mode 100644 ListOrg/run.ps1 delete mode 100644 ListPartnerRelationships/function.json delete mode 100644 ListPartnerRelationships/run.ps1 delete mode 100644 ListPhishPolicies/function.json delete mode 100644 ListPhishPolicies/run.ps1 delete mode 100644 ListPotentialApps/function.json delete mode 100644 ListPotentialApps/run.ps1 delete mode 100644 ListRoles/function.json delete mode 100644 ListRoles/run.ps1 delete mode 100644 ListScheduledItems/function.json delete mode 100644 ListScheduledItems/run.ps1 delete mode 100644 ListServiceHealth/function.json delete mode 100644 ListServiceHealth/run.ps1 delete mode 100644 ListSharedMailboxAccountEnabled/function.json delete mode 100644 ListSharedMailboxAccountEnabled/run.ps1 delete mode 100644 ListSharedMailboxStatistics/function.json delete mode 100644 ListSharedMailboxStatistics/run.ps1 delete mode 100644 ListSharepointQuota/function.json delete mode 100644 ListSharepointSettings/function.json delete mode 100644 ListSharepointSettings/run.ps1 delete mode 100644 ListSignIns/function.json delete mode 100644 ListSignIns/run.ps1 delete mode 100644 ListSites/function.json delete mode 100644 ListSites/run.ps1 delete mode 100644 ListSpamFilterTemplates/function.json delete mode 100644 ListSpamFilterTemplates/run.ps1 delete mode 100644 ListSpamfilter/function.json delete mode 100644 ListSpamfilter/run.ps1 delete mode 100644 ListStandards/function.json delete mode 100644 ListStandards/run.ps1 delete mode 100644 ListTeams/function.json delete mode 100644 ListTeams/run.ps1 delete mode 100644 ListTeamsActivity/function.json delete mode 100644 ListTeamsActivity/run.ps1 delete mode 100644 ListTeamsVoice/function.json delete mode 100644 ListTeamsVoice/run.ps1 delete mode 100644 ListTenantDetails/function.json delete mode 100644 ListTenantDetails/run.ps1 delete mode 100644 ListTenants/function.json delete mode 100644 ListTenants/run.ps1 delete mode 100644 ListTransportRules/function.json delete mode 100644 ListTransportRules/run.ps1 delete mode 100644 ListTransportRulesTemplates/function.json delete mode 100644 ListTransportRulesTemplates/run.ps1 delete mode 100644 ListUserConditionalAccessPolicies/function.json delete mode 100644 ListUserConditionalAccessPolicies/run.ps1 delete mode 100644 ListUserCounts/function.json delete mode 100644 ListUserCounts/run.ps1 delete mode 100644 ListUserDevices/function.json delete mode 100644 ListUserDevices/run.ps1 delete mode 100644 ListUserGroups/function.json delete mode 100644 ListUserGroups/run.ps1 delete mode 100644 ListUserMailboxDetails/function.json delete mode 100644 ListUserMailboxDetails/run.ps1 delete mode 100644 ListUserMailboxRules/function.json delete mode 100644 ListUserMailboxRules/run.ps1 delete mode 100644 ListUserPhoto/function.json delete mode 100644 ListUserPhoto/run.ps1 delete mode 100644 ListUserSigninLogs/function.json delete mode 100644 ListUserSigninLogs/run.ps1 delete mode 100644 ListUsers/function.json delete mode 100644 ListUsers/run.ps1 delete mode 100644 ListWebhookAlert/function.json delete mode 100644 ListWebhookAlert/run.ps1 delete mode 100644 ListmailboxPermissions/function.json delete mode 100644 ListmailboxPermissions/run.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListAPDevices.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListAlertsQueue.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListAllTenantDeviceCompliance.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppStatus.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListApplicationQueue.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListApps.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppsRepository.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListAutopilotconfig.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListAzureADConnectStatus.ps1 rename ListBPA/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 (92%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuth.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListCalendarPermissions.ps1 rename ListConditionalAccessPolicies/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ListConditionalAccessPolicies.ps1 (99%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListContacts.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListDefenderState.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListDefenderTVM.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListDeletedItems.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListDeviceDetails.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListDevices.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomainHealth.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomains.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListExConnectorTemplates.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListExchangeConnectors.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListExtensionsConfig.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPInvite.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPQueue.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPRoles.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericTestFunction.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroupTemplates.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroups.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListHaloClients.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListInactiveAccounts.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneIntents.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntunePolicy.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListKnownIPDb.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailQuarantine.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxCAS.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxMobileDevices.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxStatistics.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxes.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMessageTrace.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListNamedLocations.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListNotificationConfig.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListOAuthApps.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListOoO.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListOrg.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListPartnerRelationships.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListPhishPolicies.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListPotentialApps.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListRoles.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListScheduledItems.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListServiceHealth.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxAccountEnabled.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxStatistics.ps1 rename ListSharepointQuota/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointQuota.ps1 (88%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointSettings.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListSignIns.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListSites.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamFilterTemplates.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamfilter.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListStandards.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeams.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsActivity.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsVoice.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenantDetails.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRules.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserConditionalAccessPolicies.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserCounts.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserDevices.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserGroups.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxDetails.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxRules.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserPhoto.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListmailboxPermissions.ps1 diff --git a/ListAPDevices/function.json b/ListAPDevices/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListAPDevices/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListAPDevices/run.ps1 b/ListAPDevices/run.ps1 deleted file mode 100644 index 7139f25c8c34..000000000000 --- a/ListAPDevices/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$userid = $Request.Query.UserID -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities?`$top=999" -tenantid $TenantFilter - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListAlertsQueue/function.json b/ListAlertsQueue/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListAlertsQueue/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListAlertsQueue/run.ps1 b/ListAlertsQueue/run.ps1 deleted file mode 100644 index bcee463ba113..000000000000 --- a/ListAlertsQueue/run.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$Table = Get-CIPPTable -TableName 'SchedulerConfig' -$Filter = "PartitionKey eq 'Alert'" -$QueuedApps = Get-CIPPAzDataTableEntity @Table -Filter $Filter - -$CurrentStandards = foreach ($QueueFile in $QueuedApps) { - [PSCustomObject]@{ - tenantName = $QueueFile.tenant - AdminPassword = [bool]$QueueFile.AdminPassword - DefenderMalware = [bool]$QueueFile.DefenderMalware - DefenderStatus = [bool]$QueueFile.DefenderStatus - MFAAdmins = [bool]$QueueFile.MFAAdmins - MFAAlertUsers = [bool]$QueueFile.MFAAlertUsers - NewGA = [bool]$QueueFile.NewGA - NewRole = [bool]$QueueFile.NewRole - QuotaUsed = [bool]$QueueFile.QuotaUsed - UnusedLicenses = [bool]$QueueFile.UnusedLicenses - OverusedLicenses = [bool]$QueueFile.OverusedLicenses - AppSecretExpiry = [bool]$QueueFile.AppSecretExpiry - ApnCertExpiry = [bool]$QueueFile.ApnCertExpiry - VppTokenExpiry = [bool]$QueueFile.VppTokenExpiry - DepTokenExpiry = [bool]$QueueFile.DepTokenExpiry - NoCAConfig = [bool]$QueueFile.NoCAConfig - SecDefaultsUpsell = [bool]$QueueFile.SecDefaultsUpsell - SharepointQuota = [bool]$QueueFile.SharePointQuota - ExpiringLicenses = [bool]$QueueFile.ExpiringLicenses - tenantId = $QueueFile.tenantid - } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($CurrentStandards) - }) diff --git a/ListAllTenantDeviceCompliance/function.json b/ListAllTenantDeviceCompliance/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListAllTenantDeviceCompliance/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListAllTenantDeviceCompliance/run.ps1 b/ListAllTenantDeviceCompliance/run.ps1 deleted file mode 100644 index ef5e3119b6d5..000000000000 --- a/ListAllTenantDeviceCompliance/run.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - if ($TenantFilter -eq 'AllTenants') { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedDeviceCompliances" - $StatusCode = [HttpStatusCode]::OK - } - else { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedDeviceCompliances?`$top=999&`$filter=organizationId eq '$TenantFilter'" - $StatusCode = [HttpStatusCode]::OK - } - - if ($GraphRequest.value.count -lt 1) { - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = "No data found - This client might not be onboarded in Lighthouse" - } -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = "Could not connect to Azure Lighthouse API: $($ErrorMessage)" -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListAppStatus/function.json b/ListAppStatus/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListAppStatus/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListAppStatus/run.ps1 b/ListAppStatus/run.ps1 deleted file mode 100644 index 4788e37b468d..000000000000 --- a/ListAppStatus/run.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$tenantfilter = $Request.Query.TenantFilter -$appFilter = $Request.Query.AppFilter -Write-Host "Using $appFilter" -$body = @" -{"select":["DeviceName","UserPrincipalName","Platform","AppVersion","InstallState","InstallStateDetail","LastModifiedDateTime","DeviceId","ErrorCode","UserName","UserId","ApplicationId","AssignmentFilterIdsList","AppInstallState","AppInstallStateDetails","HexErrorCode"],"skip":0,"top":999,"filter":"(ApplicationId eq '$Appfilter')","orderBy":[]} -"@ -try { - $GraphRequest = New-Graphpostrequest -uri "https://graph.microsoft.com/beta/deviceManagement/reports/getDeviceInstallStatusReport" -tenantid $TenantFilter -body $body - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListApplicationQueue/function.json b/ListApplicationQueue/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListApplicationQueue/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListApplicationQueue/run.ps1 b/ListApplicationQueue/run.ps1 deleted file mode 100644 index ec28f3dec728..000000000000 --- a/ListApplicationQueue/run.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." -$Table = Get-CippTable -tablename 'apps' -$QueuedApps = (Get-CIPPAzDataTableEntity @Table) - -$CurrentApps = foreach ($QueueFile in $QueuedApps) { - Write-Host $QueueFile - $ApplicationFile = $QueueFile.JSON | ConvertFrom-Json -depth 10 - [PSCustomObject]@{ - tenantName = $ApplicationFile.tenant - applicationName = $ApplicationFile.Applicationname - cmdLine = $ApplicationFile.IntuneBody.installCommandLine - assignTo = $ApplicationFile.assignTo - id = $($QueueFile.RowKey) - status = $($QueueFile.status) - } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($CurrentApps) - }) diff --git a/ListApps/function.json b/ListApps/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListApps/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListApps/run.ps1 b/ListApps/run.ps1 deleted file mode 100644 index 8291fa283489..000000000000 --- a/ListApps/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?`$top=999&`$filter=(microsoft.graph.managedApp/appAvailability%20eq%20null%20or%20microsoft.graph.managedApp/appAvailability%20eq%20%27lineOfBusiness%27%20or%20isAssigned%20eq%20true)&`$orderby=displayName&" -tenantid $TenantFilter - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListAppsRepository/function.json b/ListAppsRepository/function.json deleted file mode 100644 index c26ed09a89e4..000000000000 --- a/ListAppsRepository/function.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ListAppsRepository/run.ps1 b/ListAppsRepository/run.ps1 deleted file mode 100644 index 1cd40055f84a..000000000000 --- a/ListAppsRepository/run.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$Search = $Request.Body.Search -$Repository = $Request.Body.Repository -$Packages = @() -$Message = '' -$IsError = $false - -try { - if (!([string]::IsNullOrEmpty($Search))) { - if ([string]::IsNullOrEmpty($Repository)) { - $Repository = 'https://chocolatey.org/api/v2' - } - - # Latest version, top 30 results matching search term - $SearchPath = "Search()?`$filter=IsLatestVersion&`$skip=0&`$top=30&searchTerm='$Search'&targetFramework=''&includePrerelease=false" - - $Url = "$Repository/$SearchPath" - $RepoPackages = Invoke-RestMethod $Url -ErrorAction Stop - - if (($RepoPackages | Measure-Object).Count -gt 0) { - $Packages = foreach ($RepoPackage in $RepoPackages) { - [PSCustomObject]@{ - packagename = $RepoPackage.title.'#text' - author = $RepoPackage.author.Name - applicationName = $RepoPackage.properties.Title - version = $RepoPackage.properties.Version - description = $RepoPackage.summary.'#text' - customRepo = $Repository - created = Get-Date -Date $RepoPackage.properties.Created.'#text' -Format 'MM/dd/yyyy HH:mm:ss' - } - } - } - else { - $IsError = $true - $Message = 'No results found' - } - } - else { - $IsError = $true - $Message = 'No search terms specified' - } -} -catch { - $IsError = $true - $Message = "Repository error: $($_.Exception.Message)" -} - -$PackageSearch = @{ - Search = $Search - Results = @($Packages | Sort-Object -Property packagename) - Message = $Message - IsError = $IsError -} - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $PackageSearch - }) diff --git a/ListAutopilotconfig/function.json b/ListAutopilotconfig/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListAutopilotconfig/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListAutopilotconfig/run.ps1 b/ListAutopilotconfig/run.ps1 deleted file mode 100644 index 11042abf346d..000000000000 --- a/ListAutopilotconfig/run.ps1 +++ /dev/null @@ -1,36 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$userid = $Request.Query.UserID -try { - if ($request.query.type -eq "ApProfile") { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles?`$expand=assignments" -tenantid $TenantFilter - } - - if ($request.query.type -eq "ESP") { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations?`$expand=assignments" -tenantid $TenantFilter | Where-Object -Property "@odata.type" -EQ "#microsoft.graph.windows10EnrollmentCompletionPageConfiguration" - } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListAzureADConnectStatus/function.json b/ListAzureADConnectStatus/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListAzureADConnectStatus/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListAzureADConnectStatus/run.ps1 b/ListAzureADConnectStatus/run.ps1 deleted file mode 100644 index 8a97a04820b0..000000000000 --- a/ListAzureADConnectStatus/run.ps1 +++ /dev/null @@ -1,72 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$DataToReturn = $Request.Query.DataToReturn - -if (($DataToReturn -eq 'AzureADConnectSettings') -or ([string]::IsNullOrEmpty($DataToReturn)) ) { - $ADConnectStatusGraph = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/organization" -tenantid $TenantFilter - #$ADConnectStatusGraph = New-ClassicAPIGetRequest -Resource "74658136-14ec-4630-ad9b-26e160ff0fc6" -TenantID $TenantFilter -Uri "https://main.iam.ad.ext.azure.com/api/Directories/ADConnectStatus" -Method "GET" - #$PasswordSyncStatusGraph = New-ClassicAPIGetRequest -Resource "74658136-14ec-4630-ad9b-26e160ff0fc6" -TenantID $TenantFilter -Uri "https://main.iam.ad.ext.azure.com/api/Directories/GetPasswordSyncStatus" -Method "GET" - $AzureADConnectSettings = [PSCustomObject]@{ - dirSyncEnabled = [boolean]$ADConnectStatusGraph.onPremisesSyncEnabled - #dirSyncConfigured = [boolean]$ADConnectStatusGraph.dirSyncConfigured - #passThroughAuthenticationEnabled = [boolean]$ADConnectStatusGraph.passThroughAuthenticationEnabled - #seamlessSingleSignOnEnabled = [boolean]$ADConnectStatusGraph.seamlessSingleSignOnEnabled - numberOfHoursFromLastSync = $ADConnectStatusGraph.onPremisesLastSyncDateTime - #passwordSyncStatus = [boolean]$PasswordSyncStatusGraph - raw = $ADConnectStatusGraph - } -} - -if (($DataToReturn -eq 'AzureADObjectsInError') -or ([string]::IsNullOrEmpty($DataToReturn)) ) { - $selectlist = "id", "displayName", "onPremisesProvisioningErrors", "createdDateTime" - $Types = "Users", "Contacts", "Groups" - - $GraphRequest = foreach ($Type in $types) { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/$($Type)?`$select=$($selectlist -join ',')" -tenantid $TenantFilter | ForEach-Object { - if ($_.id -ne $null) { - $_ | Add-Member -NotePropertyName ObjectType -NotePropertyValue $Type - $_ - } - - } - } - $ObjectsInError = @($GraphRequest) -} - -if ([string]::IsNullOrEmpty($DataToReturn)) { - $FinalObject = [PSCustomObject]@{ - AzureADConnectSettings = $AzureADConnectSettings - ObjectsInError = $ObjectsInError - } -} -if ($DataToReturn -eq 'AzureADConnectSettings') { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $AzureADConnectSettings - }) -} -elseif ($DataToReturn -eq 'AzureADObjectsInError') { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($ObjectsInError) - }) -} -else { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($FinalObject) - }) -} - diff --git a/ListBPA/function.json b/ListBPA/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListBPA/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListBPATemplates/function.json b/ListBPATemplates/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListBPATemplates/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListBPATemplates/run.ps1 b/ListBPATemplates/run.ps1 deleted file mode 100644 index 53b7a23034fc..000000000000 --- a/ListBPATemplates/run.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -Write-Host 'PowerShell HTTP trigger function processed a request.' -Write-Host $Request.query.id - -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$Templates = Get-ChildItem 'Config\*.BPATemplate.json' - -if ($Request.Query.RawJson) { - $Templates = $Templates | ForEach-Object { - $(Get-Content $_) | ConvertFrom-Json - } -} else { - $Templates = $Templates | ForEach-Object { - $Template = $(Get-Content $_) | ConvertFrom-Json - @{ - Data = $Template.fields - Name = $Template.Name - Style = $Template.Style - } - } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = ($Templates | ConvertTo-Json -Depth 10) - }) diff --git a/ListBasicAuth/function.json b/ListBasicAuth/function.json deleted file mode 100644 index 77f0dced2d90..000000000000 --- a/ListBasicAuth/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "baqueue" - } - ] -} diff --git a/ListBasicAuth/run.ps1 b/ListBasicAuth/run.ps1 deleted file mode 100644 index e9ccd5c46cc3..000000000000 --- a/ListBasicAuth/run.ps1 +++ /dev/null @@ -1,59 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$currentTime = Get-Date -Format "yyyy-MM-ddTHH:MM:ss" -$ts = (Get-Date).AddDays(-30) -$endTime = $ts.ToString("yyyy-MM-ddTHH:MM:ss") -##Create Filter for basic auth sign-ins -$filters = "createdDateTime ge $($endTime)Z and createdDateTime lt $($currentTime)Z and (clientAppUsed eq 'AutoDiscover' or clientAppUsed eq 'Exchange ActiveSync' or clientAppUsed eq 'Exchange Online PowerShell' or clientAppUsed eq 'Exchange Web Services' or clientAppUsed eq 'IMAP4' or clientAppUsed eq 'MAPI Over HTTP' or clientAppUsed eq 'Offline Address Book' or clientAppUsed eq 'Outlook Anywhere (RPC over HTTP)' or clientAppUsed eq 'Other clients' or clientAppUsed eq 'POP3' or clientAppUsed eq 'Reporting Web Services' or clientAppUsed eq 'Authenticated SMTP' or clientAppUsed eq 'Outlook Service')" -if ($TenantFilter -ne 'AllTenants') { - - try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/signIns?api-version=beta&filter=$($filters)" -tenantid $TenantFilter -erroraction stop | Select-Object userPrincipalName, clientAppUsed, Status | Sort-Object -Unique -Property userPrincipalName - $response = $GraphRequest - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Retrieved basic authentication report" -Sev "Debug" -tenant $TenantFilter - - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($response) - }) - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve basic authentication report: $($_.Exception.message) " -Sev "Error" -tenant $TenantFilter - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = '500' - Body = $(Get-NormalizedError -message $_.Exception.message) - }) - } -} -else { - $Table = Get-CIPPTable -TableName cachebasicauth - $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) - if (!$Rows) { - Push-OutputBinding -Name Msg -Value (Get-Date).ToString() - $GraphRequest = [PSCustomObject]@{ - Tenant = 'Loading data for all tenants. Please check back in 10 minutes' - } - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) - } - else { - $GraphRequest = $Rows - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) - } -} \ No newline at end of file diff --git a/ListBasicAuthAllTenants/function.json b/ListBasicAuthAllTenants/function.json deleted file mode 100644 index 241cfb854118..000000000000 --- a/ListBasicAuthAllTenants/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "baqueue" - } - ] -} diff --git a/ListBasicAuthAllTenants/run.ps1 b/ListBasicAuthAllTenants/run.ps1 deleted file mode 100644 index 1fcbca1b4807..000000000000 --- a/ListBasicAuthAllTenants/run.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -# Input bindings are passed in via param block. -param([string] $QueueItem, $TriggerMetadata) - -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell queue trigger function processed work item: $QueueItem" - -Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module '.\GraphHelper.psm1' - $currentTime = Get-Date -Format "yyyy-MM-ddTHH:MM:ss" - $ts = (Get-Date).AddDays(-30) - $endTime = $ts.ToString("yyyy-MM-ddTHH:MM:ss") - $filters = "createdDateTime ge $($endTime)Z and createdDateTime lt $($currentTime)Z and (clientAppUsed eq 'AutoDiscover' or clientAppUsed eq 'Exchange ActiveSync' or clientAppUsed eq 'Exchange Online PowerShell' or clientAppUsed eq 'Exchange Web Services' or clientAppUsed eq 'IMAP4' or clientAppUsed eq 'MAPI Over HTTP' or clientAppUsed eq 'Offline Address Book' or clientAppUsed eq 'Outlook Anywhere (RPC over HTTP)' or clientAppUsed eq 'Other clients' or clientAppUsed eq 'POP3' or clientAppUsed eq 'Reporting Web Services' or clientAppUsed eq 'Authenticated SMTP' or clientAppUsed eq 'Outlook Service')" - try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/signIns?api-version=beta&filter=$($filters)" -tenantid $domainName -ErrorAction stop | Sort-Object -Unique -Property clientAppUsed | ForEach-Object { - @{ - Tenant = $domainName - clientAppUsed = $_.clientAppUsed - userPrincipalName = $_.UserPrincipalName - RowKey = "$($_.UserPrincipalName)-$($_.clientAppUsed)" - PartitionKey = 'basicauth' - } - } - } - catch { - $GraphRequest = @{ - Tenant = $domainName - clientAppUsed = "Could not connect to Tenant: $($_.Exception.message)" - userPrincipalName = $domainName - RowKey = $domainName - PartitionKey = 'basicauth' - } - } - $Table = Get-CIPPTable -TableName cachebasicauth - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - -} - - diff --git a/ListCAtemplates/function.json b/ListCAtemplates/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListCAtemplates/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListCAtemplates/run.ps1 b/ListCAtemplates/run.ps1 deleted file mode 100644 index 99c5660c4345..000000000000 --- a/ListCAtemplates/run.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -Set-Location (Get-Item $PSScriptRoot).Parent.FullName - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." -Write-Host $Request.query.id -#Migrating old policies whenever you do a list -$Table = Get-CippTable -tablename 'templates' - -$Templates = Get-ChildItem "Config\*.CATemplate.json" | ForEach-Object { - $Entity = @{ - JSON = "$(Get-Content $_)" - RowKey = "$($_.name)" - PartitionKey = "CATemplate" - GUID = "$($_.name)" - } - Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force -} - -#List new policies -$Table = Get-CippTable -tablename 'templates' -$Filter = "PartitionKey eq 'CATemplate'" -$Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { - $data = $_.JSON | ConvertFrom-Json -Depth 100 - $data | Add-Member -NotePropertyName "GUID" -NotePropertyValue $_.GUID -Force - $data -} | Sort-Object -Property displayName - -if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property GUID -EQ $Request.query.id } - -$Templates = ConvertTo-Json -InputObject @($Templates) -Depth 100 -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Templates - }) diff --git a/ListCalendarPermissions/function.json b/ListCalendarPermissions/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/ListCalendarPermissions/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListCalendarPermissions/run.ps1 b/ListCalendarPermissions/run.ps1 deleted file mode 100644 index 385c64c70232..000000000000 --- a/ListCalendarPermissions/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$UserID = $request.Query.UserID -$Tenantfilter = $request.Query.tenantfilter - -try { - $GetCalParam = @{Identity = $UserID; FolderScope = 'Calendar' } - $CalendarFolder = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-MailboxFolderStatistics" -cmdParams $GetCalParam | Select-Object -First 1 - $CalParam = @{Identity = "$($UserID):\$($CalendarFolder.name)" } - $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-MailboxFolderPermission" -cmdParams $CalParam -UseSystemMailbox $true | Select-Object Identity, User, AccessRights, FolderName - Write-LogMessage -API 'List Calendar Permissions' -tenant $tenantfilter -message "Calendar permissions listed for $($tenantfilter)" -sev Debug - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListConditionalAccessPolicies/function.json b/ListConditionalAccessPolicies/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListConditionalAccessPolicies/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListContacts/function.json b/ListContacts/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListContacts/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListContacts/run.ps1 b/ListContacts/run.ps1 deleted file mode 100644 index 35b934b44fbc..000000000000 --- a/ListContacts/run.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -$selectlist = "id", "companyName", "displayName", "mail", "onPremisesSyncEnabled", "editURL" - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$ContactID = $Request.Query.ContactID - -Write-Host "Tenant Filter: $TenantFilter" -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/contacts/$($ContactID)?`$top=999&`$select=$($selectlist -join ',')" -tenantid $TenantFilter | Select-Object $selectlist | ForEach-Object { - $_.editURL = "https://outlook.office365.com/ecp/@$TenantFilter/UsersGroups/EditContact.aspx?exsvurl=1&realm=$($env:TenantID)&mkt=en-US&id=$($_.id)" - $_ - } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest | Where-Object -Property id -ne $null) - }) diff --git a/ListDefenderState/function.json b/ListDefenderState/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListDefenderState/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListDefenderState/run.ps1 b/ListDefenderState/run.ps1 deleted file mode 100644 index 896121445df0..000000000000 --- a/ListDefenderState/run.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$StatusCode = [HttpStatusCode]::OK - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsProtectionStates?`$top=999&`$filter=tenantId eq '$TenantFilter'" - if ($GraphRequest.tenantDisplayName.length -lt 1) { - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = "No data found - This client might not be onboarded in Lighthouse" - } -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = "Could not connect to Azure Lighthouse API: $($ErrorMessage)" -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListDefenderTVM/function.json b/ListDefenderTVM/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListDefenderTVM/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListDefenderTVM/run.ps1 b/ListDefenderTVM/run.ps1 deleted file mode 100644 index 16a934b94c21..000000000000 --- a/ListDefenderTVM/run.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $GraphRequest = New-GraphgetRequest -tenantid $TenantFilter -uri "https://api.securitycenter.microsoft.com/api/machines/SoftwareVulnerabilitiesByMachine?`$top=999" -scope "https://api.securitycenter.microsoft.com/.default" | Group-Object cveid - $GroupObj = foreach ($cve in $GraphRequest) { - [pscustomobject]@{ - customerId = $TenantFilter - affectedDevicesCount = $cve.count - cveId = $cve.name - affectedDevices = ($cve.group.deviceName -join ', ') - osPlatform = ($cve.group.osplatform | Sort-Object -Unique) - softwareVendor = ($cve.group.softwareVendor | Sort-Object -Unique) - softwareName = ($cve.group.softwareName | Sort-Object -Unique) - vulnerabilitySeverityLevel = ($cve.group.vulnerabilitySeverityLevel | Sort-Object -Unique) - cvssScore = ($cve.group.cvssScore | Sort-Object -Unique) - securityUpdateAvailable = ($cve.group.securityUpdateAvailable | Sort-Object -Unique) - exploitabilityLevel = ($cve.group.exploitabilityLevel | Sort-Object -Unique) - } - } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GroupObj = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GroupObj) - }) diff --git a/ListDeletedItems/function.json b/ListDeletedItems/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListDeletedItems/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListDeletedItems/run.ps1 b/ListDeletedItems/run.ps1 deleted file mode 100644 index a90b359d42a2..000000000000 --- a/ListDeletedItems/run.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -$selectlist = "id", "accountEnabled", "businessPhones", "city", "createdDateTime", "companyName", "country", "department", "displayName", "faxNumber", "givenName", "isResourceAccount", "jobTitle", "mail", "mailNickname", "mobilePhone", "onPremisesDistinguishedName", "officeLocation", "onPremisesLastSyncDateTime", "otherMails", "postalCode", "preferredDataLocation", "preferredLanguage", "proxyAddresses", "showInAddressList", "state", "streetAddress", "surname", "usageLocation", "userPrincipalName", "userType", "assignedLicenses", "onPremisesSyncEnabled", "LicJoined", "Aliases", "primDomain" - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$Types = "Application", "User", "Device", "Group" -$GraphRequest = foreach ($Type in $Types) { - (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/directory/deletedItems/microsoft.graph.$($Type)" -tenantid $TenantFilter) | Where-Object -Property '@odata.context' -NotLike "*graph.microsoft.com*" | Select-Object *, @{ Name = 'TargetType'; Expression = { $Type } } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListDeviceDetails/function.json b/ListDeviceDetails/function.json deleted file mode 100644 index 3d31416065c2..000000000000 --- a/ListDeviceDetails/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "generalAllTenantQueue" - } - ] -} diff --git a/ListDeviceDetails/run.ps1 b/ListDeviceDetails/run.ps1 deleted file mode 100644 index 14e97fb6a155..000000000000 --- a/ListDeviceDetails/run.ps1 +++ /dev/null @@ -1,93 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$DeviceID = $Request.Query.DeviceID -$DeviceName = $Request.Query.DeviceName -$DeviceSerial = $Request.Query.DeviceSerial - -try { - if ($DeviceID) { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceID" -Tenantid $tenantfilter - } elseif ($DeviceSerial -or $DeviceName) { - $Found = $False - if ($SeriaNumber -and $DeviceName) { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=serialnumber eq '$DeviceSerial' and deviceName eq '$DeviceName'" -Tenantid $tenantfilter - - if (($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 ) { - $Found = $True - } - } - if ($DeviceSerial -and $Found -eq $False) { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=serialnumber eq '$DeviceSerial'" -Tenantid $tenantfilter - if (($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 ) { - $Found = $True - } - } - if ($DeviceName -and $Found -eq $False) { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=deviceName eq '$DeviceName'" -Tenantid $tenantfilter - if (($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 ) { - $Found = $True - } - } - - } - - if (!(($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 )) { - $GraphRequest = $Null - } - - if ($GraphRequest){ - [System.Collections.Generic.List[PSCustomObject]]$BulkRequests = @( - @{ - id = 'DeviceGroups' - method = 'GET' - url = "/devices(deviceID='$($GraphRequest.azureADDeviceId)')/memberOf" - }, - @{ - id = 'CompliancePolicies' - method = 'GET' - url = "/deviceManagement/managedDevices('$($GraphRequest.id)')/deviceCompliancePolicyStates" - }, - @{ - id = 'DetectedApps' - method = 'GET' - url = "deviceManagement/managedDevices('$($GraphRequest.id)')?expand=detectedApps" - } - ) - - $BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantFilter - - $DeviceGroups = Get-GraphBulkResultByID -Results $BulkResults -ID 'DeviceGroups' -Value - $CompliancePolicies = Get-GraphBulkResultByID -Results $BulkResults -ID 'CompliancePolicies' -Value - $DetectedApps = Get-GraphBulkResultByID -Results $BulkResults -ID 'DetectedApps' - - $Null = $GraphRequest | Add-Member -NotePropertyName 'DetectedApps' -NotePropertyValue ($DetectedApps.DetectedApps | select-object id, displayName, version) - $Null = $GraphRequest | Add-Member -NotePropertyName 'CompliancePolicies' -NotePropertyValue ($CompliancePolicies | select-object id, displayname, UserPrincipalName, state) - $Null = $GraphRequest | Add-Member -NotePropertyName 'DeviceGroups' -NotePropertyValue ($DeviceGroups | select-object id, displayName, description) - - - } - - $StatusCode = [HttpStatusCode]::OK -} catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage - -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $GraphRequest - }) diff --git a/ListDevices/function.json b/ListDevices/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListDevices/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListDevices/run.ps1 b/ListDevices/run.ps1 deleted file mode 100644 index e25cc83040c7..000000000000 --- a/ListDevices/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices" -Tenantid $tenantfilter - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage - -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListDomainHealth/function.json b/ListDomainHealth/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListDomainHealth/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListDomainHealth/run.ps1 b/ListDomainHealth/run.ps1 deleted file mode 100644 index 29dc7ff06f36..000000000000 --- a/ListDomainHealth/run.ps1 +++ /dev/null @@ -1,146 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -Import-Module DNSHealth - -try { - $ConfigTable = Get-CippTable -tablename Config - $Filter = "PartitionKey eq 'Domains' and RowKey eq 'Domains'" - $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter $Filter - - $ValidResolvers = @('Google', 'CloudFlare', 'Quad9') - if ($ValidResolvers -contains $Config.Resolver) { - $Resolver = $Config.Resolver - } else { - $Resolver = 'Google' - $Config = @{ - PartitionKey = 'Domains' - RowKey = 'Domains' - Resolver = $Resolver - } - Add-CIPPAzDataTableEntity @ConfigTable -Entity $Config -Force - } -} catch { - $Resolver = 'Google' -} - -Set-DnsResolver -Resolver $Resolver - -$UserCreds = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($request.headers.'x-ms-client-principal')) | ConvertFrom-Json) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -$StatusCode = [HttpStatusCode]::OK -try { - if ($Request.Query.Action) { - if ($Request.Query.Domain -match '^(((?!-))(xn--|_{1,1})?[a-z0-9-]{0,61}[a-z0-9]{1,1}\.)*(xn--)?([a-z0-9][a-z0-9\-]{0,60}|[a-z0-9-]{1,30}\.[a-z]{2,})$') { - $DomainTable = Get-CIPPTable -Table 'Domains' - $Filter = "RowKey eq '{0}'" -f $Request.Query.Domain - $DomainInfo = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter - switch ($Request.Query.Action) { - 'ListDomainInfo' { - $Body = $DomainInfo - } - 'GetDkimSelectors' { - $Body = ($DomainInfo.DkimSelectors | ConvertFrom-Json) -join ',' - } - 'ReadSpfRecord' { - $SpfQuery = @{ - Domain = $Request.Query.Domain - } - - if ($Request.Query.ExpectedInclude) { - $SpfQuery.ExpectedInclude = $Request.Query.ExpectedInclude - } - - if ($Request.Query.Record) { - $SpfQuery.Record = $Request.Query.Record - } - - $Body = Read-SpfRecord @SpfQuery - } - 'ReadDmarcPolicy' { - $Body = Read-DmarcPolicy -Domain $Request.Query.Domain - } - 'ReadDkimRecord' { - $DkimQuery = @{ - Domain = $Request.Query.Domain - } - if ($Request.Query.Selector) { - $DkimQuery.Selectors = ($Request.Query.Selector).trim() -split '\s*,\s*' - - if ('admin' -in $UserCreds.userRoles -or 'editor' -in $UserCreds.userRoles) { - $DkimSelectors = [string]($DkimQuery.Selectors | ConvertTo-Json -Compress) - if ($DomainInfo) { - $DomainInfo.DkimSelectors = $DkimSelectors - } else { - $DomainInfo = @{ - 'RowKey' = $Request.Query.Domain - 'PartitionKey' = 'ManualEntry' - 'TenantId' = 'NoTenant' - 'MailProviders' = '' - 'TenantDetails' = '' - 'DomainAnalyser' = '' - 'DkimSelectors' = $DkimSelectors - } - } - Write-Host $DomainInfo - Add-CIPPAzDataTableEntity @DomainTable -Entity $DomainInfo -Force - } - } elseif (![string]::IsNullOrEmpty($DomainInfo.DkimSelectors)) { - $DkimQuery.Selectors = [System.Collections.Generic.List[string]]($DomainInfo.DkimSelectors | ConvertFrom-Json) - } - $Body = Read-DkimRecord @DkimQuery - - } - 'ReadMXRecord' { - $Body = Read-MXRecord -Domain $Request.Query.Domain - } - 'TestDNSSEC' { - $Body = Test-DNSSEC -Domain $Request.Query.Domain - } - 'ReadWhoisRecord' { - $Body = Read-WhoisRecord -Query $Request.Query.Domain - } - 'ReadNSRecord' { - $Body = Read-NSRecord -Domain $Request.Query.Domain - } - 'TestHttpsCertificate' { - $HttpsQuery = @{ - Domain = $Request.Query.Domain - } - if ($Request.Query.Subdomains) { - $HttpsQuery.Subdomains = ($Request.Query.Subdomains).trim() -split '\s*,\s*' - } else { - $HttpsQuery.Subdomains = 'www' - } - - $Body = Test-HttpsCertificate @HttpsQuery - } - 'TestMtaSts' { - $HttpsQuery = @{ - Domain = $Request.Query.Domain - } - $Body = Test-MtaSts @HttpsQuery - } - } - } else { - $body = [pscustomobject]@{'Results' = "Domain: $($Request.Query.Domain) is invalid" } - } - } -} catch { - Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "DNS Helper API failed. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $body - }) diff --git a/ListDomains/function.json b/ListDomains/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListDomains/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListDomains/run.ps1 b/ListDomains/run.ps1 deleted file mode 100644 index fafe94ee47a8..000000000000 --- a/ListDomains/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter - -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/domains" -tenantid $TenantFilter | Select-Object id, isdefault, isinitial | Sort-Object isdefault - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListExConnectorTemplates/function.json b/ListExConnectorTemplates/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListExConnectorTemplates/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListExConnectorTemplates/run.ps1 b/ListExConnectorTemplates/run.ps1 deleted file mode 100644 index 8ab8d57480bc..000000000000 --- a/ListExConnectorTemplates/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Table = Get-CippTable -tablename 'templates' - -#List new policies -$Table = Get-CippTable -tablename 'templates' -$Filter = "PartitionKey eq 'ExConnectorTemplate'" -$Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { - $GUID = $_.RowKey - $Direction = $_.direction - $data = $_.JSON | ConvertFrom-Json - $data | Add-Member -NotePropertyName "GUID" -NotePropertyValue $GUID - $data | Add-Member -NotePropertyName "cippconnectortype" -NotePropertyValue $Direction - $data -} - -if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property RowKey -EQ $Request.query.id } - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($Templates) - }) diff --git a/ListExchangeConnectors/function.json b/ListExchangeConnectors/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/ListExchangeConnectors/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListExchangeConnectors/run.ps1 b/ListExchangeConnectors/run.ps1 deleted file mode 100644 index 8009c69671af..000000000000 --- a/ListExchangeConnectors/run.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Tenantfilter = $request.Query.tenantfilter - -$Results = try { - New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-OutboundConnector" | Select-Object *, @{n = 'cippconnectortype'; e = { 'outbound' } } - New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-InboundConnector" | Select-Object *, @{n = 'cippconnectortype'; e = { 'Inbound' } } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($Results) - }) diff --git a/ListExtensionsConfig/function.json b/ListExtensionsConfig/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListExtensionsConfig/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListExtensionsConfig/run.ps1 b/ListExtensionsConfig/run.ps1 deleted file mode 100644 index 1de02456f1a2..000000000000 --- a/ListExtensionsConfig/run.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$Table = Get-CIPPTable -TableName Extensionsconfig -try { - $Body = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 -ErrorAction Stop -} catch { - $Body = @{} -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ListExternalTenantInfo/function.json b/ListExternalTenantInfo/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListExternalTenantInfo/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListExternalTenantInfo/run.ps1 b/ListExternalTenantInfo/run.ps1 deleted file mode 100644 index 6d34411ec825..000000000000 --- a/ListExternalTenantInfo/run.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$Tenant = $request.query.tenant - -# Normalize to tenantid and determine if tenant exists -$TenantId = (Invoke-RestMethod -Method GET "https://login.windows.net/$tenant/.well-known/openid-configuration").token_endpoint.Split('/')[3] - -if ($TenantId) { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$TenantId')" -noauthcheck $true -tenantid $TenantFilter - $StatusCode = [HttpStatusCode]::OK -} - -if ($GraphRequest) { - - $TenantDefaultDomain = $GraphRequest.defaultDomainName - - $body = @" - - - - http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation - https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc - - http://www.w3.org/2005/08/addressing/anonymous - - - - - - $TenantDefaultDomain - - - - -"@ - - # Create the headers - $headers = @{ - "Content-Type" = "text/xml; charset=utf-8" - "SOAPAction" = '"http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation"' - "User-Agent" = "AutodiscoverClient" - } - - # Invoke - $response = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc" -Body $body -Headers $headers - - # Return - $TenantDomains = $response.Envelope.body.GetFederationInformationResponseMessage.response.Domains.Domain | Sort-Object -} - -$results = [PSCustomObject]@{ - GraphRequest = $GraphRequest - Domains = $TenantDomains -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $results - }) diff --git a/ListGDAPInvite/function.json b/ListGDAPInvite/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListGDAPInvite/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListGDAPInvite/run.ps1 b/ListGDAPInvite/run.ps1 deleted file mode 100644 index f8f61376c5f8..000000000000 --- a/ListGDAPInvite/run.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -Write-Host ($Request | ConvertTo-Json) -if (![string]::IsNullOrEmpty($Request.Query.RelationshipId)) { - $Table = Get-CIPPTable -TableName 'GDAPInvites' - $Invite = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$($Request.Query.RelationshipId)'" - Write-Host $Invite -} else { - $Invite = @{} -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Invite - }) diff --git a/ListGDAPQueue/function.json b/ListGDAPQueue/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListGDAPQueue/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListGDAPQueue/run.ps1 b/ListGDAPQueue/run.ps1 deleted file mode 100644 index 384b509cdc63..000000000000 --- a/ListGDAPQueue/run.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$Table = Get-CIPPTable -TableName 'GDAPMigration' -$QueuedApps = Get-CIPPAzDataTableEntity @Table - -$CurrentStandards = foreach ($QueueFile in $QueuedApps) { - [PSCustomObject]@{ - Tenant = $QueueFile.tenant - Status = $QueueFile.status - StartAt = $QueueFile.startAt - } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($CurrentStandards) - }) diff --git a/ListGDAPRoles/function.json b/ListGDAPRoles/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListGDAPRoles/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListGDAPRoles/run.ps1 b/ListGDAPRoles/run.ps1 deleted file mode 100644 index fc389cb54702..000000000000 --- a/ListGDAPRoles/run.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$Table = Get-CIPPTable -TableName 'GDAPRoles' -$Groups = Get-CIPPAzDataTableEntity @Table - -$MappedGroups = foreach ($Group in $Groups) { - [PSCustomObject]@{ - GroupName = $Group.GroupName - GroupId = $Group.GroupId - RoleName = $Group.RoleName - roleDefinitionId = $Group.roleDefinitionId - } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($MappedGroups) - }) diff --git a/ListGenericTestFunction/function.json b/ListGenericTestFunction/function.json deleted file mode 100644 index 3d31416065c2..000000000000 --- a/ListGenericTestFunction/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "generalAllTenantQueue" - } - ] -} diff --git a/ListGenericTestFunction/run.ps1 b/ListGenericTestFunction/run.ps1 deleted file mode 100644 index 64b7583f9f3c..000000000000 --- a/ListGenericTestFunction/run.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -$graphRequest = ($request.headers.'x-ms-original-url').split('/api') | Select-Object -First 1 - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($graphRequest) - }) -clobber \ No newline at end of file diff --git a/ListGroupTemplates/function.json b/ListGroupTemplates/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListGroupTemplates/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListGroupTemplates/run.ps1 b/ListGroupTemplates/run.ps1 deleted file mode 100644 index df07537a56a7..000000000000 --- a/ListGroupTemplates/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -Set-Location (Get-Item $PSScriptRoot).Parent.FullName - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." -Write-Host $Request.query.id - -#List new policies -$Table = Get-CippTable -tablename 'templates' -$Filter = "PartitionKey eq 'GroupTemplate'" -$Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { - $data = $_.JSON | ConvertFrom-Json - $data | Add-Member -MemberType NoteProperty -Name GUID -Value $_.RowKey -Force - $data -} | Sort-Object -Property displayName - -if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property GUID -EQ $Request.query.id } - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($Templates) - }) diff --git a/ListGroups/function.json b/ListGroups/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListGroups/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListGroups/run.ps1 b/ListGroups/run.ps1 deleted file mode 100644 index 8739c362b27e..000000000000 --- a/ListGroups/run.ps1 +++ /dev/null @@ -1,72 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. - -$TenantFilter = $Request.Query.TenantFilter -$selectstring = "id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule,grouptypes,onPremisesSyncEnabled,resourceProvisioningOptions,userPrincipalName&`$expand=members(`$select=userPrincipalName)" - -if ($Request.Query.GroupID) { - $groupid = $Request.query.groupid - $selectstring = "id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule,groupTypes,userPrincipalName" -} -if ($Request.Query.members) { - $members = "members" - $selectstring = "id,userPrincipalName,displayName,hideFromOutlookClients,hideFromAddressLists,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule" -} - -if ($Request.Query.owners) { - $members = "owners" - $selectstring = "id,userPrincipalName,displayName,hideFromOutlookClients,hideFromAddressLists,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule" -} -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/$($GroupID)/$($members)?`$top=999&select=$selectstring" -tenantid $TenantFilter | Select-Object *, @{ Name = 'primDomain'; Expression = { $_.mail -split "@" | Select-Object -Last 1 } }, - @{Name = 'membersCsv'; Expression = { $_.members.userPrincipalName -join "," } }, - @{Name = 'teamsEnabled'; Expression = { if ($_.resourceProvisioningOptions -Like '*Team*') { $true }else { $false } } }, - @{Name = 'calculatedGroupType'; Expression = { - - if ($_.mailEnabled -and $_.securityEnabled) { - "Mail-Enabled Security" - } - if (!$_.mailEnabled -and $_.securityEnabled) { - "Security" - } - if ($_.groupTypes -contains 'Unified') { - "Microsoft 365" - } - if (([string]::isNullOrEmpty($_.groupTypes)) -and ($_.mailEnabled) -and (!$_.securityEnabled)) { - "Distribution List" - } - } - }, - @{Name = 'dynamicGroupBool'; Expression = { - if ($_.groupTypes -contains 'DynamicMembership') { - $true - } - else { - $false - } - } - } - - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListHaloClients/function.json b/ListHaloClients/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListHaloClients/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListHaloClients/run.ps1 b/ListHaloClients/run.ps1 deleted file mode 100644 index dc5d64867528..000000000000 --- a/ListHaloClients/run.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -try { - $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = ((Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json).HaloPSA - $Token = Get-HaloToken -configuration $Configuration - $i = 1 - $RawHaloClients = do { - $Result = Invoke-RestMethod -Uri "$($Configuration.ResourceURL)/Client?page_no=$i&page_size=999&pageinate=true" -ContentType 'application/json' -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } - $Result.clients | Select-Object * -ExcludeProperty logo - $i++ - $pagecount = [Math]::Ceiling($Result.record_count / 999) - } while ($i -le $pagecount) - $HaloClients = $RawHaloClients | ForEach-Object { - [PSCustomObject]@{ - label = $_.name - value = $_.id - } - } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $HaloClients = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($HaloClients) - }) diff --git a/ListInactiveAccounts/function.json b/ListInactiveAccounts/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListInactiveAccounts/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListInactiveAccounts/run.ps1 b/ListInactiveAccounts/run.ps1 deleted file mode 100644 index 097e65b15c85..000000000000 --- a/ListInactiveAccounts/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -if ($TenantFilter -eq "AllTenants") { $TenantFilter = (get-tenants).customerId } -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/inactiveUsers?`$count=true" -tenantid $env:TenantId | Where-Object { $_.tenantId -in $TenantFilter } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = "Could not connect to Azure Lighthouse API: $($ErrorMessage)" -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListIntuneIntents/function.json b/ListIntuneIntents/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListIntuneIntents/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListIntuneIntents/run.ps1 b/ListIntuneIntents/run.ps1 deleted file mode 100644 index 09f3e3a50464..000000000000 --- a/ListIntuneIntents/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/Intents?`$expand=settings,categories" -tenantid $TenantFilter - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListIntunePolicy/function.json b/ListIntunePolicy/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListIntunePolicy/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListIntunePolicy/run.ps1 b/ListIntunePolicy/run.ps1 deleted file mode 100644 index 048d6f60f50b..000000000000 --- a/ListIntunePolicy/run.ps1 +++ /dev/null @@ -1,62 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$id = $Request.Query.ID -$urlname = $Request.Query.URLName -try { - if ($ID) { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$($urlname)('$ID')" -tenantid $tenantfilter - } - else { - - $GraphURLS = @("https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations?`$select=id,displayName,lastModifiedDateTime,roleScopeTagIds,microsoft.graph.unsupportedDeviceConfiguration/originalEntityTypeName&`$expand=assignments&top=1000", - "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations?`$expand=assignments&top=1000" - "https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations?`$expand=assignments&`$filter=microsoft.graph.androidManagedStoreAppConfiguration/appSupportsOemConfig%20eq%20true" - "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies" - ) - - $GraphRequest = $GraphURLS | ForEach-Object { - $URLName = (($_).split('?') | Select-Object -First 1) -replace 'https://graph.microsoft.com/beta/deviceManagement/', '' - New-GraphGetRequest -uri $_ -tenantid $TenantFilter - - } | ForEach-Object { - $policyTypeName = switch -Wildcard ($_."assignments@odata.context") { - "*microsoft.graph.windowsIdentityProtectionConfiguration*" { "Identity Protection" } - "*microsoft.graph.windows10EndpointProtectionConfiguration*" { "Endpoint Protection" } - "*microsoft.graph.windows10CustomConfiguration*" { "Custom" } - "*groupPolicyConfigurations*" { "Administrative Templates" } - "*windowsDomainJoinConfiguration*" { "Domain Join configuration" } - "*windowsUpdateForBusinessConfiguration*" { "Update Configuration" } - "*windowsHealthMonitoringConfiguration*" { "Health Monitoring" } - default { $_."assignments@odata.context" } - } - if ($_.displayname -eq $null) { $_ | Add-Member -NotePropertyName displayName -NotePropertyValue $_.name } - $_ | Add-Member -NotePropertyName PolicyTypeName -NotePropertyValue $policyTypeName - $_ | Add-Member -NotePropertyName URLName -NotePropertyValue $URLName - $_ - } | Where-Object { $_.DisplayName -ne $null } - - } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListIntuneTemplates/function.json b/ListIntuneTemplates/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListIntuneTemplates/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListIntuneTemplates/run.ps1 b/ListIntuneTemplates/run.ps1 deleted file mode 100644 index f35dd082841b..000000000000 --- a/ListIntuneTemplates/run.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -Set-Location (Get-Item $PSScriptRoot).Parent.FullName - -$Table = Get-CippTable -tablename 'templates' - -$Templates = Get-ChildItem 'Config\*.IntuneTemplate.json' | ForEach-Object { - $Entity = @{ - JSON = "$(Get-Content $_)" - RowKey = "$($_.name)" - PartitionKey = 'IntuneTemplate' - GUID = "$($_.name)" - } - Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force -} - -#List new policies -$Table = Get-CippTable -tablename 'templates' -$Filter = "PartitionKey eq 'IntuneTemplate'" -$Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -if ($Request.query.View) { - $Templates = $Templates | ForEach-Object { - $data = $_.RAWJson | ConvertFrom-Json - $data | Add-Member -NotePropertyName 'displayName' -NotePropertyValue $_.Displayname -Force - $data | Add-Member -NotePropertyName 'description' -NotePropertyValue $_.Description -Force - $data | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_.Type - $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.GUID - $data - } -} - -if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property guid -EQ $Request.query.id } - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = ($Templates | ConvertTo-Json -Depth 10) - }) diff --git a/ListKnownIPDb/function.json b/ListKnownIPDb/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListKnownIPDb/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListKnownIPDb/run.ps1 b/ListKnownIPDb/run.ps1 deleted file mode 100644 index 5a2b9996f7b5..000000000000 --- a/ListKnownIPDb/run.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$Table = Get-CIPPTable -TableName 'knownlocationdb' -$Filter = "Tenant eq '$($Request.Query.TenantFilter)'" -$KnownIPDb = Get-CIPPAzDataTableEntity @Table -Filter $Filter - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($KnownIPDb) - }) diff --git a/ListLicenses/function.json b/ListLicenses/function.json deleted file mode 100644 index 8968c43fc37f..000000000000 --- a/ListLicenses/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "licqueue" - } - ] -} diff --git a/ListLicenses/run.ps1 b/ListLicenses/run.ps1 deleted file mode 100644 index 869d5044184e..000000000000 --- a/ListLicenses/run.ps1 +++ /dev/null @@ -1,36 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$RawGraphRequest = if ($TenantFilter -ne 'AllTenants') { - $GraphRequest = Get-CIPPLicenseOverview -TenantFilter $TenantFilter -} -else { - $Table = Get-CIPPTable -TableName cachelicenses - $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) - if (!$Rows) { - Push-OutputBinding -Name Msg -Value (Get-Date).ToString() - $GraphRequest = [PSCustomObject]@{ - Tenant = 'Loading data for all tenants. Please check back in 1 minute' - License = 'Loading data for all tenants. Please check back in 1 minute' - } - } - else { - $GraphRequest = $Rows - } -} - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) -Clobber \ No newline at end of file diff --git a/ListLogs/function.json b/ListLogs/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListLogs/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListLogs/run.ps1 b/ListLogs/run.ps1 deleted file mode 100644 index ee201dbea8d7..000000000000 --- a/ListLogs/run.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -if ($request.Query.Filter -eq 'True') { - $LogLevel = if ($Request.query.Severity) { ($Request.query.Severity).split(',') } else { 'Info', 'Warn', 'Error', 'Critical', 'Alert' } - $PartitionKey = $Request.query.DateFilter - $username = $Request.Query.User -} -else { - $LogLevel = 'Info', 'Warn', 'Error', 'Critical', 'Alert' - $PartitionKey = Get-Date -UFormat '%Y%m%d' - $username = '*' -} -$Table = Get-CIPPTable - -$ReturnedLog = if ($Request.Query.ListLogs) { - - Get-CIPPAzDataTableEntity @Table -Property PartitionKey | Sort-Object -Unique PartitionKey | Select-Object PartitionKey | ForEach-Object { - @{ - value = $_.PartitionKey - label = $_.PartitionKey - } - } -} -else { - $Filter = "PartitionKey eq '{0}'" -f $PartitionKey - $Rows = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username } - foreach ($Row in $Rows) { - @{ - DateTime = $Row.Timestamp - Tenant = $Row.Tenant - API = $Row.API - Message = $Row.Message - User = $Row.Username - Severity = $Row.Severity - TenantID = if ($Row.TenantID -ne $null) { - $Row.TenantID - } else { - 'None' - } - } - } - -} - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($ReturnedLog) - }) diff --git a/ListMFAUsers/function.json b/ListMFAUsers/function.json deleted file mode 100644 index 26f0727fe6fd..000000000000 --- a/ListMFAUsers/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "mfaqueue" - } - ] -} diff --git a/ListMFAUsers/run.ps1 b/ListMFAUsers/run.ps1 deleted file mode 100644 index 40bc53bed91d..000000000000 --- a/ListMFAUsers/run.ps1 +++ /dev/null @@ -1,36 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -if ($Request.query.TenantFilter -ne 'AllTenants') { - $GraphRequest = Get-CIPPMFAState -TenantFilter $Request.query.TenantFilter -} -else { - $Table = Get-CIPPTable -TableName cachemfa - - $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-2) - if (!$Rows) { - $Queue = New-CippQueueEntry -Name 'MFA Users - All Tenants' -Link '/identity/reports/mfa-report?customerId=AllTenants' - Write-Information ($Queue | ConvertTo-Json) - Push-OutputBinding -Name Msg -Value $Queue.RowKey - $GraphRequest = [PSCustomObject]@{ - UPN = 'Loading data for all tenants. Please check back in 10 minutes' - } - } - else { - $GraphRequest = $Rows - } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) diff --git a/ListMailQuarantine/function.json b/ListMailQuarantine/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/ListMailQuarantine/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListMailQuarantine/run.ps1 b/ListMailQuarantine/run.ps1 deleted file mode 100644 index 2d558ddb3776..000000000000 --- a/ListMailQuarantine/run.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Tenantfilter = $request.Query.tenantfilter - -try { - $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-QuarantineMessage" - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListMailboxCAS/function.json b/ListMailboxCAS/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListMailboxCAS/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListMailboxCAS/run.ps1 b/ListMailboxCAS/run.ps1 deleted file mode 100644 index 60d0b4d145db..000000000000 --- a/ListMailboxCAS/run.ps1 +++ /dev/null @@ -1,36 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $GraphRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox" -Tenantid $tenantfilter -scope ExchangeOnline | Select-Object @{ Name = 'displayName'; Expression = { $_.'DisplayName' } }, - @{ Name = 'primarySmtpAddress'; Expression = { $_.'PrimarySMTPAddress' } }, - @{ Name = 'ecpenabled'; Expression = { $_.'ECPEnabled' } }, - @{ Name = 'owaenabled'; Expression = { $_.'OWAEnabled' } }, - @{ Name = 'imapenabled'; Expression = { $_.'IMAPEnabled' } }, - @{ Name = 'popenabled'; Expression = { $_.'POPEnabled' } }, - @{ Name = 'mapienabled'; Expression = { $_.'MAPIEnabled' } }, - @{ Name = 'ewsenabled'; Expression = { $_.'EWSEnabled' } }, - @{ Name = 'activesyncenabled'; Expression = { $_.'ActiveSyncEnabled' } } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListMailboxMobileDevices/function.json b/ListMailboxMobileDevices/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListMailboxMobileDevices/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListMailboxMobileDevices/run.ps1 b/ListMailboxMobileDevices/run.ps1 deleted file mode 100644 index 59924b4a2765..000000000000 --- a/ListMailboxMobileDevices/run.ps1 +++ /dev/null @@ -1,49 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$Mailbox = $Request.Query.Mailbox - -Write-Host $TenantFilter -Write-Host $Mailbox - -$Bytes = [System.Text.Encoding]::UTF8.GetBytes($Mailbox) -$base64IdentityParam = [Convert]::ToBase64String($Bytes) - -try { - $GraphRequest = New-GraphGetRequest -uri "https://outlook.office365.com:443/adminapi/beta/$($TenantFilter)/mailbox('$($base64IdentityParam)')/MobileDevice/Exchange.GetMobileDeviceStatistics()/?IsEncoded=True" -Tenantid $tenantfilter -scope ExchangeOnline | Select-Object @{ Name = 'clientType'; Expression = { $_.ClientType } }, - @{ Name = 'clientVersion'; Expression = { $_.ClientVersion } }, - @{ Name = 'deviceAccessState'; Expression = { $_.DeviceAccessState } }, - @{ Name = 'deviceFriendlyName'; Expression = { if ([string]::IsNullOrEmpty($_.DeviceFriendlyName)) { "Unknown" }else { $_.DeviceFriendlyName } } }, - @{ Name = 'deviceModel'; Expression = { $_.DeviceModel } }, - @{ Name = 'deviceOS'; Expression = { $_.DeviceOS } }, - @{ Name = 'deviceType'; Expression = { $_.DeviceType } }, - @{ Name = 'firstSync'; Expression = { $_.FirstSyncTime.toString() } }, - @{ Name = 'lastSyncAttempt'; Expression = { $_.LastSyncAttemptTime.toString() } }, - @{ Name = 'lastSuccessSync'; Expression = { $_.LastSuccessSync.toString() } }, - @{ Name = 'status'; Expression = { $_.Status } }, - @{ Name = 'deviceID'; Expression = { $_.deviceID } }, - @{ Name = 'Guid'; Expression = { $_.Guid } } - - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListMailboxRules/function.json b/ListMailboxRules/function.json deleted file mode 100644 index ccf1e3f0a86d..000000000000 --- a/ListMailboxRules/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "mbxrulequeue" - } - ] -} diff --git a/ListMailboxRules/run.ps1 b/ListMailboxRules/run.ps1 deleted file mode 100644 index 08cc420b409a..000000000000 --- a/ListMailboxRules/run.ps1 +++ /dev/null @@ -1,49 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter - -$Table = Get-CIPPTable -TableName cachembxrules -$Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).Addhours(-1) -if (!$Rows) { - Push-OutputBinding -Name Msg -Value $TenantFilter - $GraphRequest = [PSCustomObject]@{ - Tenant = 'Loading data. Please check back in 1 minute' - Licenses = 'Loading data. Please check back in 1 minute' - } -} -else { - if ($TenantFilter -ne 'AllTenants') { - $GraphRequest = $Rows | Where-Object -Property Tenant -EQ $TenantFilter | ForEach-Object { - $NewObj = $_.Rules | ConvertFrom-Json - $NewObj | Add-Member -NotePropertyName 'Tenant' -NotePropertyValue $TenantFilter - $NewObj - } - } - else { - $GraphRequest = $Rows | ForEach-Object { - $TenantName = $_.Tenant - $NewObj = $_.Rules | ConvertFrom-Json - $NewObj | Add-Member -NotePropertyName 'Tenant' -NotePropertyValue $TenantName - $NewObj - } - } -} -#Remove all old cache -#Remove-AzDataTableEntity @Table -Entity (Get-CIPPAzDataTableEntity @Table -Property PartitionKey, RowKey, Timestamp | Where-Object -Property Timestamp -LT (Get-Date).AddMinutes(-15)) - - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListMailboxStatistics/function.json b/ListMailboxStatistics/function.json deleted file mode 100644 index 3d31416065c2..000000000000 --- a/ListMailboxStatistics/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "generalAllTenantQueue" - } - ] -} diff --git a/ListMailboxStatistics/run.ps1 b/ListMailboxStatistics/run.ps1 deleted file mode 100644 index 507a96ac924e..000000000000 --- a/ListMailboxStatistics/run.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - - $GraphRequest = if ($TenantFilter -ne 'AllTenants') { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getMailboxUsageDetail(period='D7')" -tenantid $TenantFilter | ConvertFrom-Csv | Select-Object @{ Name = 'UPN'; Expression = { $_.'User Principal Name' } }, - @{ Name = 'displayName'; Expression = { $_.'Display Name' } }, - @{ Name = 'MailboxType'; Expression = { $_.'Recipient Type' } }, - @{ Name = 'LastActive'; Expression = { $_.'Last Activity Date' } }, - @{ Name = 'UsedGB'; Expression = { [math]::round($_.'Storage Used (Byte)' / 1GB, 2) } }, - @{ Name = 'QuotaGB'; Expression = { [math]::round($_.'Prohibit Send/Receive Quota (Byte)' / 1GB, 2) } }, - @{ Name = 'ItemCount'; Expression = { $_.'Item Count' } }, - @{ Name = 'HasArchive'; Expression = { If (($_.'Has Archive').ToLower() -eq 'true') { [bool]$true } else { [bool]$false } } } - $StatusCode = [HttpStatusCode]::OK - } - else { - $Table = Get-CIPPTable -TableName "cachereports" - $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) - if (!$Rows) { - $Queue = New-CippQueueEntry -Name "Reports" -Link '/email/reports/mailbox-statistics?customerId=AllTenants' - Push-OutputBinding -Name Msg -Value "reports/getMailboxUsageDetail(period='D7')?`$format=application/json" - [PSCustomObject]@{ - Tenant = 'Loading data for all tenants. Please check back after the job completes' - } - $StatusCode = [HttpStatusCode]::OK - } - else { - $Rows.Data | ConvertFrom-Json | Select-Object *, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, - @{ Name = 'MailboxType'; Expression = { $_.'RecipientType' } }, - @{ Name = 'LastActive'; Expression = { $_.'LastActivityDate' } }, - @{ Name = 'UsedGB'; Expression = { [math]::round($_.'storageUsedInBytes' / 1GB, 2) } }, - @{ Name = 'QuotaGB'; Expression = { [math]::round($_.'prohibitSendReceiveQuotaInBytes' / 1GB, 2) } } - $StatusCode = [HttpStatusCode]::OK - } - } - - -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) -clobber diff --git a/ListMailboxes/function.json b/ListMailboxes/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListMailboxes/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListMailboxes/run.ps1 b/ListMailboxes/run.ps1 deleted file mode 100644 index 532c02da36c2..000000000000 --- a/ListMailboxes/run.ps1 +++ /dev/null @@ -1,71 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -# 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" - $ExoRequest = @{ - tenantid = $TenantFilter - cmdlet = 'Get-Mailbox' - cmdParams = @{resultsize = 'unlimited' } - Select = $select - } - - $AllowedParameters = @( - @{Parameter = 'Anr'; Type = 'String' } - @{Parameter = 'Archive'; Type = 'Bool' } - @{Parameter = 'Filter'; Type = 'String' } - @{Parameter = 'GroupMailbox'; Type = 'Bool' } - @{Parameter = 'PublicFolder'; Type = 'Bool' } - @{Parameter = 'RecipientTypeDetails'; Type = 'String' } - @{Parameter = 'SoftDeletedMailbox'; Type = 'Bool' } - ) - - foreach ($Param in $Request.Query.Keys) { - $CmdParam = $AllowedParameters | Where-Object { $_.Parameter -eq $Param } - if ($CmdParam) { - switch ($CmdParam.Type) { - 'String' { - if (![string]::IsNullOrEmpty($Request.Query.$Param)) { - $ExoRequest.cmdParams.$Param = $Request.Query.$Param - } - } - 'Bool' { - if ([bool]$Request.Query.$Param -eq $true) { - $ExoRequest.cmdParams.$Param = $true - } - } - } - } - } - - Write-Host ($ExoRequest | ConvertTo-Json) - $GraphRequest = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, @{ 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 ', ' } } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListMessageTrace/function.json b/ListMessageTrace/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListMessageTrace/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListMessageTrace/run.ps1 b/ListMessageTrace/run.ps1 deleted file mode 100644 index 6aae3ba30afa..000000000000 --- a/ListMessageTrace/run.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -try { - $TenantFilter = $request.query.TenantFilter - $SearchParams = @{ - StartDate = (Get-Date).AddDays( - $($request.query.days)).ToString('s') - EndDate = (Get-Date).ToString('s') - } - - if ($null -ne $request.query.recipient) { $Searchparams.Add('RecipientAddress', $($request.query.recipient)) } - if ($null -ne $request.query.sender) { $Searchparams.Add('SenderAddress', $($request.query.sender)) } - $type = $request.query.Tracedetail - $trace = if ($Request.Query.Tracedetail) { - New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-MessageTraceDetail" -cmdParams $Searchparams - Get-MessageTraceDetail -MessageTraceId $Request.Query.ID -RecipientAddress $request.query.recipient -erroraction stop | Select-Object Event, Action, Detail, @{ Name = 'Date'; Expression = { $_.Date.Tostring('s') } } - } - else { - New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-MessageTrace" -cmdParams $Searchparams | Select-Object MessageTraceId, Status, Subject, RecipientAddress, SenderAddress, @{ Name = 'Date'; Expression = { $_.Received.tostring('s') } } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Executed message trace" -Sev "Info" - - } -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed executing messagetrace. Error: $($_.Exception.Message)" -Sev "Error" - $trace = @{Status = "Failed to retrieve message trace $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($trace) - }) diff --git a/ListNamedLocations/function.json b/ListNamedLocations/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListNamedLocations/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListNamedLocations/run.ps1 b/ListNamedLocations/run.ps1 deleted file mode 100644 index 3fec85f0a0fd..000000000000 --- a/ListNamedLocations/run.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations" -Tenantid $tenantfilter | Select-Object *, - @{ - name = "rangeOrLocation" - expression = { if ($_.ipRanges) { $_.ipranges.cidrAddress -join ', ' } else { $_.countriesAndRegions -join ', ' } } - } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage - -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListNotificationConfig/function.json b/ListNotificationConfig/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListNotificationConfig/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListNotificationConfig/run.ps1 b/ListNotificationConfig/run.ps1 deleted file mode 100644 index f65c1f16b644..000000000000 --- a/ListNotificationConfig/run.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$Table = Get-CIPPTable -TableName SchedulerConfig -$Filter = "RowKey eq 'CippNotifications' and PartitionKey eq 'CippNotifications'" -$Config = Get-CIPPAzDataTableEntity @Table -Filter $Filter -if ($Config) { - $Config = $Config | ConvertTo-Json -Depth 10 | ConvertFrom-Json -Depth 10 -AsHashtable -} else { - $Config = @{} -} -#$config | Add-Member -NotePropertyValue @() -NotePropertyName 'logsToInclude' -Force -$config.logsToInclude = @(([pscustomobject]$config | Select-Object * -ExcludeProperty schedule, type, tenantid, onepertenant, sendtoIntegration, partitionkey, rowkey, tenant, ETag, email, logsToInclude, Severity, Alert, Info, Error, timestamp, webhook, includeTenantId).psobject.properties.name) -if (!$config.logsToInclude) { - $config.logsToInclude = @('None') -} -if (!$config.Severity) { - $config.Severity = @('Alert') -} else { - $config.Severity = $config.Severity -split ',' -} -$body = [PSCustomObject]$Config - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ListOAuthApps/function.json b/ListOAuthApps/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListOAuthApps/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListOAuthApps/run.ps1 b/ListOAuthApps/run.ps1 deleted file mode 100644 index d88ab1c30664..000000000000 --- a/ListOAuthApps/run.ps1 +++ /dev/null @@ -1,49 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -if ($TenantFilter -eq "AllTenants") { $Tenants = (Get-Tenants).defaultDomainName } else { $tenants = $TenantFilter } - -try { - $GraphRequest = foreach ($Tenant in $Tenants) { - try { - $ServicePrincipals = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$select=id,displayName,appid" -tenantid $Tenant - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/oauth2PermissionGrants" -tenantid $Tenant | ForEach-Object { - $CurrentServicePrincipal = ($ServicePrincipals | Where-Object -Property id -EQ $_.clientId) - [PSCustomObject]@{ - Tenant = $Tenant - Name = $CurrentServicePrincipal.displayName - ApplicationID = $CurrentServicePrincipal.appid - ObjectID = $_.clientId - Scope = ($_.scope -join ',') - StartTime = $_.startTime - } - } - $StatusCode = [HttpStatusCode]::OK - } - catch { - continue - } - } -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListOoO/function.json b/ListOoO/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListOoO/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListOoO/run.ps1 b/ListOoO/run.ps1 deleted file mode 100644 index 7a6a7222358d..000000000000 --- a/ListOoO/run.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -$Tenantfilter = $request.query.tenantFilter -try { - $Body = Get-CIPPOutOfOffice -userid $Request.query.userid -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $Body = [pscustomobject]@{"Results" = "Failed. $ErrorMessage" } - -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) diff --git a/ListOrg/function.json b/ListOrg/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListOrg/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListOrg/run.ps1 b/ListOrg/run.ps1 deleted file mode 100644 index 92ee467c194f..000000000000 --- a/ListOrg/run.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -if ($TenantFilter -eq "AllTenants") { - -} -else { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/organization" -tenantid $TenantFilter -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $GraphRequest - }) diff --git a/ListPartnerRelationships/function.json b/ListPartnerRelationships/function.json deleted file mode 100644 index 0847eee4046a..000000000000 --- a/ListPartnerRelationships/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "QueueTenant", - "queueName": "GraphRequestQueue" - } - ] -} diff --git a/ListPartnerRelationships/run.ps1 b/ListPartnerRelationships/run.ps1 deleted file mode 100644 index 4a6d47d586c7..000000000000 --- a/ListPartnerRelationships/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -try { - $GraphRequestList = @{ - Endpoint = 'policies/crossTenantAccessPolicy/partners' - TenantFilter = $Request.Query.TenantFilter - QueueNameOverride = 'Partner Relationships' - ReverseTenantLookup = $true - } - $GraphRequest = Get-GraphRequestList @GraphRequestList -} catch { - $GraphRequest = @() -} - -$StatusCode = [HttpStatusCode]::OK - -$results = [PSCustomObject]@{ - Results = @($GraphRequest) -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $results - }) diff --git a/ListPhishPolicies/function.json b/ListPhishPolicies/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListPhishPolicies/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListPhishPolicies/run.ps1 b/ListPhishPolicies/run.ps1 deleted file mode 100644 index 3d9dff6712ab..000000000000 --- a/ListPhishPolicies/run.ps1 +++ /dev/null @@ -1,41 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$AntiPhishRules = New-ExoRequest -tenantid $TenantFilter -cmdlet "Get-AntiPhishRule" -$AntiPhishPolicies = New-ExoRequest -tenantid $TenantFilter -cmdlet "Get-AntiPhishPolicy" - -$GraphRequest = $AntiPhishPolicies | Select-Object name, -@{Name = 'GUID'; Expression = {$(( -join (( 0x41..0x5A) + ( 0x61..0x7A) | Get-Random -Count 13 | % {[char]$_}) ))}}, -@{ Name = 'ExcludedDomains'; Expression = {$($_.ExcludedDomains) -join "
    "}}, -@{ Name = 'ExcludedSenders'; Expression = {$($_.ExcludedSenders) -join "
    " } }, -@{ Name = 'PhishThresholdLevel'; Expression = { - switch ($_.PhishThresholdLevel) { - 1 { $result = 'Standard' } - 2 { $result = 'Aggressive' } - 3 { $result = 'More Aggressive' } - 4 { $result = 'Most Aggressive' } - Default { $result = 'Unknown' } - } - $result - } -}, -@{ Name = 'ExcludedDomainCount'; Expression = { $_.ExcludedDomains | Measure-Object | Select-Object -ExpandProperty Count } }, -@{ Name = 'ExcludedSenderCount'; Expression = { $_.ExcludedSenders | Measure-Object | Select-Object -ExpandProperty Count } }, Enabled, WhenChangedUTC, -@{ Name = 'Priority'; Expression = { foreach ($item in $AntiPhishRules) { if ($item.name -eq $_.name) { $item.priority } } } } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) diff --git a/ListPotentialApps/function.json b/ListPotentialApps/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListPotentialApps/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListPotentialApps/run.ps1 b/ListPotentialApps/run.ps1 deleted file mode 100644 index 338b8b92e306..000000000000 --- a/ListPotentialApps/run.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -if ($request.body.type -eq "WinGet") { - $body = @" -{"MaximumResults":50,"Filters":[{"PackageMatchField":"Market","RequestMatch":{"KeyWord":"US","MatchType":"CaseInsensitive"}}],"Query":{"KeyWord":"$($Request.Body.SearchString)","MatchType":"Substring"}} -"@ - $DataRequest = (Invoke-RestMethod -Uri "https://storeedgefd.dsx.mp.microsoft.com/v9.0/manifestSearch" -Method POST -Body $body -ContentType "Application/json").data | Select-Object @{l = 'applicationName'; e = { $_.packagename } }, @{l = 'packagename'; e = { $_.packageIdentifier } } | Sort-Object -Property applicationName -} - -if ($Request.body.type -eq "Choco") { - $DataRequest = Invoke-RestMethod -Uri "https://community.chocolatey.org/api/v2/Search()?`$filter=IsLatestVersion&`$skip=0&`$top=999&searchTerm=%27$($request.body.SearchString)%27&targetFramework=%27%27&includePrerelease=false" -ContentType 'application/json' | Select-Object @{l = 'applicationName'; e = { $_.properties.Title } }, @{l = 'packagename'; e = { $_.title.'#text' } } | Sort-Object -Property applicationName -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($DataRequest) - }) \ No newline at end of file diff --git a/ListRoles/function.json b/ListRoles/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListRoles/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListRoles/run.ps1 b/ListRoles/run.ps1 deleted file mode 100644 index fb774e322424..000000000000 --- a/ListRoles/run.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$SelectList = 'id', 'displayName', 'userPrincipalName' - -[System.Collections.Generic.List[PSCustomObject]]$Roles = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/directoryRoles?`$expand=members" -tenantid $TenantFilter -$GraphRequest = foreach ($Role in $Roles) { - - #[System.Collections.Generic.List[PSCustomObject]]$Members = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/directoryRoles/$($Role.id)/members?`$select=$($selectlist -join ',')" -tenantid $TenantFilter | Select-Object $SelectList - $Members = if ($Role.members) { $role.members | ForEach-Object { " $($_.displayName) ($($_.userPrincipalName))" } } else { "none" } - [PSCustomObject]@{ - DisplayName = $Role.displayName - Description = $Role.description - Members = $Members -join ',' - } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $GraphRequest - }) \ No newline at end of file diff --git a/ListScheduledItems/function.json b/ListScheduledItems/function.json deleted file mode 100644 index b0ca1676cc0b..000000000000 --- a/ListScheduledItems/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] - } \ No newline at end of file diff --git a/ListScheduledItems/run.ps1 b/ListScheduledItems/run.ps1 deleted file mode 100644 index 748dbcfeafd3..000000000000 --- a/ListScheduledItems/run.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$Table = Get-CIPPTable -TableName 'ScheduledTasks' -$ScheduledTasks = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'ScheduledTask' and Hidden ne 'True'" - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($ScheduledTasks) - }) \ No newline at end of file diff --git a/ListServiceHealth/function.json b/ListServiceHealth/function.json deleted file mode 100644 index 4ee273331c44..000000000000 --- a/ListServiceHealth/function.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ListServiceHealth/run.ps1 b/ListServiceHealth/run.ps1 deleted file mode 100644 index 738b6c1feed2..000000000000 --- a/ListServiceHealth/run.ps1 +++ /dev/null @@ -1,36 +0,0 @@ -using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - - -$ResultHealthSummary = Get-Tenants | ForEach-Object -Parallel { - Import-Module '.\GraphHelper.psm1' - $tenantname = $_.displayName - Write-Host $tenantname - $prop = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/admin/serviceAnnouncement/issues?`$filter=endDateTime eq null" -tenantid $_.defaultDomainName - $prop | Add-Member -NotePropertyName 'tenant' -NotePropertyValue $tenantname - $prop -} -$Results = foreach ($h in $ResultHealthSummary) { - [PSCustomObject]@{ - TenantName = $h.tenant - issueId = $h.ID - service = $h.service - type = $h.feature - desc = $h.impactDescription - } -} - -$StatusCode = [HttpStatusCode]::OK - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($Results) - }) diff --git a/ListSharedMailboxAccountEnabled/function.json b/ListSharedMailboxAccountEnabled/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListSharedMailboxAccountEnabled/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListSharedMailboxAccountEnabled/run.ps1 b/ListSharedMailboxAccountEnabled/run.ps1 deleted file mode 100644 index 45762d45d8f5..000000000000 --- a/ListSharedMailboxAccountEnabled/run.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -$TenantFilter = $Request.Query.TenantFilter - -# Get Shared Mailbox Stuff -try { - $SharedMailboxList = (New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($TenantFilter)/Mailbox?`$filter=RecipientTypeDetails eq 'SharedMailbox'" -Tenantid $TenantFilter -scope ExchangeOnline) - $AllUsersAccountState = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?select=userPrincipalName,accountEnabled,displayName,givenName,surname' -tenantid $Tenantfilter - $EnabledUsersWithSharedMailbox = foreach ($SharedMailbox in $SharedMailboxList) { - # Match the User - $User = $AllUsersAccountState | Where-Object { $_.userPrincipalName -eq $SharedMailbox.userPrincipalName } | Select-Object -Property userPrincipalName, accountEnabled, displayName, givenName, surname -First 1 - if ($User.accountEnabled) { - $User | Select-Object ` - @{Name='UserPrincipalName';Expression={$User.UserPrincipalName}}, ` - @{Name='displayName';Expression={$User.displayName}}, - @{Name='givenName';Expression={$User.givenName}}, - @{Name='surname';Expression={$User.surname}}, - @{Name='accountEnabled';Expression={$User.accountEnabled}} - - } - } -} -catch { - Write-LogMessage -API 'Tenant' -tenant $tenantfilter -message "Shared Mailbox Enabled Accounts on $($tenantfilter). Error: $($_.exception.message)" -sev 'Error' -} - -$GraphRequest = $EnabledUsersWithSharedMailbox -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListSharedMailboxStatistics/function.json b/ListSharedMailboxStatistics/function.json deleted file mode 100644 index 4ee273331c44..000000000000 --- a/ListSharedMailboxStatistics/function.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ListSharedMailboxStatistics/run.ps1 b/ListSharedMailboxStatistics/run.ps1 deleted file mode 100644 index fb4c19680a47..000000000000 --- a/ListSharedMailboxStatistics/run.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $GraphRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantFilter)/Mailbox?RecipientTypeDetails=sharedmailbox" -Tenantid $tenantFilter -scope ExchangeOnline | ForEach-Object { - try { - New-ExoRequest -tenantid $TenantFilter -cmdlet "Get-MailboxStatistics" -cmdParams @{Identity = $_.GUID } - } - catch { - continue - } - } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListSharepointQuota/function.json b/ListSharepointQuota/function.json deleted file mode 100644 index 925eab5aeae1..000000000000 --- a/ListSharepointQuota/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] - } \ No newline at end of file diff --git a/ListSharepointSettings/function.json b/ListSharepointSettings/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListSharepointSettings/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListSharepointSettings/run.ps1 b/ListSharepointSettings/run.ps1 deleted file mode 100644 index 17c1f8e773d7..000000000000 --- a/ListSharepointSettings/run.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$tenant = $Request.Query.TenantFilter -$User = $Request.query.user -$USERToGet = $Request.query.usertoGet -$body = '{"isResharingByExternalUsersEnabled": "False"}' -$Request = New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/admin/sharepoint/settings" -Type patch -Body $body -ContentType "application/json" - -Write-LogMessage -API "Standards" -tenant $tenantFilter -message "Disabled Password Expiration" -sev Info -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) diff --git a/ListSignIns/function.json b/ListSignIns/function.json deleted file mode 100644 index 77f0dced2d90..000000000000 --- a/ListSignIns/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "baqueue" - } - ] -} diff --git a/ListSignIns/run.ps1 b/ListSignIns/run.ps1 deleted file mode 100644 index 2883c345c8e5..000000000000 --- a/ListSignIns/run.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - if ($Request.query.failedlogonOnly) { - $FailedLogons = " and (status/errorCode eq 50126)" - } - - $filters = if ($Request.query.Filter) { - $request.query.filter - } - else { - $currentTime = Get-Date -Format 'yyyy-MM-dd' - $ts = (Get-Date).AddDays(-7) - $endTime = $ts.ToString('yyyy-MM-dd') - "createdDateTime ge $($endTime) and createdDateTime lt $($currentTime) and userDisplayName ne 'On-Premises Directory Synchronization Service Account' $FailedLogons" - } - Write-Host $Filters - - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/signIns?api-version=beta&`$filter=$($filters)" -tenantid $TenantFilter -erroraction stop - $response = $GraphRequest | Select-Object *, - @{l = "additionalDetails"; e = { $_.status.additionalDetails } } , - @{l = "errorCode"; e = { $_.status.errorCode } }, - @{l = "locationcipp"; e = { "$($_.location.city) - $($_.location.countryOrRegion)" } } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Retrieved sign in report' -Sev 'Debug' -tenant $TenantFilter - - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($response) - }) -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve Sign In report: $($_.Exception.message) " -Sev 'Error' -tenant $TenantFilter - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = '500' - Body = $(Get-NormalizedError -message $_.Exception.message) - }) -} diff --git a/ListSites/function.json b/ListSites/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListSites/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListSites/run.ps1 b/ListSites/run.ps1 deleted file mode 100644 index 64300086ccae..000000000000 --- a/ListSites/run.ps1 +++ /dev/null @@ -1,47 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$type = $request.query.Type -$UserUPN = $request.query.UserUPN -try { - $Result = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/get$($type)Detail(period='D7')" -tenantid $TenantFilter | ConvertFrom-Csv - - if ($UserUPN) { - $ParsedRequest = $Result | Where-Object { $_.'Owner Principal Name' -eq $UserUPN } - } - else { - $ParsedRequest = $Result - } - - - $GraphRequest = $ParsedRequest | Select-Object @{ Name = 'UPN'; Expression = { $_.'Owner Principal Name' } }, - @{ Name = 'displayName'; Expression = { $_.'Owner Display Name' } }, - @{ Name = 'LastActive'; Expression = { $_.'Last Activity Date' } }, - @{ Name = 'FileCount'; Expression = { [int]$_.'File Count' } }, - @{ Name = 'UsedGB'; Expression = { [math]::round($_.'Storage Used (Byte)' / 1GB, 2) } }, - @{ Name = 'URL'; Expression = { $_.'Site URL' } }, - @{ Name = 'Allocated'; Expression = { [math]::round($_.'Storage Allocated (Byte)' / 1GB, 2) } }, - @{ Name = 'Template'; Expression = { $_.'Root Web Template' } } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListSpamFilterTemplates/function.json b/ListSpamFilterTemplates/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListSpamFilterTemplates/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListSpamFilterTemplates/run.ps1 b/ListSpamFilterTemplates/run.ps1 deleted file mode 100644 index cbee13867932..000000000000 --- a/ListSpamFilterTemplates/run.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Table = Get-CippTable -tablename 'templates' - -#List new policies -$Table = Get-CippTable -tablename 'templates' -$Filter = "PartitionKey eq 'SpamfilterTemplate'" -$Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { - $GUID = $_.RowKey - $data = $_.JSON | ConvertFrom-Json - $data | Add-Member -NotePropertyName "GUID" -NotePropertyValue $GUID - $data -} - -if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property RowKey -EQ $Request.query.id } - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($Templates) - }) diff --git a/ListSpamfilter/function.json b/ListSpamfilter/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/ListSpamfilter/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListSpamfilter/run.ps1 b/ListSpamfilter/run.ps1 deleted file mode 100644 index 8e9e131dfe50..000000000000 --- a/ListSpamfilter/run.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Tenantfilter = $request.Query.tenantfilter - -try { - $Policies = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-HostedContentFilterPolicy" | Select-Object * -ExcludeProperty *odata*, *data.type* - $RuleState = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-HostedContentFilterRule" | Select-Object * -ExcludeProperty *odata*, *data.type* - $GraphRequest = $Policies | Select-Object *, @{l = 'ruleState'; e = { $name = $_.name; ($RuleState | Where-Object name -EQ $name).State } }, @{l = 'rulePrio'; e = { $name = $_.name; ($RuleState | Where-Object name -EQ $name).Priority } } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListStandards/function.json b/ListStandards/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListStandards/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListStandards/run.ps1 b/ListStandards/run.ps1 deleted file mode 100644 index 84cc3ea0acf4..000000000000 --- a/ListStandards/run.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -$Table = Get-CippTable -tablename 'standards' - -$Filter = "PartitionKey eq 'standards'" - -try { - if ($Request.query.TenantFilter) { - $tenants = (Get-CIPPAzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -Depth 15 -ErrorAction Stop | Where-Object Tenant -EQ $Request.query.tenantFilter - } - else { - $Tenants = (Get-CIPPAzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -Depth 15 -ErrorAction Stop - } -} -catch {} - -$CurrentStandards = foreach ($tenant in $tenants) { - [PSCustomObject]@{ - displayName = $tenant.tenant - appliedBy = $tenant.addedBy - appliedAt = $tenant.appliedAt - standards = $tenant.Standards - StandardsExport = ($tenant.Standards.psobject.properties.name) -join ', ' - } -} -if (!$CurrentStandards) { - $CurrentStandards = [PSCustomObject]@{ - displayName = 'No Standards applied' - appliedBy = $null - appliedAt = $null - standards = @{none = $null } - } -} - -$CurrentStandards = ConvertTo-Json -InputObject @($CurrentStandards) -Depth 15 -Compress - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $CurrentStandards - }) diff --git a/ListTeams/function.json b/ListTeams/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListTeams/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListTeams/run.ps1 b/ListTeams/run.ps1 deleted file mode 100644 index 2acf5160bdd5..000000000000 --- a/ListTeams/run.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -if ($request.query.type -eq "List") { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')&`$select=id,displayname,description,visibility,mailNickname" -tenantid $TenantFilter -} -$TeamID = $request.query.ID -Write-Host $TeamID -if ($request.query.type -eq "Team") { - $Team = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/teams/$($TeamID)" -tenantid $TenantFilter -asapp $true - $Channels = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/teams/$($TeamID)/Channels" -tenantid $TenantFilter -asapp $true - $UserList = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/teams/$($TeamID)/Members" -tenantid $TenantFilter -asapp $true - $AppsList = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/teams/$($TeamID)/installedApps?`$expand=teamsAppDefinition" -tenantid $TenantFilter -asapp $true - - $Owners = $UserList | Where-Object -Property Roles -EQ "Owner" - $Members = $UserList | Where-Object -Property email -NotIn $owners.email - $GraphRequest = [PSCustomObject]@{ - Name = $team.DisplayName - TeamInfo = @($team) - ChannelInfo = @($channels) - Members = @($Members) - Owners = @($owners) - InstalledApps = @($AppsList) - } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListTeamsActivity/function.json b/ListTeamsActivity/function.json deleted file mode 100644 index c26ed09a89e4..000000000000 --- a/ListTeamsActivity/function.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ListTeamsActivity/run.ps1 b/ListTeamsActivity/run.ps1 deleted file mode 100644 index 88246bffcf8c..000000000000 --- a/ListTeamsActivity/run.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$type = $request.query.Type -$GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/get$($type)Detail(period='D30')" -tenantid $TenantFilter | ConvertFrom-Csv | Select-Object @{ Name = 'UPN'; Expression = { $_.'User Principal Name' } }, -@{ Name = 'LastActive'; Expression = { $_.'Last Activity Date' } }, -@{ Name = 'TeamsChat'; Expression = { $_.'Team Chat Message Count' } }, -@{ Name = 'CallCount'; Expression = { $_.'Call Count' } }, -@{ Name = 'MeetingCount'; Expression = { $_.'Meeting Count' } } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListTeamsVoice/function.json b/ListTeamsVoice/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListTeamsVoice/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListTeamsVoice/run.ps1 b/ListTeamsVoice/run.ps1 deleted file mode 100644 index 6b8dcdbda05e..000000000000 --- a/ListTeamsVoice/run.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$tenantid = (Get-Tenants | Where-Object -Property defaultDomainName -EQ $Request.Query.TenantFilter).customerId -try { - $users = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=id,userPrincipalName,displayname" -tenantid $TenantFilter) - $GraphRequest = (New-TeamsAPIGetRequest -uri "https://api.interfaces.records.teams.microsoft.com/Skype.TelephoneNumberMgmt/Tenants/$($Tenantid)/telephone-numbers?locale=en-US" -tenantid $TenantFilter).TelephoneNumbers | ForEach-Object { - $CompleteRequest = $_ | Select-Object *, "AssignedTo" - $CompleteRequest.AcquisitionDate = $CompleteRequest.AcquisitionDate -split 'T' | Select-Object -First 1 - - if ($CompleteRequest.TargetId -eq "00000000-0000-0000-0000-000000000000") { - $CompleteRequest.AssignedTo = "Unassigned" - } - else { - $CompleteRequest.AssignedTo = ($users | Where-Object -Property Id -EQ $CompleteRequest.TargetId).userPrincipalName - - } - $CompleteRequest - } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListTenantDetails/function.json b/ListTenantDetails/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListTenantDetails/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListTenantDetails/run.ps1 b/ListTenantDetails/run.ps1 deleted file mode 100644 index 765e899d48e0..000000000000 --- a/ListTenantDetails/run.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName - -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -try { - $tenantfilter = $Request.Query.TenantFilter - $org = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/organization' -tenantid $tenantfilter | Select-Object displayName, city, country, countryLetterCode, street, state, postalCode, - @{ Name = 'businessPhones'; Expression = { $_.businessPhones -join ', ' } }, - @{ Name = 'technicalNotificationMails'; Expression = { $_.technicalNotificationMails -join ', ' } }, - tenantType, createdDateTime, onPremisesLastPasswordSyncDateTime, onPremisesLastSyncDateTime, onPremisesSyncEnabled, assignedPlans -} -catch { - $org = [PSCustomObject]@{ - displayName = 'Error loading tenant' - city = '' - country = '' - countryLetterCode = '' - street = '' - state = '' - postalCode = '' - businessPhones = '' - technicalNotificationMails = '' - createdDateTime = '' - onPremisesLastPasswordSyncDateTime = '' - onPremisesLastSyncDateTime = '' - onPremisesSyncEnabled = '' - assignedPlans = @() - } -} -finally { - $Body = $org -} - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) - diff --git a/ListTenants/function.json b/ListTenants/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListTenants/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListTenants/run.ps1 b/ListTenants/run.ps1 deleted file mode 100644 index 13fc9bc5f736..000000000000 --- a/ListTenants/run.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName - -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Clear Cache -if ($request.Query.ClearCache -eq 'true') { - Remove-CIPPCache -tenantsOnly $request.query.TenantsOnly - $GraphRequest = [pscustomobject]@{'Results' = 'Successfully completed request.' } - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $GraphRequest - }) - exit -} - -try { - $tenantfilter = $Request.Query.TenantFilter - $Tenants = Get-Tenants -IncludeErrors - - if ($null -eq $TenantFilter -or $TenantFilter -eq 'null') { - $TenantList = [system.collections.generic.list[object]]::new() - if ($Request.Query.AllTenantSelector -eq $true) { - $TenantList.Add(@{ - customerId = 'AllTenants' - defaultDomainName = 'AllTenants' - displayName = '*All Tenants' - domains = 'AllTenants' - GraphErrorCount = 0 - }) | Out-Null - - if (($Tenants).length -gt 1) { - $TenantList.AddRange($Tenants) | Out-Null - } - elseif ($Tenants) { - $TenantList.Add($Tenants) | Out-Null - } - $body = $TenantList - } - else { - $Body = $Tenants - } - } - else { - $body = $Tenants | Where-Object -Property defaultDomainName -EQ $Tenantfilter - } - - Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $Tenantfilter -API $APINAME -message 'Listed Tenant Details' -Sev 'Debug' -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $Tenantfilter -API $APINAME -message "List Tenant failed. The error is: $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{ - 'Results' = "Failed to retrieve tenants: $($_.Exception.Message)" - defaultDomainName = '' - displayName = 'Failed to retrieve tenants. Perform a permission check.' - customerId = '' - - } -} - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($Body) - }) - diff --git a/ListTransportRules/function.json b/ListTransportRules/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/ListTransportRules/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListTransportRules/run.ps1 b/ListTransportRules/run.ps1 deleted file mode 100644 index 4bdbab41b2ca..000000000000 --- a/ListTransportRules/run.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Tenantfilter = $request.Query.tenantfilter - -try { - $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "Get-TransportRule" - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ListTransportRulesTemplates/function.json b/ListTransportRulesTemplates/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListTransportRulesTemplates/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListTransportRulesTemplates/run.ps1 b/ListTransportRulesTemplates/run.ps1 deleted file mode 100644 index 6e6b8f6d761f..000000000000 --- a/ListTransportRulesTemplates/run.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Table = Get-CippTable -tablename 'templates' - -$Templates = Get-ChildItem "Config\*.TransportRuleTemplate.json" | ForEach-Object { - - $Entity = @{ - JSON = "$(Get-Content $_)" - RowKey = "$($_.name)" - PartitionKey = "TransportTemplate" - GUID = "$($_.name)" - } - Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force - -} - -#List new policies -$Table = Get-CippTable -tablename 'templates' -$Filter = "PartitionKey eq 'TransportTemplate'" -$Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { - $GUID = $_.RowKey - $data = $_.JSON | ConvertFrom-Json - $data | Add-Member -NotePropertyName "GUID" -NotePropertyValue $GUID - $data -} - -if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property RowKey -EQ $Request.query.id } - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($Templates) - }) diff --git a/ListUserConditionalAccessPolicies/function.json b/ListUserConditionalAccessPolicies/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListUserConditionalAccessPolicies/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListUserConditionalAccessPolicies/run.ps1 b/ListUserConditionalAccessPolicies/run.ps1 deleted file mode 100644 index d84419daff67..000000000000 --- a/ListUserConditionalAccessPolicies/run.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$UserID = $Request.Query.UserID - -try { - $json = '{"conditions":{"users":{"allUsers":2,"included":{"userIds":["' + $UserID + '"],"groupIds":[]},"excluded":{"userIds":[],"groupIds":[]}},"servicePrincipals":{"allServicePrincipals":1,"includeAllMicrosoftApps":false,"excludeAllMicrosoftApps":false,"userActions":[],"stepUpTags":[]},"conditions":{"minUserRisk":{"noRisk":false,"lowRisk":false,"mediumRisk":false,"highRisk":false,"applyCondition":false},"minSigninRisk":{"noRisk":false,"lowRisk":false,"mediumRisk":false,"highRisk":false,"applyCondition":false},"servicePrincipalRiskLevels":{"noRisk":false,"lowRisk":false,"mediumRisk":false,"highRisk":false,"applyCondition":false},"devicePlatforms":{"all":2,"included":{"android":false,"ios":false,"windowsPhone":false,"windows":false,"macOs":false,"linux":false},"excluded":null,"applyCondition":false},"locations":{"applyCondition":true,"includeLocationType":2,"excludeAllTrusted":false},"clientApps":{"applyCondition":false,"specificClientApps":false,"webBrowsers":false,"exchangeActiveSync":false,"onlyAllowSupportedPlatforms":false,"mobileDesktop":false},"clientAppsV2":{"applyCondition":false,"webBrowsers":false,"mobileDesktop":false,"modernAuth":false,"exchangeActiveSync":false,"onlyAllowSupportedPlatforms":false,"otherClients":false},"deviceState":{"includeDeviceStateType":1,"excludeDomainJoionedDevice":false,"excludeCompliantDevice":false,"applyCondition":true}}},"country":"","device":{}}' - $UserPolicies = (New-ClassicAPIPostRequest -uri "https://main.iam.ad.ext.azure.com/api/Policies/Evaluate?" -tenantid $tenantfilter -Method POST -body $json -resource '74658136-14ec-4630-ad9b-26e160ff0fc6' -verbose | Where-Object { $_.applied -eq $true }) - $ConditionalAccessPolicyOutput = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies" -tenantid $tenantfilter -} -catch { - $ConditionalAccessPolicyOutput = @{} -} - -$GraphRequest = foreach ($cap in $ConditionalAccessPolicyOutput) { - if ($cap.id -in $UserPolicies.policyId) { - $temp = [PSCustomObject]@{ - id = $cap.id - displayName = $cap.displayName - } - $temp - } -} - -Write-Host $GraphRequest - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) diff --git a/ListUserCounts/function.json b/ListUserCounts/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListUserCounts/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListUserCounts/run.ps1 b/ListUserCounts/run.ps1 deleted file mode 100644 index 419884710cdf..000000000000 --- a/ListUserCounts/run.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -if ($Request.Query.TenantFilter -eq 'AllTenants') { - $users = 'Not Supported' - $LicUsers = 'Not Supported' - $GAs = 'Not Supported' - $Guests = 'Not Supported' -} else { - $Users = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$count=true&`$top=1" -CountOnly -ComplexFilter -tenantid $TenantFilter - $LicUsers = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$count=true&`$top=1&`$filter=assignedLicenses/`$count ne 0" -CountOnly -ComplexFilter -tenantid $TenantFilter - $GAs = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/directoryRoles/roleTemplateId=62e90394-69f5-4237-9190-012177145e10/members?`$count=true" -CountOnly -ComplexFilter -tenantid $TenantFilter - $guests = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$count=true&`$top=1&`$filter=userType eq 'Guest'" -CountOnly -ComplexFilter -tenantid $TenantFilter -} -$StatusCode = [HttpStatusCode]::OK -$Counts = @{ - Users = $users - LicUsers = $LicUsers - Gas = $Gas - Guests = $guests -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $Counts - }) diff --git a/ListUserDevices/function.json b/ListUserDevices/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListUserDevices/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListUserDevices/run.ps1 b/ListUserDevices/run.ps1 deleted file mode 100644 index c31bb6a92c6b..000000000000 --- a/ListUserDevices/run.ps1 +++ /dev/null @@ -1,56 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$UserID = $Request.Query.UserID - -function Get-EPMID { - param( - $deviceID, - $EPMDevices - ) - try { - return ($EPMDevices | Where-Object { $_.azureADDeviceId -eq $deviceID }).id - } - catch { - return $null - } -} -try { - $EPMDevices = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$UserID/managedDevices" -Tenantid $tenantfilter - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$UserID/ownedDevices?`$top=999" -Tenantid $tenantfilter | Select-Object @{ Name = 'ID'; Expression = { $_.'id' } }, - @{ Name = 'accountEnabled'; Expression = { $_.'accountEnabled' } }, - @{ Name = 'approximateLastSignInDateTime'; Expression = { $_.'approximateLastSignInDateTime' | Out-String } }, - @{ Name = 'createdDateTime'; Expression = { $_.'createdDateTime' | Out-String } }, - @{ Name = 'deviceOwnership'; Expression = { $_.'deviceOwnership' } }, - @{ Name = 'displayName'; Expression = { $_.'displayName' } }, - @{ Name = 'enrollmentType'; Expression = { $_.'enrollmentType' } }, - @{ Name = 'isCompliant'; Expression = { $_.'isCompliant' } }, - @{ Name = 'managementType'; Expression = { $_.'managementType' } }, - @{ Name = 'manufacturer'; Expression = { $_.'manufacturer' } }, - @{ Name = 'model'; Expression = { $_.'model' } }, - @{ Name = 'operatingSystem'; Expression = { $_.'operatingSystem' } }, - @{ Name = 'onPremisesSyncEnabled'; Expression = { $(if ([string]::IsNullOrEmpty($_.'onPremisesSyncEnabled')) { $false }else { $true }) } }, - @{ Name = 'operatingSystemVersion'; Expression = { $_.'operatingSystemVersion' } }, - @{ Name = 'trustType'; Expression = { $_.'trustType' } }, - @{ Name = 'EPMID'; Expression = { $(Get-EPMID -deviceID $_.'deviceId' -EPMDevices $EPMDevices) } } -} -catch { - $GraphRequest = @() -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) diff --git a/ListUserGroups/function.json b/ListUserGroups/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListUserGroups/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListUserGroups/run.ps1 b/ListUserGroups/run.ps1 deleted file mode 100644 index 6a97be613f40..000000000000 --- a/ListUserGroups/run.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$UserID = $Request.Query.UserID - - -$URI = "https://graph.microsoft.com/beta/users/$UserID/memberOf/$/microsoft.graph.group?`$select=id,displayName,mailEnabled,securityEnabled,groupTypes,onPremisesSyncEnabled,mail,isAssignableToRole`&$orderby=displayName asc" -Write-Host $URI -$GraphRequest = New-GraphGetRequest -uri $URI -tenantid $TenantFilter -noPagination $true -verbose | select-object id, -@{ Name = 'DisplayName'; Expression = { $_.displayName} }, -@{ Name = 'MailEnabled'; Expression = { $_.mailEnabled} }, -@{ Name = 'Mail'; Expression = { $_.mail} }, -@{ Name = 'SecurityGroup'; Expression = {$_.securityEnabled} }, -@{ Name = 'GroupTypes'; Expression = { $_.groupTypes -join ','} }, -@{ Name = 'OnPremisesSync'; Expression = { $_.onPremisesSyncEnabled} }, -@{ Name = 'IsAssignableToRole'; Expression = { $_.isAssignableToRole} } - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListUserMailboxDetails/function.json b/ListUserMailboxDetails/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListUserMailboxDetails/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListUserMailboxDetails/run.ps1 b/ListUserMailboxDetails/run.ps1 deleted file mode 100644 index 90affe48fe95..000000000000 --- a/ListUserMailboxDetails/run.ps1 +++ /dev/null @@ -1,164 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$UserID = $Request.Query.UserID - - -$TenantFilter = $Request.Query.TenantFilter -try { - $Bytes = [System.Text.Encoding]::UTF8.GetBytes($Request.Query.UserID) - $base64IdentityParam = [Convert]::ToBase64String($Bytes) - $CASRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox('$UserID')" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true - $MailRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$UserID')" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true - $FetchParam = @{ - anr = $MailRequest.PrimarySmtpAddress - } - $MailboxDetailedRequest = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-Mailbox' -cmdParams $FetchParam - try { - $Archive = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-Mailbox' -cmdParams $FetchParam - if ($Archive.ArchiveStatus -eq "Active") { - $ArchiveEnabled = $True - } - else { - $ArchiveEnabled = $False - } - - $FetchParam = @{ - Identity = $MailRequest.PrimarySmtpAddress - Archive = $true - } - - $ArchiveSize = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-MailboxStatistics' -cmdParams $FetchParam - } - catch { - $ArchiveEnabled = $False - $ArchiveSize = @{ - TotalItemSize = "0" - ItemCount = "0" - } - } - $FetchParam = @{ - SenderAddress = $MailRequest.PrimarySmtpAddress - } - $BlockedSender = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-BlockedSenderAddress' -cmdParams $FetchParam - if ($BlockedSender) { - $BlockedForSpam = $True - } - else { - $BlockedForSpam = $False - } - $StatsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($MailRequest.PrimarySmtpAddress)')/Exchange.GetMailboxStatistics()" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true - $PermsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($MailRequest.PrimarySmtpAddress)')/MailboxPermission" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true - $PermsRequest2 = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Recipient('$base64IdentityParam')?`$expand=RecipientPermission&isEncoded=true" -Tenantid $tenantfilter -scope ExchangeOnline - -} -catch { - Write-Error "Failed Fetching Data $($_.Exception.message): $($_.InvocationInfo.ScriptLineNumber)" -} - -$ParsedPerms = foreach ($Perm in $PermsRequest, $PermsRequest2.RecipientPermission) { - - if ($perm.Trustee) { - $perm | Where-Object Trustee | ForEach-Object { [PSCustomObject]@{ - User = $_.Trustee - AccessRights = $_.accessRights -join ', ' - } - } - - } - if ($perm.PermissionList) { - $perm | Where-Object User | ForEach-Object { [PSCustomObject]@{ - User = $_.User - AccessRights = $_.PermissionList.accessRights -join ', ' - } - } - } -} - -$forwardingaddress = if ($MailboxDetailedRequest.ForwardingAddress) { - $MailboxDetailedRequest.ForwardingAddress -} -elseif ($MailboxDetailedRequest.ForwardingSmtpAddress -and $MailboxDetailedRequest.ForwardingAddress) { - $MailboxDetailedRequest.ForwardingAddress + ' ' + $MailboxDetailedRequest.ForwardingSmtpAddress -} -else { - $MailboxDetailedRequest.ForwardingSmtpAddress -} - -if ($ArchiveSize) { - $GraphRequest = [ordered]@{ - ForwardAndDeliver = $MailboxDetailedRequest.DeliverToMailboxAndForward - ForwardingAddress = $ForwardingAddress - LitiationHold = $MailboxDetailedRequest.LitigationHoldEnabled - HiddenFromAddressLists = $MailboxDetailedRequest.HiddenFromAddressListsEnabled - EWSEnabled = $CASRequest.EwsEnabled - MailboxMAPIEnabled = $CASRequest.MAPIEnabled - MailboxOWAEnabled = $CASRequest.OWAEnabled - MailboxImapEnabled = $CASRequest.ImapEnabled - MailboxPopEnabled = $CASRequest.PopEnabled - MailboxActiveSyncEnabled = $CASRequest.ActiveSyncEnabled - Permissions = $ParsedPerms - ProhibitSendQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendQuota -split ' GB')[0], 2) - ProhibitSendReceiveQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' GB')[0], 2) - ItemCount = [math]::Round($StatsRequest.ItemCount, 2) - TotalItemSize = [math]::Round($StatsRequest.TotalItemSize / 1Gb, 2) - TotalArchiveItemSize = $ArchiveSize.totalItemSize.split('(')[0] - TotalArchiveItemCount = [math]::Round($ArchiveSize.ItemCount, 2) - BlockedForSpam = $BlockedForSpam - ArchiveMailBox = $ArchiveEnabled - AutoExpandingArchive = $Archive.AutoExpandingArchiveEnabled - RecipientTypeDetails = $MailboxDetailedRequest.RecipientTypeDetails - } -} -else { - $GraphRequest = [ordered]@{ - ForwardAndDeliver = $MailboxDetailedRequest.DeliverToMailboxAndForward - ForwardingAddress = $ForwardingAddress - LitiationHold = $MailboxDetailedRequest.LitigationHoldEnabled - HiddenFromAddressLists = $MailboxDetailedRequest.HiddenFromAddressListsEnabled - EWSEnabled = $CASRequest.EwsEnabled - MailboxMAPIEnabled = $CASRequest.MAPIEnabled - MailboxOWAEnabled = $CASRequest.OWAEnabled - MailboxImapEnabled = $CASRequest.ImapEnabled - MailboxPopEnabled = $CASRequest.PopEnabled - MailboxActiveSyncEnabled = $CASRequest.ActiveSyncEnabled - Permissions = $ParsedPerms - ProhibitSendQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendQuota -split ' GB')[0], 2) - ProhibitSendReceiveQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' GB')[0], 2) - ItemCount = [math]::Round($StatsRequest.ItemCount, 2) - TotalItemSize = [math]::Round($StatsRequest.TotalItemSize / 1Gb, 2) - TotalArchiveItemSize = 0 - TotalArchiveItemCount = 0 - BlockedForSpam = $BlockedForSpam - ArchiveMailBox = $ArchiveEnabled - AutoExpandingArchive = $Archive.AutoExpandingArchiveEnabled - RecipientTypeDetails = $MailboxDetailedRequest.RecipientTypeDetails - } -} - - -#$GraphRequest = [ordered]@{ -# Connectivity = $CASRequest -# Mailbox = $MailRequest -# MailboxDetail = $MailboxDetailedRequest -# Stats = $StatsRequest -# Permissions = $ParsedPerms -# Result = $Result -#} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListUserMailboxRules/function.json b/ListUserMailboxRules/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListUserMailboxRules/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListUserMailboxRules/run.ps1 b/ListUserMailboxRules/run.ps1 deleted file mode 100644 index 37e218452d9b..000000000000 --- a/ListUserMailboxRules/run.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -try { -$TenantFilter = $Request.Query.TenantFilter -$UserID = $Request.Query.UserID - $GraphRequest = New-ExoRequest -tenantid $TenantFilter -cmdlet "Get-InboxRule" -cmdParams @{mailbox = $UserID} | Select-Object - @{ Name = 'DisplayName'; Expression = { $_.displayName} }, - @{ Name = 'Description'; Expression = { $_.Description} }, - @{ Name = 'Redirect To'; Expression = { $_.RedirectTo} }, - @{ Name = 'Copy To Folder'; Expression = { $_.CopyToFolder} }, - @{ Name = 'Move To Folder'; Expression = { $_.MoveToFolder} }, - @{ Name = 'Soft Delete Message'; Expression = { $_.SoftDeleteMessage} }, - @{ Name = 'Delete Message'; Expression = { $_.DeleteMessage} } -} - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve mailbox rules $($request.query.id): $($_.Exception.message) " -Sev 'Error' -tenant $TenantFilter - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = '500' - Body = $(Get-NormalizedError -message $_.Exception.message) - }) - } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) -}) \ No newline at end of file diff --git a/ListUserPhoto/function.json b/ListUserPhoto/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListUserPhoto/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListUserPhoto/run.ps1 b/ListUserPhoto/run.ps1 deleted file mode 100644 index e142f8382913..000000000000 --- a/ListUserPhoto/run.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$tenantFilter = $Request.Query.TenantFilter -$userId = $Request.Query.UserID - - -$URI = "https://graph.microsoft.com/v1.0/users/$userId/photos/240x240/`$value" -Write-Host $URI -$graphRequest = New-GraphGetRequest -uri $URI -tenantid $tenantFilter - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($graphRequest) - }) \ No newline at end of file diff --git a/ListUserSigninLogs/function.json b/ListUserSigninLogs/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListUserSigninLogs/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListUserSigninLogs/run.ps1 b/ListUserSigninLogs/run.ps1 deleted file mode 100644 index fa8f7edb1fce..000000000000 --- a/ListUserSigninLogs/run.ps1 +++ /dev/null @@ -1,51 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$UserID = $Request.Query.UserID -try { - $URI = "https://graph.microsoft.com/beta/auditLogs/signIns?`$filter=(userId eq '$UserID')&`$top=50&`$orderby=createdDateTime desc" - Write-Host $URI - $GraphRequest = New-GraphGetRequest -uri $URI -tenantid $TenantFilter -noPagination $true -verbose | Select-Object @{ Name = 'Date'; Expression = { $(($_.createdDateTime | Out-String) -replace '\r\n') } }, - id, - @{ Name = 'Application'; Expression = { $_.resourceDisplayName } }, - @{ Name = 'LoginStatus'; Expression = { $_.status.errorCode } }, - @{ Name = 'ConditionalAccessStatus'; Expression = { $_.conditionalAccessStatus } }, - @{ Name = 'OverallLoginStatus'; Expression = { if (($_.conditionalAccessStatus -eq 'Success' -or 'Not Applied') -and $_.status.errorCode -eq 0) { 'Success' } else { 'Failed' } } }, - @{ Name = 'IPAddress'; Expression = { $_.ipAddress } }, - @{ Name = 'Town'; Expression = { $_.location.city } }, - @{ Name = 'State'; Expression = { $_.location.state } }, - @{ Name = 'Country'; Expression = { $_.location.countryOrRegion } }, - @{ Name = 'Device'; Expression = { $_.deviceDetail.displayName } }, - @{ Name = 'DeviceCompliant'; Expression = { $_.deviceDetail.isCompliant } }, - @{ Name = 'OS'; Expression = { $_.deviceDetail.operatingSystem } }, - @{ Name = 'Browser'; Expression = { $_.deviceDetail.browser } }, - @{ Name = 'AppliedCAPs'; Expression = { ($_.appliedConditionalAccessPolicies | ForEach-Object { @{Result = $_.result; Name = $_.displayName } }) } }, - @{ Name = 'AdditionalDetails'; Expression = { $_.status.additionalDetails } }, - @{ Name = 'FailureReason'; Expression = { $_.status.failureReason } }, - @{ Name = 'FullDetails'; Expression = { $_ } } - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve Sign In report: $($_.Exception.message) " -Sev 'Error' -tenant $TenantFilter - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = '500' - Body = $(Get-NormalizedError -message $_.Exception.message) - }) -} - diff --git a/ListUsers/function.json b/ListUsers/function.json deleted file mode 100644 index 3d31416065c2..000000000000 --- a/ListUsers/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "generalAllTenantQueue" - } - ] -} diff --git a/ListUsers/run.ps1 b/ListUsers/run.ps1 deleted file mode 100644 index 4c50e052e256..000000000000 --- a/ListUsers/run.ps1 +++ /dev/null @@ -1,83 +0,0 @@ -using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$selectlist = 'id', 'accountEnabled', 'businessPhones', 'city', 'createdDateTime', 'companyName', 'country', 'department', 'displayName', 'faxNumber', 'givenName', 'isResourceAccount', 'jobTitle', 'mail', 'mailNickname', 'mobilePhone', 'onPremisesDistinguishedName', 'officeLocation', 'onPremisesLastSyncDateTime', 'otherMails', 'postalCode', 'preferredDataLocation', 'preferredLanguage', 'proxyAddresses', 'showInAddressList', 'state', 'streetAddress', 'surname', 'usageLocation', 'userPrincipalName', 'userType', 'assignedLicenses', 'onPremisesSyncEnabled', 'LicJoined', 'Aliases', 'primDomain', 'Tenant', 'CippStatus' -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$ConvertTable = Import-Csv Conversiontable.csv | Sort-Object -Property 'guid' -Unique -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$GraphFilter = $Request.Query.graphFilter -$userid = $Request.Query.UserID - -$GraphRequest = if ($TenantFilter -ne 'AllTenants') { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userid)?`$top=999&`$select=$($selectlist -join ',')&`$filter=$GraphFilter&`$count=true" -tenantid $TenantFilter -ComplexFilter | Select-Object $selectlist | ForEach-Object { - $_.onPremisesSyncEnabled = [bool]($_.onPremisesSyncEnabled) - $_.Aliases = $_.Proxyaddresses -join ', ' - $SkuID = $_.AssignedLicenses.skuid - $_.LicJoined = ($ConvertTable | Where-Object { $_.guid -in $skuid }).'Product_Display_Name' -join ', ' - $_.primDomain = ($_.userPrincipalName -split '@' | Select-Object -Last 1) - $_ - } -} -else { - $Table = Get-CIPPTable -TableName 'cacheusers' - $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) - if (!$Rows) { - $Queue = New-CippQueueEntry -Name 'Users' -Link '/identity/administration/users?customerId=AllTenants' - Push-OutputBinding -Name Msg -Value "users/$($userid)?`$top=999&`$select=$($selectlist -join ',')&`$filter=$GraphFilter&`$count=true" - [PSCustomObject]@{ - Tenant = 'Loading data for all tenants. Please check back after the job completes' - QueueId = $Queue.RowKey - } - } - else { - $Rows.Data | ConvertFrom-Json | Select-Object $selectlist | ForEach-Object { - $_.onPremisesSyncEnabled = [bool]($_.onPremisesSyncEnabled) - $_.Aliases = $_.Proxyaddresses -join ', ' - $SkuID = $_.AssignedLicenses.skuid - $_.LicJoined = ($ConvertTable | Where-Object { $_.guid -in $skuid }).'Product_Display_Name' -join ', ' - $_.primDomain = ($_.userPrincipalName -split '@' | Select-Object -Last 1) - $_ - } - } -} - - -if ($userid -and $Request.query.IncludeLogonDetails) { - $startDate = (Get-Date).AddDays(-7) - $endDate = (Get-Date) - $sessionid = Get-Random -Maximum 1000 -Minimum 1 - $SearchParam = @{ - SessionCommand = 'ReturnLargeSet' - Operations = @('UserLoggedIn', 'UserLoginFailed', 'TeamsSessionStarted', 'MailboxLogin') - sessionid = $sessionid - startDate = $startDate - endDate = $endDate - UserIds = @($GraphRequest.userPrincipalName) - } - $AuditlogsLogon = (New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Search-unifiedAuditLog' -cmdParams $SearchParam | Sort-Object -Property CreationDate | Select-Object -Last 1).auditdata | ConvertFrom-Json - $Appname = '[{"Application Name":"ACOM Azure Website","Application IDs":"23523755-3a2b-41ca-9315-f81f3f566a95"},{"Application Name":"AEM-DualAuth","Application IDs":"69893ee3-dd10-4b1c-832d-4870354be3d8"},{"Application Name":"ASM Campaign Servicing","Application IDs":"0cb7b9ec-5336-483b-bc31-b15b5788de71"},{"Application Name":"Azure Advanced Threat Protection","Application IDs":"7b7531ad-5926-4f2d-8a1d-38495ad33e17"},{"Application Name":"Azure Data Lake","Application IDs":"e9f49c6b-5ce5-44c8-925d-015017e9f7ad"},{"Application Name":"Azure Lab Services Portal","Application IDs":"835b2a73-6e10-4aa5-a979-21dfda45231c"},{"Application Name":"Azure Portal","Application IDs":"c44b4083-3bb0-49c1-b47d-974e53cbdf3c"},{"Application Name":"AzureSupportCenter","Application IDs":"37182072-3c9c-4f6a-a4b3-b3f91cacffce"},{"Application Name":"Bing","Application IDs":"9ea1ad79-fdb6-4f9a-8bc3-2b70f96e34c7"},{"Application Name":"CPIM Service","Application IDs":"bb2a2e3a-c5e7-4f0a-88e0-8e01fd3fc1f4"},{"Application Name":"CRM Power BI Integration","Application IDs":"e64aa8bc-8eb4-40e2-898b-cf261a25954f"},{"Application Name":"Dataverse","Application IDs":"00000007-0000-0000-c000-000000000000"},{"Application Name":"Enterprise Roaming and Backup","Application IDs":"60c8bde5-3167-4f92-8fdb-059f6176dc0f"},{"Application Name":"IAM Supportability","Application IDs":"a57aca87-cbc0-4f3c-8b9e-dc095fdc8978"},{"Application Name":"IrisSelectionFrontDoor","Application IDs":"16aeb910-ce68-41d1-9ac3-9e1673ac9575"},{"Application Name":"MCAPI Authorization Prod","Application IDs":"d73f4b35-55c9-48c7-8b10-651f6f2acb2e"},{"Application Name":"Media Analysis and Transformation Service","Application IDs":"944f0bd1-117b-4b1c-af26-804ed95e767e
    0cd196ee-71bf-4fd6-a57c-b491ffd4fb1e"},{"Application Name":"Microsoft 365 Support Service","Application IDs":"ee272b19-4411-433f-8f28-5c13cb6fd407"},{"Application Name":"Microsoft App Access Panel","Application IDs":"0000000c-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Approval Management","Application IDs":"65d91a3d-ab74-42e6-8a2f-0add61688c74
    38049638-cc2c-4cde-abe4-4479d721ed44"},{"Application Name":"Microsoft Authentication Broker","Application IDs":"29d9ed98-a469-4536-ade2-f981bc1d605e"},{"Application Name":"Microsoft Azure CLI","Application IDs":"04b07795-8ddb-461a-bbee-02f9e1bf7b46"},{"Application Name":"Microsoft Azure PowerShell","Application IDs":"1950a258-227b-4e31-a9cf-717495945fc2"},{"Application Name":"Microsoft Bing Search","Application IDs":"cf36b471-5b44-428c-9ce7-313bf84528de"},{"Application Name":"Microsoft Bing Search for Microsoft Edge","Application IDs":"2d7f3606-b07d-41d1-b9d2-0d0c9296a6e8"},{"Application Name":"Microsoft Bing Default Search Engine","Application IDs":"1786c5ed-9644-47b2-8aa0-7201292175b6"},{"Application Name":"Microsoft Defender for Cloud Apps","Application IDs":"3090ab82-f1c1-4cdf-af2c-5d7a6f3e2cc7"},{"Application Name":"Microsoft Docs","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Dynamics ERP","Application IDs":"00000015-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Edge Insider Addons Prod","Application IDs":"6253bca8-faf2-4587-8f2f-b056d80998a7"},{"Application Name":"Microsoft Exchange Online Protection","Application IDs":"00000007-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Forms","Application IDs":"c9a559d2-7aab-4f13-a6ed-e7e9c52aec87"},{"Application Name":"Microsoft Graph","Application IDs":"00000003-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Intune Web Company Portal","Application IDs":"74bcdadc-2fdc-4bb3-8459-76d06952a0e9"},{"Application Name":"Microsoft Intune Windows Agent","Application IDs":"fc0f3af4-6835-4174-b806-f7db311fd2f3"},{"Application Name":"Microsoft Learn","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Office","Application IDs":"d3590ed6-52b3-4102-aeff-aad2292ab01c"},{"Application Name":"Microsoft Office 365 Portal","Application IDs":"00000006-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Office Web Apps Service","Application IDs":"67e3df25-268a-4324-a550-0de1c7f97287"},{"Application Name":"Microsoft Online Syndication Partner Portal","Application IDs":"d176f6e7-38e5-40c9-8a78-3998aab820e7"},{"Application Name":"Microsoft password reset service","Application IDs":"93625bc8-bfe2-437a-97e0-3d0060024faa"},{"Application Name":"Microsoft Power BI","Application IDs":"871c010f-5e61-4fb1-83ac-98610a7e9110"},{"Application Name":"Microsoft Storefronts","Application IDs":"28b567f6-162c-4f54-99a0-6887f387bbcc"},{"Application Name":"Microsoft Stream Portal","Application IDs":"cf53fce8-def6-4aeb-8d30-b158e7b1cf83"},{"Application Name":"Microsoft Substrate Management","Application IDs":"98db8bd6-0cc0-4e67-9de5-f187f1cd1b41"},{"Application Name":"Microsoft Support","Application IDs":"fdf9885b-dd37-42bf-82e5-c3129ef5a302"},{"Application Name":"Microsoft Teams","Application IDs":"1fec8e78-bce4-4aaf-ab1b-5451cc387264"},{"Application Name":"Microsoft Teams Services","Application IDs":"cc15fd57-2c6c-4117-a88c-83b1d56b4bbe"},{"Application Name":"Microsoft Teams Web Client","Application IDs":"5e3ce6c0-2b1f-4285-8d4b-75ee78787346"},{"Application Name":"Microsoft Whiteboard Services","Application IDs":"95de633a-083e-42f5-b444-a4295d8e9314"},{"Application Name":"O365 Suite UX","Application IDs":"4345a7b9-9a63-4910-a426-35363201d503"},{"Application Name":"Office 365 Exchange Online","Application IDs":"00000002-0000-0ff1-ce00-000000000000"},{"Application Name":"Office 365 Management","Application IDs":"00b41c95-dab0-4487-9791-b9d2c32c80f2"},{"Application Name":"Office 365 Search Service","Application IDs":"66a88757-258c-4c72-893c-3e8bed4d6899"},{"Application Name":"Office 365 SharePoint Online","Application IDs":"00000003-0000-0ff1-ce00-000000000000"},{"Application Name":"Office Delve","Application IDs":"94c63fef-13a3-47bc-8074-75af8c65887a"},{"Application Name":"Office Online Add-in SSO","Application IDs":"93d53678-613d-4013-afc1-62e9e444a0a5"},{"Application Name":"Office Online Client AAD- Augmentation Loop","Application IDs":"2abdc806-e091-4495-9b10-b04d93c3f040"},{"Application Name":"Office Online Client AAD- Loki","Application IDs":"b23dd4db-9142-4734-867f-3577f640ad0c"},{"Application Name":"Office Online Client AAD- Maker","Application IDs":"17d5e35f-655b-4fb0-8ae6-86356e9a49f5"},{"Application Name":"Office Online Client MSA- Loki","Application IDs":"b6e69c34-5f1f-4c34-8cdf-7fea120b8670"},{"Application Name":"Office Online Core SSO","Application IDs":"243c63a3-247d-41c5-9d83-7788c43f1c43"},{"Application Name":"Office Online Search","Application IDs":"a9b49b65-0a12-430b-9540-c80b3332c127"},{"Application Name":"Office.com","Application IDs":"4b233688-031c-404b-9a80-a4f3f2351f90"},{"Application Name":"Office365 Shell WCSS-Client","Application IDs":"89bee1f7-5e6e-4d8a-9f3d-ecd601259da7"},{"Application Name":"OfficeClientService","Application IDs":"0f698dd4-f011-4d23-a33e-b36416dcb1e6"},{"Application Name":"OfficeHome","Application IDs":"4765445b-32c6-49b0-83e6-1d93765276ca"},{"Application Name":"OfficeShredderWacClient","Application IDs":"4d5c2d63-cf83-4365-853c-925fd1a64357"},{"Application Name":"OMSOctopiPROD","Application IDs":"62256cef-54c0-4cb4-bcac-4c67989bdc40"},{"Application Name":"OneDrive SyncEngine","Application IDs":"ab9b8c07-8f02-4f72-87fa-80105867a763"},{"Application Name":"OneNote","Application IDs":"2d4d3d8e-2be3-4bef-9f87-7875a61c29de"},{"Application Name":"Outlook Mobile","Application IDs":"27922004-5251-4030-b22d-91ecd9a37ea4"},{"Application Name":"Partner Customer Delegated Admin Offline Processor","Application IDs":"a3475900-ccec-4a69-98f5-a65cd5dc5306"},{"Application Name":"Password Breach Authenticator","Application IDs":"bdd48c81-3a58-4ea9-849c-ebea7f6b6360"},{"Application Name":"Power BI Service","Application IDs":"00000009-0000-0000-c000-000000000000"},{"Application Name":"SharedWithMe","Application IDs":"ffcb16e8-f789-467c-8ce9-f826a080d987"},{"Application Name":"SharePoint Online Web Client Extensibility","Application IDs":"08e18876-6177-487e-b8b5-cf950c1e598c"},{"Application Name":"Signup","Application IDs":"b4bddae8-ab25-483e-8670-df09b9f1d0ea"},{"Application Name":"Skype for Business Online","Application IDs":"00000004-0000-0ff1-ce00-000000000000"},{"Application Name":"Sway","Application IDs":"905fcf26-4eb7-48a0-9ff0-8dcc7194b5ba"},{"Application Name":"Universal Store Native Client","Application IDs":"268761a2-03f3-40df-8a8b-c3db24145b6b"},{"Application Name":"Vortex [wsfed enabled]","Application IDs":"5572c4c0-d078-44ce-b81c-6cbf8d3ed39e"},{"Application Name":"Windows Azure Active Directory","Application IDs":"00000002-0000-0000-c000-000000000000"},{"Application Name":"Windows Azure Service Management API","Application IDs":"797f4846-ba00-4fd7-ba43-dac1f8f63013"},{"Application Name":"WindowsDefenderATP Portal","Application IDs":"a3b79187-70b2-4139-83f9-6016c58cd27b"},{"Application Name":"Windows Search","Application IDs":"26a7ee05-5602-4d76-a7ba-eae8b7b67941"},{"Application Name":"Windows Spotlight","Application IDs":"1b3c667f-cde3-4090-b60b-3d2abd0117f0"},{"Application Name":"Windows Store for Business","Application IDs":"45a330b1-b1ec-4cc1-9161-9f03992aa49f"},{"Application Name":"Yammer","Application IDs":"00000005-0000-0ff1-ce00-000000000000"},{"Application Name":"Yammer Web","Application IDs":"c1c74fed-04c9-4704-80dc-9f79a2e515cb"},{"Application Name":"Yammer Web Embed","Application IDs":"e1ef36fd-b883-4dbf-97f0-9ece4b576fc6"}]' | ConvertFrom-Json | Where-Object -Property 'Application IDs' -EQ $AuditlogsLogon.applicationId - $LastSignIn = [PSCustomObject]@{ - AppDisplayName = if ($AppName) { $AppName.'Application Name' } else { "$($AuditlogsLogon.Workload) - $($AuditlogsLogon.ApplicationId) " } - CreatedDateTime = $AuditlogsLogon.CreationTime - Id = $AuditlogsLogon.errorNumber - Status = $AuditlogsLogon.ResultStatus - } - $GraphRequest = $GraphRequest | Select-Object *, - @{ Name = 'LastSigninApplication'; Expression = { $LastSignIn.AppDisplayName } }, - @{ Name = 'LastSigninDate'; Expression = { $($LastSignIn.CreatedDateTime | Out-String) } }, - @{ Name = 'LastSigninStatus'; Expression = { $AuditlogsLogon.operation } }, - @{ Name = 'LastSigninResult'; Expression = { $LastSignIn.status } }, - @{ Name = 'LastSigninFailureReason'; Expression = { if ($LastSignIn.Id -eq 0) { 'Sucessfully signed in' } else { $LastSignIn.Id } } } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) - }) \ No newline at end of file diff --git a/ListWebhookAlert/function.json b/ListWebhookAlert/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListWebhookAlert/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListWebhookAlert/run.ps1 b/ListWebhookAlert/run.ps1 deleted file mode 100644 index add6382e248a..000000000000 --- a/ListWebhookAlert/run.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$WebhookTable = Get-CIPPTable -TableName webhookTable -$WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @($WebhookRow) - }) - diff --git a/ListmailboxPermissions/function.json b/ListmailboxPermissions/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ListmailboxPermissions/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ListmailboxPermissions/run.ps1 b/ListmailboxPermissions/run.ps1 deleted file mode 100644 index 571485b8f123..000000000000 --- a/ListmailboxPermissions/run.ps1 +++ /dev/null @@ -1,62 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter - -Write-Host "Tenant Filter: $TenantFilter" -try { - $Bytes = [System.Text.Encoding]::UTF8.GetBytes($Request.Query.UserID) - $base64IdentityParam = [Convert]::ToBase64String($Bytes) - $PermsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($Request.Query.UserID)')/MailboxPermission" -Tenantid $tenantfilter -scope ExchangeOnline - $PermsRequest2 = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Recipient('$base64IdentityParam')?`$expand=RecipientPermission&isEncoded=true" -Tenantid $tenantfilter -scope ExchangeOnline - $PermRequest3 = New-ExoRequest -Anchor $Request.Query.UserID -tenantid $Tenantfilter -cmdlet "Get-Mailbox" -cmdParams @{Identity = $($Request.Query.UserID); } - - $GraphRequest = foreach ($Perm in $PermsRequest, $PermsRequest2.RecipientPermission, $PermRequest3) { - - if ($perm.Trustee) { - $perm | Where-Object Trustee | ForEach-Object { [PSCustomObject]@{ - User = $_.Trustee - Permissions = $_.accessRights - } - } - - } - if ($perm.PermissionList) { - $perm | Where-Object User | ForEach-Object { [PSCustomObject]@{ - User = $_.User - Permissions = $_.PermissionList.accessRights -join ', ' - } - } - } - if ($perm.GrantSendonBehalfTo -ne $null) { - $perm.GrantSendonBehalfTo | ForEach-Object { [PSCustomObject]@{ - User = $_ - Permissions = "SendOnBehalf" - } - } - } - } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) - - diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAPDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAPDevices.ps1 new file mode 100644 index 000000000000..fce90a91487c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAPDevices.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-ListAPDevices { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $userid = $Request.Query.UserID + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities?`$top=999" -tenantid $TenantFilter + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAlertsQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAlertsQueue.ps1 new file mode 100644 index 000000000000..af4f752ac582 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAlertsQueue.ps1 @@ -0,0 +1,53 @@ +using namespace System.Net + +Function Invoke-ListAlertsQueue { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $Table = Get-CIPPTable -TableName 'SchedulerConfig' + $Filter = "PartitionKey eq 'Alert'" + $QueuedApps = Get-CIPPAzDataTableEntity @Table -Filter $Filter + + $CurrentStandards = foreach ($QueueFile in $QueuedApps) { + [PSCustomObject]@{ + tenantName = $QueueFile.tenant + AdminPassword = [bool]$QueueFile.AdminPassword + DefenderMalware = [bool]$QueueFile.DefenderMalware + DefenderStatus = [bool]$QueueFile.DefenderStatus + MFAAdmins = [bool]$QueueFile.MFAAdmins + MFAAlertUsers = [bool]$QueueFile.MFAAlertUsers + NewGA = [bool]$QueueFile.NewGA + NewRole = [bool]$QueueFile.NewRole + QuotaUsed = [bool]$QueueFile.QuotaUsed + UnusedLicenses = [bool]$QueueFile.UnusedLicenses + OverusedLicenses = [bool]$QueueFile.OverusedLicenses + AppSecretExpiry = [bool]$QueueFile.AppSecretExpiry + ApnCertExpiry = [bool]$QueueFile.ApnCertExpiry + VppTokenExpiry = [bool]$QueueFile.VppTokenExpiry + DepTokenExpiry = [bool]$QueueFile.DepTokenExpiry + NoCAConfig = [bool]$QueueFile.NoCAConfig + SecDefaultsUpsell = [bool]$QueueFile.SecDefaultsUpsell + SharepointQuota = [bool]$QueueFile.SharePointQuota + ExpiringLicenses = [bool]$QueueFile.ExpiringLicenses + tenantId = $QueueFile.tenantid + } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($CurrentStandards) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAllTenantDeviceCompliance.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAllTenantDeviceCompliance.ps1 new file mode 100644 index 000000000000..d9d49840c7b4 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAllTenantDeviceCompliance.ps1 @@ -0,0 +1,44 @@ +using namespace System.Net + +Function Invoke-ListAllTenantDeviceCompliance { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + if ($TenantFilter -eq 'AllTenants') { + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedDeviceCompliances' + $StatusCode = [HttpStatusCode]::OK + } else { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedDeviceCompliances?`$top=999&`$filter=organizationId eq '$TenantFilter'" + $StatusCode = [HttpStatusCode]::OK + } + + if ($GraphRequest.value.count -lt 1) { + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = 'No data found - This client might not be onboarded in Lighthouse' + } + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = "Could not connect to Azure Lighthouse API: $($ErrorMessage)" + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 index 89ecf70bd9ba..9cfc10853e23 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppConsentRequests.ps1 @@ -12,42 +12,43 @@ function Invoke-ListAppConsentRequests { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' try { - if ($Request.Query.TenantFilter -eq "AllTenants") { - throw "AllTenants is not yet supported" + if ($Request.Query.TenantFilter -eq 'AllTenants') { + throw 'AllTenants is not yet supported' } else { $TenantFilter = $Request.Query.TenantFilter } - $appConsentRequests = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/identityGovernance/appConsent/appConsentRequests" -tenantid $TenantFilter # Need the beta endpoint to get consentType + $appConsentRequests = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identityGovernance/appConsent/appConsentRequests' -tenantid $TenantFilter # Need the beta endpoint to get consentType $Results = foreach ($app in $appConsentRequests) { $userConsentRequests = New-GraphGetRequest -Uri "https://graph.microsoft.com/v1.0/identityGovernance/appConsent/appConsentRequests/$($app.id)/userConsentRequests" -tenantid $TenantFilter $userConsentRequests | ForEach-Object { [pscustomobject]@{ - appId = $app.appId - appDisplayName = $app.appDisplayName - requestUser = $_.createdBy.user.userPrincipalName - requestReason = $_.reason - requestDate = $_.createdDateTime - requestStatus = $_.status - reviewedBy = $_.approval.stages.reviewedBy.userPrincipalName + appId = $app.appId + appDisplayName = $app.appDisplayName + requestUser = $_.createdBy.user.userPrincipalName + requestReason = $_.reason + requestDate = $_.createdDateTime + requestStatus = $_.status + reviewedBy = $_.approval.stages.reviewedBy.userPrincipalName reviewedJustification = $_.approval.stages.justification - reviewedDate = $_.approval.stages.reviewedDateTime - reviewedStatus = $_.approval.stages.status - scopes = $app.pendingScopes.displayName - consentUrl = if ($app.consentType -eq "Static") { # if something is going wrong here you've probably stumbled on a fourth variation - rvdwegen + reviewedDate = $_.approval.stages.reviewedDateTime + reviewedStatus = $_.approval.stages.status + scopes = $app.pendingScopes.displayName + 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" } - } + } } } $StatusCode = [HttpStatusCode]::OK } catch { $StatusCode = [HttpStatusCode]::OK - Write-LogMessage -user $ExecutingUser -API $APIName -message "app consent request list failed" -Sev "Error" -tenant $TenantFilter + Write-LogMessage -user $ExecutingUser -API $APIName -message 'app consent request list failed' -Sev 'Error' -tenant $TenantFilter $Results = @{ appDisplayName = "Error: $($_.Exception.Message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppStatus.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppStatus.ps1 new file mode 100644 index 000000000000..1b6723d21c17 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppStatus.ps1 @@ -0,0 +1,40 @@ +using namespace System.Net + +Function Invoke-ListAppStatus { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $tenantfilter = $Request.Query.TenantFilter + $appFilter = $Request.Query.AppFilter + Write-Host "Using $appFilter" + $body = @" +{"select":["DeviceName","UserPrincipalName","Platform","AppVersion","InstallState","InstallStateDetail","LastModifiedDateTime","DeviceId","ErrorCode","UserName","UserId","ApplicationId","AssignmentFilterIdsList","AppInstallState","AppInstallStateDetails","HexErrorCode"],"skip":0,"top":999,"filter":"(ApplicationId eq '$Appfilter')","orderBy":[]} +"@ + try { + $GraphRequest = New-Graphpostrequest -uri 'https://graph.microsoft.com/beta/deviceManagement/reports/getDeviceInstallStatusReport' -tenantid $TenantFilter -body $body + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListApplicationQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListApplicationQueue.ps1 new file mode 100644 index 000000000000..13e945aa16ed --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListApplicationQueue.ps1 @@ -0,0 +1,40 @@ +using namespace System.Net + +Function Invoke-ListApplicationQueue { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $Table = Get-CippTable -tablename 'apps' + $QueuedApps = (Get-CIPPAzDataTableEntity @Table) + + $CurrentApps = foreach ($QueueFile in $QueuedApps) { + Write-Host $QueueFile + $ApplicationFile = $QueueFile.JSON | ConvertFrom-Json -Depth 10 + [PSCustomObject]@{ + tenantName = $ApplicationFile.tenant + applicationName = $ApplicationFile.Applicationname + cmdLine = $ApplicationFile.IntuneBody.installCommandLine + assignTo = $ApplicationFile.assignTo + id = $($QueueFile.RowKey) + status = $($QueueFile.status) + } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($CurrentApps) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListApps.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListApps.ps1 new file mode 100644 index 000000000000..aca3cafdcd2f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListApps.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-ListApps { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?`$top=999&`$filter=(microsoft.graph.managedApp/appAvailability%20eq%20null%20or%20microsoft.graph.managedApp/appAvailability%20eq%20%27lineOfBusiness%27%20or%20isAssigned%20eq%20true)&`$orderby=displayName&" -tenantid $TenantFilter + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppsRepository.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppsRepository.ps1 new file mode 100644 index 000000000000..e85a43e1740b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAppsRepository.ps1 @@ -0,0 +1,69 @@ +using namespace System.Net + +Function Invoke-ListAppsRepository { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Search = $Request.Body.Search + $Repository = $Request.Body.Repository + $Packages = @() + $Message = '' + $IsError = $false + + try { + if (!([string]::IsNullOrEmpty($Search))) { + if ([string]::IsNullOrEmpty($Repository)) { + $Repository = 'https://chocolatey.org/api/v2' + } + + # Latest version, top 30 results matching search term + $SearchPath = "Search()?`$filter=IsLatestVersion&`$skip=0&`$top=30&searchTerm='$Search'&targetFramework=''&includePrerelease=false" + + $Url = "$Repository/$SearchPath" + $RepoPackages = Invoke-RestMethod $Url -ErrorAction Stop + + if (($RepoPackages | Measure-Object).Count -gt 0) { + $Packages = foreach ($RepoPackage in $RepoPackages) { + [PSCustomObject]@{ + packagename = $RepoPackage.title.'#text' + author = $RepoPackage.author.Name + applicationName = $RepoPackage.properties.Title + version = $RepoPackage.properties.Version + description = $RepoPackage.summary.'#text' + customRepo = $Repository + created = Get-Date -Date $RepoPackage.properties.Created.'#text' -Format 'MM/dd/yyyy HH:mm:ss' + } + } + } else { + $IsError = $true + $Message = 'No results found' + } + } else { + $IsError = $true + $Message = 'No search terms specified' + } + } catch { + $IsError = $true + $Message = "Repository error: $($_.Exception.Message)" + } + + $PackageSearch = @{ + Search = $Search + Results = @($Packages | Sort-Object -Property packagename) + Message = $Message + IsError = $IsError + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $PackageSearch + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAutopilotconfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAutopilotconfig.ps1 new file mode 100644 index 000000000000..43ae12082452 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAutopilotconfig.ps1 @@ -0,0 +1,42 @@ +using namespace System.Net + +Function Invoke-ListAutopilotconfig { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $userid = $Request.Query.UserID + try { + if ($request.query.type -eq 'ApProfile') { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles?`$expand=assignments" -tenantid $TenantFilter + } + + if ($request.query.type -eq 'ESP') { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations?`$expand=assignments" -tenantid $TenantFilter | Where-Object -Property '@odata.type' -EQ '#microsoft.graph.windows10EnrollmentCompletionPageConfiguration' + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAzureADConnectStatus.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAzureADConnectStatus.ps1 new file mode 100644 index 000000000000..f1b1b0592e76 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListAzureADConnectStatus.ps1 @@ -0,0 +1,77 @@ +using namespace System.Net + +Function Invoke-ListAzureADConnectStatus { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $DataToReturn = $Request.Query.DataToReturn + + if (($DataToReturn -eq 'AzureADConnectSettings') -or ([string]::IsNullOrEmpty($DataToReturn)) ) { + $ADConnectStatusGraph = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/organization' -tenantid $TenantFilter + #$ADConnectStatusGraph = New-ClassicAPIGetRequest -Resource "74658136-14ec-4630-ad9b-26e160ff0fc6" -TenantID $TenantFilter -Uri "https://main.iam.ad.ext.azure.com/api/Directories/ADConnectStatus" -Method "GET" + #$PasswordSyncStatusGraph = New-ClassicAPIGetRequest -Resource "74658136-14ec-4630-ad9b-26e160ff0fc6" -TenantID $TenantFilter -Uri "https://main.iam.ad.ext.azure.com/api/Directories/GetPasswordSyncStatus" -Method "GET" + $AzureADConnectSettings = [PSCustomObject]@{ + dirSyncEnabled = [boolean]$ADConnectStatusGraph.onPremisesSyncEnabled + #dirSyncConfigured = [boolean]$ADConnectStatusGraph.dirSyncConfigured + #passThroughAuthenticationEnabled = [boolean]$ADConnectStatusGraph.passThroughAuthenticationEnabled + #seamlessSingleSignOnEnabled = [boolean]$ADConnectStatusGraph.seamlessSingleSignOnEnabled + numberOfHoursFromLastSync = $ADConnectStatusGraph.onPremisesLastSyncDateTime + #passwordSyncStatus = [boolean]$PasswordSyncStatusGraph + raw = $ADConnectStatusGraph + } + } + + if (($DataToReturn -eq 'AzureADObjectsInError') -or ([string]::IsNullOrEmpty($DataToReturn)) ) { + $selectlist = 'id', 'displayName', 'onPremisesProvisioningErrors', 'createdDateTime' + $Types = 'Users', 'Contacts', 'Groups' + + $GraphRequest = foreach ($Type in $types) { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/$($Type)?`$select=$($selectlist -join ',')" -tenantid $TenantFilter | ForEach-Object { + if ($_.id -ne $null) { + $_ | Add-Member -NotePropertyName ObjectType -NotePropertyValue $Type + $_ + } + + } + } + $ObjectsInError = @($GraphRequest) + } + + if ([string]::IsNullOrEmpty($DataToReturn)) { + $FinalObject = [PSCustomObject]@{ + AzureADConnectSettings = $AzureADConnectSettings + ObjectsInError = $ObjectsInError + } + } + if ($DataToReturn -eq 'AzureADConnectSettings') { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $AzureADConnectSettings + }) + } elseif ($DataToReturn -eq 'AzureADObjectsInError') { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($ObjectsInError) + }) + } else { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($FinalObject) + }) + } + + +} diff --git a/ListBPA/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 similarity index 92% rename from ListBPA/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 index 247175c44ebf..1933e6776f2d 100644 --- a/ListBPA/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-ListBPA { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName # Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Table = get-cipptable 'cachebpav2' @@ -76,3 +81,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = ($Results | ConvertTo-Json -Depth 15) }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 new file mode 100644 index 000000000000..66c095f69d69 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 @@ -0,0 +1,40 @@ +using namespace System.Net + +Function Invoke-ListBPATemplates { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + Write-Host 'PowerShell HTTP trigger function processed a request.' + Write-Host $Request.query.id + + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + $Templates = Get-ChildItem 'Config\*.BPATemplate.json' + + if ($Request.Query.RawJson) { + $Templates = $Templates | ForEach-Object { + $(Get-Content $_) | ConvertFrom-Json + } + } else { + $Templates = $Templates | ForEach-Object { + $Template = $(Get-Content $_) | ConvertFrom-Json + @{ + Data = $Template.fields + Name = $Template.Name + Style = $Template.Style + } + } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = ($Templates | ConvertTo-Json -Depth 10) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuth.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuth.ps1 new file mode 100644 index 000000000000..bf831b65cc9b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuth.ps1 @@ -0,0 +1,63 @@ +using namespace System.Net + +Function Invoke-ListBasicAuth { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $currentTime = Get-Date -Format 'yyyy-MM-ddTHH:MM:ss' + $ts = (Get-Date).AddDays(-30) + $endTime = $ts.ToString('yyyy-MM-ddTHH:MM:ss') + ##Create Filter for basic auth sign-ins + $filters = "createdDateTime ge $($endTime)Z and createdDateTime lt $($currentTime)Z and (clientAppUsed eq 'AutoDiscover' or clientAppUsed eq 'Exchange ActiveSync' or clientAppUsed eq 'Exchange Online PowerShell' or clientAppUsed eq 'Exchange Web Services' or clientAppUsed eq 'IMAP4' or clientAppUsed eq 'MAPI Over HTTP' or clientAppUsed eq 'Offline Address Book' or clientAppUsed eq 'Outlook Anywhere (RPC over HTTP)' or clientAppUsed eq 'Other clients' or clientAppUsed eq 'POP3' or clientAppUsed eq 'Reporting Web Services' or clientAppUsed eq 'Authenticated SMTP' or clientAppUsed eq 'Outlook Service')" + if ($TenantFilter -ne 'AllTenants') { + + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/signIns?api-version=beta&filter=$($filters)" -tenantid $TenantFilter -erroraction stop | Select-Object userPrincipalName, clientAppUsed, Status | Sort-Object -Unique -Property userPrincipalName + $response = $GraphRequest + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Retrieved basic authentication report' -Sev 'Debug' -tenant $TenantFilter + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($response) + }) + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve basic authentication report: $($_.Exception.message) " -Sev 'Error' -tenant $TenantFilter + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = '500' + Body = $(Get-NormalizedError -message $_.Exception.message) + }) + } + } else { + $Table = Get-CIPPTable -TableName cachebasicauth + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) + if (!$Rows) { + Push-OutputBinding -Name Msg -Value (Get-Date).ToString() + $GraphRequest = [PSCustomObject]@{ + Tenant = 'Loading data for all tenants. Please check back in 10 minutes' + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + } else { + $GraphRequest = $Rows + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + } + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 new file mode 100644 index 000000000000..c1c379e7b65a --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 @@ -0,0 +1,45 @@ +using namespace System.Net + +Function Invoke-ListBasicAuthAllTenants { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + Get-Tenants | ForEach-Object -Parallel { + $domainName = $_.defaultDomainName + Import-Module '.\GraphHelper.psm1' + $currentTime = Get-Date -Format 'yyyy-MM-ddTHH:MM:ss' + $ts = (Get-Date).AddDays(-30) + $endTime = $ts.ToString('yyyy-MM-ddTHH:MM:ss') + $filters = "createdDateTime ge $($endTime)Z and createdDateTime lt $($currentTime)Z and (clientAppUsed eq 'AutoDiscover' or clientAppUsed eq 'Exchange ActiveSync' or clientAppUsed eq 'Exchange Online PowerShell' or clientAppUsed eq 'Exchange Web Services' or clientAppUsed eq 'IMAP4' or clientAppUsed eq 'MAPI Over HTTP' or clientAppUsed eq 'Offline Address Book' or clientAppUsed eq 'Outlook Anywhere (RPC over HTTP)' or clientAppUsed eq 'Other clients' or clientAppUsed eq 'POP3' or clientAppUsed eq 'Reporting Web Services' or clientAppUsed eq 'Authenticated SMTP' or clientAppUsed eq 'Outlook Service')" + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/signIns?api-version=beta&filter=$($filters)" -tenantid $domainName -ErrorAction stop | Sort-Object -Unique -Property clientAppUsed | ForEach-Object { + @{ + Tenant = $domainName + clientAppUsed = $_.clientAppUsed + userPrincipalName = $_.UserPrincipalName + RowKey = "$($_.UserPrincipalName)-$($_.clientAppUsed)" + PartitionKey = 'basicauth' + } + } + } catch { + $GraphRequest = @{ + Tenant = $domainName + clientAppUsed = "Could not connect to Tenant: $($_.Exception.message)" + userPrincipalName = $domainName + RowKey = $domainName + PartitionKey = 'basicauth' + } + } + $Table = Get-CIPPTable -TableName cachebasicauth + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + + } + + + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 new file mode 100644 index 000000000000..7604aa1b5961 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 @@ -0,0 +1,49 @@ +using namespace System.Net + +Function Invoke-ListCAtemplates { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + Write-Host $Request.query.id + #Migrating old policies whenever you do a list + $Table = Get-CippTable -tablename 'templates' + + $Templates = Get-ChildItem 'Config\*.CATemplate.json' | ForEach-Object { + $Entity = @{ + JSON = "$(Get-Content $_)" + RowKey = "$($_.name)" + PartitionKey = 'CATemplate' + GUID = "$($_.name)" + } + Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force + } + + #List new policies + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'CATemplate'" + $Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { + $data = $_.JSON | ConvertFrom-Json -Depth 100 + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.GUID -Force + $data + } | Sort-Object -Property displayName + + if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property GUID -EQ $Request.query.id } + + $Templates = ConvertTo-Json -InputObject @($Templates) -Depth 100 + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Templates + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCalendarPermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCalendarPermissions.ps1 new file mode 100644 index 000000000000..3bbefa764b84 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCalendarPermissions.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-ListCalendarPermissions { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $UserID = $request.Query.UserID + $Tenantfilter = $request.Query.tenantfilter + + try { + $GetCalParam = @{Identity = $UserID; FolderScope = 'Calendar' } + $CalendarFolder = New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-MailboxFolderStatistics' -cmdParams $GetCalParam | Select-Object -First 1 + $CalParam = @{Identity = "$($UserID):\$($CalendarFolder.name)" } + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-MailboxFolderPermission' -cmdParams $CalParam -UseSystemMailbox $true | Select-Object Identity, User, AccessRights, FolderName + Write-LogMessage -API 'List Calendar Permissions' -tenant $tenantfilter -message "Calendar permissions listed for $($tenantfilter)" -sev Debug + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/ListConditionalAccessPolicies/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListConditionalAccessPolicies.ps1 similarity index 99% rename from ListConditionalAccessPolicies/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ListConditionalAccessPolicies.ps1 index a14f71181e1d..affec8e72291 100644 --- a/ListConditionalAccessPolicies/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListConditionalAccessPolicies.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-ListConditionalAccessPolicies { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" @@ -448,4 +453,6 @@ catch { Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode Body = @($GraphRequest) - }) \ No newline at end of file + }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListContacts.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListContacts.ps1 new file mode 100644 index 000000000000..768417a1db23 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListContacts.ps1 @@ -0,0 +1,41 @@ +using namespace System.Net + +Function Invoke-ListContacts { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $selectlist = 'id', 'companyName', 'displayName', 'mail', 'onPremisesSyncEnabled', 'editURL' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $ContactID = $Request.Query.ContactID + + Write-Host "Tenant Filter: $TenantFilter" + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/contacts/$($ContactID)?`$top=999&`$select=$($selectlist -join ',')" -tenantid $TenantFilter | Select-Object $selectlist | ForEach-Object { + $_.editURL = "https://outlook.office365.com/ecp/@$TenantFilter/UsersGroups/EditContact.aspx?exsvurl=1&realm=$($env:TenantID)&mkt=en-US&id=$($_.id)" + $_ + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest | Where-Object -Property id -NE $null) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDefenderState.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDefenderState.ps1 new file mode 100644 index 000000000000..5b5a244c7e72 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDefenderState.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-ListDefenderState { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $StatusCode = [HttpStatusCode]::OK + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsProtectionStates?`$top=999&`$filter=tenantId eq '$TenantFilter'" + if ($GraphRequest.tenantDisplayName.length -lt 1) { + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = 'No data found - This client might not be onboarded in Lighthouse' + } + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = "Could not connect to Azure Lighthouse API: $($ErrorMessage)" + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDefenderTVM.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDefenderTVM.ps1 new file mode 100644 index 000000000000..2011f161abaf --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDefenderTVM.ps1 @@ -0,0 +1,49 @@ +using namespace System.Net + +Function Invoke-ListDefenderTVM { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $GraphRequest = New-GraphgetRequest -tenantid $TenantFilter -uri "https://api.securitycenter.microsoft.com/api/machines/SoftwareVulnerabilitiesByMachine?`$top=999" -scope 'https://api.securitycenter.microsoft.com/.default' | Group-Object cveid + $GroupObj = foreach ($cve in $GraphRequest) { + [pscustomobject]@{ + customerId = $TenantFilter + affectedDevicesCount = $cve.count + cveId = $cve.name + affectedDevices = ($cve.group.deviceName -join ', ') + osPlatform = ($cve.group.osplatform | Sort-Object -Unique) + softwareVendor = ($cve.group.softwareVendor | Sort-Object -Unique) + softwareName = ($cve.group.softwareName | Sort-Object -Unique) + vulnerabilitySeverityLevel = ($cve.group.vulnerabilitySeverityLevel | Sort-Object -Unique) + cvssScore = ($cve.group.cvssScore | Sort-Object -Unique) + securityUpdateAvailable = ($cve.group.securityUpdateAvailable | Sort-Object -Unique) + exploitabilityLevel = ($cve.group.exploitabilityLevel | Sort-Object -Unique) + } + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GroupObj = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GroupObj) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDeletedItems.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDeletedItems.ps1 new file mode 100644 index 000000000000..2f3488655fa0 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDeletedItems.ps1 @@ -0,0 +1,30 @@ +using namespace System.Net + +Function Invoke-ListDeletedItems { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $selectlist = 'id', 'accountEnabled', 'businessPhones', 'city', 'createdDateTime', 'companyName', 'country', 'department', 'displayName', 'faxNumber', 'givenName', 'isResourceAccount', 'jobTitle', 'mail', 'mailNickname', 'mobilePhone', 'onPremisesDistinguishedName', 'officeLocation', 'onPremisesLastSyncDateTime', 'otherMails', 'postalCode', 'preferredDataLocation', 'preferredLanguage', 'proxyAddresses', 'showInAddressList', 'state', 'streetAddress', 'surname', 'usageLocation', 'userPrincipalName', 'userType', 'assignedLicenses', 'onPremisesSyncEnabled', 'LicJoined', 'Aliases', 'primDomain' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $Types = 'Application', 'User', 'Device', 'Group' + $GraphRequest = foreach ($Type in $Types) { + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/directory/deletedItems/microsoft.graph.$($Type)" -tenantid $TenantFilter) | Where-Object -Property '@odata.context' -NotLike '*graph.microsoft.com*' | Select-Object *, @{ Name = 'TargetType'; Expression = { $Type } } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDeviceDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDeviceDetails.ps1 new file mode 100644 index 000000000000..f3a4d9f309c9 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDeviceDetails.ps1 @@ -0,0 +1,100 @@ +using namespace System.Net + +Function Invoke-ListDeviceDetails { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $DeviceID = $Request.Query.DeviceID + $DeviceName = $Request.Query.DeviceName + $DeviceSerial = $Request.Query.DeviceSerial + + try { + if ($DeviceID) { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceID" -Tenantid $tenantfilter + } elseif ($DeviceSerial -or $DeviceName) { + $Found = $False + if ($SeriaNumber -and $DeviceName) { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=serialnumber eq '$DeviceSerial' and deviceName eq '$DeviceName'" -Tenantid $tenantfilter + + if (($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 ) { + $Found = $True + } + } + if ($DeviceSerial -and $Found -eq $False) { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=serialnumber eq '$DeviceSerial'" -Tenantid $tenantfilter + if (($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 ) { + $Found = $True + } + } + if ($DeviceName -and $Found -eq $False) { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=deviceName eq '$DeviceName'" -Tenantid $tenantfilter + if (($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 ) { + $Found = $True + } + } + + } + + if (!(($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 )) { + $GraphRequest = $Null + } + + if ($GraphRequest) { + [System.Collections.Generic.List[PSCustomObject]]$BulkRequests = @( + @{ + id = 'DeviceGroups' + method = 'GET' + url = "/devices(deviceID='$($GraphRequest.azureADDeviceId)')/memberOf" + }, + @{ + id = 'CompliancePolicies' + method = 'GET' + url = "/deviceManagement/managedDevices('$($GraphRequest.id)')/deviceCompliancePolicyStates" + }, + @{ + id = 'DetectedApps' + method = 'GET' + url = "deviceManagement/managedDevices('$($GraphRequest.id)')?expand=detectedApps" + } + ) + + $BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantFilter + + $DeviceGroups = Get-GraphBulkResultByID -Results $BulkResults -ID 'DeviceGroups' -Value + $CompliancePolicies = Get-GraphBulkResultByID -Results $BulkResults -ID 'CompliancePolicies' -Value + $DetectedApps = Get-GraphBulkResultByID -Results $BulkResults -ID 'DetectedApps' + + $Null = $GraphRequest | Add-Member -NotePropertyName 'DetectedApps' -NotePropertyValue ($DetectedApps.DetectedApps | Select-Object id, displayName, version) + $Null = $GraphRequest | Add-Member -NotePropertyName 'CompliancePolicies' -NotePropertyValue ($CompliancePolicies | Select-Object id, displayname, UserPrincipalName, state) + $Null = $GraphRequest | Add-Member -NotePropertyName 'DeviceGroups' -NotePropertyValue ($DeviceGroups | Select-Object id, displayName, description) + + + } + + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $GraphRequest + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDevices.ps1 new file mode 100644 index 000000000000..8587a32155e3 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDevices.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-ListDevices { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/managedDevices' -Tenantid $tenantfilter + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomainHealth.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomainHealth.ps1 new file mode 100644 index 000000000000..695fb64ffa29 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomainHealth.ps1 @@ -0,0 +1,153 @@ +using namespace System.Net + +Function Invoke-ListDomainHealth { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Import-Module DNSHealth + + try { + $ConfigTable = Get-CippTable -tablename Config + $Filter = "PartitionKey eq 'Domains' and RowKey eq 'Domains'" + $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter $Filter + + $ValidResolvers = @('Google', 'CloudFlare', 'Quad9') + if ($ValidResolvers -contains $Config.Resolver) { + $Resolver = $Config.Resolver + } else { + $Resolver = 'Google' + $Config = @{ + PartitionKey = 'Domains' + RowKey = 'Domains' + Resolver = $Resolver + } + Add-CIPPAzDataTableEntity @ConfigTable -Entity $Config -Force + } + } catch { + $Resolver = 'Google' + } + + Set-DnsResolver -Resolver $Resolver + + $UserCreds = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($request.headers.'x-ms-client-principal')) | ConvertFrom-Json) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + $StatusCode = [HttpStatusCode]::OK + try { + if ($Request.Query.Action) { + if ($Request.Query.Domain -match '^(((?!-))(xn--|_{1,1})?[a-z0-9-]{0,61}[a-z0-9]{1,1}\.)*(xn--)?([a-z0-9][a-z0-9\-]{0,60}|[a-z0-9-]{1,30}\.[a-z]{2,})$') { + $DomainTable = Get-CIPPTable -Table 'Domains' + $Filter = "RowKey eq '{0}'" -f $Request.Query.Domain + $DomainInfo = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter + switch ($Request.Query.Action) { + 'ListDomainInfo' { + $Body = $DomainInfo + } + 'GetDkimSelectors' { + $Body = ($DomainInfo.DkimSelectors | ConvertFrom-Json) -join ',' + } + 'ReadSpfRecord' { + $SpfQuery = @{ + Domain = $Request.Query.Domain + } + + if ($Request.Query.ExpectedInclude) { + $SpfQuery.ExpectedInclude = $Request.Query.ExpectedInclude + } + + if ($Request.Query.Record) { + $SpfQuery.Record = $Request.Query.Record + } + + $Body = Read-SpfRecord @SpfQuery + } + 'ReadDmarcPolicy' { + $Body = Read-DmarcPolicy -Domain $Request.Query.Domain + } + 'ReadDkimRecord' { + $DkimQuery = @{ + Domain = $Request.Query.Domain + } + if ($Request.Query.Selector) { + $DkimQuery.Selectors = ($Request.Query.Selector).trim() -split '\s*,\s*' + + if ('admin' -in $UserCreds.userRoles -or 'editor' -in $UserCreds.userRoles) { + $DkimSelectors = [string]($DkimQuery.Selectors | ConvertTo-Json -Compress) + if ($DomainInfo) { + $DomainInfo.DkimSelectors = $DkimSelectors + } else { + $DomainInfo = @{ + 'RowKey' = $Request.Query.Domain + 'PartitionKey' = 'ManualEntry' + 'TenantId' = 'NoTenant' + 'MailProviders' = '' + 'TenantDetails' = '' + 'DomainAnalyser' = '' + 'DkimSelectors' = $DkimSelectors + } + } + Write-Host $DomainInfo + Add-CIPPAzDataTableEntity @DomainTable -Entity $DomainInfo -Force + } + } elseif (![string]::IsNullOrEmpty($DomainInfo.DkimSelectors)) { + $DkimQuery.Selectors = [System.Collections.Generic.List[string]]($DomainInfo.DkimSelectors | ConvertFrom-Json) + } + $Body = Read-DkimRecord @DkimQuery + + } + 'ReadMXRecord' { + $Body = Read-MXRecord -Domain $Request.Query.Domain + } + 'TestDNSSEC' { + $Body = Test-DNSSEC -Domain $Request.Query.Domain + } + 'ReadWhoisRecord' { + $Body = Read-WhoisRecord -Query $Request.Query.Domain + } + 'ReadNSRecord' { + $Body = Read-NSRecord -Domain $Request.Query.Domain + } + 'TestHttpsCertificate' { + $HttpsQuery = @{ + Domain = $Request.Query.Domain + } + if ($Request.Query.Subdomains) { + $HttpsQuery.Subdomains = ($Request.Query.Subdomains).trim() -split '\s*,\s*' + } else { + $HttpsQuery.Subdomains = 'www' + } + + $Body = Test-HttpsCertificate @HttpsQuery + } + 'TestMtaSts' { + $HttpsQuery = @{ + Domain = $Request.Query.Domain + } + $Body = Test-MtaSts @HttpsQuery + } + } + } else { + $body = [pscustomobject]@{'Results' = "Domain: $($Request.Query.Domain) is invalid" } + } + } + } catch { + Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "DNS Helper API failed. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomains.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomains.ps1 new file mode 100644 index 000000000000..113b9d53a304 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListDomains.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-ListDomains { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + + try { + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $TenantFilter | Select-Object id, isdefault, isinitial | Sort-Object isdefault + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExConnectorTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExConnectorTemplates.ps1 new file mode 100644 index 000000000000..28dc15275c83 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExConnectorTemplates.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-ListExConnectorTemplates { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Table = Get-CippTable -tablename 'templates' + + #List new policies + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'ExConnectorTemplate'" + $Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { + $GUID = $_.RowKey + $Direction = $_.direction + $data = $_.JSON | ConvertFrom-Json + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $GUID + $data | Add-Member -NotePropertyName 'cippconnectortype' -NotePropertyValue $Direction + $data + } + + if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property RowKey -EQ $Request.query.id } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Templates) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExchangeConnectors.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExchangeConnectors.ps1 new file mode 100644 index 000000000000..35b1863f45f8 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExchangeConnectors.ps1 @@ -0,0 +1,31 @@ +using namespace System.Net + +Function Invoke-ListExchangeConnectors { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Tenantfilter = $request.Query.tenantfilter + + $Results = try { + New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-OutboundConnector' | Select-Object *, @{n = 'cippconnectortype'; e = { 'outbound' } } + New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-InboundConnector' | Select-Object *, @{n = 'cippconnectortype'; e = { 'Inbound' } } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($Results) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExtensionsConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExtensionsConfig.ps1 new file mode 100644 index 000000000000..5e9cd48198c8 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExtensionsConfig.ps1 @@ -0,0 +1,26 @@ +using namespace System.Net + +Function Invoke-ListExtensionsConfig { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Table = Get-CIPPTable -TableName Extensionsconfig + try { + $Body = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 -ErrorAction Stop + } catch { + $Body = @{} + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 new file mode 100644 index 000000000000..2bec443954b5 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 @@ -0,0 +1,77 @@ +using namespace System.Net + +Function Invoke-ListExternalTenantInfo { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $Tenant = $request.query.tenant + + # Normalize to tenantid and determine if tenant exists + $TenantId = (Invoke-RestMethod -Method GET "https://login.windows.net/$tenant/.well-known/openid-configuration").token_endpoint.Split('/')[3] + + if ($TenantId) { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$TenantId')" -noauthcheck $true -tenantid $TenantFilter + $StatusCode = [HttpStatusCode]::OK + } + + if ($GraphRequest) { + + $TenantDefaultDomain = $GraphRequest.defaultDomainName + + $body = @" + + + + http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation + https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc + + http://www.w3.org/2005/08/addressing/anonymous + + + + + + $TenantDefaultDomain + + + + +"@ + + # Create the headers + $headers = @{ + 'Content-Type' = 'text/xml; charset=utf-8' + 'SOAPAction' = '"http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation"' + 'User-Agent' = 'AutodiscoverClient' + } + + # Invoke + $response = Invoke-RestMethod -UseBasicParsing -Method Post -Uri 'https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc' -Body $body -Headers $headers + + # Return + $TenantDomains = $response.Envelope.body.GetFederationInformationResponseMessage.response.Domains.Domain | Sort-Object + } + + $results = [PSCustomObject]@{ + GraphRequest = $GraphRequest + Domains = $TenantDomains + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPInvite.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPInvite.ps1 new file mode 100644 index 000000000000..3ae888f60ec8 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPInvite.ps1 @@ -0,0 +1,32 @@ +using namespace System.Net + +Function Invoke-ListGDAPInvite { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + Write-Host ($Request | ConvertTo-Json) + if (![string]::IsNullOrEmpty($Request.Query.RelationshipId)) { + $Table = Get-CIPPTable -TableName 'GDAPInvites' + $Invite = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$($Request.Query.RelationshipId)'" + Write-Host $Invite + } else { + $Invite = @{} + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Invite + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPQueue.ps1 new file mode 100644 index 000000000000..94f43d623abe --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPQueue.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-ListGDAPQueue { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $Table = Get-CIPPTable -TableName 'GDAPMigration' + $QueuedApps = Get-CIPPAzDataTableEntity @Table + + $CurrentStandards = foreach ($QueueFile in $QueuedApps) { + [PSCustomObject]@{ + Tenant = $QueueFile.tenant + Status = $QueueFile.status + StartAt = $QueueFile.startAt + } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($CurrentStandards) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPRoles.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPRoles.ps1 new file mode 100644 index 000000000000..0102748cb4f8 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGDAPRoles.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-ListGDAPRoles { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $Table = Get-CIPPTable -TableName 'GDAPRoles' + $Groups = Get-CIPPAzDataTableEntity @Table + + $MappedGroups = foreach ($Group in $Groups) { + [PSCustomObject]@{ + GroupName = $Group.GroupName + GroupId = $Group.GroupId + RoleName = $Group.RoleName + roleDefinitionId = $Group.roleDefinitionId + } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($MappedGroups) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 new file mode 100644 index 000000000000..c94a79f1f0a9 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 @@ -0,0 +1,48 @@ +using namespace System.Net + +Function Invoke-ListGenericAllTenants { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $TableURLName = ($QueueItem.tolower().split('?').Split('/') | Select-Object -First 1).toString() + $QueueKey = (Get-CippQueue | Where-Object -Property Name -EQ $TableURLName | Select-Object -Last 1).RowKey + Update-CippQueueEntry -RowKey $QueueKey -Status 'Started' + $Table = Get-CIPPTable -TableName "cache$TableURLName" + $fullUrl = "https://graph.microsoft.com/beta/$QueueItem" + Get-CIPPAzDataTableEntity @Table | Remove-AzDataTableEntity @table + + $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { + $domainName = $_.defaultDomainName + Import-Module '.\GraphHelper.psm1' + try { + Write-Host $using:fullUrl + New-GraphGetRequest -uri $using:fullUrl -tenantid $_.defaultDomainName -ComplexFilter -ErrorAction Stop | Select-Object *, @{l = 'Tenant'; e = { $domainName } }, @{l = 'CippStatus'; e = { 'Good' } } + } catch { + [PSCustomObject]@{ + Tenant = $domainName + CippStatus = "Could not connect to tenant. $($_.Exception.message)" + } + } + } + + Update-CippQueueEntry -RowKey $QueueKey -Status 'Processing' + foreach ($Request in $RawGraphRequest) { + $Json = ConvertTo-Json -Compress -InputObject $request + $GraphRequest = [PSCustomObject]@{ + Tenant = [string]$Request.tenant + RowKey = [string](New-Guid) + PartitionKey = [string]$URL + Data = [string]$Json + + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + } + + + Update-CippQueueEntry -RowKey $QueueKey -Status 'Completed' + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericTestFunction.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericTestFunction.ps1 new file mode 100644 index 000000000000..80012cd30de9 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericTestFunction.ps1 @@ -0,0 +1,20 @@ +using namespace System.Net + +Function Invoke-ListGenericTestFunction { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $graphRequest = ($request.headers.'x-ms-original-url').split('/api') | Select-Object -First 1 + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($graphRequest) + }) -clobber + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroupTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroupTemplates.ps1 new file mode 100644 index 000000000000..97443c415144 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroupTemplates.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-ListGroupTemplates { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + Write-Host $Request.query.id + + #List new policies + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'GroupTemplate'" + $Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { + $data = $_.JSON | ConvertFrom-Json + $data | Add-Member -MemberType NoteProperty -Name GUID -Value $_.RowKey -Force + $data + } | Sort-Object -Property displayName + + if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property GUID -EQ $Request.query.id } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Templates) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroups.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroups.ps1 new file mode 100644 index 000000000000..c2196b1a4e4a --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroups.ps1 @@ -0,0 +1,77 @@ +using namespace System.Net + +Function Invoke-ListGroups { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + + $TenantFilter = $Request.Query.TenantFilter + $selectstring = "id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule,grouptypes,onPremisesSyncEnabled,resourceProvisioningOptions,userPrincipalName&`$expand=members(`$select=userPrincipalName)" + + if ($Request.Query.GroupID) { + $groupid = $Request.query.groupid + $selectstring = 'id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule,groupTypes,userPrincipalName' + } + if ($Request.Query.members) { + $members = 'members' + $selectstring = 'id,userPrincipalName,displayName,hideFromOutlookClients,hideFromAddressLists,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule' + } + + if ($Request.Query.owners) { + $members = 'owners' + $selectstring = 'id,userPrincipalName,displayName,hideFromOutlookClients,hideFromAddressLists,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule' + } + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/$($GroupID)/$($members)?`$top=999&select=$selectstring" -tenantid $TenantFilter | Select-Object *, @{ Name = 'primDomain'; Expression = { $_.mail -split '@' | Select-Object -Last 1 } }, + @{Name = 'membersCsv'; Expression = { $_.members.userPrincipalName -join ',' } }, + @{Name = 'teamsEnabled'; Expression = { if ($_.resourceProvisioningOptions -Like '*Team*') { $true }else { $false } } }, + @{Name = 'calculatedGroupType'; Expression = { + + if ($_.mailEnabled -and $_.securityEnabled) { + 'Mail-Enabled Security' + } + if (!$_.mailEnabled -and $_.securityEnabled) { + 'Security' + } + if ($_.groupTypes -contains 'Unified') { + 'Microsoft 365' + } + if (([string]::isNullOrEmpty($_.groupTypes)) -and ($_.mailEnabled) -and (!$_.securityEnabled)) { + 'Distribution List' + } + } + }, + @{Name = 'dynamicGroupBool'; Expression = { + if ($_.groupTypes -contains 'DynamicMembership') { + $true + } else { + $false + } + } + } + + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListHaloClients.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListHaloClients.ps1 new file mode 100644 index 000000000000..84b1b3cae514 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListHaloClients.ps1 @@ -0,0 +1,49 @@ +using namespace System.Net + +Function Invoke-ListHaloClients { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + try { + $Table = Get-CIPPTable -TableName Extensionsconfig + $Configuration = ((Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json).HaloPSA + $Token = Get-HaloToken -configuration $Configuration + $i = 1 + $RawHaloClients = do { + $Result = Invoke-RestMethod -Uri "$($Configuration.ResourceURL)/Client?page_no=$i&page_size=999&pageinate=true" -ContentType 'application/json' -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } + $Result.clients | Select-Object * -ExcludeProperty logo + $i++ + $pagecount = [Math]::Ceiling($Result.record_count / 999) + } while ($i -le $pagecount) + $HaloClients = $RawHaloClients | ForEach-Object { + [PSCustomObject]@{ + label = $_.name + value = $_.id + } + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $HaloClients = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($HaloClients) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListInactiveAccounts.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListInactiveAccounts.ps1 new file mode 100644 index 000000000000..3ce42719c362 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListInactiveAccounts.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-ListInactiveAccounts { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + if ($TenantFilter -eq 'AllTenants') { $TenantFilter = (get-tenants).customerId } + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/inactiveUsers?`$count=true" -tenantid $env:TenantId | Where-Object { $_.tenantId -in $TenantFilter } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = "Could not connect to Azure Lighthouse API: $($ErrorMessage)" + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneIntents.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneIntents.ps1 new file mode 100644 index 000000000000..cb98c87d6e67 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneIntents.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-ListIntuneIntents { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/Intents?`$expand=settings,categories" -tenantid $TenantFilter + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntunePolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntunePolicy.ps1 new file mode 100644 index 000000000000..5429292d5a6c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntunePolicy.ps1 @@ -0,0 +1,67 @@ +using namespace System.Net + +Function Invoke-ListIntunePolicy { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $id = $Request.Query.ID + $urlname = $Request.Query.URLName + try { + if ($ID) { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$($urlname)('$ID')" -tenantid $tenantfilter + } else { + + $GraphURLS = @("https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations?`$select=id,displayName,lastModifiedDateTime,roleScopeTagIds,microsoft.graph.unsupportedDeviceConfiguration/originalEntityTypeName&`$expand=assignments&top=1000", + "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations?`$expand=assignments&top=1000" + "https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations?`$expand=assignments&`$filter=microsoft.graph.androidManagedStoreAppConfiguration/appSupportsOemConfig%20eq%20true" + 'https://graph.microsoft.com/beta/deviceManagement/configurationPolicies' + ) + + $GraphRequest = $GraphURLS | ForEach-Object { + $URLName = (($_).split('?') | Select-Object -First 1) -replace 'https://graph.microsoft.com/beta/deviceManagement/', '' + New-GraphGetRequest -uri $_ -tenantid $TenantFilter + + } | ForEach-Object { + $policyTypeName = switch -Wildcard ($_.'assignments@odata.context') { + '*microsoft.graph.windowsIdentityProtectionConfiguration*' { 'Identity Protection' } + '*microsoft.graph.windows10EndpointProtectionConfiguration*' { 'Endpoint Protection' } + '*microsoft.graph.windows10CustomConfiguration*' { 'Custom' } + '*groupPolicyConfigurations*' { 'Administrative Templates' } + '*windowsDomainJoinConfiguration*' { 'Domain Join configuration' } + '*windowsUpdateForBusinessConfiguration*' { 'Update Configuration' } + '*windowsHealthMonitoringConfiguration*' { 'Health Monitoring' } + default { $_.'assignments@odata.context' } + } + if ($_.displayname -eq $null) { $_ | Add-Member -NotePropertyName displayName -NotePropertyValue $_.name } + $_ | Add-Member -NotePropertyName PolicyTypeName -NotePropertyValue $policyTypeName + $_ | Add-Member -NotePropertyName URLName -NotePropertyValue $URLName + $_ + } | Where-Object { $_.DisplayName -ne $null } + + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 new file mode 100644 index 000000000000..95a56cc1106f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 @@ -0,0 +1,51 @@ +using namespace System.Net + +Function Invoke-ListIntuneTemplates { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + + $Table = Get-CippTable -tablename 'templates' + + $Templates = Get-ChildItem 'Config\*.IntuneTemplate.json' | ForEach-Object { + $Entity = @{ + JSON = "$(Get-Content $_)" + RowKey = "$($_.name)" + PartitionKey = 'IntuneTemplate' + GUID = "$($_.name)" + } + Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force + } + + #List new policies + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'IntuneTemplate'" + $Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json + if ($Request.query.View) { + $Templates = $Templates | ForEach-Object { + $data = $_.RAWJson | ConvertFrom-Json + $data | Add-Member -NotePropertyName 'displayName' -NotePropertyValue $_.Displayname -Force + $data | Add-Member -NotePropertyName 'description' -NotePropertyValue $_.Description -Force + $data | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_.Type + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.GUID + $data + } + } + + if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property guid -EQ $Request.query.id } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = ($Templates | ConvertTo-Json -Depth 10) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListKnownIPDb.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListKnownIPDb.ps1 new file mode 100644 index 000000000000..061146ae725f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListKnownIPDb.ps1 @@ -0,0 +1,27 @@ +using namespace System.Net + +Function Invoke-ListKnownIPDb { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $Table = Get-CIPPTable -TableName 'knownlocationdb' + $Filter = "Tenant eq '$($Request.Query.TenantFilter)'" + $KnownIPDb = Get-CIPPAzDataTableEntity @Table -Filter $Filter + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($KnownIPDb) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 new file mode 100644 index 000000000000..27c2db9767a3 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 @@ -0,0 +1,41 @@ +using namespace System.Net + +Function Invoke-ListLicenses { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $RawGraphRequest = if ($TenantFilter -ne 'AllTenants') { + $GraphRequest = Get-CIPPLicenseOverview -TenantFilter $TenantFilter + } else { + $Table = Get-CIPPTable -TableName cachelicenses + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) + if (!$Rows) { + Push-OutputBinding -Name LicenseQueue -Value (Get-Date).ToString() + $GraphRequest = [PSCustomObject]@{ + Tenant = 'Loading data for all tenants. Please check back in 1 minute' + License = 'Loading data for all tenants. Please check back in 1 minute' + } + } else { + $GraphRequest = $Rows + } + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) -Clobber + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 new file mode 100644 index 000000000000..1aec2d4ec6ce --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-ListLicensesAllTenants { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { + $domainName = $_.defaultDomainName + Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' + try { + Write-Host "Processing $domainName" + Get-CIPPLicenseOverview -TenantFilter $domainName + } catch { + [pscustomobject]@{ + Tenant = [string]$domainName + License = "Could not connect to client: $($_.Exception.Message)" + 'PartitionKey' = 'License' + 'RowKey' = "$($domainName)-$(New-Guid)" + } + } + } + + $Table = Get-CIPPTable -TableName cachelicenses + foreach ($GraphRequest in $RawGraphRequest) { + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 new file mode 100644 index 000000000000..319b43a00995 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 @@ -0,0 +1,59 @@ +using namespace System.Net + +Function Invoke-ListLogs { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + if ($request.Query.Filter -eq 'True') { + $LogLevel = if ($Request.query.Severity) { ($Request.query.Severity).split(',') } else { 'Info', 'Warn', 'Error', 'Critical', 'Alert' } + $PartitionKey = $Request.query.DateFilter + $username = $Request.Query.User + } else { + $LogLevel = 'Info', 'Warn', 'Error', 'Critical', 'Alert' + $PartitionKey = Get-Date -UFormat '%Y%m%d' + $username = '*' + } + $Table = Get-CIPPTable + + $ReturnedLog = if ($Request.Query.ListLogs) { + + Get-CIPPAzDataTableEntity @Table -Property PartitionKey | Sort-Object -Unique PartitionKey | Select-Object PartitionKey | ForEach-Object { + @{ + value = $_.PartitionKey + label = $_.PartitionKey + } + } + } else { + $Filter = "PartitionKey eq '{0}'" -f $PartitionKey + $Rows = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username } + foreach ($Row in $Rows) { + @{ + DateTime = $Row.Timestamp + Tenant = $Row.Tenant + API = $Row.API + Message = $Row.Message + User = $Row.Username + Severity = $Row.Severity + TenantID = if ($Row.TenantID -ne $null) { + $Row.TenantID + } else { + 'None' + } + } + } + + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($ReturnedLog) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 new file mode 100644 index 000000000000..32314d2298d8 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 @@ -0,0 +1,41 @@ +using namespace System.Net + +Function Invoke-ListMFAUsers { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + if ($Request.query.TenantFilter -ne 'AllTenants') { + $GraphRequest = Get-CIPPMFAState -TenantFilter $Request.query.TenantFilter + } else { + $Table = Get-CIPPTable -TableName cachemfa + + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-2) + if (!$Rows) { + $Queue = New-CippQueueEntry -Name 'MFA Users - All Tenants' -Link '/identity/reports/mfa-report?customerId=AllTenants' + Write-Information ($Queue | ConvertTo-Json) + Push-OutputBinding -Name mfaqueue -Value $Queue.RowKey + $GraphRequest = [PSCustomObject]@{ + UPN = 'Loading data for all tenants. Please check back in 10 minutes' + } + } else { + $GraphRequest = $Rows + } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 new file mode 100644 index 000000000000..e052448aad8e --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 @@ -0,0 +1,62 @@ +using namespace System.Net + +Function Invoke-ListMFAUsersAllTenants { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + + Write-Information "Item: $QueueItem" + Write-Information ($TriggerMetadata | ConvertTo-Json) + + try { + Update-CippQueueEntry -RowKey $QueueItem -Status 'Running' + + $GraphRequest = Get-Tenants | ForEach-Object -Parallel { + $domainName = $_.defaultDomainName + Import-Module '.\GraphHelper.psm1' + Import-Module '.\modules\CippCore' + $Table = Get-CIPPTable -TableName cachemfa + Try { + $GraphRequest = Get-CIPPMFAState -TenantFilter $domainName -ErrorAction Stop + } catch { + $GraphRequest = $null + } + if (!$GraphRequest) { + $GraphRequest = @{ + Tenant = [string]$tenantName + UPN = [string]$domainName + AccountEnabled = 'none' + PerUser = [string]'Could not connect to tenant' + MFARegistration = 'none' + CoveredByCA = [string]'Could not connect to tenant' + CoveredBySD = 'none' + RowKey = [string]"$domainName" + PartitionKey = 'users' + } + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + } + } catch { + $Table = Get-CIPPTable -TableName cachemfa + $GraphRequest = @{ + Tenant = [string]$tenantName + UPN = [string]$domainName + AccountEnabled = 'none' + PerUser = [string]'Could not connect to tenant' + MFARegistration = 'none' + CoveredByCA = [string]'Could not connect to tenant' + CoveredBySD = 'none' + RowKey = [string]"$domainName" + PartitionKey = 'users' + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + } finally { + Update-CippQueueEntry -RowKey $QueueItem -Status 'Completed' + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailQuarantine.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailQuarantine.ps1 new file mode 100644 index 000000000000..dba4d4a425aa --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailQuarantine.ps1 @@ -0,0 +1,30 @@ +using namespace System.Net + +Function Invoke-ListMailQuarantine { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Tenantfilter = $request.Query.tenantfilter + + try { + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-QuarantineMessage' + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxCAS.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxCAS.ps1 new file mode 100644 index 000000000000..ef93210d4c25 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxCAS.ps1 @@ -0,0 +1,42 @@ +using namespace System.Net + +Function Invoke-ListMailboxCAS { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $GraphRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox" -Tenantid $tenantfilter -scope ExchangeOnline | Select-Object @{ Name = 'displayName'; Expression = { $_.'DisplayName' } }, + @{ Name = 'primarySmtpAddress'; Expression = { $_.'PrimarySMTPAddress' } }, + @{ Name = 'ecpenabled'; Expression = { $_.'ECPEnabled' } }, + @{ Name = 'owaenabled'; Expression = { $_.'OWAEnabled' } }, + @{ Name = 'imapenabled'; Expression = { $_.'IMAPEnabled' } }, + @{ Name = 'popenabled'; Expression = { $_.'POPEnabled' } }, + @{ Name = 'mapienabled'; Expression = { $_.'MAPIEnabled' } }, + @{ Name = 'ewsenabled'; Expression = { $_.'EWSEnabled' } }, + @{ Name = 'activesyncenabled'; Expression = { $_.'ActiveSyncEnabled' } } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxMobileDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxMobileDevices.ps1 new file mode 100644 index 000000000000..8199d4204326 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxMobileDevices.ps1 @@ -0,0 +1,55 @@ +using namespace System.Net + +Function Invoke-ListMailboxMobileDevices { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $Mailbox = $Request.Query.Mailbox + + Write-Host $TenantFilter + Write-Host $Mailbox + + $Bytes = [System.Text.Encoding]::UTF8.GetBytes($Mailbox) + $base64IdentityParam = [Convert]::ToBase64String($Bytes) + + try { + $GraphRequest = New-GraphGetRequest -uri "https://outlook.office365.com:443/adminapi/beta/$($TenantFilter)/mailbox('$($base64IdentityParam)')/MobileDevice/Exchange.GetMobileDeviceStatistics()/?IsEncoded=True" -Tenantid $tenantfilter -scope ExchangeOnline | Select-Object @{ Name = 'clientType'; Expression = { $_.ClientType } }, + @{ Name = 'clientVersion'; Expression = { $_.ClientVersion } }, + @{ Name = 'deviceAccessState'; Expression = { $_.DeviceAccessState } }, + @{ Name = 'deviceFriendlyName'; Expression = { if ([string]::IsNullOrEmpty($_.DeviceFriendlyName)) { 'Unknown' }else { $_.DeviceFriendlyName } } }, + @{ Name = 'deviceModel'; Expression = { $_.DeviceModel } }, + @{ Name = 'deviceOS'; Expression = { $_.DeviceOS } }, + @{ Name = 'deviceType'; Expression = { $_.DeviceType } }, + @{ Name = 'firstSync'; Expression = { $_.FirstSyncTime.toString() } }, + @{ Name = 'lastSyncAttempt'; Expression = { $_.LastSyncAttemptTime.toString() } }, + @{ Name = 'lastSuccessSync'; Expression = { $_.LastSuccessSync.toString() } }, + @{ Name = 'status'; Expression = { $_.Status } }, + @{ Name = 'deviceID'; Expression = { $_.deviceID } }, + @{ Name = 'Guid'; Expression = { $_.Guid } } + + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 new file mode 100644 index 000000000000..7eda4d2074af --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 @@ -0,0 +1,54 @@ +using namespace System.Net + +Function Invoke-ListMailboxRules { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + + $Table = Get-CIPPTable -TableName cachembxrules + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).Addhours(-1) + if (!$Rows) { + Push-OutputBinding -Name mbxrulequeue -Value $TenantFilter + $GraphRequest = [PSCustomObject]@{ + Tenant = 'Loading data. Please check back in 1 minute' + Licenses = 'Loading data. Please check back in 1 minute' + } + } else { + if ($TenantFilter -ne 'AllTenants') { + $GraphRequest = $Rows | Where-Object -Property Tenant -EQ $TenantFilter | ForEach-Object { + $NewObj = $_.Rules | ConvertFrom-Json + $NewObj | Add-Member -NotePropertyName 'Tenant' -NotePropertyValue $TenantFilter + $NewObj + } + } else { + $GraphRequest = $Rows | ForEach-Object { + $TenantName = $_.Tenant + $NewObj = $_.Rules | ConvertFrom-Json + $NewObj | Add-Member -NotePropertyName 'Tenant' -NotePropertyValue $TenantName + $NewObj + } + } + } + #Remove all old cache + #Remove-AzDataTableEntity @Table -Entity (Get-CIPPAzDataTableEntity @Table -Property PartitionKey, RowKey, Timestamp | Where-Object -Property Timestamp -LT (Get-Date).AddMinutes(-15)) + + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 new file mode 100644 index 000000000000..0a8130fa7245 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 @@ -0,0 +1,59 @@ +using namespace System.Net + +Function Invoke-ListMailboxRulesAllTenants { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $Tenants = if ($QueueItem -ne 'AllTenants') { + [PSCustomObject]@{ + defaultDomainName = $QueueItem + } + } else { + Get-Tenants + } + $Tenants | ForEach-Object -Parallel { + $domainName = $_.defaultDomainName + Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\CIPPcore' + try { + + $Rules = New-ExoRequest -tenantid $domainName -cmdlet 'Get-Mailbox' | ForEach-Object -Parallel { + New-ExoRequest -Anchor $_.UserPrincipalName -tenantid $domainName -cmdlet 'Get-InboxRule' -cmdParams @{Mailbox = $_.GUID } + } + foreach ($Rule in $Rules) { + $GraphRequest = @{ + Rules = [string]($Rule | ConvertTo-Json) + RowKey = [string](New-Guid).guid + Tenant = [string]$domainName + PartitionKey = 'mailboxrules' + } + $Table = Get-CIPPTable -TableName cachembxrules + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + } + } catch { + $Rules = @{ + Name = "Could not connect to tenant $($_.Exception.message)" + } | ConvertTo-Json + $GraphRequest = @{ + Rules = [string]$Rules + RowKey = [string]$domainName + Tenant = [string]$domainName + + PartitionKey = 'mailboxrules' + } + $Table = Get-CIPPTable -TableName cachembxrules + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + } + } + + + + $Table = Get-CIPPTable -TableName cachembxrules + Write-Host "$($GraphRequest.RowKey) - $($GraphRequest.tenant)" + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxStatistics.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxStatistics.ps1 new file mode 100644 index 000000000000..d1f1af749fb7 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxStatistics.ps1 @@ -0,0 +1,64 @@ +using namespace System.Net + +Function Invoke-ListMailboxStatistics { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + + $GraphRequest = if ($TenantFilter -ne 'AllTenants') { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getMailboxUsageDetail(period='D7')" -tenantid $TenantFilter | ConvertFrom-Csv | Select-Object @{ Name = 'UPN'; Expression = { $_.'User Principal Name' } }, + @{ Name = 'displayName'; Expression = { $_.'Display Name' } }, + @{ Name = 'MailboxType'; Expression = { $_.'Recipient Type' } }, + @{ Name = 'LastActive'; Expression = { $_.'Last Activity Date' } }, + @{ Name = 'UsedGB'; Expression = { [math]::round($_.'Storage Used (Byte)' / 1GB, 2) } }, + @{ Name = 'QuotaGB'; Expression = { [math]::round($_.'Prohibit Send/Receive Quota (Byte)' / 1GB, 2) } }, + @{ Name = 'ItemCount'; Expression = { $_.'Item Count' } }, + @{ Name = 'HasArchive'; Expression = { If (($_.'Has Archive').ToLower() -eq 'true') { [bool]$true } else { [bool]$false } } } + $StatusCode = [HttpStatusCode]::OK + } else { + $Table = Get-CIPPTable -TableName 'cachereports' + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) + if (!$Rows) { + $Queue = New-CippQueueEntry -Name 'Reports' -Link '/email/reports/mailbox-statistics?customerId=AllTenants' + Push-OutputBinding -Name mailboxstats -Value "reports/getMailboxUsageDetail(period='D7')?`$format=application/json" + [PSCustomObject]@{ + Tenant = 'Loading data for all tenants. Please check back after the job completes' + } + $StatusCode = [HttpStatusCode]::OK + } else { + $Rows.Data | ConvertFrom-Json | Select-Object *, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, + @{ Name = 'MailboxType'; Expression = { $_.'RecipientType' } }, + @{ Name = 'LastActive'; Expression = { $_.'LastActivityDate' } }, + @{ Name = 'UsedGB'; Expression = { [math]::round($_.'storageUsedInBytes' / 1GB, 2) } }, + @{ Name = 'QuotaGB'; Expression = { [math]::round($_.'prohibitSendReceiveQuotaInBytes' / 1GB, 2) } } + $StatusCode = [HttpStatusCode]::OK + } + } + + + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) -clobber + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxes.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxes.ps1 new file mode 100644 index 000000000000..c924a85ea1dd --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxes.ps1 @@ -0,0 +1,77 @@ +using namespace System.Net + +Function Invoke-ListMailboxes { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # 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' + $ExoRequest = @{ + tenantid = $TenantFilter + cmdlet = 'Get-Mailbox' + cmdParams = @{resultsize = 'unlimited' } + Select = $select + } + + $AllowedParameters = @( + @{Parameter = 'Anr'; Type = 'String' } + @{Parameter = 'Archive'; Type = 'Bool' } + @{Parameter = 'Filter'; Type = 'String' } + @{Parameter = 'GroupMailbox'; Type = 'Bool' } + @{Parameter = 'PublicFolder'; Type = 'Bool' } + @{Parameter = 'RecipientTypeDetails'; Type = 'String' } + @{Parameter = 'SoftDeletedMailbox'; Type = 'Bool' } + ) + + foreach ($Param in $Request.Query.Keys) { + $CmdParam = $AllowedParameters | Where-Object { $_.Parameter -eq $Param } + if ($CmdParam) { + switch ($CmdParam.Type) { + 'String' { + if (![string]::IsNullOrEmpty($Request.Query.$Param)) { + $ExoRequest.cmdParams.$Param = $Request.Query.$Param + } + } + 'Bool' { + if ([bool]$Request.Query.$Param -eq $true) { + $ExoRequest.cmdParams.$Param = $true + } + } + } + } + } + + Write-Host ($ExoRequest | ConvertTo-Json) + $GraphRequest = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, @{ 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 ', ' } } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMessageTrace.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMessageTrace.ps1 new file mode 100644 index 000000000000..5ec6203892c4 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMessageTrace.ps1 @@ -0,0 +1,44 @@ +using namespace System.Net + +Function Invoke-ListMessageTrace { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + try { + $TenantFilter = $request.query.TenantFilter + $SearchParams = @{ + StartDate = (Get-Date).AddDays( - $($request.query.days)).ToString('s') + EndDate = (Get-Date).ToString('s') + } + + if ($null -ne $request.query.recipient) { $Searchparams.Add('RecipientAddress', $($request.query.recipient)) } + if ($null -ne $request.query.sender) { $Searchparams.Add('SenderAddress', $($request.query.sender)) } + $type = $request.query.Tracedetail + $trace = if ($Request.Query.Tracedetail) { + New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-MessageTraceDetail' -cmdParams $Searchparams + Get-MessageTraceDetail -MessageTraceId $Request.Query.ID -RecipientAddress $request.query.recipient -erroraction stop | Select-Object Event, Action, Detail, @{ Name = 'Date'; Expression = { $_.Date.Tostring('s') } } + } else { + New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-MessageTrace' -cmdParams $Searchparams | Select-Object MessageTraceId, Status, Subject, RecipientAddress, SenderAddress, @{ Name = 'Date'; Expression = { $_.Received.tostring('s') } } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message 'Executed message trace' -Sev 'Info' + + } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed executing messagetrace. Error: $($_.Exception.Message)" -Sev 'Error' + $trace = @{Status = "Failed to retrieve message trace $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($trace) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListNamedLocations.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListNamedLocations.ps1 new file mode 100644 index 000000000000..0acac97628d5 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListNamedLocations.ps1 @@ -0,0 +1,39 @@ +using namespace System.Net + +Function Invoke-ListNamedLocations { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations' -Tenantid $tenantfilter | Select-Object *, + @{ + name = 'rangeOrLocation' + expression = { if ($_.ipRanges) { $_.ipranges.cidrAddress -join ', ' } else { $_.countriesAndRegions -join ', ' } } + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListNotificationConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListNotificationConfig.ps1 new file mode 100644 index 000000000000..68545565ee6b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListNotificationConfig.ps1 @@ -0,0 +1,40 @@ +using namespace System.Net + +Function Invoke-ListNotificationConfig { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Table = Get-CIPPTable -TableName SchedulerConfig + $Filter = "RowKey eq 'CippNotifications' and PartitionKey eq 'CippNotifications'" + $Config = Get-CIPPAzDataTableEntity @Table -Filter $Filter + if ($Config) { + $Config = $Config | ConvertTo-Json -Depth 10 | ConvertFrom-Json -Depth 10 -AsHashtable + } else { + $Config = @{} + } + #$config | Add-Member -NotePropertyValue @() -NotePropertyName 'logsToInclude' -Force + $config.logsToInclude = @(([pscustomobject]$config | Select-Object * -ExcludeProperty schedule, type, tenantid, onepertenant, sendtoIntegration, partitionkey, rowkey, tenant, ETag, email, logsToInclude, Severity, Alert, Info, Error, timestamp, webhook, includeTenantId).psobject.properties.name) + if (!$config.logsToInclude) { + $config.logsToInclude = @('None') + } + if (!$config.Severity) { + $config.Severity = @('Alert') + } else { + $config.Severity = $config.Severity -split ',' + } + $body = [PSCustomObject]$Config + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOAuthApps.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOAuthApps.ps1 new file mode 100644 index 000000000000..13fafdf39a2c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOAuthApps.ps1 @@ -0,0 +1,54 @@ +using namespace System.Net + +Function Invoke-ListOAuthApps { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + if ($TenantFilter -eq 'AllTenants') { $Tenants = (Get-Tenants).defaultDomainName } else { $tenants = $TenantFilter } + + try { + $GraphRequest = foreach ($Tenant in $Tenants) { + try { + $ServicePrincipals = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$select=id,displayName,appid" -tenantid $Tenant + New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/oauth2PermissionGrants' -tenantid $Tenant | ForEach-Object { + $CurrentServicePrincipal = ($ServicePrincipals | Where-Object -Property id -EQ $_.clientId) + [PSCustomObject]@{ + Tenant = $Tenant + Name = $CurrentServicePrincipal.displayName + ApplicationID = $CurrentServicePrincipal.appid + ObjectID = $_.clientId + Scope = ($_.scope -join ',') + StartTime = $_.startTime + } + } + $StatusCode = [HttpStatusCode]::OK + } catch { + continue + } + } + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOoO.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOoO.ps1 new file mode 100644 index 000000000000..b83c74d7dd42 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOoO.ps1 @@ -0,0 +1,27 @@ +using namespace System.Net + +Function Invoke-ListOoO { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + $Tenantfilter = $request.query.tenantFilter + try { + $Body = Get-CIPPOutOfOffice -userid $Request.query.userid -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $Body = [pscustomobject]@{'Results' = "Failed. $ErrorMessage" } + + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOrg.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOrg.ps1 new file mode 100644 index 000000000000..20842ce9dafb --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListOrg.ps1 @@ -0,0 +1,32 @@ +using namespace System.Net + +Function Invoke-ListOrg { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + if ($TenantFilter -eq 'AllTenants') { + + } else { + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/organization' -tenantid $TenantFilter + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $GraphRequest + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPartnerRelationships.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPartnerRelationships.ps1 new file mode 100644 index 000000000000..591b6b23d3bf --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPartnerRelationships.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-ListPartnerRelationships { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + try { + $GraphRequestList = @{ + Endpoint = 'policies/crossTenantAccessPolicy/partners' + TenantFilter = $Request.Query.TenantFilter + QueueNameOverride = 'Partner Relationships' + ReverseTenantLookup = $true + } + $GraphRequest = Get-GraphRequestList @GraphRequestList + } catch { + $GraphRequest = @() + } + + $StatusCode = [HttpStatusCode]::OK + + $results = [PSCustomObject]@{ + Results = @($GraphRequest) + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPhishPolicies.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPhishPolicies.ps1 new file mode 100644 index 000000000000..1b3d767f85fd --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPhishPolicies.ps1 @@ -0,0 +1,48 @@ +using namespace System.Net + +Function Invoke-ListPhishPolicies { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $AntiPhishRules = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-AntiPhishRule' + $AntiPhishPolicies = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-AntiPhishPolicy' + + $GraphRequest = $AntiPhishPolicies | Select-Object name, + @{Name = 'GUID'; Expression = { $(( -join (( 0x41..0x5A) + ( 0x61..0x7A) | Get-Random -Count 13 | ForEach-Object { [char]$_ }) )) } }, + @{ Name = 'ExcludedDomains'; Expression = { $($_.ExcludedDomains) -join '
    ' } }, + @{ Name = 'ExcludedSenders'; Expression = { $($_.ExcludedSenders) -join '
    ' } }, + @{ Name = 'PhishThresholdLevel'; Expression = { + switch ($_.PhishThresholdLevel) { + 1 { $result = 'Standard' } + 2 { $result = 'Aggressive' } + 3 { $result = 'More Aggressive' } + 4 { $result = 'Most Aggressive' } + Default { $result = 'Unknown' } + } + $result + } + }, + @{ Name = 'ExcludedDomainCount'; Expression = { $_.ExcludedDomains | Measure-Object | Select-Object -ExpandProperty Count } }, + @{ Name = 'ExcludedSenderCount'; Expression = { $_.ExcludedSenders | Measure-Object | Select-Object -ExpandProperty Count } }, Enabled, WhenChangedUTC, + @{ Name = 'Priority'; Expression = { foreach ($item in $AntiPhishRules) { if ($item.name -eq $_.name) { $item.priority } } } } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPotentialApps.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPotentialApps.ps1 new file mode 100644 index 000000000000..86062e4e80fa --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPotentialApps.ps1 @@ -0,0 +1,32 @@ +using namespace System.Net + +Function Invoke-ListPotentialApps { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + if ($request.body.type -eq 'WinGet') { + $body = @" +{"MaximumResults":50,"Filters":[{"PackageMatchField":"Market","RequestMatch":{"KeyWord":"US","MatchType":"CaseInsensitive"}}],"Query":{"KeyWord":"$($Request.Body.SearchString)","MatchType":"Substring"}} +"@ + $DataRequest = (Invoke-RestMethod -Uri 'https://storeedgefd.dsx.mp.microsoft.com/v9.0/manifestSearch' -Method POST -Body $body -ContentType 'Application/json').data | Select-Object @{l = 'applicationName'; e = { $_.packagename } }, @{l = 'packagename'; e = { $_.packageIdentifier } } | Sort-Object -Property applicationName + } + + if ($Request.body.type -eq 'Choco') { + $DataRequest = Invoke-RestMethod -Uri "https://community.chocolatey.org/api/v2/Search()?`$filter=IsLatestVersion&`$skip=0&`$top=999&searchTerm=%27$($request.body.SearchString)%27&targetFramework=%27%27&includePrerelease=false" -ContentType 'application/json' | Select-Object @{l = 'applicationName'; e = { $_.properties.Title } }, @{l = 'packagename'; e = { $_.title.'#text' } } | Sort-Object -Property applicationName + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($DataRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListRoles.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListRoles.ps1 new file mode 100644 index 000000000000..6eca5db05c5b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListRoles.ps1 @@ -0,0 +1,40 @@ +using namespace System.Net + +Function Invoke-ListRoles { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $SelectList = 'id', 'displayName', 'userPrincipalName' + + [System.Collections.Generic.List[PSCustomObject]]$Roles = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/directoryRoles?`$expand=members" -tenantid $TenantFilter + $GraphRequest = foreach ($Role in $Roles) { + + #[System.Collections.Generic.List[PSCustomObject]]$Members = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/directoryRoles/$($Role.id)/members?`$select=$($selectlist -join ',')" -tenantid $TenantFilter | Select-Object $SelectList + $Members = if ($Role.members) { $role.members | ForEach-Object { " $($_.displayName) ($($_.userPrincipalName))" } } else { 'none' } + [PSCustomObject]@{ + DisplayName = $Role.displayName + Description = $Role.description + Members = $Members -join ',' + } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $GraphRequest + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListScheduledItems.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListScheduledItems.ps1 new file mode 100644 index 000000000000..d23de674c779 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListScheduledItems.ps1 @@ -0,0 +1,22 @@ +using namespace System.Net + +Function Invoke-ListScheduledItems { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $Table = Get-CIPPTable -TableName 'ScheduledTasks' + $ScheduledTasks = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'ScheduledTask' and Hidden ne 'True'" + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($ScheduledTasks) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListServiceHealth.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListServiceHealth.ps1 new file mode 100644 index 000000000000..4e99ab610e70 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListServiceHealth.ps1 @@ -0,0 +1,43 @@ +using namespace System.Net + +Function Invoke-ListServiceHealth { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + $ResultHealthSummary = Get-Tenants | ForEach-Object -Parallel { + Import-Module '.\GraphHelper.psm1' + $tenantname = $_.displayName + Write-Host $tenantname + $prop = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/admin/serviceAnnouncement/issues?`$filter=endDateTime eq null" -tenantid $_.defaultDomainName + $prop | Add-Member -NotePropertyName 'tenant' -NotePropertyValue $tenantname + $prop + } + $Results = foreach ($h in $ResultHealthSummary) { + [PSCustomObject]@{ + TenantName = $h.tenant + issueId = $h.ID + service = $h.service + type = $h.feature + desc = $h.impactDescription + } + } + + $StatusCode = [HttpStatusCode]::OK + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($Results) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxAccountEnabled.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxAccountEnabled.ps1 new file mode 100644 index 000000000000..f40095596f18 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxAccountEnabled.ps1 @@ -0,0 +1,48 @@ +using namespace System.Net + +Function Invoke-ListSharedMailboxAccountEnabled { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + $TenantFilter = $Request.Query.TenantFilter + + # Get Shared Mailbox Stuff + try { + $SharedMailboxList = (New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($TenantFilter)/Mailbox?`$filter=RecipientTypeDetails eq 'SharedMailbox'" -Tenantid $TenantFilter -scope ExchangeOnline) + $AllUsersAccountState = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?select=userPrincipalName,accountEnabled,displayName,givenName,surname' -tenantid $Tenantfilter + $EnabledUsersWithSharedMailbox = foreach ($SharedMailbox in $SharedMailboxList) { + # Match the User + $User = $AllUsersAccountState | Where-Object { $_.userPrincipalName -eq $SharedMailbox.userPrincipalName } | Select-Object -Property userPrincipalName, accountEnabled, displayName, givenName, surname -First 1 + if ($User.accountEnabled) { + $User | Select-Object ` + @{Name = 'UserPrincipalName'; Expression = { $User.UserPrincipalName } }, ` + @{Name = 'displayName'; Expression = { $User.displayName } }, + @{Name = 'givenName'; Expression = { $User.givenName } }, + @{Name = 'surname'; Expression = { $User.surname } }, + @{Name = 'accountEnabled'; Expression = { $User.accountEnabled } } + + } + } + } catch { + Write-LogMessage -API 'Tenant' -tenant $tenantfilter -message "Shared Mailbox Enabled Accounts on $($tenantfilter). Error: $($_.exception.message)" -sev 'Error' + } + + $GraphRequest = $EnabledUsersWithSharedMailbox + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxStatistics.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxStatistics.ps1 new file mode 100644 index 000000000000..387d82b3f6dd --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharedMailboxStatistics.ps1 @@ -0,0 +1,40 @@ +using namespace System.Net + +Function Invoke-ListSharedMailboxStatistics { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $GraphRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantFilter)/Mailbox?RecipientTypeDetails=sharedmailbox" -Tenantid $tenantFilter -scope ExchangeOnline | ForEach-Object { + try { + New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-MailboxStatistics' -cmdParams @{Identity = $_.GUID } + } catch { + continue + } + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/ListSharepointQuota/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointQuota.ps1 similarity index 88% rename from ListSharepointQuota/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointQuota.ps1 index df0f0abf3f0f..6efbb47e7e96 100644 --- a/ListSharepointQuota/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointQuota.ps1 @@ -1,43 +1,50 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request' - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter - -if ($Request.Query.TenantFilter -eq 'AllTenants') { - $UsedStoragePercentage = 'Not Supported' -} else { - $tenantName = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $TenantFilter | Where-Object { $_.isInitial -eq $true }).id.Split('.')[0] - - $sharepointToken = (Get-GraphToken -scope "https://$($tenantName)-admin.sharepoint.com/.default" -tenantid $TenantFilter) - $sharepointToken.Add('accept', 'application/json') - # Implement a try catch later to deal with sharepoint guest user settings - $sharepointQuota = (Invoke-RestMethod -Method 'GET' -Headers $sharepointToken -Uri "https://$($tenantName)-admin.sharepoint.com/_api/StorageQuotas()?api-version=1.3.2" -ErrorAction Stop).value | Sort-Object -Property GeoUsedStorageMB -Descending | Select-Object -First 1 - - if ($sharepointQuota) { - $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) - } -} - -$sharepointQuotaDetails = @{ - GeoUsedStorageMB = $sharepointQuota.GeoUsedStorageMB - TenantStorageMB = $sharepointQuota.TenantStorageMB - Percentage = $UsedStoragePercentage - Dashboard = "$($UsedStoragePercentage) / 100" -} - -$StatusCode = [HttpStatusCode]::OK - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $sharepointQuotaDetails - }) \ No newline at end of file + using namespace System.Net + + Function Invoke-ListSharepointQuota { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName +Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + +# Write to the Azure Functions log stream. +Write-Host 'PowerShell HTTP trigger function processed a request' + +# Interact with query parameters or the body of the request. +$TenantFilter = $Request.Query.TenantFilter + +if ($Request.Query.TenantFilter -eq 'AllTenants') { + $UsedStoragePercentage = 'Not Supported' +} else { + $tenantName = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $TenantFilter | Where-Object { $_.isInitial -eq $true }).id.Split('.')[0] + + $sharepointToken = (Get-GraphToken -scope "https://$($tenantName)-admin.sharepoint.com/.default" -tenantid $TenantFilter) + $sharepointToken.Add('accept', 'application/json') + # Implement a try catch later to deal with sharepoint guest user settings + $sharepointQuota = (Invoke-RestMethod -Method 'GET' -Headers $sharepointToken -Uri "https://$($tenantName)-admin.sharepoint.com/_api/StorageQuotas()?api-version=1.3.2" -ErrorAction Stop).value | Sort-Object -Property GeoUsedStorageMB -Descending | Select-Object -First 1 + + if ($sharepointQuota) { + $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) + } +} + +$sharepointQuotaDetails = @{ + GeoUsedStorageMB = $sharepointQuota.GeoUsedStorageMB + TenantStorageMB = $sharepointQuota.TenantStorageMB + Percentage = $UsedStoragePercentage + Dashboard = "$($UsedStoragePercentage) / 100" +} + +$StatusCode = [HttpStatusCode]::OK + +# Associate values to output bindings by calling 'Push-OutputBinding'. +Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $sharepointQuotaDetails + }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointSettings.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointSettings.ps1 new file mode 100644 index 000000000000..e769c7ee9879 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSharepointSettings.ps1 @@ -0,0 +1,32 @@ +using namespace System.Net + +Function Invoke-ListSharepointSettings { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $tenant = $Request.Query.TenantFilter + $User = $Request.query.user + $USERToGet = $Request.query.usertoGet + $body = '{"isResharingByExternalUsersEnabled": "False"}' + $Request = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -Type patch -Body $body -ContentType 'application/json' + + Write-LogMessage -API 'Standards' -tenant $tenantFilter -message 'Disabled Password Expiration' -sev Info + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSignIns.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSignIns.ps1 new file mode 100644 index 000000000000..debfb0931ac0 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSignIns.ps1 @@ -0,0 +1,53 @@ +using namespace System.Net + +Function Invoke-ListSignIns { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + if ($Request.query.failedlogonOnly) { + $FailedLogons = ' and (status/errorCode eq 50126)' + } + + $filters = if ($Request.query.Filter) { + $request.query.filter + } else { + $currentTime = Get-Date -Format 'yyyy-MM-dd' + $ts = (Get-Date).AddDays(-7) + $endTime = $ts.ToString('yyyy-MM-dd') + "createdDateTime ge $($endTime) and createdDateTime lt $($currentTime) and userDisplayName ne 'On-Premises Directory Synchronization Service Account' $FailedLogons" + } + Write-Host $Filters + + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/signIns?api-version=beta&`$filter=$($filters)" -tenantid $TenantFilter -erroraction stop + $response = $GraphRequest | Select-Object *, + @{l = 'additionalDetails'; e = { $_.status.additionalDetails } } , + @{l = 'errorCode'; e = { $_.status.errorCode } }, + @{l = 'locationcipp'; e = { "$($_.location.city) - $($_.location.countryOrRegion)" } } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Retrieved sign in report' -Sev 'Debug' -tenant $TenantFilter + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($response) + }) + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve Sign In report: $($_.Exception.message) " -Sev 'Error' -tenant $TenantFilter + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = '500' + Body = $(Get-NormalizedError -message $_.Exception.message) + }) + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSites.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSites.ps1 new file mode 100644 index 000000000000..f224af6d2282 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSites.ps1 @@ -0,0 +1,52 @@ +using namespace System.Net + +Function Invoke-ListSites { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $type = $request.query.Type + $UserUPN = $request.query.UserUPN + try { + $Result = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/get$($type)Detail(period='D7')" -tenantid $TenantFilter | ConvertFrom-Csv + + if ($UserUPN) { + $ParsedRequest = $Result | Where-Object { $_.'Owner Principal Name' -eq $UserUPN } + } else { + $ParsedRequest = $Result + } + + + $GraphRequest = $ParsedRequest | Select-Object @{ Name = 'UPN'; Expression = { $_.'Owner Principal Name' } }, + @{ Name = 'displayName'; Expression = { $_.'Owner Display Name' } }, + @{ Name = 'LastActive'; Expression = { $_.'Last Activity Date' } }, + @{ Name = 'FileCount'; Expression = { [int]$_.'File Count' } }, + @{ Name = 'UsedGB'; Expression = { [math]::round($_.'Storage Used (Byte)' / 1GB, 2) } }, + @{ Name = 'URL'; Expression = { $_.'Site URL' } }, + @{ Name = 'Allocated'; Expression = { [math]::round($_.'Storage Allocated (Byte)' / 1GB, 2) } }, + @{ Name = 'Template'; Expression = { $_.'Root Web Template' } } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamFilterTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamFilterTemplates.ps1 new file mode 100644 index 000000000000..9782073ffb25 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamFilterTemplates.ps1 @@ -0,0 +1,34 @@ +using namespace System.Net + +Function Invoke-ListSpamFilterTemplates { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Table = Get-CippTable -tablename 'templates' + + #List new policies + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'SpamfilterTemplate'" + $Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { + $GUID = $_.RowKey + $data = $_.JSON | ConvertFrom-Json + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $GUID + $data + } + + if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property RowKey -EQ $Request.query.id } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Templates) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamfilter.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamfilter.ps1 new file mode 100644 index 000000000000..3e705b9f46a5 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListSpamfilter.ps1 @@ -0,0 +1,32 @@ +using namespace System.Net + +Function Invoke-ListSpamfilter { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Tenantfilter = $request.Query.tenantfilter + + try { + $Policies = New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-HostedContentFilterPolicy' | Select-Object * -ExcludeProperty *odata*, *data.type* + $RuleState = New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-HostedContentFilterRule' | Select-Object * -ExcludeProperty *odata*, *data.type* + $GraphRequest = $Policies | Select-Object *, @{l = 'ruleState'; e = { $name = $_.name; ($RuleState | Where-Object name -EQ $name).State } }, @{l = 'rulePrio'; e = { $name = $_.name; ($RuleState | Where-Object name -EQ $name).Priority } } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListStandards.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListStandards.ps1 new file mode 100644 index 000000000000..de338c53e239 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListStandards.ps1 @@ -0,0 +1,51 @@ +using namespace System.Net + +Function Invoke-ListStandards { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Table = Get-CippTable -tablename 'standards' + + $Filter = "PartitionKey eq 'standards'" + + try { + if ($Request.query.TenantFilter) { + $tenants = (Get-CIPPAzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -Depth 15 -ErrorAction Stop | Where-Object Tenant -EQ $Request.query.tenantFilter + } else { + $Tenants = (Get-CIPPAzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json -Depth 15 -ErrorAction Stop + } + } catch {} + + $CurrentStandards = foreach ($tenant in $tenants) { + [PSCustomObject]@{ + displayName = $tenant.tenant + appliedBy = $tenant.addedBy + appliedAt = $tenant.appliedAt + standards = $tenant.Standards + StandardsExport = ($tenant.Standards.psobject.properties.name) -join ', ' + } + } + if (!$CurrentStandards) { + $CurrentStandards = [PSCustomObject]@{ + displayName = 'No Standards applied' + appliedBy = $null + appliedAt = $null + standards = @{none = $null } + } + } + + $CurrentStandards = ConvertTo-Json -InputObject @($CurrentStandards) -Depth 15 -Compress + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $CurrentStandards + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeams.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeams.ps1 new file mode 100644 index 000000000000..5c83df245e5f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeams.ps1 @@ -0,0 +1,50 @@ +using namespace System.Net + +Function Invoke-ListTeams { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + if ($request.query.type -eq 'List') { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')&`$select=id,displayname,description,visibility,mailNickname" -tenantid $TenantFilter + } + $TeamID = $request.query.ID + Write-Host $TeamID + if ($request.query.type -eq 'Team') { + $Team = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/teams/$($TeamID)" -tenantid $TenantFilter -asapp $true + $Channels = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/teams/$($TeamID)/Channels" -tenantid $TenantFilter -asapp $true + $UserList = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/teams/$($TeamID)/Members" -tenantid $TenantFilter -asapp $true + $AppsList = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/teams/$($TeamID)/installedApps?`$expand=teamsAppDefinition" -tenantid $TenantFilter -asapp $true + + $Owners = $UserList | Where-Object -Property Roles -EQ 'Owner' + $Members = $UserList | Where-Object -Property email -NotIn $owners.email + $GraphRequest = [PSCustomObject]@{ + Name = $team.DisplayName + TeamInfo = @($team) + ChannelInfo = @($channels) + Members = @($Members) + Owners = @($owners) + InstalledApps = @($AppsList) + } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsActivity.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsActivity.ps1 new file mode 100644 index 000000000000..944fb90c7bf7 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsActivity.ps1 @@ -0,0 +1,30 @@ +using namespace System.Net + +Function Invoke-ListTeamsActivity { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $type = $request.query.Type + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/get$($type)Detail(period='D30')" -tenantid $TenantFilter | ConvertFrom-Csv | Select-Object @{ Name = 'UPN'; Expression = { $_.'User Principal Name' } }, + @{ Name = 'LastActive'; Expression = { $_.'Last Activity Date' } }, + @{ Name = 'TeamsChat'; Expression = { $_.'Team Chat Message Count' } }, + @{ Name = 'CallCount'; Expression = { $_.'Call Count' } }, + @{ Name = 'MeetingCount'; Expression = { $_.'Meeting Count' } } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsVoice.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsVoice.ps1 new file mode 100644 index 000000000000..ef5e1ca9d3d2 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTeamsVoice.ps1 @@ -0,0 +1,47 @@ +using namespace System.Net + +Function Invoke-ListTeamsVoice { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $tenantid = (Get-Tenants | Where-Object -Property defaultDomainName -EQ $Request.Query.TenantFilter).customerId + try { + $users = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=id,userPrincipalName,displayname" -tenantid $TenantFilter) + $GraphRequest = (New-TeamsAPIGetRequest -uri "https://api.interfaces.records.teams.microsoft.com/Skype.TelephoneNumberMgmt/Tenants/$($Tenantid)/telephone-numbers?locale=en-US" -tenantid $TenantFilter).TelephoneNumbers | ForEach-Object { + $CompleteRequest = $_ | Select-Object *, 'AssignedTo' + $CompleteRequest.AcquisitionDate = $CompleteRequest.AcquisitionDate -split 'T' | Select-Object -First 1 + + if ($CompleteRequest.TargetId -eq '00000000-0000-0000-0000-000000000000') { + $CompleteRequest.AssignedTo = 'Unassigned' + } else { + $CompleteRequest.AssignedTo = ($users | Where-Object -Property Id -EQ $CompleteRequest.TargetId).userPrincipalName + + } + $CompleteRequest + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenantDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenantDetails.ps1 new file mode 100644 index 000000000000..85e3330926c0 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenantDetails.ps1 @@ -0,0 +1,48 @@ +using namespace System.Net + +Function Invoke-ListTenantDetails { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + try { + $tenantfilter = $Request.Query.TenantFilter + $org = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/organization' -tenantid $tenantfilter | Select-Object displayName, city, country, countryLetterCode, street, state, postalCode, + @{ Name = 'businessPhones'; Expression = { $_.businessPhones -join ', ' } }, + @{ Name = 'technicalNotificationMails'; Expression = { $_.technicalNotificationMails -join ', ' } }, + tenantType, createdDateTime, onPremisesLastPasswordSyncDateTime, onPremisesLastSyncDateTime, onPremisesSyncEnabled, assignedPlans + } catch { + $org = [PSCustomObject]@{ + displayName = 'Error loading tenant' + city = '' + country = '' + countryLetterCode = '' + street = '' + state = '' + postalCode = '' + businessPhones = '' + technicalNotificationMails = '' + createdDateTime = '' + onPremisesLastPasswordSyncDateTime = '' + onPremisesLastSyncDateTime = '' + onPremisesSyncEnabled = '' + assignedPlans = @() + } + } finally { + $Body = $org + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenants.ps1 new file mode 100644 index 000000000000..93425c195e45 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTenants.ps1 @@ -0,0 +1,73 @@ +using namespace System.Net + +Function Invoke-ListTenants { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Clear Cache + if ($request.Query.ClearCache -eq 'true') { + Remove-CIPPCache -tenantsOnly $request.query.TenantsOnly + $GraphRequest = [pscustomobject]@{'Results' = 'Successfully completed request.' } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $GraphRequest + }) + exit + } + + try { + $tenantfilter = $Request.Query.TenantFilter + $Tenants = Get-Tenants -IncludeErrors + + if ($null -eq $TenantFilter -or $TenantFilter -eq 'null') { + $TenantList = [system.collections.generic.list[object]]::new() + if ($Request.Query.AllTenantSelector -eq $true) { + $TenantList.Add(@{ + customerId = 'AllTenants' + defaultDomainName = 'AllTenants' + displayName = '*All Tenants' + domains = 'AllTenants' + GraphErrorCount = 0 + }) | Out-Null + + if (($Tenants).length -gt 1) { + $TenantList.AddRange($Tenants) | Out-Null + } elseif ($Tenants) { + $TenantList.Add($Tenants) | Out-Null + } + $body = $TenantList + } else { + $Body = $Tenants + } + } else { + $body = $Tenants | Where-Object -Property defaultDomainName -EQ $Tenantfilter + } + + Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $Tenantfilter -API $APINAME -message 'Listed Tenant Details' -Sev 'Debug' + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -tenant $Tenantfilter -API $APINAME -message "List Tenant failed. The error is: $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{ + 'Results' = "Failed to retrieve tenants: $($_.Exception.Message)" + defaultDomainName = '' + displayName = 'Failed to retrieve tenants. Perform a permission check.' + customerId = '' + + } + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Body) + }) + + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRules.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRules.ps1 new file mode 100644 index 000000000000..18e9fb3959d2 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRules.ps1 @@ -0,0 +1,30 @@ +using namespace System.Net + +Function Invoke-ListTransportRules { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Tenantfilter = $request.Query.tenantfilter + + try { + $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Get-TransportRule' + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 new file mode 100644 index 000000000000..1c42c21d9470 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 @@ -0,0 +1,46 @@ +using namespace System.Net + +Function Invoke-ListTransportRulesTemplates { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Table = Get-CippTable -tablename 'templates' + + $Templates = Get-ChildItem 'Config\*.TransportRuleTemplate.json' | ForEach-Object { + + $Entity = @{ + JSON = "$(Get-Content $_)" + RowKey = "$($_.name)" + PartitionKey = 'TransportTemplate' + GUID = "$($_.name)" + } + Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force + + } + + #List new policies + $Table = Get-CippTable -tablename 'templates' + $Filter = "PartitionKey eq 'TransportTemplate'" + $Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { + $GUID = $_.RowKey + $data = $_.JSON | ConvertFrom-Json + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $GUID + $data + } + + if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property RowKey -EQ $Request.query.id } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Templates) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserConditionalAccessPolicies.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserConditionalAccessPolicies.ps1 new file mode 100644 index 000000000000..d3e6e3295593 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserConditionalAccessPolicies.ps1 @@ -0,0 +1,48 @@ +using namespace System.Net + +Function Invoke-ListUserConditionalAccessPolicies { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $UserID = $Request.Query.UserID + + try { + $json = '{"conditions":{"users":{"allUsers":2,"included":{"userIds":["' + $UserID + '"],"groupIds":[]},"excluded":{"userIds":[],"groupIds":[]}},"servicePrincipals":{"allServicePrincipals":1,"includeAllMicrosoftApps":false,"excludeAllMicrosoftApps":false,"userActions":[],"stepUpTags":[]},"conditions":{"minUserRisk":{"noRisk":false,"lowRisk":false,"mediumRisk":false,"highRisk":false,"applyCondition":false},"minSigninRisk":{"noRisk":false,"lowRisk":false,"mediumRisk":false,"highRisk":false,"applyCondition":false},"servicePrincipalRiskLevels":{"noRisk":false,"lowRisk":false,"mediumRisk":false,"highRisk":false,"applyCondition":false},"devicePlatforms":{"all":2,"included":{"android":false,"ios":false,"windowsPhone":false,"windows":false,"macOs":false,"linux":false},"excluded":null,"applyCondition":false},"locations":{"applyCondition":true,"includeLocationType":2,"excludeAllTrusted":false},"clientApps":{"applyCondition":false,"specificClientApps":false,"webBrowsers":false,"exchangeActiveSync":false,"onlyAllowSupportedPlatforms":false,"mobileDesktop":false},"clientAppsV2":{"applyCondition":false,"webBrowsers":false,"mobileDesktop":false,"modernAuth":false,"exchangeActiveSync":false,"onlyAllowSupportedPlatforms":false,"otherClients":false},"deviceState":{"includeDeviceStateType":1,"excludeDomainJoionedDevice":false,"excludeCompliantDevice":false,"applyCondition":true}}},"country":"","device":{}}' + $UserPolicies = (New-ClassicAPIPostRequest -uri 'https://main.iam.ad.ext.azure.com/api/Policies/Evaluate?' -tenantid $tenantfilter -Method POST -body $json -resource '74658136-14ec-4630-ad9b-26e160ff0fc6' -verbose | Where-Object { $_.applied -eq $true }) + $ConditionalAccessPolicyOutput = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies' -tenantid $tenantfilter + } catch { + $ConditionalAccessPolicyOutput = @{} + } + + $GraphRequest = foreach ($cap in $ConditionalAccessPolicyOutput) { + if ($cap.id -in $UserPolicies.policyId) { + $temp = [PSCustomObject]@{ + id = $cap.id + displayName = $cap.displayName + } + $temp + } + } + + Write-Host $GraphRequest + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserCounts.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserCounts.ps1 new file mode 100644 index 000000000000..f94a0a79027c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserCounts.ps1 @@ -0,0 +1,45 @@ +using namespace System.Net + +Function Invoke-ListUserCounts { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + if ($Request.Query.TenantFilter -eq 'AllTenants') { + $users = 'Not Supported' + $LicUsers = 'Not Supported' + $GAs = 'Not Supported' + $Guests = 'Not Supported' + } else { + $Users = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$count=true&`$top=1" -CountOnly -ComplexFilter -tenantid $TenantFilter + $LicUsers = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$count=true&`$top=1&`$filter=assignedLicenses/`$count ne 0" -CountOnly -ComplexFilter -tenantid $TenantFilter + $GAs = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/directoryRoles/roleTemplateId=62e90394-69f5-4237-9190-012177145e10/members?`$count=true" -CountOnly -ComplexFilter -tenantid $TenantFilter + $guests = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$count=true&`$top=1&`$filter=userType eq 'Guest'" -CountOnly -ComplexFilter -tenantid $TenantFilter + } + $StatusCode = [HttpStatusCode]::OK + $Counts = @{ + Users = $users + LicUsers = $LicUsers + Gas = $Gas + Guests = $guests + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Counts + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserDevices.ps1 new file mode 100644 index 000000000000..0bbcb27b4422 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserDevices.ps1 @@ -0,0 +1,61 @@ +using namespace System.Net + +Function Invoke-ListUserDevices { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $UserID = $Request.Query.UserID + + function Get-EPMID { + param( + $deviceID, + $EPMDevices + ) + try { + return ($EPMDevices | Where-Object { $_.azureADDeviceId -eq $deviceID }).id + } catch { + return $null + } + } + try { + $EPMDevices = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$UserID/managedDevices" -Tenantid $tenantfilter + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$UserID/ownedDevices?`$top=999" -Tenantid $tenantfilter | Select-Object @{ Name = 'ID'; Expression = { $_.'id' } }, + @{ Name = 'accountEnabled'; Expression = { $_.'accountEnabled' } }, + @{ Name = 'approximateLastSignInDateTime'; Expression = { $_.'approximateLastSignInDateTime' | Out-String } }, + @{ Name = 'createdDateTime'; Expression = { $_.'createdDateTime' | Out-String } }, + @{ Name = 'deviceOwnership'; Expression = { $_.'deviceOwnership' } }, + @{ Name = 'displayName'; Expression = { $_.'displayName' } }, + @{ Name = 'enrollmentType'; Expression = { $_.'enrollmentType' } }, + @{ Name = 'isCompliant'; Expression = { $_.'isCompliant' } }, + @{ Name = 'managementType'; Expression = { $_.'managementType' } }, + @{ Name = 'manufacturer'; Expression = { $_.'manufacturer' } }, + @{ Name = 'model'; Expression = { $_.'model' } }, + @{ Name = 'operatingSystem'; Expression = { $_.'operatingSystem' } }, + @{ Name = 'onPremisesSyncEnabled'; Expression = { $(if ([string]::IsNullOrEmpty($_.'onPremisesSyncEnabled')) { $false }else { $true }) } }, + @{ Name = 'operatingSystemVersion'; Expression = { $_.'operatingSystemVersion' } }, + @{ Name = 'trustType'; Expression = { $_.'trustType' } }, + @{ Name = 'EPMID'; Expression = { $(Get-EPMID -deviceID $_.'deviceId' -EPMDevices $EPMDevices) } } + } catch { + $GraphRequest = @() + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserGroups.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserGroups.ps1 new file mode 100644 index 000000000000..fff7eb222d9c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserGroups.ps1 @@ -0,0 +1,41 @@ +using namespace System.Net + +Function Invoke-ListUserGroups { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $UserID = $Request.Query.UserID + + + $URI = "https://graph.microsoft.com/beta/users/$UserID/memberOf/$/microsoft.graph.group?`$select=id,displayName,mailEnabled,securityEnabled,groupTypes,onPremisesSyncEnabled,mail,isAssignableToRole`&$orderby=displayName asc" + Write-Host $URI + $GraphRequest = New-GraphGetRequest -uri $URI -tenantid $TenantFilter -noPagination $true -verbose | Select-Object id, + @{ Name = 'DisplayName'; Expression = { $_.displayName } }, + @{ Name = 'MailEnabled'; Expression = { $_.mailEnabled } }, + @{ Name = 'Mail'; Expression = { $_.mail } }, + @{ Name = 'SecurityGroup'; Expression = { $_.securityEnabled } }, + @{ Name = 'GroupTypes'; Expression = { $_.groupTypes -join ',' } }, + @{ Name = 'OnPremisesSync'; Expression = { $_.onPremisesSyncEnabled } }, + @{ Name = 'IsAssignableToRole'; Expression = { $_.isAssignableToRole } } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxDetails.ps1 new file mode 100644 index 000000000000..e59ecf17a22a --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxDetails.ps1 @@ -0,0 +1,164 @@ +using namespace System.Net + +Function Invoke-ListUserMailboxDetails { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $UserID = $Request.Query.UserID + + + $TenantFilter = $Request.Query.TenantFilter + try { + $Bytes = [System.Text.Encoding]::UTF8.GetBytes($Request.Query.UserID) + $base64IdentityParam = [Convert]::ToBase64String($Bytes) + $CASRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox('$UserID')" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true + $MailRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$UserID')" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true + $FetchParam = @{ + anr = $MailRequest.PrimarySmtpAddress + } + $MailboxDetailedRequest = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-Mailbox' -cmdParams $FetchParam + try { + $Archive = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-Mailbox' -cmdParams $FetchParam + if ($Archive.ArchiveStatus -eq 'Active') { + $ArchiveEnabled = $True + } else { + $ArchiveEnabled = $False + } + + $FetchParam = @{ + Identity = $MailRequest.PrimarySmtpAddress + Archive = $true + } + + $ArchiveSize = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-MailboxStatistics' -cmdParams $FetchParam + } catch { + $ArchiveEnabled = $False + $ArchiveSize = @{ + TotalItemSize = '0' + ItemCount = '0' + } + } + $FetchParam = @{ + SenderAddress = $MailRequest.PrimarySmtpAddress + } + $BlockedSender = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-BlockedSenderAddress' -cmdParams $FetchParam + if ($BlockedSender) { + $BlockedForSpam = $True + } else { + $BlockedForSpam = $False + } + $StatsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($MailRequest.PrimarySmtpAddress)')/Exchange.GetMailboxStatistics()" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true + $PermsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($MailRequest.PrimarySmtpAddress)')/MailboxPermission" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true + $PermsRequest2 = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Recipient('$base64IdentityParam')?`$expand=RecipientPermission&isEncoded=true" -Tenantid $tenantfilter -scope ExchangeOnline + + } catch { + Write-Error "Failed Fetching Data $($_.Exception.message): $($_.InvocationInfo.ScriptLineNumber)" + } + + $ParsedPerms = foreach ($Perm in $PermsRequest, $PermsRequest2.RecipientPermission) { + + if ($perm.Trustee) { + $perm | Where-Object Trustee | ForEach-Object { [PSCustomObject]@{ + User = $_.Trustee + AccessRights = $_.accessRights -join ', ' + } + } + + } + if ($perm.PermissionList) { + $perm | Where-Object User | ForEach-Object { [PSCustomObject]@{ + User = $_.User + AccessRights = $_.PermissionList.accessRights -join ', ' + } + } + } + } + + $forwardingaddress = if ($MailboxDetailedRequest.ForwardingAddress) { + $MailboxDetailedRequest.ForwardingAddress + } elseif ($MailboxDetailedRequest.ForwardingSmtpAddress -and $MailboxDetailedRequest.ForwardingAddress) { + $MailboxDetailedRequest.ForwardingAddress + ' ' + $MailboxDetailedRequest.ForwardingSmtpAddress + } else { + $MailboxDetailedRequest.ForwardingSmtpAddress + } + + if ($ArchiveSize) { + $GraphRequest = [ordered]@{ + ForwardAndDeliver = $MailboxDetailedRequest.DeliverToMailboxAndForward + ForwardingAddress = $ForwardingAddress + LitiationHold = $MailboxDetailedRequest.LitigationHoldEnabled + HiddenFromAddressLists = $MailboxDetailedRequest.HiddenFromAddressListsEnabled + EWSEnabled = $CASRequest.EwsEnabled + MailboxMAPIEnabled = $CASRequest.MAPIEnabled + MailboxOWAEnabled = $CASRequest.OWAEnabled + MailboxImapEnabled = $CASRequest.ImapEnabled + MailboxPopEnabled = $CASRequest.PopEnabled + MailboxActiveSyncEnabled = $CASRequest.ActiveSyncEnabled + Permissions = $ParsedPerms + ProhibitSendQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendQuota -split ' GB')[0], 2) + ProhibitSendReceiveQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' GB')[0], 2) + ItemCount = [math]::Round($StatsRequest.ItemCount, 2) + TotalItemSize = [math]::Round($StatsRequest.TotalItemSize / 1Gb, 2) + TotalArchiveItemSize = $ArchiveSize.totalItemSize.split('(')[0] + TotalArchiveItemCount = [math]::Round($ArchiveSize.ItemCount, 2) + BlockedForSpam = $BlockedForSpam + ArchiveMailBox = $ArchiveEnabled + AutoExpandingArchive = $Archive.AutoExpandingArchiveEnabled + RecipientTypeDetails = $MailboxDetailedRequest.RecipientTypeDetails + } + } else { + $GraphRequest = [ordered]@{ + ForwardAndDeliver = $MailboxDetailedRequest.DeliverToMailboxAndForward + ForwardingAddress = $ForwardingAddress + LitiationHold = $MailboxDetailedRequest.LitigationHoldEnabled + HiddenFromAddressLists = $MailboxDetailedRequest.HiddenFromAddressListsEnabled + EWSEnabled = $CASRequest.EwsEnabled + MailboxMAPIEnabled = $CASRequest.MAPIEnabled + MailboxOWAEnabled = $CASRequest.OWAEnabled + MailboxImapEnabled = $CASRequest.ImapEnabled + MailboxPopEnabled = $CASRequest.PopEnabled + MailboxActiveSyncEnabled = $CASRequest.ActiveSyncEnabled + Permissions = $ParsedPerms + ProhibitSendQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendQuota -split ' GB')[0], 2) + ProhibitSendReceiveQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' GB')[0], 2) + ItemCount = [math]::Round($StatsRequest.ItemCount, 2) + TotalItemSize = [math]::Round($StatsRequest.TotalItemSize / 1Gb, 2) + TotalArchiveItemSize = 0 + TotalArchiveItemCount = 0 + BlockedForSpam = $BlockedForSpam + ArchiveMailBox = $ArchiveEnabled + AutoExpandingArchive = $Archive.AutoExpandingArchiveEnabled + RecipientTypeDetails = $MailboxDetailedRequest.RecipientTypeDetails + } + } + + + #$GraphRequest = [ordered]@{ + # Connectivity = $CASRequest + # Mailbox = $MailRequest + # MailboxDetail = $MailboxDetailedRequest + # Stats = $StatsRequest + # Permissions = $ParsedPerms + # Result = $Result + #} + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxRules.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxRules.ps1 new file mode 100644 index 000000000000..4a0a433fc1d4 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserMailboxRules.ps1 @@ -0,0 +1,45 @@ +using namespace System.Net + +Function Invoke-ListUserMailboxRules { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + try { + $TenantFilter = $Request.Query.TenantFilter + $UserID = $Request.Query.UserID + $GraphRequest = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-InboxRule' -cmdParams @{mailbox = $UserID } | Select-Object + @{ Name = 'DisplayName'; Expression = { $_.displayName } }, + @{ Name = 'Description'; Expression = { $_.Description } }, + @{ Name = 'Redirect To'; Expression = { $_.RedirectTo } }, + @{ Name = 'Copy To Folder'; Expression = { $_.CopyToFolder } }, + @{ Name = 'Move To Folder'; Expression = { $_.MoveToFolder } }, + @{ Name = 'Soft Delete Message'; Expression = { $_.SoftDeleteMessage } }, + @{ Name = 'Delete Message'; Expression = { $_.DeleteMessage } } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve mailbox rules $($request.query.id): $($_.Exception.message) " -Sev 'Error' -tenant $TenantFilter + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = '500' + Body = $(Get-NormalizedError -message $_.Exception.message) + }) + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserPhoto.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserPhoto.ps1 new file mode 100644 index 000000000000..bd596e0a379a --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserPhoto.ps1 @@ -0,0 +1,34 @@ +using namespace System.Net + +Function Invoke-ListUserPhoto { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $tenantFilter = $Request.Query.TenantFilter + $userId = $Request.Query.UserID + + + $URI = "https://graph.microsoft.com/v1.0/users/$userId/photos/240x240/`$value" + Write-Host $URI + $graphRequest = New-GraphGetRequest -uri $URI -tenantid $tenantFilter + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($graphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 index 47ae2f685830..274c5d8383f1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 @@ -18,8 +18,7 @@ function Invoke-ListUserSettings { $UserSettings = $UserSettings | Select-Object -ExpandProperty JSON | ConvertFrom-Json -Depth 10 -ErrorAction SilentlyContinue $StatusCode = [HttpStatusCode]::OK $Results = $UserSettings - } - catch { + } catch { $Results = "Function Error: $($_.Exception.Message)" $StatusCode = [HttpStatusCode]::BadRequest } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 new file mode 100644 index 000000000000..d4fe27a93764 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 @@ -0,0 +1,57 @@ +using namespace System.Net + +Function Invoke-ListUserSigninLogs { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $UserID = $Request.Query.UserID + try { + $URI = "https://graph.microsoft.com/beta/auditLogs/signIns?`$filter=(userId eq '$UserID')&`$top=50&`$orderby=createdDateTime desc" + Write-Host $URI + $GraphRequest = New-GraphGetRequest -uri $URI -tenantid $TenantFilter -noPagination $true -verbose | Select-Object @{ Name = 'Date'; Expression = { $(($_.createdDateTime | Out-String) -replace '\r\n') } }, + id, + @{ Name = 'Application'; Expression = { $_.resourceDisplayName } }, + @{ Name = 'LoginStatus'; Expression = { $_.status.errorCode } }, + @{ Name = 'ConditionalAccessStatus'; Expression = { $_.conditionalAccessStatus } }, + @{ Name = 'OverallLoginStatus'; Expression = { if (($_.conditionalAccessStatus -eq 'Success' -or 'Not Applied') -and $_.status.errorCode -eq 0) { 'Success' } else { 'Failed' } } }, + @{ Name = 'IPAddress'; Expression = { $_.ipAddress } }, + @{ Name = 'Town'; Expression = { $_.location.city } }, + @{ Name = 'State'; Expression = { $_.location.state } }, + @{ Name = 'Country'; Expression = { $_.location.countryOrRegion } }, + @{ Name = 'Device'; Expression = { $_.deviceDetail.displayName } }, + @{ Name = 'DeviceCompliant'; Expression = { $_.deviceDetail.isCompliant } }, + @{ Name = 'OS'; Expression = { $_.deviceDetail.operatingSystem } }, + @{ Name = 'Browser'; Expression = { $_.deviceDetail.browser } }, + @{ Name = 'AppliedCAPs'; Expression = { ($_.appliedConditionalAccessPolicies | ForEach-Object { @{Result = $_.result; Name = $_.displayName } }) } }, + @{ Name = 'AdditionalDetails'; Expression = { $_.status.additionalDetails } }, + @{ Name = 'FailureReason'; Expression = { $_.status.failureReason } }, + @{ Name = 'FullDetails'; Expression = { $_ } } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve Sign In report: $($_.Exception.message) " -Sev 'Error' -tenant $TenantFilter + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = '500' + Body = $(Get-NormalizedError -message $_.Exception.message) + }) + } + + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 new file mode 100644 index 000000000000..489df3ba15a0 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 @@ -0,0 +1,88 @@ +using namespace System.Net + +Function Invoke-ListUsers { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $selectlist = 'id', 'accountEnabled', 'businessPhones', 'city', 'createdDateTime', 'companyName', 'country', 'department', 'displayName', 'faxNumber', 'givenName', 'isResourceAccount', 'jobTitle', 'mail', 'mailNickname', 'mobilePhone', 'onPremisesDistinguishedName', 'officeLocation', 'onPremisesLastSyncDateTime', 'otherMails', 'postalCode', 'preferredDataLocation', 'preferredLanguage', 'proxyAddresses', 'showInAddressList', 'state', 'streetAddress', 'surname', 'usageLocation', 'userPrincipalName', 'userType', 'assignedLicenses', 'onPremisesSyncEnabled', 'LicJoined', 'Aliases', 'primDomain', 'Tenant', 'CippStatus' + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $ConvertTable = Import-Csv Conversiontable.csv | Sort-Object -Property 'guid' -Unique + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $GraphFilter = $Request.Query.graphFilter + $userid = $Request.Query.UserID + + $GraphRequest = if ($TenantFilter -ne 'AllTenants') { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userid)?`$top=999&`$select=$($selectlist -join ',')&`$filter=$GraphFilter&`$count=true" -tenantid $TenantFilter -ComplexFilter | Select-Object $selectlist | ForEach-Object { + $_.onPremisesSyncEnabled = [bool]($_.onPremisesSyncEnabled) + $_.Aliases = $_.Proxyaddresses -join ', ' + $SkuID = $_.AssignedLicenses.skuid + $_.LicJoined = ($ConvertTable | Where-Object { $_.guid -in $skuid }).'Product_Display_Name' -join ', ' + $_.primDomain = ($_.userPrincipalName -split '@' | Select-Object -Last 1) + $_ + } + } else { + $Table = Get-CIPPTable -TableName 'cacheusers' + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) + if (!$Rows) { + $Queue = New-CippQueueEntry -Name 'Users' -Link '/identity/administration/users?customerId=AllTenants' + Push-OutputBinding -Name listusers -Value "users/$($userid)?`$top=999&`$select=$($selectlist -join ',')&`$filter=$GraphFilter&`$count=true" + [PSCustomObject]@{ + Tenant = 'Loading data for all tenants. Please check back after the job completes' + QueueId = $Queue.RowKey + } + } else { + $Rows.Data | ConvertFrom-Json | Select-Object $selectlist | ForEach-Object { + $_.onPremisesSyncEnabled = [bool]($_.onPremisesSyncEnabled) + $_.Aliases = $_.Proxyaddresses -join ', ' + $SkuID = $_.AssignedLicenses.skuid + $_.LicJoined = ($ConvertTable | Where-Object { $_.guid -in $skuid }).'Product_Display_Name' -join ', ' + $_.primDomain = ($_.userPrincipalName -split '@' | Select-Object -Last 1) + $_ + } + } + } + + + if ($userid -and $Request.query.IncludeLogonDetails) { + $startDate = (Get-Date).AddDays(-7) + $endDate = (Get-Date) + $sessionid = Get-Random -Maximum 1000 -Minimum 1 + $SearchParam = @{ + SessionCommand = 'ReturnLargeSet' + Operations = @('UserLoggedIn', 'UserLoginFailed', 'TeamsSessionStarted', 'MailboxLogin') + sessionid = $sessionid + startDate = $startDate + endDate = $endDate + UserIds = @($GraphRequest.userPrincipalName) + } + $AuditlogsLogon = (New-ExoRequest -tenantid $Tenantfilter -cmdlet 'Search-unifiedAuditLog' -cmdParams $SearchParam | Sort-Object -Property CreationDate | Select-Object -Last 1).auditdata | ConvertFrom-Json + $Appname = '[{"Application Name":"ACOM Azure Website","Application IDs":"23523755-3a2b-41ca-9315-f81f3f566a95"},{"Application Name":"AEM-DualAuth","Application IDs":"69893ee3-dd10-4b1c-832d-4870354be3d8"},{"Application Name":"ASM Campaign Servicing","Application IDs":"0cb7b9ec-5336-483b-bc31-b15b5788de71"},{"Application Name":"Azure Advanced Threat Protection","Application IDs":"7b7531ad-5926-4f2d-8a1d-38495ad33e17"},{"Application Name":"Azure Data Lake","Application IDs":"e9f49c6b-5ce5-44c8-925d-015017e9f7ad"},{"Application Name":"Azure Lab Services Portal","Application IDs":"835b2a73-6e10-4aa5-a979-21dfda45231c"},{"Application Name":"Azure Portal","Application IDs":"c44b4083-3bb0-49c1-b47d-974e53cbdf3c"},{"Application Name":"AzureSupportCenter","Application IDs":"37182072-3c9c-4f6a-a4b3-b3f91cacffce"},{"Application Name":"Bing","Application IDs":"9ea1ad79-fdb6-4f9a-8bc3-2b70f96e34c7"},{"Application Name":"CPIM Service","Application IDs":"bb2a2e3a-c5e7-4f0a-88e0-8e01fd3fc1f4"},{"Application Name":"CRM Power BI Integration","Application IDs":"e64aa8bc-8eb4-40e2-898b-cf261a25954f"},{"Application Name":"Dataverse","Application IDs":"00000007-0000-0000-c000-000000000000"},{"Application Name":"Enterprise Roaming and Backup","Application IDs":"60c8bde5-3167-4f92-8fdb-059f6176dc0f"},{"Application Name":"IAM Supportability","Application IDs":"a57aca87-cbc0-4f3c-8b9e-dc095fdc8978"},{"Application Name":"IrisSelectionFrontDoor","Application IDs":"16aeb910-ce68-41d1-9ac3-9e1673ac9575"},{"Application Name":"MCAPI Authorization Prod","Application IDs":"d73f4b35-55c9-48c7-8b10-651f6f2acb2e"},{"Application Name":"Media Analysis and Transformation Service","Application IDs":"944f0bd1-117b-4b1c-af26-804ed95e767e
    0cd196ee-71bf-4fd6-a57c-b491ffd4fb1e"},{"Application Name":"Microsoft 365 Support Service","Application IDs":"ee272b19-4411-433f-8f28-5c13cb6fd407"},{"Application Name":"Microsoft App Access Panel","Application IDs":"0000000c-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Approval Management","Application IDs":"65d91a3d-ab74-42e6-8a2f-0add61688c74
    38049638-cc2c-4cde-abe4-4479d721ed44"},{"Application Name":"Microsoft Authentication Broker","Application IDs":"29d9ed98-a469-4536-ade2-f981bc1d605e"},{"Application Name":"Microsoft Azure CLI","Application IDs":"04b07795-8ddb-461a-bbee-02f9e1bf7b46"},{"Application Name":"Microsoft Azure PowerShell","Application IDs":"1950a258-227b-4e31-a9cf-717495945fc2"},{"Application Name":"Microsoft Bing Search","Application IDs":"cf36b471-5b44-428c-9ce7-313bf84528de"},{"Application Name":"Microsoft Bing Search for Microsoft Edge","Application IDs":"2d7f3606-b07d-41d1-b9d2-0d0c9296a6e8"},{"Application Name":"Microsoft Bing Default Search Engine","Application IDs":"1786c5ed-9644-47b2-8aa0-7201292175b6"},{"Application Name":"Microsoft Defender for Cloud Apps","Application IDs":"3090ab82-f1c1-4cdf-af2c-5d7a6f3e2cc7"},{"Application Name":"Microsoft Docs","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Dynamics ERP","Application IDs":"00000015-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Edge Insider Addons Prod","Application IDs":"6253bca8-faf2-4587-8f2f-b056d80998a7"},{"Application Name":"Microsoft Exchange Online Protection","Application IDs":"00000007-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Forms","Application IDs":"c9a559d2-7aab-4f13-a6ed-e7e9c52aec87"},{"Application Name":"Microsoft Graph","Application IDs":"00000003-0000-0000-c000-000000000000"},{"Application Name":"Microsoft Intune Web Company Portal","Application IDs":"74bcdadc-2fdc-4bb3-8459-76d06952a0e9"},{"Application Name":"Microsoft Intune Windows Agent","Application IDs":"fc0f3af4-6835-4174-b806-f7db311fd2f3"},{"Application Name":"Microsoft Learn","Application IDs":"18fbca16-2224-45f6-85b0-f7bf2b39b3f3"},{"Application Name":"Microsoft Office","Application IDs":"d3590ed6-52b3-4102-aeff-aad2292ab01c"},{"Application Name":"Microsoft Office 365 Portal","Application IDs":"00000006-0000-0ff1-ce00-000000000000"},{"Application Name":"Microsoft Office Web Apps Service","Application IDs":"67e3df25-268a-4324-a550-0de1c7f97287"},{"Application Name":"Microsoft Online Syndication Partner Portal","Application IDs":"d176f6e7-38e5-40c9-8a78-3998aab820e7"},{"Application Name":"Microsoft password reset service","Application IDs":"93625bc8-bfe2-437a-97e0-3d0060024faa"},{"Application Name":"Microsoft Power BI","Application IDs":"871c010f-5e61-4fb1-83ac-98610a7e9110"},{"Application Name":"Microsoft Storefronts","Application IDs":"28b567f6-162c-4f54-99a0-6887f387bbcc"},{"Application Name":"Microsoft Stream Portal","Application IDs":"cf53fce8-def6-4aeb-8d30-b158e7b1cf83"},{"Application Name":"Microsoft Substrate Management","Application IDs":"98db8bd6-0cc0-4e67-9de5-f187f1cd1b41"},{"Application Name":"Microsoft Support","Application IDs":"fdf9885b-dd37-42bf-82e5-c3129ef5a302"},{"Application Name":"Microsoft Teams","Application IDs":"1fec8e78-bce4-4aaf-ab1b-5451cc387264"},{"Application Name":"Microsoft Teams Services","Application IDs":"cc15fd57-2c6c-4117-a88c-83b1d56b4bbe"},{"Application Name":"Microsoft Teams Web Client","Application IDs":"5e3ce6c0-2b1f-4285-8d4b-75ee78787346"},{"Application Name":"Microsoft Whiteboard Services","Application IDs":"95de633a-083e-42f5-b444-a4295d8e9314"},{"Application Name":"O365 Suite UX","Application IDs":"4345a7b9-9a63-4910-a426-35363201d503"},{"Application Name":"Office 365 Exchange Online","Application IDs":"00000002-0000-0ff1-ce00-000000000000"},{"Application Name":"Office 365 Management","Application IDs":"00b41c95-dab0-4487-9791-b9d2c32c80f2"},{"Application Name":"Office 365 Search Service","Application IDs":"66a88757-258c-4c72-893c-3e8bed4d6899"},{"Application Name":"Office 365 SharePoint Online","Application IDs":"00000003-0000-0ff1-ce00-000000000000"},{"Application Name":"Office Delve","Application IDs":"94c63fef-13a3-47bc-8074-75af8c65887a"},{"Application Name":"Office Online Add-in SSO","Application IDs":"93d53678-613d-4013-afc1-62e9e444a0a5"},{"Application Name":"Office Online Client AAD- Augmentation Loop","Application IDs":"2abdc806-e091-4495-9b10-b04d93c3f040"},{"Application Name":"Office Online Client AAD- Loki","Application IDs":"b23dd4db-9142-4734-867f-3577f640ad0c"},{"Application Name":"Office Online Client AAD- Maker","Application IDs":"17d5e35f-655b-4fb0-8ae6-86356e9a49f5"},{"Application Name":"Office Online Client MSA- Loki","Application IDs":"b6e69c34-5f1f-4c34-8cdf-7fea120b8670"},{"Application Name":"Office Online Core SSO","Application IDs":"243c63a3-247d-41c5-9d83-7788c43f1c43"},{"Application Name":"Office Online Search","Application IDs":"a9b49b65-0a12-430b-9540-c80b3332c127"},{"Application Name":"Office.com","Application IDs":"4b233688-031c-404b-9a80-a4f3f2351f90"},{"Application Name":"Office365 Shell WCSS-Client","Application IDs":"89bee1f7-5e6e-4d8a-9f3d-ecd601259da7"},{"Application Name":"OfficeClientService","Application IDs":"0f698dd4-f011-4d23-a33e-b36416dcb1e6"},{"Application Name":"OfficeHome","Application IDs":"4765445b-32c6-49b0-83e6-1d93765276ca"},{"Application Name":"OfficeShredderWacClient","Application IDs":"4d5c2d63-cf83-4365-853c-925fd1a64357"},{"Application Name":"OMSOctopiPROD","Application IDs":"62256cef-54c0-4cb4-bcac-4c67989bdc40"},{"Application Name":"OneDrive SyncEngine","Application IDs":"ab9b8c07-8f02-4f72-87fa-80105867a763"},{"Application Name":"OneNote","Application IDs":"2d4d3d8e-2be3-4bef-9f87-7875a61c29de"},{"Application Name":"Outlook Mobile","Application IDs":"27922004-5251-4030-b22d-91ecd9a37ea4"},{"Application Name":"Partner Customer Delegated Admin Offline Processor","Application IDs":"a3475900-ccec-4a69-98f5-a65cd5dc5306"},{"Application Name":"Password Breach Authenticator","Application IDs":"bdd48c81-3a58-4ea9-849c-ebea7f6b6360"},{"Application Name":"Power BI Service","Application IDs":"00000009-0000-0000-c000-000000000000"},{"Application Name":"SharedWithMe","Application IDs":"ffcb16e8-f789-467c-8ce9-f826a080d987"},{"Application Name":"SharePoint Online Web Client Extensibility","Application IDs":"08e18876-6177-487e-b8b5-cf950c1e598c"},{"Application Name":"Signup","Application IDs":"b4bddae8-ab25-483e-8670-df09b9f1d0ea"},{"Application Name":"Skype for Business Online","Application IDs":"00000004-0000-0ff1-ce00-000000000000"},{"Application Name":"Sway","Application IDs":"905fcf26-4eb7-48a0-9ff0-8dcc7194b5ba"},{"Application Name":"Universal Store Native Client","Application IDs":"268761a2-03f3-40df-8a8b-c3db24145b6b"},{"Application Name":"Vortex [wsfed enabled]","Application IDs":"5572c4c0-d078-44ce-b81c-6cbf8d3ed39e"},{"Application Name":"Windows Azure Active Directory","Application IDs":"00000002-0000-0000-c000-000000000000"},{"Application Name":"Windows Azure Service Management API","Application IDs":"797f4846-ba00-4fd7-ba43-dac1f8f63013"},{"Application Name":"WindowsDefenderATP Portal","Application IDs":"a3b79187-70b2-4139-83f9-6016c58cd27b"},{"Application Name":"Windows Search","Application IDs":"26a7ee05-5602-4d76-a7ba-eae8b7b67941"},{"Application Name":"Windows Spotlight","Application IDs":"1b3c667f-cde3-4090-b60b-3d2abd0117f0"},{"Application Name":"Windows Store for Business","Application IDs":"45a330b1-b1ec-4cc1-9161-9f03992aa49f"},{"Application Name":"Yammer","Application IDs":"00000005-0000-0ff1-ce00-000000000000"},{"Application Name":"Yammer Web","Application IDs":"c1c74fed-04c9-4704-80dc-9f79a2e515cb"},{"Application Name":"Yammer Web Embed","Application IDs":"e1ef36fd-b883-4dbf-97f0-9ece4b576fc6"}]' | ConvertFrom-Json | Where-Object -Property 'Application IDs' -EQ $AuditlogsLogon.applicationId + $LastSignIn = [PSCustomObject]@{ + AppDisplayName = if ($AppName) { $AppName.'Application Name' } else { "$($AuditlogsLogon.Workload) - $($AuditlogsLogon.ApplicationId) " } + CreatedDateTime = $AuditlogsLogon.CreationTime + Id = $AuditlogsLogon.errorNumber + Status = $AuditlogsLogon.ResultStatus + } + $GraphRequest = $GraphRequest | Select-Object *, + @{ Name = 'LastSigninApplication'; Expression = { $LastSignIn.AppDisplayName } }, + @{ Name = 'LastSigninDate'; Expression = { $($LastSignIn.CreatedDateTime | Out-String) } }, + @{ Name = 'LastSigninStatus'; Expression = { $AuditlogsLogon.operation } }, + @{ Name = 'LastSigninResult'; Expression = { $LastSignIn.status } }, + @{ Name = 'LastSigninFailureReason'; Expression = { if ($LastSignIn.Id -eq 0) { 'Sucessfully signed in' } else { $LastSignIn.Id } } } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 new file mode 100644 index 000000000000..6eb63f8a57ae --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListWebhookAlert.ps1 @@ -0,0 +1,23 @@ +using namespace System.Net + +Function Invoke-ListWebhookAlert { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $WebhookTable = Get-CIPPTable -TableName webhookTable + $WebhookRow = Get-CIPPAzDataTableEntity @WebhookTable + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($WebhookRow) + }) + + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListmailboxPermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListmailboxPermissions.ps1 new file mode 100644 index 000000000000..d855df71778b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListmailboxPermissions.ps1 @@ -0,0 +1,68 @@ +using namespace System.Net + +Function Invoke-ListmailboxPermissions { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + + Write-Host "Tenant Filter: $TenantFilter" + try { + $Bytes = [System.Text.Encoding]::UTF8.GetBytes($Request.Query.UserID) + $base64IdentityParam = [Convert]::ToBase64String($Bytes) + $PermsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($Request.Query.UserID)')/MailboxPermission" -Tenantid $tenantfilter -scope ExchangeOnline + $PermsRequest2 = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Recipient('$base64IdentityParam')?`$expand=RecipientPermission&isEncoded=true" -Tenantid $tenantfilter -scope ExchangeOnline + $PermRequest3 = New-ExoRequest -Anchor $Request.Query.UserID -tenantid $Tenantfilter -cmdlet 'Get-Mailbox' -cmdParams @{Identity = $($Request.Query.UserID); } + + $GraphRequest = foreach ($Perm in $PermsRequest, $PermsRequest2.RecipientPermission, $PermRequest3) { + + if ($perm.Trustee) { + $perm | Where-Object Trustee | ForEach-Object { [PSCustomObject]@{ + User = $_.Trustee + Permissions = $_.accessRights + } + } + + } + if ($perm.PermissionList) { + $perm | Where-Object User | ForEach-Object { [PSCustomObject]@{ + User = $_.User + Permissions = $_.PermissionList.accessRights -join ', ' + } + } + } + if ($perm.GrantSendonBehalfTo -ne $null) { + $perm.GrantSendonBehalfTo | ForEach-Object { [PSCustomObject]@{ + User = $_ + Permissions = 'SendOnBehalf' + } + } + } + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + + + +} diff --git a/Modules/CIPPCore/Public/Invoke-RemoveScheduledItem.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveScheduledItem.ps1 index 888e4a906980..8c5a00f3a086 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveScheduledItem.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveScheduledItem.ps1 @@ -8,14 +8,21 @@ Function Invoke-RemoveScheduledItem { [CmdletBinding()] param($Request, $TriggerMetadata) - RowKey = $Request.Query.ID - PartitionKey = 'ScheduledTask' -} -$Table = Get-CIPPTable -TableName 'ScheduledTasks' -Remove-AzDataTableEntity @Table -Entity $task -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{ Results = 'Task removed successfully.' } - }) + $task = @{ + RowKey = $Request.Query.ID + PartitionKey = 'ScheduledTask' + } + $Table = Get-CIPPTable -TableName 'ScheduledTasks' + Remove-AzDataTableEntity @Table -Entity $task + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ Results = 'Task removed successfully.' } + }) + $Table = Get-CIPPTable -TableName 'ScheduledTasks' + Remove-AzDataTableEntity @Table -Entity $task + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ Results = 'Task removed successfully.' } + }) } diff --git a/Z_CIPPHttpTrigger/function.json b/Z_CIPPHttpTrigger/function.json index 9abea99584fd..d1a8f9c684ad 100644 --- a/Z_CIPPHttpTrigger/function.json +++ b/Z_CIPPHttpTrigger/function.json @@ -27,6 +27,36 @@ "name": "Subscription", "queueName": "AlertSubscriptions" }, + { + "type": "queue", + "direction": "out", + "name": "LicenseQueue", + "queueName": "licqueue" + }, + { + "type": "queue", + "direction": "out", + "name": "mbxrulequeue", + "queueName": "mbxrulequeue" + }, + { + "type": "queue", + "direction": "out", + "name": "mfaqueue", + "queueName": "mfaqueue" + }, + { + "type": "queue", + "direction": "out", + "name": "mailboxstats", + "queueName": "generalAllTenantQueue" + }, + { + "type": "queue", + "direction": "out", + "name": "listusers", + "queueName": "generalAllTenantQueue" + }, { "name": "starter", "type": "durableClient", From 3b28d3018f6bc4000f738da66d2e9005b6e11b0b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 20:46:21 +0100 Subject: [PATCH 56/77] move all execs to entrypoints --- ExecAddGDAPRole/function.json | 19 -- ExecAddGDAPRole/run.ps1 | 62 ------ ExecAddSPN/function.json | 19 -- ExecAddSPN/run.ps1 | 23 -- ExecAlertsList/function.json | 22 -- ExecAlertsList/run.ps1 | 105 --------- ExecAppApproval/function.json | 19 -- ExecAppApproval/run.ps1 | 26 --- ExecAssignApp/function.json | 19 -- ExecAssignApp/run.ps1 | 53 ----- ExecAutoExtendGDAP/function.json | 19 -- ExecAutoExtendGDAP/run.ps1 | 16 -- ExecBECCheck/function.json | 24 --- ExecBECCheck/run.ps1 | 31 --- ExecBECRemediate/function.json | 19 -- ExecBECRemediate/run.ps1 | 46 ---- ExecBackendURLs/function.json | 19 -- ExecBackendURLs/run.ps1 | 35 --- ExecCPVPermissions/function.json | 19 -- ExecCPVPermissions/run.ps1 | 44 ---- ExecClrImmId/function.json | 18 -- ExecClrImmId/run.ps1 | 31 --- ExecConverttoSharedMailbox/function.json | 19 -- ExecConverttoSharedMailbox/run.ps1 | 27 --- ExecCopyForSent/function.json | 19 -- ExecCopyForSent/run.ps1 | 28 --- ExecCreateTAP/function.json | 19 -- ExecCreateTAP/run.ps1 | 22 -- ExecDeleteGDAPRelationship/function.json | 19 -- ExecDeleteGDAPRelationship/run.ps1 | 25 --- ExecDeleteGDAPRoleMapping/function.json | 19 -- ExecDeleteGDAPRoleMapping/run.ps1 | 26 --- ExecDeviceAction/function.json | 19 -- ExecDeviceAction/run.ps1 | 28 --- ExecDisableEmailForward/function.json | 19 -- ExecDisableUser/function.json | 19 -- ExecDisableUser/run.ps1 | 20 -- ExecDnsConfig/function.json | 19 -- ExecDnsConfig/run.ps1 | 106 ---------- ExecEditCalendarPermissions/function.json | 19 -- ExecEditCalendarPermissions/run.ps1 | 32 --- ExecEditMailboxPermissions/function.json | 19 -- ExecEditMailboxPermissions/run.ps1 | 116 ---------- ExecEditTemplate/function.json | 19 -- ExecEditTemplate/run.ps1 | 44 ---- ExecEmailForward/function.json | 19 -- ExecEmailForward/run.ps1 | 70 ------ ExecEnableArchive/function.json | 19 -- ExecEnableArchive/run.ps1 | 26 --- ExecExcludeLicenses/function.json | 19 -- ExecExcludeLicenses/run.ps1 | 65 ------ ExecExcludeTenant/function.json | 19 -- ExecExcludeTenant/run.ps1 | 66 ------ ExecExtensionMapping/function.json | 25 --- ExecExtensionSync/function.json | 28 --- ExecExtensionSync/run.ps1 | 69 ------ ExecExtensionTest/function.json | 19 -- ExecExtensionsConfig/function.json | 19 -- ExecGDAPInvite/function.json | 16 -- ExecGDAPInviteApproved/function.json | 22 -- ExecGDAPInviteApproved/run.ps1 | 15 -- ExecGDAPInviteApproved_Timer/function.json | 16 -- ExecGDAPInviteApproved_Timer/run.ps1 | 5 - ExecGDAPInviteQueue/function.json | 10 - ExecGDAPInviteQueue/run.ps1 | 35 --- ExecGDAPMigration/function.json | 22 -- ExecGDAPMigration/run.ps1 | 26 --- ExecGeoIPLookup/function.json | 19 -- ExecGetLocalAdminPassword/function.json | 19 -- ExecGetRecoveryKey/function.json | 19 -- ExecGetRecoveryKey/run.ps1 | 30 --- ExecGraphRequest/function.json | 19 -- ExecGroupsDelete/function.json | 19 -- ExecGroupsDelete/run.ps1 | 26 --- ExecGroupsDeliveryManagement/function.json | 19 -- ExecGroupsDeliveryManagement/run.ps1 | 27 --- ExecGroupsHideFromGAL/function.json | 19 -- ExecGroupsHideFromGAL/run.ps1 | 22 -- ExecHideFromGAL/function.json | 19 -- ExecHideFromGAL/run.ps1 | 25 --- ExecIncidentsList/function.json | 22 -- ExecIncidentsList/run.ps1 | 80 ------- ExecListAppId/function.json | 19 -- ExecListAppId/run.ps1 | 19 -- ExecMailboxMobileDevices/function.json | 19 -- ExecMailboxMobileDevices/run.ps1 | 27 --- ExecMailboxRestore/function.json | 18 -- .../Scripts/Add-CippUser.ps1 | 67 ------ .../Enable-FunctionAppGitHubActions.ps1 | 38 ---- .../Scripts/Grant-CippConditionalAccess.ps1 | 122 ----------- ExecMaintenanceScripts/function.json | 19 -- ExecMaintenanceScripts/run.ps1 | 78 ------- ExecNotificationConfig/function.json | 19 -- ExecNotificationConfig/run.ps1 | 47 ----- ExecOffboardTenant/function.json | 16 -- ExecOffboardTenant/run.ps1 | 108 ---------- ExecOffboardUser/function.json | 22 -- ExecOffboardUser/run.ps1 | 45 ---- ExecOneDriveShortCut/function.json | 19 -- ExecOneDriveShortCut/run.ps1 | 21 -- ExecPasswordConfig/function.json | 19 -- ExecPasswordConfig/run.ps1 | 41 ---- ExecQuarantineManagement/function.json | 19 -- ExecQuarantineManagement/run.ps1 | 36 ---- ExecResetMFA/function.json | 19 -- ExecResetPass/function.json | 19 -- ExecResetPass/run.ps1 | 31 --- ExecRestoreBackup/function.json | 19 -- ExecRestoreBackup/run.ps1 | 34 --- ExecRestoreDeleted/function.json | 19 -- ExecRestoreDeleted/run.ps1 | 24 --- ExecRevokeSessions/function.json | 19 -- ExecRevokeSessions/run.ps1 | 23 -- ExecRunBackup/function.json | 19 -- ExecRunBackup/run.ps1 | 44 ---- ExecSAMSetup/function.json | 19 -- ExecSAMSetup/run.ps1 | 199 ------------------ ExecScheduledCommand/function.json | 10 - ExecSendOrgMessage/function.json | 19 -- ExecSendOrgMessage/run.ps1 | 116 ---------- ExecSendPush/function.json | 19 -- ExecSendPush/run.ps1 | 118 ----------- ExecSetMailboxQuota/function.json | 19 -- ExecSetOoO/function.json | 19 -- ExecSetSecurityAlert/function.json | 19 -- ExecSetSecurityAlert/run.ps1 | 29 --- ExecSetSecurityIncident/function.json | 19 -- ExecSetSecurityIncident/run.ps1 | 79 ------- ExecUniversalSearch/function.json | 19 -- ExecUniversalSearch/run.ps1 | 30 --- ExecUserSettings/function.json | 18 -- .../Entrypoints/Invoke-ExecAddGDAPRole.ps1 | 66 ++++++ .../Public/Entrypoints/Invoke-ExecAddSPN.ps1 | 29 +++ .../Entrypoints/Invoke-ExecAlertsList.ps1 | 108 ++++++++++ .../Invoke-ExecAlertsListAllTenants.ps1 | 59 ++++++ .../Entrypoints/Invoke-ExecAppApproval.ps1 | 33 +++ .../Entrypoints/Invoke-ExecAssignApp.ps1 | 59 ++++++ .../Entrypoints/Invoke-ExecAutoExtendGDAP.ps1 | 23 ++ .../Entrypoints/Invoke-ExecBECCheck.ps1 | 36 ++++ .../Entrypoints/Invoke-ExecBECRemediate.ps1 | 51 +++++ .../Entrypoints/Invoke-ExecBackendURLs.ps1 | 42 ++++ .../Entrypoints/Invoke-ExecCPVPermissions.ps1 | 51 +++++ .../Entrypoints/Invoke-ExecClrImmId.ps1 | 37 ++++ .../Invoke-ExecConverttoSharedMailbox.ps1 | 33 +++ .../Entrypoints/Invoke-ExecCopyForSent.ps1 | 34 +++ .../Entrypoints/Invoke-ExecCreateTAP.ps1 | 28 +++ .../Invoke-ExecDeleteGDAPRelationship.ps1 | 31 +++ .../Invoke-ExecDeleteGDAPRoleMapping.ps1 | 33 +++ .../Entrypoints/Invoke-ExecDeviceAction.ps1 | 34 +++ .../Invoke-ExecDisableEmailForward.ps1 | 25 ++- .../Entrypoints/Invoke-ExecDisableUser.ps1 | 26 +++ .../Entrypoints/Invoke-ExecDnsConfig.ps1 | 113 ++++++++++ .../Invoke-ExecEditCalendarPermissions.ps1 | 37 ++++ .../Invoke-ExecEditMailboxPermissions.ps1 | 116 ++++++++++ .../Entrypoints/Invoke-ExecEditTemplate.ps1 | 49 +++++ .../Entrypoints/Invoke-ExecEmailForward.ps1 | 72 +++++++ .../Entrypoints/Invoke-ExecEnableArchive.ps1 | 32 +++ .../Invoke-ExecExcludeLicenses.ps1 | 71 +++++++ .../Entrypoints/Invoke-ExecExcludeTenant.ps1 | 71 +++++++ .../Invoke-ExecExtensionMapping.ps1 | 15 +- .../Invoke-ExecExtensionNinjaOneQueue.ps1 | 19 ++ .../Entrypoints/Invoke-ExecExtensionSync.ps1 | 76 +++++++ .../Entrypoints/Invoke-ExecExtensionTest.ps1 | 17 +- .../Invoke-ExecExtensionsConfig.ps1 | 15 +- .../Entrypoints/Invoke-ExecGDAPInvite.ps1 | 17 +- .../Invoke-ExecGDAPInviteApproved.ps1 | 22 ++ .../Invoke-ExecGDAPInviteQueue.ps1 | 42 ++++ .../Entrypoints/Invoke-ExecGDAPMigration.ps1 | 33 +++ .../Invoke-ExecGDAPMigrationQueue.ps1 | 99 +++++++++ .../Entrypoints/Invoke-ExecGeoIPLookup.ps1 | 15 +- .../Invoke-ExecGetLocalAdminPassword.ps1 | 15 +- .../Entrypoints/Invoke-ExecGetRecoveryKey.ps1 | 36 ++++ .../Entrypoints/Invoke-ExecGraphRequest.ps1 | 15 +- .../Entrypoints/Invoke-ExecGroupsDelete.ps1 | 32 +++ .../Invoke-ExecGroupsDeliveryManagement.ps1 | 33 +++ .../Invoke-ExecGroupsHideFromGAL.ps1 | 28 +++ .../Entrypoints/Invoke-ExecHideFromGAL.ps1 | 31 +++ .../Entrypoints/Invoke-ExecIncidentsList.ps1 | 84 ++++++++ .../Invoke-ExecIncidentsListAllTenants.ps1 | 56 +++++ .../Entrypoints/Invoke-ExecListAppId.ps1 | 26 +++ .../Invoke-ExecMailboxMobileDevices.ps1 | 33 +++ .../Invoke-ExecMaintenanceScripts.ps1 | 79 +++++++ .../Invoke-ExecNotificationConfig.ps1 | 53 +++++ .../Entrypoints/Invoke-ExecOffboardTenant.ps1 | 113 ++++++++++ .../Entrypoints/Invoke-ExecOffboardUser.ps1 | 50 +++++ ...Invoke-ExecOffboard_Mailboxpermissions.ps1 | 15 ++ .../Invoke-ExecOneDriveShortCut.ps1 | 27 +++ .../Entrypoints/Invoke-ExecPasswordConfig.ps1 | 46 ++++ .../Invoke-ExecQuarantineManagement.ps1 | 42 ++++ .../Entrypoints/Invoke-ExecResetMFA.ps1 | 64 +++--- .../Entrypoints/Invoke-ExecResetPass.ps1 | 37 ++++ .../Entrypoints/Invoke-ExecRestoreBackup.ps1 | 40 ++++ .../Entrypoints/Invoke-ExecRestoreDeleted.ps1 | 30 +++ .../Entrypoints/Invoke-ExecRevokeSessions.ps1 | 29 +++ .../Entrypoints/Invoke-ExecRunBackup.ps1 | 49 +++++ .../Entrypoints/Invoke-ExecSAMSetup.ps1 | 198 +++++++++++++++++ .../Invoke-ExecScheduledCommand.ps1 | 19 +- .../Invoke-ExecSchedulerBillingRun.ps1 | 28 +++ .../Entrypoints/Invoke-ExecSendOrgMessage.ps1 | 122 +++++++++++ .../Entrypoints/Invoke-ExecSendPush.ps1 | 122 +++++++++++ .../Invoke-ExecSetMailboxQuota.ps1 | 39 ++-- .../Public/Entrypoints/Invoke-ExecSetOoO.ps1 | 33 +-- .../Invoke-ExecSetSecurityAlert.ps1 | 35 +++ .../Invoke-ExecSetSecurityIncident.ps1 | 79 +++++++ .../Invoke-ExecUniversalSearch.ps1 | 36 ++++ .../Public/Invoke-CIPPOffboardingJob.ps1 | 41 ++-- .../Public/Set-CIPPGDAPInviteGroups.ps1 | 2 +- Z_CIPPHttpTrigger/function.json | 48 +++++ 208 files changed, 3505 insertions(+), 4392 deletions(-) delete mode 100644 ExecAddGDAPRole/function.json delete mode 100644 ExecAddGDAPRole/run.ps1 delete mode 100644 ExecAddSPN/function.json delete mode 100644 ExecAddSPN/run.ps1 delete mode 100644 ExecAlertsList/function.json delete mode 100644 ExecAlertsList/run.ps1 delete mode 100644 ExecAppApproval/function.json delete mode 100644 ExecAppApproval/run.ps1 delete mode 100644 ExecAssignApp/function.json delete mode 100644 ExecAssignApp/run.ps1 delete mode 100644 ExecAutoExtendGDAP/function.json delete mode 100644 ExecAutoExtendGDAP/run.ps1 delete mode 100644 ExecBECCheck/function.json delete mode 100644 ExecBECCheck/run.ps1 delete mode 100644 ExecBECRemediate/function.json delete mode 100644 ExecBECRemediate/run.ps1 delete mode 100644 ExecBackendURLs/function.json delete mode 100644 ExecBackendURLs/run.ps1 delete mode 100644 ExecCPVPermissions/function.json delete mode 100644 ExecCPVPermissions/run.ps1 delete mode 100644 ExecClrImmId/function.json delete mode 100644 ExecClrImmId/run.ps1 delete mode 100644 ExecConverttoSharedMailbox/function.json delete mode 100644 ExecConverttoSharedMailbox/run.ps1 delete mode 100644 ExecCopyForSent/function.json delete mode 100644 ExecCopyForSent/run.ps1 delete mode 100644 ExecCreateTAP/function.json delete mode 100644 ExecCreateTAP/run.ps1 delete mode 100644 ExecDeleteGDAPRelationship/function.json delete mode 100644 ExecDeleteGDAPRelationship/run.ps1 delete mode 100644 ExecDeleteGDAPRoleMapping/function.json delete mode 100644 ExecDeleteGDAPRoleMapping/run.ps1 delete mode 100644 ExecDeviceAction/function.json delete mode 100644 ExecDeviceAction/run.ps1 delete mode 100644 ExecDisableEmailForward/function.json delete mode 100644 ExecDisableUser/function.json delete mode 100644 ExecDisableUser/run.ps1 delete mode 100644 ExecDnsConfig/function.json delete mode 100644 ExecDnsConfig/run.ps1 delete mode 100644 ExecEditCalendarPermissions/function.json delete mode 100644 ExecEditCalendarPermissions/run.ps1 delete mode 100644 ExecEditMailboxPermissions/function.json delete mode 100644 ExecEditMailboxPermissions/run.ps1 delete mode 100644 ExecEditTemplate/function.json delete mode 100644 ExecEditTemplate/run.ps1 delete mode 100644 ExecEmailForward/function.json delete mode 100644 ExecEmailForward/run.ps1 delete mode 100644 ExecEnableArchive/function.json delete mode 100644 ExecEnableArchive/run.ps1 delete mode 100644 ExecExcludeLicenses/function.json delete mode 100644 ExecExcludeLicenses/run.ps1 delete mode 100644 ExecExcludeTenant/function.json delete mode 100644 ExecExcludeTenant/run.ps1 delete mode 100644 ExecExtensionMapping/function.json delete mode 100644 ExecExtensionSync/function.json delete mode 100644 ExecExtensionSync/run.ps1 delete mode 100644 ExecExtensionTest/function.json delete mode 100644 ExecExtensionsConfig/function.json delete mode 100644 ExecGDAPInvite/function.json delete mode 100644 ExecGDAPInviteApproved/function.json delete mode 100644 ExecGDAPInviteApproved/run.ps1 delete mode 100644 ExecGDAPInviteApproved_Timer/function.json delete mode 100644 ExecGDAPInviteApproved_Timer/run.ps1 delete mode 100644 ExecGDAPInviteQueue/function.json delete mode 100644 ExecGDAPInviteQueue/run.ps1 delete mode 100644 ExecGDAPMigration/function.json delete mode 100644 ExecGDAPMigration/run.ps1 delete mode 100644 ExecGeoIPLookup/function.json delete mode 100644 ExecGetLocalAdminPassword/function.json delete mode 100644 ExecGetRecoveryKey/function.json delete mode 100644 ExecGetRecoveryKey/run.ps1 delete mode 100644 ExecGraphRequest/function.json delete mode 100644 ExecGroupsDelete/function.json delete mode 100644 ExecGroupsDelete/run.ps1 delete mode 100644 ExecGroupsDeliveryManagement/function.json delete mode 100644 ExecGroupsDeliveryManagement/run.ps1 delete mode 100644 ExecGroupsHideFromGAL/function.json delete mode 100644 ExecGroupsHideFromGAL/run.ps1 delete mode 100644 ExecHideFromGAL/function.json delete mode 100644 ExecHideFromGAL/run.ps1 delete mode 100644 ExecIncidentsList/function.json delete mode 100644 ExecIncidentsList/run.ps1 delete mode 100644 ExecListAppId/function.json delete mode 100644 ExecListAppId/run.ps1 delete mode 100644 ExecMailboxMobileDevices/function.json delete mode 100644 ExecMailboxMobileDevices/run.ps1 delete mode 100644 ExecMailboxRestore/function.json delete mode 100644 ExecMaintenanceScripts/Scripts/Add-CippUser.ps1 delete mode 100644 ExecMaintenanceScripts/Scripts/Enable-FunctionAppGitHubActions.ps1 delete mode 100644 ExecMaintenanceScripts/Scripts/Grant-CippConditionalAccess.ps1 delete mode 100644 ExecMaintenanceScripts/function.json delete mode 100644 ExecMaintenanceScripts/run.ps1 delete mode 100644 ExecNotificationConfig/function.json delete mode 100644 ExecNotificationConfig/run.ps1 delete mode 100644 ExecOffboardTenant/function.json delete mode 100644 ExecOffboardTenant/run.ps1 delete mode 100644 ExecOffboardUser/function.json delete mode 100644 ExecOffboardUser/run.ps1 delete mode 100644 ExecOneDriveShortCut/function.json delete mode 100644 ExecOneDriveShortCut/run.ps1 delete mode 100644 ExecPasswordConfig/function.json delete mode 100644 ExecPasswordConfig/run.ps1 delete mode 100644 ExecQuarantineManagement/function.json delete mode 100644 ExecQuarantineManagement/run.ps1 delete mode 100644 ExecResetMFA/function.json delete mode 100644 ExecResetPass/function.json delete mode 100644 ExecResetPass/run.ps1 delete mode 100644 ExecRestoreBackup/function.json delete mode 100644 ExecRestoreBackup/run.ps1 delete mode 100644 ExecRestoreDeleted/function.json delete mode 100644 ExecRestoreDeleted/run.ps1 delete mode 100644 ExecRevokeSessions/function.json delete mode 100644 ExecRevokeSessions/run.ps1 delete mode 100644 ExecRunBackup/function.json delete mode 100644 ExecRunBackup/run.ps1 delete mode 100644 ExecSAMSetup/function.json delete mode 100644 ExecSAMSetup/run.ps1 delete mode 100644 ExecScheduledCommand/function.json delete mode 100644 ExecSendOrgMessage/function.json delete mode 100644 ExecSendOrgMessage/run.ps1 delete mode 100644 ExecSendPush/function.json delete mode 100644 ExecSendPush/run.ps1 delete mode 100644 ExecSetMailboxQuota/function.json delete mode 100644 ExecSetOoO/function.json delete mode 100644 ExecSetSecurityAlert/function.json delete mode 100644 ExecSetSecurityAlert/run.ps1 delete mode 100644 ExecSetSecurityIncident/function.json delete mode 100644 ExecSetSecurityIncident/run.ps1 delete mode 100644 ExecUniversalSearch/function.json delete mode 100644 ExecUniversalSearch/run.ps1 delete mode 100644 ExecUserSettings/function.json create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddGDAPRole.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddSPN.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsList.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAppApproval.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignApp.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAutoExtendGDAP.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECCheck.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECRemediate.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBackendURLs.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCPVPermissions.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecClrImmId.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecConverttoSharedMailbox.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCopyForSent.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCreateTAP.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRelationship.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRoleMapping.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceAction.ps1 rename ExecDisableEmailForward/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 (70%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableUser.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDnsConfig.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditCalendarPermissions.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditMailboxPermissions.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditTemplate.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEmailForward.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEnableArchive.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeLicenses.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeTenant.ps1 rename ExecExtensionMapping/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 (90%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionNinjaOneQueue.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 rename ExecExtensionTest/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionTest.ps1 (89%) rename ExecExtensionsConfig/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionsConfig.ps1 (92%) rename ExecGDAPInvite/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 (92%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteApproved.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteQueue.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigration.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigrationQueue.ps1 rename ExecGeoIPLookup/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGeoIPLookup.ps1 (79%) rename ExecGetLocalAdminPassword/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetLocalAdminPassword.ps1 (71%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetRecoveryKey.ps1 rename ExecGraphRequest/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 (95%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDelete.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDeliveryManagement.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsHideFromGAL.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecHideFromGAL.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsList.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecListAppId.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMailboxMobileDevices.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecNotificationConfig.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardUser.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboard_Mailboxpermissions.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOneDriveShortCut.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecPasswordConfig.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecQuarantineManagement.ps1 rename ExecResetMFA/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetMFA.ps1 (71%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetPass.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreBackup.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreDeleted.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRevokeSessions.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRunBackup.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 rename ExecScheduledCommand/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecScheduledCommand.ps1 (93%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSchedulerBillingRun.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendOrgMessage.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendPush.ps1 rename ExecSetMailboxQuota/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 (59%) rename ExecSetOoO/run.ps1 => Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 (75%) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityAlert.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityIncident.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUniversalSearch.ps1 diff --git a/ExecAddGDAPRole/function.json b/ExecAddGDAPRole/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecAddGDAPRole/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecAddGDAPRole/run.ps1 b/ExecAddGDAPRole/run.ps1 deleted file mode 100644 index a78d130486a7..000000000000 --- a/ExecAddGDAPRole/run.ps1 +++ /dev/null @@ -1,62 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -$Groups = $Request.body.gdapRoles -$CustomSuffix = $Request.body.customSuffix -$Table = Get-CIPPTable -TableName 'GDAPRoles' - -$Results = [System.Collections.Generic.List[string]]::new() -$ExistingGroups = New-GraphGetRequest -NoAuthCheck $True -uri 'https://graph.microsoft.com/beta/groups' -tenantid $env:TenantID - -$RoleMappings = foreach ($group in $Groups) { - if ($CustomSuffix) { - $GroupName = "M365 GDAP $($Group.Name) - $CustomSuffix" - $MailNickname = "M365GDAP$(($Group.Name).replace(' ',''))$($CustomSuffix)" - } - else { - $GroupName = "M365 GDAP $($Group.Name)" - $MailNickname = "M365GDAP$(($Group.Name).replace(' ',''))" - } - try { - if ($GroupName -in $ExistingGroups.displayName) { - @{ - PartitionKey = 'Roles' - RowKey = ($ExistingGroups | Where-Object -Property displayName -EQ $GroupName).id - RoleName = $Group.Name - GroupName = $GroupName - GroupId = ($ExistingGroups | Where-Object -Property displayName -EQ $GroupName).id - roleDefinitionId = $group.ObjectId - } - $Results.Add("M365 GDAP $($Group.Name) already exists") - } - else { - $BodyToship = [pscustomobject] @{'displayName' = $GroupName; 'description' = "This group is used to manage M365 partner tenants at the $($group.name) level."; securityEnabled = $true; mailEnabled = $false; mailNickname = $MailNickname } | ConvertTo-Json - $GraphRequest = New-GraphPostRequest -NoAuthCheck $True -uri 'https://graph.microsoft.com/beta/groups' -tenantid $env:TenantID -type POST -body $BodyToship -verbose - @{ - PartitionKey = 'Roles' - RowKey = $GraphRequest.Id - RoleName = $Group.Name - GroupName = $GroupName - GroupId = $GraphRequest.Id - roleDefinitionId = $group.ObjectId - } - $Results.Add("$GroupName added successfully") - } - } - catch { - $Results.Add("Could not create GDAP group $($GroupName): $($_.Exception.Message)") - } -} - -Add-CIPPAzDataTableEntity @Table -Entity $RoleMappings -Force - -$body = @{Results = @($Results) } -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) \ No newline at end of file diff --git a/ExecAddSPN/function.json b/ExecAddSPN/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecAddSPN/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecAddSPN/run.ps1 b/ExecAddSPN/run.ps1 deleted file mode 100644 index c954af2de341..000000000000 --- a/ExecAddSPN/run.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$Body = if ($Request.Query.Enable) { '{"accountEnabled":"true"}' } else { '{"accountEnabled":"false"}' } -try { - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/servicePrincipals" -tenantid $ENV:TenantID -type POST -Body "{ `"appId`": `"2832473f-ec63-45fb-976f-5d45a7d4bb91`" }" -NoAuthCheck $true - $Results = [pscustomobject]@{"Results" = "Successfully completed request. Add your GDAP migration permissions to your SAM application here: https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/CallAnAPI/appId/$($ENV:ApplicationID)/isMSAApp/ " } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed to add SPN. Please manually execute 'New-AzureADServicePrincipal -AppId 2832473f-ec63-45fb-976f-5d45a7d4bb91' The error was $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) \ No newline at end of file diff --git a/ExecAlertsList/function.json b/ExecAlertsList/function.json deleted file mode 100644 index 6276e76c91b7..000000000000 --- a/ExecAlertsList/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "alertqueue" - } - ] -} diff --git a/ExecAlertsList/run.ps1 b/ExecAlertsList/run.ps1 deleted file mode 100644 index 7fad1371efbf..000000000000 --- a/ExecAlertsList/run.ps1 +++ /dev/null @@ -1,105 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -function New-FlatArray ([Array]$arr) { - $arr | ForEach-Object { - if ($_ -is 'Array') { - New-FlatArray $_ - } - else { $_ } - } -} -try { - # Interact with query parameters or the body of the request. - $TenantFilter = $Request.Query.TenantFilter - $GraphRequest = if ($TenantFilter -ne 'AllTenants') { - $Alerts = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/security/alerts' -tenantid $TenantFilter - $AlertsObj = foreach ($Alert In $alerts) { - @{ - Tenant = $TenantFilter - GUID = $GUID - Id = $alert.Id - Title = $alert.Title - Category = $alert.category - EventDateTime = $alert.eventDateTime - Severity = $alert.Severity - Status = $alert.Status - RawResult = $($Alerts | Where-Object { $_.Id -eq $alert.Id }) - InvolvedUsers = $($Alerts | Where-Object { $_.Id -eq $alert.Id }).userStates - } - } - - $DisplayableAlerts = New-FlatArray $AlertsObj | Where-Object { $_.Id -ne $null } | Sort-Object -Property EventDateTime -Descending - - [PSCustomObject]@{ - NewAlertsCount = $DisplayableAlerts | Where-Object { $_.Status -eq 'newAlert' } | Measure-Object | Select-Object -ExpandProperty Count - InProgressAlertsCount = $DisplayableAlerts | Where-Object { $_.Status -eq 'inProgress' } | Measure-Object | Select-Object -ExpandProperty Count - SeverityHighAlertsCount = ($DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'high' } | Measure-Object | Select-Object -ExpandProperty Count) - SeverityMediumAlertsCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'medium' } | Measure-Object | Select-Object -ExpandProperty Count - SeverityLowAlertsCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'low' } | Measure-Object | Select-Object -ExpandProperty Count - SeverityInformationalCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'informational' } | Measure-Object | Select-Object -ExpandProperty Count - MSResults = $DisplayableAlerts - } - } - else { - $Table = Get-CIPPTable -TableName cachealertsandincidents - $Filter = "PartitionKey eq 'alert'" - $Rows = Get-CIPPAzDataTableEntity @Table -filter $Filter | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) - if (!$Rows) { - Push-OutputBinding -Name Msg -Value (Get-Date).ToString() - [PSCustomObject]@{ - Waiting = $true - } - } - else { - $Alerts = $Rows - $AlertsObj = foreach ($Alert in $alerts) { - $AlertInfo = $Alert.Alert | ConvertFrom-Json - @{ - Tenant = $Alert.Tenant - GUID = $GUID - Id = $AlertInfo.Id - Title = $AlertInfo.Title - Category = $AlertInfo.category - EventDateTime = $AlertInfo.eventDateTime - Severity = $AlertInfo.Severity - Status = $AlertInfo.Status - RawResult = $AlertInfo - InvolvedUsers = $AlertInfo.userStates - } - } - $DisplayableAlerts = New-FlatArray $AlertsObj | Where-Object { $_.Id -ne $null } | Sort-Object -Property EventDateTime -Descending - [PSCustomObject]@{ - NewAlertsCount = $DisplayableAlerts | Where-Object { $_.Status -eq 'newAlert' } | Measure-Object | Select-Object -ExpandProperty Count - InProgressAlertsCount = $DisplayableAlerts | Where-Object { $_.Status -eq 'inProgress' } | Measure-Object | Select-Object -ExpandProperty Count - SeverityHighAlertsCount = ($DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'high' } | Measure-Object | Select-Object -ExpandProperty Count) - SeverityMediumAlertsCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'medium' } | Measure-Object | Select-Object -ExpandProperty Count - SeverityLowAlertsCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'low' } | Measure-Object | Select-Object -ExpandProperty Count - SeverityInformationalCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'informational' } | Measure-Object | Select-Object -ExpandProperty Count - MSResults = $DisplayableAlerts - } - } - } - -} -catch { - $StatusCode = [HttpStatusCode]::Forbidden - $body = $_.Exception.message -} -if (!$body) { - $StatusCode = [HttpStatusCode]::OK - $body = $GraphRequest -} -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $Body - }) \ No newline at end of file diff --git a/ExecAppApproval/function.json b/ExecAppApproval/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecAppApproval/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecAppApproval/run.ps1 b/ExecAppApproval/run.ps1 deleted file mode 100644 index 51fa478763e7..000000000000 --- a/ExecAppApproval/run.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." -Write-Host "$($Request.query.ID)" -# Interact with query parameters or the body of the request. - -$applicationid = if ($request.query.applicationid) { $request.query.applicationid } else { $env:ApplicationID } -$Results = get-tenants | ForEach-Object { - [PSCustomObject]@{ - defaultDomainName = $_.defaultDomainName - link = "https://login.microsoftonline.com/$($_.customerId)/v2.0/adminconsent?client_id=$applicationid&scope=$applicationid/.default" - } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecAssignApp/function.json b/ExecAssignApp/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecAssignApp/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecAssignApp/run.ps1 b/ExecAssignApp/run.ps1 deleted file mode 100644 index 3c009b9386c0..000000000000 --- a/ExecAssignApp/run.ps1 +++ /dev/null @@ -1,53 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$tenantfilter = $Request.Query.TenantFilter -$appFilter = $Request.Query.ID -$AssignTo = $Request.Query.AssignTo -$AssignBody = switch ($AssignTo) { - - 'AllUsers' { - @" -{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allLicensedUsersAssignmentTarget"},"intent":"Required","settings":null}]} -"@ - } - - 'AllDevices' { - @" -{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"},"intent":"Required","settings":null}]} -"@ - } - - 'Both' { - @" -{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allLicensedUsersAssignmentTarget"},"intent":"Required","settings":null},{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"},"intent":"Required","settings":null}]} -"@ - } - -} -$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 -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Assigned $($appFilter) to $assignTo" -Sev "Info" - -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to assign app $($appFilter): $($_.Exception.Message)" -Sev "Error" - $body = [pscustomobject]@{"Results" = "Failed to assign. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecAutoExtendGDAP/function.json b/ExecAutoExtendGDAP/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecAutoExtendGDAP/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecAutoExtendGDAP/run.ps1 b/ExecAutoExtendGDAP/run.ps1 deleted file mode 100644 index 8106ae4db6d1..000000000000 --- a/ExecAutoExtendGDAP/run.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$Results = Set-CIPPGDAPAutoExtend -RelationShipid $Request.query.ID - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{ Results = $Results } - }) \ No newline at end of file diff --git a/ExecBECCheck/function.json b/ExecBECCheck/function.json deleted file mode 100644 index 3de6affa2d01..000000000000 --- a/ExecBECCheck/function.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} \ No newline at end of file diff --git a/ExecBECCheck/run.ps1 b/ExecBECCheck/run.ps1 deleted file mode 100644 index 6790917a0af7..000000000000 --- a/ExecBECCheck/run.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - - -$body = if ($request.query.GUID) { - $Table = Get-CippTable -tablename 'cachebec' - $Filter = "PartitionKey eq 'bec' and RowKey eq '$($request.query.GUID)'" - $JSONOutput = Get-CIPPAzDataTableEntity @Table -Filter $Filter - if (!$JSONOutput) { - @{ Waiting = $true } - } - else { - $JSONOutput.Results - } -} -else { - $OrchRequest = [PSCustomObject]@{ - TenantFilter = $request.query.tenantfilter - UserID = $request.query.userid - userName = $request.query.userName - } - $InstanceId = Start-NewOrchestration -FunctionName 'Durable_BECRun' -InputObject $OrchRequest - @{ GUID = $request.query.userid } -} - - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) \ No newline at end of file diff --git a/ExecBECRemediate/function.json b/ExecBECRemediate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecBECRemediate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecBECRemediate/run.ps1 b/ExecBECRemediate/run.ps1 deleted file mode 100644 index 6f4a25eee516..000000000000 --- a/ExecBECRemediate/run.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -Write-Host "PowerShell HTTP trigger function processed a request." - -$TenantFilter = $request.body.tenantfilter -$SuspectUser = $request.body.userid -$username = $request.body.username -Write-Host $TenantFilter -Write-Host $SuspectUser -$Results = try { - Set-CIPPResetPassword -userid $username -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' - Set-CIPPSignInState -userid $username -AccountEnabled $false -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' - Revoke-CIPPSessions -userid $SuspectUser -username $request.body.username -ExecutingUser $request.headers.'x-ms-client-principal' -APIName $APINAME -tenantFilter $TenantFilter - $RuleDisabled = 0 - New-ExoRequest -anchor $username -tenantid $TenantFilter -cmdlet "get-inboxrule" -cmdParams @{Mailbox = $username } | ForEach-Object { - $null = New-ExoRequest -anchor $username -tenantid $TenantFilter -cmdlet "Disable-InboxRule" -cmdParams @{Confirm = $false; Identity = $_.Identity } - "Disabled Inbox Rule $($_.Identity) for $username" - $RuleDisabled ++ - } - if ($RuleDisabled) { - "Disabled $RuleDisabled Inbox Rules for $username" - } - else { - "No Inbox Rules found for $username. We have not disabled any rules." - } - - Write-LogMessage -API "BECRemediate" -tenant $tenantfilter -message "Executed Remediation for $SuspectUser" -sev "Info" - -} -catch { - #Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to assign app $($appFilter): $($_.Exception.Message)" -Sev "Error" - $results = [pscustomobject]@{"Results" = "Failed to execute remediation. $($_.Exception.Message)" } - Write-LogMessage -API "BECRemediate" -tenant $tenantfilter -message "Executed Remediation for $SuspectUser failed" -sev "Error" -} -$results = [pscustomobject]@{"Results" = @($Results) } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecBackendURLs/function.json b/ExecBackendURLs/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecBackendURLs/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecBackendURLs/run.ps1 b/ExecBackendURLs/run.ps1 deleted file mode 100644 index 3789d17af8f9..000000000000 --- a/ExecBackendURLs/run.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$Subscription = ($ENV:WEBSITE_OWNER_NAME).split('+') | Select-Object -First 1 -$SWAName = $ENV:Website_SITE_NAME -replace 'cipp', 'CIPP-SWA-' -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -$results = [PSCustomObject]@{ - ResourceGroup = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/overview" - KeyVault = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.KeyVault/vaults/$($ENV:WEBSITE_SITE_NAME)/secrets" - FunctionApp = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/sites/$($ENV:WEBSITE_SITE_NAME)/appServices" - FunctionConfig = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/sites/$($ENV:WEBSITE_SITE_NAME)/configuration" - FunctionDeployment = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/sites/$($ENV:WEBSITE_SITE_NAME)/vstscd" - SWADomains = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/staticSites/$SWAName/customDomains" - SWARoles = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/staticSites/$SWAName/roleManagement" - Subscription = $Subscription - RGName = $ENV:Website_Resource_Group - FunctionName = $ENV:WEBSITE_SITE_NAME - SWAName = $SWAName -} - - -$body = @{Results = $Results } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [httpstatusCode]::OK - Body = $body - }) diff --git a/ExecCPVPermissions/function.json b/ExecCPVPermissions/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecCPVPermissions/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecCPVPermissions/run.ps1 b/ExecCPVPermissions/run.ps1 deleted file mode 100644 index a015d769f4a2..000000000000 --- a/ExecCPVPermissions/run.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$TenantFilter = (get-tenants -IncludeAll -IncludeErrors | Where-Object -Property customerId -EQ $Request.query.Tenantfilter).defaultDomainName -Write-Host "Our Tenantfilter is $TenantFilter" - -$CPVConsentParams = @{ - Tenantfilter = $TenantFilter -} -if ($Request.Query.ResetSP -eq 'true') { - $CPVConsentParams.ResetSP = $true -} - -$GraphRequest = try { - Set-CIPPCPVConsent @CPVConsentParams - Add-CIPPApplicationPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $TenantFilter - Add-CIPPDelegatedPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $TenantFilter - $Success = $true -} catch { - "Failed to update permissions for $($TenantFilter): $($_.Exception.Message)" - $Success = $false -} - -$Tenant = Get-Tenants -IncludeAll -IncludeErrors | Where-Object -Property defaultDomainName -EQ $Tenantfilter - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{ - Results = $GraphRequest - Metadata = @{ - Heading = 'CPV Permission - {0} ({1})' -f $Tenant.displayName, $Tenant.defaultDomainName - Success = $Success - } - } - }) diff --git a/ExecClrImmId/function.json b/ExecClrImmId/function.json deleted file mode 100644 index 68d371c20ff6..000000000000 --- a/ExecClrImmId/function.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] - } \ No newline at end of file diff --git a/ExecClrImmId/run.ps1 b/ExecClrImmId/run.ps1 deleted file mode 100644 index b4464b1d6fa6..000000000000 --- a/ExecClrImmId/run.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -Try { - $TenantFilter = $Request.Query.TenantFilter - $UserID = $Request.Query.ID - $Body = [pscustomobject] @{ - onPremisesImmutableId = $null - } | ConvertTo-Json - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$UserID" -tenantid $TenantFilter -type PATCH -body $Body - $Results = [pscustomobject]@{"Results" = "Successfully Cleared ImmutableId" } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $_.Exception.Message"; colour = "danger" } - $_.Exception -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecConverttoSharedMailbox/function.json b/ExecConverttoSharedMailbox/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecConverttoSharedMailbox/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecConverttoSharedMailbox/run.ps1 b/ExecConverttoSharedMailbox/run.ps1 deleted file mode 100644 index 9ecbb30b2236..000000000000 --- a/ExecConverttoSharedMailbox/run.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - - -# Interact with query parameters or the body of the request. -Try { - $MailboxType = if ($request.query.ConvertToUser -eq 'true') { "Regular" } else { "Shared" } - $ConvertedMailbox = Set-CIPPMailboxType -userid $Request.query.id -tenantFilter $Request.query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -MailboxType $MailboxType - $Results = [pscustomobject]@{"Results" = "$ConvertedMailbox" } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed to convert $($request.query.id) - $($_.Exception.Message)" } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecCopyForSent/function.json b/ExecCopyForSent/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecCopyForSent/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecCopyForSent/run.ps1 b/ExecCopyForSent/run.ps1 deleted file mode 100644 index 2e318eca9042..000000000000 --- a/ExecCopyForSent/run.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - - -# Interact with query parameters or the body of the request. -Try { - $MessageCopyForSentAsEnabled = if ($request.query.MessageCopyForSentAsEnabled -eq 'false') { "false" } else { "true" } - $MessageResult = Set-CIPPMessageCopy -userid $Request.query.id -tenantFilter $Request.query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -MessageCopyForSentAsEnabled $MessageCopyForSentAsEnabled - $Results = [pscustomobject]@{"Results" = "$MessageResult" } -} -catch { - $Results = [pscustomobject]@{"Results" = "set MessageCopyForSentAsEnabled to $MessageCopyForSentAsEnabled failed - $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecCreateTAP/function.json b/ExecCreateTAP/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecCreateTAP/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecCreateTAP/run.ps1 b/ExecCreateTAP/run.ps1 deleted file mode 100644 index ef9ac68ca88d..000000000000 --- a/ExecCreateTAP/run.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -try { - $TAP = New-CIPPTAP -userid $Request.query.ID -TenantFilter $Request.query.tenantfilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' - $Results = [pscustomobject]@{"Results" = "$TAP" } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) \ No newline at end of file diff --git a/ExecDeleteGDAPRelationship/function.json b/ExecDeleteGDAPRelationship/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecDeleteGDAPRelationship/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecDeleteGDAPRelationship/run.ps1 b/ExecDeleteGDAPRelationship/run.ps1 deleted file mode 100644 index 63d65434147a..000000000000 --- a/ExecDeleteGDAPRelationship/run.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$GDAPID = $request.query.GDAPId -try { - $DELETE = New-GraphPostRequest -NoAuthCheck $True -uri "https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web/v1/delegatedAdminRelationships/$($GDAPID)/requests" -type POST -body '{"action":"terminate"}' -tenantid $env:TenantID -scope 'https://api.partnercustomeradministration.microsoft.com/.default' - $Results = [pscustomobject]@{"Results" = "Success. GDAP relationship for $($GDAPID) been revoked" } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Success. GDAP relationship for $($GDAPID) been revoked" -Sev "Info" - -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) \ No newline at end of file diff --git a/ExecDeleteGDAPRoleMapping/function.json b/ExecDeleteGDAPRoleMapping/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecDeleteGDAPRoleMapping/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecDeleteGDAPRoleMapping/run.ps1 b/ExecDeleteGDAPRoleMapping/run.ps1 deleted file mode 100644 index d94e2964a3a4..000000000000 --- a/ExecDeleteGDAPRoleMapping/run.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -$Table = Get-CIPPTable -TableName 'GDAPRoles' - -Write-Host $Table -try { - $Filter = "PartitionKey eq 'Roles' and RowKey eq '{0}'" -f $Request.Query.GroupId - $Entity = Get-CIPPAzDataTableEntity @Table -Filter $Filter - Remove-AzDataTableEntity @Table -Entity $Entity - $Results = [pscustomobject]@{'Results' = 'Success. GDAP relationship mapping deleted' } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "GDAP relationship mapping deleted for $($Request.Query.GroupId)" -Sev 'Info' - -} catch { - $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecDeviceAction/function.json b/ExecDeviceAction/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecDeviceAction/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecDeviceAction/run.ps1 b/ExecDeviceAction/run.ps1 deleted file mode 100644 index d74c194d37dc..000000000000 --- a/ExecDeviceAction/run.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. - - -try { - if ($Request.Query.Action -eq "setDeviceName") { - $ActionBody = @{ deviceName = $Request.Body.input } | ConvertTo-Json -Compress - } - $ActionResult = New-CIPPDeviceAction -Action $Request.Query.Action -ActionBody $ActionBody -DeviceFilter $Request.Query.GUID -TenantFilter $Request.Query.TenantFilter -ExecutingUser $request.headers.'x-ms-client-principal' -APINAME $APINAME - $body = [pscustomobject]@{"Results" = "$ActionResult" } - -} -catch { - $body = [pscustomobject]@{"Results" = "Failed to queue action $action on $DeviceFilter $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecDisableEmailForward/function.json b/ExecDisableEmailForward/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecDisableEmailForward/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecDisableUser/function.json b/ExecDisableUser/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecDisableUser/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecDisableUser/run.ps1 b/ExecDisableUser/run.ps1 deleted file mode 100644 index 428e12d454cc..000000000000 --- a/ExecDisableUser/run.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -try { - ([System.Convert]::ToBoolean($Request.Query.Enable)) - $State = Set-CIPPSignInState -userid $Request.query.ID -TenantFilter $Request.Query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -AccountEnabled ([System.Convert]::ToBoolean($Request.Query.Enable)) - $Results = [pscustomobject]@{"Results" = "$State" } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) \ No newline at end of file diff --git a/ExecDnsConfig/function.json b/ExecDnsConfig/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecDnsConfig/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecDnsConfig/run.ps1 b/ExecDnsConfig/run.ps1 deleted file mode 100644 index f224d28c1e9e..000000000000 --- a/ExecDnsConfig/run.ps1 +++ /dev/null @@ -1,106 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -# List of supported resolvers -$ValidResolvers = @( - 'Google' - 'Cloudflare' - 'Quad9' -) - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -$StatusCode = [HttpStatusCode]::OK -try { - $ConfigTable = Get-CippTable -tablename Config - $Filter = "PartitionKey eq 'Domains' and RowKey eq 'Domains'" - $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter $Filter - - $DomainTable = Get-CippTable -tablename 'Domains' - - if ($ValidResolvers -notcontains $Config.Resolver) { - $Config = @{ - PartitionKey = 'Domains' - RowKey = 'Domains' - Resolver = 'Google' - } - Add-CIPPAzDataTableEntity @ConfigTable -Entity $Config -Force - } - - $updated = $false - - switch ($Request.Query.Action) { - 'SetConfig' { - if ($Request.Query.Resolver) { - $Resolver = $Request.Query.Resolver - if ($ValidResolvers -contains $Resolver) { - try { - $Config.Resolver = $Resolver - } catch { - $Config = @{ - Resolver = $Resolver - } - } - $updated = $true - } - } - if ($updated) { - Add-CIPPAzDataTableEntity @ConfigTable -Entity $Config -Force - Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'DNS configuration updated' -Sev 'Info' - $body = [pscustomobject]@{'Results' = 'Success: DNS configuration updated.' } - } else { - $StatusCode = [HttpStatusCode]::BadRequest - $body = [pscustomobject]@{'Results' = 'Error: No DNS resolver provided.' } - } - } - 'SetDkimConfig' { - $Domain = $Request.Query.Domain - $Selector = ($Request.Query.Selector).trim() -split '\s*,\s*' - $DomainTable = Get-CIPPTable -Table 'Domains' - $Filter = "RowKey eq '{0}'" -f $Domain - $DomainInfo = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter - $DkimSelectors = [string]($Selector | ConvertTo-Json -Compress) - if ($DomainInfo) { - $DomainInfo.DkimSelectors = $DkimSelectors - } else { - $DomainInfo = @{ - 'RowKey' = $Request.Query.Domain - 'PartitionKey' = 'ManualEntry' - 'TenantId' = 'NoTenant' - 'MailProviders' = '' - 'TenantDetails' = '' - 'DomainAnalyser' = '' - 'DkimSelectors' = $DkimSelectors - } - } - Add-CIPPAzDataTableEntity @DomainTable -Entity $DomainInfo -Force - } - 'GetConfig' { - $body = [pscustomobject]$Config - Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'Retrieved DNS configuration' -Sev 'Info' - } - 'RemoveDomain' { - $Filter = "RowKey eq '{0}'" -f $Request.Query.Domain - $DomainRow = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @DomainTable -Entity $DomainRow - Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message "Removed Domain - $($Request.Query.Domain) " -Sev 'Info' - $body = [pscustomobject]@{ 'Results' = "Domain removed - $($Request.Query.Domain)" } - } - } -} catch { - Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "DNS Config API failed. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } - $StatusCode = [HttpStatusCode]::BadRequest -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $body - }) diff --git a/ExecEditCalendarPermissions/function.json b/ExecEditCalendarPermissions/function.json deleted file mode 100644 index bec6849b58ab..000000000000 --- a/ExecEditCalendarPermissions/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecEditCalendarPermissions/run.ps1 b/ExecEditCalendarPermissions/run.ps1 deleted file mode 100644 index 9f1e96ee80c0..000000000000 --- a/ExecEditCalendarPermissions/run.ps1 +++ /dev/null @@ -1,32 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$UserID = ($request.query.UserID) -$UserToGetPermissions = $Request.query.UserToGetPermissions -$Tenantfilter = $request.Query.tenantfilter -$Permissions = @($Request.query.permissions) -$folderName = $Request.query.folderName - - -try { - if ($Request.query.removeaccess) { - $result = Set-CIPPCalenderPermission -UserID $UserID -folderName $folderName -RemoveAccess $Request.query.removeaccess -TenantFilter $TenantFilter - } - else { - $result = Set-CIPPCalenderPermission -UserID $UserID -folderName $folderName -TenantFilter $Tenantfilter -UserToGetPermissions $UserToGetPermissions -Permissions $Permissions - $Result = "Successfully set permissions on folder $($CalParam.Identity). The user $UserToGetPermissions now has $Permissions permissions on this folder." - } -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception - $Result = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{Results = $Result } - }) diff --git a/ExecEditMailboxPermissions/function.json b/ExecEditMailboxPermissions/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecEditMailboxPermissions/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecEditMailboxPermissions/run.ps1 b/ExecEditMailboxPermissions/run.ps1 deleted file mode 100644 index 0b72530ce29a..000000000000 --- a/ExecEditMailboxPermissions/run.ps1 +++ /dev/null @@ -1,116 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Accessed this API" -Sev "Debug" -$Username = $request.body.userID -$Tenantfilter = $request.body.tenantfilter -if ($username -eq $null) { exit } -$userid = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($username)" -tenantid $Tenantfilter).id -$Results = [System.Collections.ArrayList]@() - -$RemoveFullAccess = ($Request.body.RemoveFullAccess).value -foreach ($RemoveUser in $RemoveFullAccess) { - try { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet "Remove-mailboxpermission" -cmdParams @{Identity = $userid; user = $RemoveUser; accessRights = @("FullAccess"); } - $results.add("Removed $($removeuser) from $($username) Shared Mailbox permissions") - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Removed $($RemoveUser) from $($username) Shared Mailbox permission" -Sev "Info" -tenant $TenantFilter - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not remove mailbox permissions for $($removeuser) on $($username)" -Sev "Error" -tenant $TenantFilter - $results.add("Could not remove $($removeuser) shared mailbox permissions for $($username). Error: $($_.Exception.Message)") - } -} -$AddFullAccess = ($Request.body.AddFullAccess).value - -foreach ($UserAutomap in $AddFullAccess) { - try { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet "Add-MailboxPermission" -cmdParams @{Identity = $userid; user = $UserAutomap; accessRights = @("FullAccess"); automapping = $true } - $results.add( "Granted $($UserAutomap) access to $($username) Mailbox with automapping") - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Granted $($UserAutomap) access to $($username) Mailbox with automapping" -Sev "Info" -tenant $TenantFilter - - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not add mailbox permissions for $($UserAutomap) on $($username)" -Sev "Error" -tenant $TenantFilter - $results.add( "Could not add $($UserAutomap) shared mailbox permissions for $($username). Error: $($_.Exception.Message)") - } -} -$AddFullAccessNoAutoMap = ($Request.body.AddFullAccessNoAutoMap).value - -foreach ($UserNoAutomap in $AddFullAccessNoAutoMap) { - try { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet "Add-MailboxPermission" -cmdParams @{Identity = $userid; user = $UserNoAutomap; accessRights = @("FullAccess"); automapping = $false } - $results.add( "Granted $UserNoAutomap access to $($username) Mailbox without automapping") - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Granted $UserNoAutomap access to $($username) Mailbox without automapping" -Sev "Info" -tenant $TenantFilter - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not add mailbox permissions for $($UserNoAutomap) on $($username)" -Sev "Error" -tenant $TenantFilter - $results.add("Could not add $($UserNoAutomap) shared mailbox permissions for $($username). Error: $($_.Exception.Message)") - } -} - -$AddSendAS = ($Request.body.AddSendAs).value - -foreach ($UserSendAs in $AddSendAS) { - try { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet "Add-RecipientPermission" -cmdParams @{Identity = $userid; Trustee = $UserSendAs; accessRights = @("SendAs") } - $results.add( "Granted $UserSendAs access to $($username) with Send As permissions") - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Granted $UserSendAs access to $($username) with Send As permissions" -Sev "Info" -tenant $TenantFilter - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not add mailbox permissions for $($UserSendAs) on $($username)" -Sev "Error" -tenant $TenantFilter - $results.add("Could not add $($UserSendAs) send-as permissions for $($username). Error: $($_.Exception.Message)") - } -} - -$RemoveSendAs = ($Request.body.RemoveSendAs).value - -foreach ($UserSendAs in $RemoveSendAs) { - try { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet "Remove-RecipientPermission" -cmdParams @{Identity = $userid; Trustee = $UserSendAs; accessRights = @("SendAs") } - $results.add( "Removed $UserSendAs from $($username) with Send As permissions") - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Removed $UserSendAs from $($username) with Send As permissions" -Sev "Info" -tenant $TenantFilter - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not remove mailbox permissions for $($UserSendAs) on $($username)" -Sev "Error" -tenant $TenantFilter - $results.add("Could not remove $($UserSendAs) send-as permissions for $($username). Error: $($_.Exception.Message)") - } -} - -$AddSendOnBehalf = ($Request.body.AddSendOnBehalf).value - -foreach ($UserSendOnBehalf in $AddSendOnBehalf) { - try { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet "Set-Mailbox" -cmdParams @{Identity = $userid; GrantSendonBehalfTo = @{'@odata.type' = '#Exchange.GenericHashTable'; add = $UserSendOnBehalf}; } - $results.add( "Granted $UserSendOnBehalf access to $($username) with Send On Behalf Permissions") - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Granted $UserSendOnBehalf access to $($username) with Send On Behalf Permissions" -Sev "Info" -tenant $TenantFilter - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not add send on behalf permissions for $($UserSendOnBehalf) on $($username)" -Sev "Error" -tenant $TenantFilter - $results.add("Could not add $($UserSendOnBehalf) send on behalf permissions for $($username). Error: $($_.Exception.Message)") - } -} - -$RemoveSendOnBehalf = ($Request.body.RemoveSendOnBehalf).value - -foreach ($UserSendOnBehalf in $RemoveSendOnBehalf) { - try { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet "Set-Mailbox" -cmdParams @{Identity = $userid; GrantSendonBehalfTo = @{'@odata.type' = '#Exchange.GenericHashTable'; remove = $UserSendOnBehalf}; } - $results.add( "Removed $UserSendOnBehalf from $($username) Send on Behalf Permissions") - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Removed $UserSendOnBehalf from $($username) Send on Behalf Permissions" -Sev "Info" -tenant $TenantFilter - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not Remove send on behalf permissions for $($UserSendOnBehalf) on $($username)" -Sev "Error" -tenant $TenantFilter - $results.add("Could not remove $($UserSendOnBehalf) send on behalf permissions for $($username). Error: $($_.Exception.Message)") - } -} - -$body = [pscustomobject]@{"Results" = @($results) } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) diff --git a/ExecEditTemplate/function.json b/ExecEditTemplate/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecEditTemplate/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecEditTemplate/run.ps1 b/ExecEditTemplate/run.ps1 deleted file mode 100644 index fe8139e089e4..000000000000 --- a/ExecEditTemplate/run.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -try { - $Table = Get-CippTable -tablename 'templates' - $Table.Force = $true - $guid = $request.body.guid - $JSON = $request.body | Select-Object * -ExcludeProperty GUID | ConvertTo-Json - $Type = $request.Query.Type - - if ($Type -eq "IntuneTemplate") { - write-host "Intune Template" - write-host "" - $RawJSON = $request.body | Select-Object * -ExcludeProperty displayName, description, type, GUID | ConvertTo-Json -Depth 10 -Compress - Set-CIPPIntuneTemplate -RawJSON $RawJSON -GUID $GUID -DisplayName $Request.body.displayName -Description $Request.body.description -templateType $Request.body.type - } - else { - Add-CIPPAzDataTableEntity @Table -Entity @{ - JSON = "$JSON" - RowKey = "$GUID" - PartitionKey = "$Type" - GUID = "$GUID" - } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Edited template $($Request.body.name) with GUID $GUID" -Sev "Debug" - } - $body = [pscustomobject]@{ "Results" = "Successfully saved the template" } - -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to edit template: $($_.Exception.Message)" -Sev "Error" - $body = [pscustomobject]@{"Results" = "Editing template failed: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecEmailForward/function.json b/ExecEmailForward/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecEmailForward/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecEmailForward/run.ps1 b/ExecEmailForward/run.ps1 deleted file mode 100644 index d0586980fb7e..000000000000 --- a/ExecEmailForward/run.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -$Username = $request.body.userID -$Tenantfilter = $request.body.tenantfilter -$ForwardingAddress = $request.body.ForwardInternal.value -$ForwardingSMTPAddress = $request.body.ForwardExternal -$DisableForwarding = $request.body.disableForwarding -$APIName = $TriggerMetadata.FunctionName - -if ($ForwardingAddress) { - try { - New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-mailbox" -cmdParams @{Identity = $Username; ForwardingAddress = $ForwardingAddress ; DeliverToMailboxAndForward = [bool]$request.body.keepCopy } -Anchor $username - if (-not $request.body.KeepCopy) { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set Forwarding for $($username) to $($ForwardingAddress) and not keeping a copy" -Sev "Info" -tenant $TenantFilter - $results = "Forwarding all email for $($username) to $($ForwardingAddress) and not keeping a copy" - } - elseif ($request.body.KeepCopy) { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set Forwarding for $($username) to $($ForwardingAddress) and keeping a copy" -Sev "Info" -tenant $TenantFilter - $results = "Forwarding all email for $($username) to $($ForwardingAddress) and keeping a copy" - } - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add forwarding for $($username)" -Sev "Error" -tenant $TenantFilter - $results = "Could not add forwarding for $($username). Error: $($_.Exception.Message)" - - } -} - -elseif ($ForwardingSMTPAddress) { - try { - New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-mailbox" -cmdParams @{Identity = $Username; ForwardingSMTPAddress = $ForwardingSMTPAddress ; DeliverToMailboxAndForward = [bool]$request.body.keepCopy } -Anchor $username - if (-not $request.body.KeepCopy) { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set forwarding for $($username) to $($ForwardingSMTPAddress) and not keeping a copy" -Sev "Info" -tenant $TenantFilter - $results = "Forwarding all email for $($username) to $($ForwardingSMTPAddress) and not keeping a copy" - } - elseif ($request.body.KeepCopy) { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set forwarding for $($username) to $($ForwardingSMTPAddress) and keeping a copy" -Sev "Info" -tenant $TenantFilter - $results = "Forwarding all email for $($username) to $($ForwardingSMTPAddress) and keeping a copy" - } - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add forwarding for $($username)" -Sev "Error" -tenant $TenantFilter - $results = "Could not add forwarding for $($username). Error: $($_.Exception.Message)" - - } - -} - -elseif ($DisableForwarding -eq "True") { - try { - New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-Mailbox" -cmdParams @{Identity = $Username; ForwardingAddress = $null; ForwardingSMTPAddress = $null; DeliverToMailboxAndForward = $false } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Disabled Email forwarding for $($username)" -Sev "Info" -tenant $TenantFilter - $results = "Disabled Email Forwarding for $($username)" -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not disable Email forwarding for $($username)" -Sev "Error" -tenant $TenantFilter - $results = "Could not disable Email forwarding for $($username). Error: $($_.Exception.Message)" - -} -} - -$Body = @{"Results" = @($results) } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) \ No newline at end of file diff --git a/ExecEnableArchive/function.json b/ExecEnableArchive/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecEnableArchive/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecEnableArchive/run.ps1 b/ExecEnableArchive/run.ps1 deleted file mode 100644 index e11202af39cb..000000000000 --- a/ExecEnableArchive/run.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - - -# Interact with query parameters or the body of the request. -Try { - $ResultsArch = Set-CIPPMailboxArchive -userid $Request.query.id -tenantFilter $Request.query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -ArchiveEnabled $true - $Results = [pscustomobject]@{"Results" = "$ResultsArch" } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecExcludeLicenses/function.json b/ExecExcludeLicenses/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecExcludeLicenses/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecExcludeLicenses/run.ps1 b/ExecExcludeLicenses/run.ps1 deleted file mode 100644 index 1ecedd09c706..000000000000 --- a/ExecExcludeLicenses/run.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$Table = Get-CIPPTable -TableName ExcludedLicenses -try { - - if ($Request.Query.List) { - $Rows = Get-CIPPAzDataTableEntity @Table - if ($Rows.Count -lt 1) { - $TableBaseData = '[{"GUID":"16ddbbfc-09ea-4de2-b1d7-312db6112d70","Product_Display_Name":"MICROSOFT TEAMS (FREE)"},{"GUID":"1f2f344a-700d-42c9-9427-5cea1d5d7ba6","Product_Display_Name":"MICROSOFT STREAM"},{"GUID":"338148b6-1b11-4102-afb9-f92b6cdc0f8d","Product_Display_Name":"DYNAMICS 365 P1 TRIAL FOR INFORMATION WORKERS"},{"GUID":"606b54a9-78d8-4298-ad8b-df6ef4481c80","Product_Display_Name":"Power Virtual Agents Viral Trial"},{"GUID":"61e6bd70-fbdb-4deb-82ea-912842f39431","Product_Display_Name":"Dynamics 365 Customer Service Insights Trial"},{"GUID":"6470687e-a428-4b7a-bef2-8a291ad947c9","Product_Display_Name":"WINDOWS STORE FOR BUSINESS"},{"GUID":"710779e8-3d4a-4c88-adb9-386c958d1fdf","Product_Display_Name":"MICROSOFT TEAMS EXPLORATORY"},{"GUID":"74fbf1bb-47c6-4796-9623-77dc7371723b","Product_Display_Name":"Microsoft Teams Trial"},{"GUID":"90d8b3f8-712e-4f7b-aa1e-62e7ae6cbe96","Product_Display_Name":"Business Apps (free)"},{"GUID":"a403ebcc-fae0-4ca2-8c8c-7a907fd6c235","Product_Display_Name":"Power BI (free)"},{"GUID":"bc946dac-7877-4271-b2f7-99d2db13cd2c","Product_Display_Name":"Dynamics 365 Customer Voice Trial"},{"GUID":"dcb1a3ae-b33f-4487-846a-a640262fadf4","Product_Display_Name":"Microsoft Power Apps Plan 2 Trial"},{"GUID":"f30db892-07e9-47e9-837c-80727f46fd3d","Product_Display_Name":"MICROSOFT FLOW FREE"},{"GUID":"fcecd1f9-a91e-488d-a918-a96cdb6ce2b0","Product_Display_Name":"Microsoft Dynamics AX7 User Trial"}]' | ConvertFrom-Json -AsHashtable -Depth 10 - $TableRows = foreach ($Row in $TableBaseData) { - $Row.PartitionKey = 'License' - $Row.RowKey = $Row.GUID - - Add-CIPPAzDataTableEntity @Table -Entity ([pscustomobject]$Row) -Force | Out-Null - } - - $Rows = Get-CIPPAzDataTableEntity @Table - - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded licenses list' -Sev 'Info' - } - $body = @($Rows) - } - - # Interact with query parameters or the body of the request. - $name = $Request.Query.TenantFilter - if ($Request.Query.AddExclusion) { - $AddObject = @{ - PartitionKey = 'License' - RowKey = $Request.body.GUID - 'GUID' = $Request.body.GUID - 'Product_Display_Name' = $request.body.SKUName - } - Add-CIPPAzDataTableEntity @Table -Entity $AddObject -Force - - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Added exclusion $($request.body.SKUName)" -Sev 'Info' - $body = [pscustomobject]@{'Results' = "Success. We've added $($request.body.SKUName) to the excluded list." } - } - - if ($Request.Query.RemoveExclusion) { - $Filter = "RowKey eq '{0}' and PartitionKey eq 'License'" -f $Request.Query.Guid - $Entity = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey - Remove-AzDataTableEntity @Table -Entity $Entity - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Removed exclusion $($Request.Query.GUID)" -Sev 'Info' - $body = [pscustomobject]@{'Results' = "Success. We've removed $($Request.query.guid) from the excluded list." } - } -} -catch { - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Exclusion API failed. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecExcludeTenant/function.json b/ExecExcludeTenant/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecExcludeTenant/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecExcludeTenant/run.ps1 b/ExecExcludeTenant/run.ps1 deleted file mode 100644 index f01c0873bd8d..000000000000 --- a/ExecExcludeTenant/run.ps1 +++ /dev/null @@ -1,66 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$user = $request.headers.'x-ms-client-principal' -$username = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($user)) | ConvertFrom-Json).userDetails -$date = (Get-Date).tostring('yyyy-MM-dd') -$TenantsTable = Get-CippTable -tablename Tenants - -if ($Request.Query.List) { - $ExcludedFilter = "PartitionKey eq 'Tenants' and Excluded eq true" - $ExcludedTenants = Get-CIPPAzDataTableEntity @TenantsTable -Filter $ExcludedFilter - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded tenants list' -Sev 'Info' - $body = @($ExcludedTenants) -} -elseif ($Request.query.ListAll) { - $ExcludedTenants = Get-CIPPAzDataTableEntity @TenantsTable -filter "PartitionKey eq 'Tenants'" - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded tenants list' -Sev 'Info' - $body = @($ExcludedTenants) -} -try { - # Interact with query parameters or the body of the request. - $name = $Request.Query.TenantFilter - if ($Request.Query.AddExclusion) { - $Tenants = Get-Tenants -IncludeAll | Where-Object { $Request.body.value -contains $_.customerId } - - $Excluded = foreach ($Tenant in $Tenants) { - $Tenant.Excluded = $true - $Tenant.ExcludeUser = $username - $Tenant.ExcludeDate = $date - $Tenant - } - Write-Host ($Excluded | ConvertTo-Json) - Update-AzDataTableEntity @TenantsTable -Entity ([pscustomobject]$Excluded) - #Remove-CIPPCache - Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Added exclusion for customer(s): $($Excluded.defaultDomainName -join ',')" -Sev 'Info' - $body = [pscustomobject]@{'Results' = "Success. Added exclusions for customer(s): $($Excluded.defaultDomainName -join ',')" } - } - - if ($Request.Query.RemoveExclusion) { - $Filter = "PartitionKey eq 'Tenants' and defaultDomainName eq '{0}'" -f $name - $Tenant = Get-CIPPAzDataTableEntity @TenantsTable -Filter $Filter - $Tenant.Excluded = $false - $Tenant.ExcludeUser = '' - $Tenant.ExcludeDate = '' - Update-AzDataTableEntity @TenantsTable -Entity $Tenant - #Remove-CIPPCache - Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Removed exclusion for customer $($name)" -Sev 'Info' - $body = [pscustomobject]@{'Results' = "Success. We've removed $name from the excluded tenants." } - } -} -catch { - Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Exclusion API failed. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } -} -if (!$body) { $body = @() } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecExtensionMapping/function.json b/ExecExtensionMapping/function.json deleted file mode 100644 index 73fe27294a9b..000000000000 --- a/ExecExtensionMapping/function.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "NinjaProcess", - "queueName": "NinjaOneQueue" - } - ] -} \ No newline at end of file diff --git a/ExecExtensionSync/function.json b/ExecExtensionSync/function.json deleted file mode 100644 index 6fc0a8c6d4a3..000000000000 --- a/ExecExtensionSync/function.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "billqueue" - }, - { - "type": "queue", - "direction": "out", - "name": "NinjaProcess", - "queueName": "NinjaOneQueue" - } - ] -} diff --git a/ExecExtensionSync/run.ps1 b/ExecExtensionSync/run.ps1 deleted file mode 100644 index 477662fa6cf4..000000000000 --- a/ExecExtensionSync/run.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -if ($Request.Query.Extension -eq 'Gradient') { - try { - Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Starting billing processing." -sev Info - $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 - foreach ($ConfigItem in $Configuration.psobject.properties.name) { - switch ($ConfigItem) { - "Gradient" { - If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { - Push-OutputBinding -Name msg -Value "LetsGo" - $Results = [pscustomobject]@{"Results" = "Succesfully started Gradient Sync" } - } - } - } - } - } catch { - $Results = [pscustomobject]@{"Results" = "Could not start Gradient Sync: $($_.Exception.Message)" } - - Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Could not start billing processing $($_.Exception.Message)" -sev Error - } -} - -if ($Request.Query.Extension -eq 'NinjaOne') { - try { - $Table = Get-CIPPTable -TableName NinjaOneSettings - - $CIPPMapping = Get-CIPPTable -TableName CippMapping - $Filter = "PartitionKey eq 'NinjaOrgsMapping'" - $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } - - foreach ($Tenant in $TenantsToProcess) { - Push-OutputBinding -Name NinjaProcess -Value @{ - 'NinjaAction' = 'SyncTenant' - 'MappedTenant' = $Tenant - } - - } - - $AddObject = @{ - PartitionKey = 'NinjaConfig' - RowKey = 'NinjaLastRunTime' - 'SettingValue' = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffK") - } - - Add-AzDataTableEntity @Table -Entity $AddObject -Force - - Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' - - $Results = [pscustomobject]@{"Results" = "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" } - } catch { - $Results = [pscustomobject]@{"Results" = "Could not start NinjaOne Sync: $($_.Exception.Message)" } - Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Could not start NinjaOne Sync $($_.Exception.Message)" -sev Error - } - -} - - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) -clobber \ No newline at end of file diff --git a/ExecExtensionTest/function.json b/ExecExtensionTest/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecExtensionTest/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecExtensionsConfig/function.json b/ExecExtensionsConfig/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecExtensionsConfig/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecGDAPInvite/function.json b/ExecGDAPInvite/function.json deleted file mode 100644 index 4ee273331c44..000000000000 --- a/ExecGDAPInvite/function.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ExecGDAPInviteApproved/function.json b/ExecGDAPInviteApproved/function.json deleted file mode 100644 index 7f36cd5bb076..000000000000 --- a/ExecGDAPInviteApproved/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "gdapinvitequeue" - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ExecGDAPInviteApproved/run.ps1 b/ExecGDAPInviteApproved/run.ps1 deleted file mode 100644 index 911927e6a6b2..000000000000 --- a/ExecGDAPInviteApproved/run.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -using namespace System.Net - -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -Set-CIPPGDAPInviteGroups - -$body = @{Results = @('Processing recently activated GDAP relationships') } - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecGDAPInviteApproved_Timer/function.json b/ExecGDAPInviteApproved_Timer/function.json deleted file mode 100644 index 6b68992375e9..000000000000 --- a/ExecGDAPInviteApproved_Timer/function.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 0 */3 * * *" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "gdapinvitequeue" - } - ] -} diff --git a/ExecGDAPInviteApproved_Timer/run.ps1 b/ExecGDAPInviteApproved_Timer/run.ps1 deleted file mode 100644 index 08370014869f..000000000000 --- a/ExecGDAPInviteApproved_Timer/run.ps1 +++ /dev/null @@ -1,5 +0,0 @@ -using namespace System.Net - -param($Timer) - -Set-CIPPGDAPInviteGroups diff --git a/ExecGDAPInviteQueue/function.json b/ExecGDAPInviteQueue/function.json deleted file mode 100644 index e51e66299d6f..000000000000 --- a/ExecGDAPInviteQueue/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "gdapinvitequeue" - } - ] -} diff --git a/ExecGDAPInviteQueue/run.ps1 b/ExecGDAPInviteQueue/run.ps1 deleted file mode 100644 index 78e43c118449..000000000000 --- a/ExecGDAPInviteQueue/run.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -# Input bindings are passed in via param block. -param( $QueueItem, $TriggerMetadata) - -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell queue trigger function processed work item: $QueueItem" -#$TenantFilter = $env:TenantID - -$Table = Get-CIPPTable -TableName 'GDAPInvites' -$Invite = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$QueueItem'" -$APINAME = 'GDAPInvites' -$RoleMappings = $Invite.RoleMappings | ConvertFrom-Json -Write-Host ($Invite | ConvertTo-Json -Compress) - -foreach ($role in $RoleMappings) { - try { - $Mappingbody = ConvertTo-Json -Depth 10 -InputObject @{ - 'accessContainer' = @{ - 'accessContainerId' = "$($Role.GroupId)" - 'accessContainerType' = 'securityGroup' - } - 'accessDetails' = @{ - 'unifiedRoles' = @(@{ - 'roleDefinitionId' = "$($Role.roleDefinitionId)" - }) - } - } - New-GraphPostRequest -NoAuthCheck $True -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$($QueueItem)/accessAssignments" -tenantid $env:TenantID -type POST -body $MappingBody -verbose - Start-Sleep -Milliseconds 100 - } catch { - Write-LogMessage -API $APINAME -message "GDAP Group mapping failed - $($role.GroupId): $($_.Exception.Message)" -Sev Error - exit 1 - } - Write-LogMessage -API $APINAME -message "Groups mapped for GDAP Relationship: $($GdapInvite.RowKey)" -Sev Info -} -Remove-AzDataTableEntity @Table -Entity $Invite diff --git a/ExecGDAPMigration/function.json b/ExecGDAPMigration/function.json deleted file mode 100644 index 9ed224fecfec..000000000000 --- a/ExecGDAPMigration/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "gdapqueue" - } - ] -} diff --git a/ExecGDAPMigration/run.ps1 b/ExecGDAPMigration/run.ps1 deleted file mode 100644 index 16a2353c166a..000000000000 --- a/ExecGDAPMigration/run.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -$Groups = $Request.body.gdapRoles -$Tenants = $Request.body.selectedTenants -$Results = [System.Collections.ArrayList]@() - -foreach ($Tenant in $Tenants) { - $obj = [PSCustomObject]@{ - tenant = $Tenant - gdapRoles = $Groups - } - Push-OutputBinding -Name Msg -Value $obj - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Started GDAP Migration for $($tenant.displayName)" -Sev "Debug" - $results.add("Started GDAP Migration for $($tenant.displayName)") | Out-Null -} -$body = @{Results = @($Results) } -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) \ No newline at end of file diff --git a/ExecGeoIPLookup/function.json b/ExecGeoIPLookup/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecGeoIPLookup/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecGetLocalAdminPassword/function.json b/ExecGetLocalAdminPassword/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecGetLocalAdminPassword/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecGetRecoveryKey/function.json b/ExecGetRecoveryKey/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecGetRecoveryKey/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecGetRecoveryKey/run.ps1 b/ExecGetRecoveryKey/run.ps1 deleted file mode 100644 index c8374209d683..000000000000 --- a/ExecGetRecoveryKey/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $GraphRequest = Get-CIPPBitlockerKey -device $Request.query.GUID -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' - $Body = [pscustomobject]@{"Results" = $GraphRequest } - -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $Body = [pscustomobject]@{"Results" = "Failed. $ErrorMessage" } - -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) diff --git a/ExecGraphRequest/function.json b/ExecGraphRequest/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecGraphRequest/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecGroupsDelete/function.json b/ExecGroupsDelete/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecGroupsDelete/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecGroupsDelete/run.ps1 b/ExecGroupsDelete/run.ps1 deleted file mode 100644 index 723d1256e9a6..000000000000 --- a/ExecGroupsDelete/run.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - - -# Interact with query parameters or the body of the request. -Try { - $RemoveResults = Remove-CIPPGroup -ID $Request.query.id -GroupType $Request.query.GroupType -tenantFilter $Request.query.TenantFilter -displayName $Request.query.displayName -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' - $Results = [pscustomobject]@{"Results" = $RemoveResults } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecGroupsDeliveryManagement/function.json b/ExecGroupsDeliveryManagement/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecGroupsDeliveryManagement/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecGroupsDeliveryManagement/run.ps1 b/ExecGroupsDeliveryManagement/run.ps1 deleted file mode 100644 index fdba10a56243..000000000000 --- a/ExecGroupsDeliveryManagement/run.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - - -# Interact with query parameters or the body of the request. -Try { - $SetResults = Set-CIPPGroupAuthentication -ID $Request.query.id -GroupType $Request.query.GroupType -OnlyAllowInternalString $Request.query.OnlyAllowInternal -tenantFilter $Request.query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' - $Results = [pscustomobject]@{"Results" = $SetResults } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Delivery Management failed: $($_.Exception.Message)" -Sev "Error" -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecGroupsHideFromGAL/function.json b/ExecGroupsHideFromGAL/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecGroupsHideFromGAL/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecGroupsHideFromGAL/run.ps1 b/ExecGroupsHideFromGAL/run.ps1 deleted file mode 100644 index de8517b7abd4..000000000000 --- a/ExecGroupsHideFromGAL/run.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -Try { - $GroupStatus = Set-CIPPGroupGAL -Id $Request.query.id -tenantFilter $Request.query.TenantFilter -GroupType $Request.query.groupType -HiddenString $Request.query.HidefromGAL -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' - $Results = [pscustomobject]@{"Results" = $GroupStatus } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Hide/UnHide from GAL failed: $($_.Exception.Message)" -Sev "Error" -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecHideFromGAL/function.json b/ExecHideFromGAL/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecHideFromGAL/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecHideFromGAL/run.ps1 b/ExecHideFromGAL/run.ps1 deleted file mode 100644 index f5dbe73a12de..000000000000 --- a/ExecHideFromGAL/run.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -$TenantFilter = $request.query.tenantfilter -Try { - $Hidden = [System.Convert]::ToBoolean($Request.query.HideFromGal) - $HideResults = Set-CIPPHideFromGAL -tenantFilter $tenantFilter -userid $Request.query.ID -HideFromGAL $Hidden -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser" - $Results = [pscustomobject]@{"Results" = $HideResults } - -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Hide/UnHide from GAL failed: $($_.Exception.Message)" -Sev "Error" -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecIncidentsList/function.json b/ExecIncidentsList/function.json deleted file mode 100644 index c4fbb02afdb9..000000000000 --- a/ExecIncidentsList/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "incidentqueue" - } - ] -} diff --git a/ExecIncidentsList/run.ps1 b/ExecIncidentsList/run.ps1 deleted file mode 100644 index 0a043bba2d03..000000000000 --- a/ExecIncidentsList/run.ps1 +++ /dev/null @@ -1,80 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -try { - # Interact with query parameters or the body of the request. - $TenantFilter = $Request.Query.TenantFilter - $GraphRequest = if ($TenantFilter -ne 'AllTenants') { - $incidents = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/security/incidents' -tenantid $Request.Query.TenantFilter -AsApp $true - - foreach ($incident in $incidents) { - [PSCustomObject]@{ - Tenant = $Request.Query.TenantFilter - Id = $incident.id - Status = $incident.status - IncidentUrl = $incident.incidentWebUrl - RedirectId = $incident.redirectIncidentId - DisplayName = $incident.displayName - Created = $incident.createdDateTime - Updated = $incident.lastUpdateDateTime - AssignedTo = $incident.assignedTo - Classification = $incident.classification - Determination = $incident.determination - Severity = $incident.severity - Tags = ($IncidentObj.tags -join ', ') - Comments = $incident.comments - } - } - } - else { - $Table = Get-CIPPTable -TableName cachealertsandincidents - $Filter = "PartitionKey eq 'Incident'" - $Rows = Get-CIPPAzDataTableEntity @Table -filter $Filter | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) - if (!$Rows) { - Push-OutputBinding -Name Msg -Value (Get-Date).ToString() - [PSCustomObject]@{ - Waiting = $true - } - } - else { - $incidents = $Rows - foreach ($incident in $incidents) { - $IncidentObj = $incident.Incident | ConvertFrom-Json - [PSCustomObject]@{ - Tenant = $incident.Tenant - Id = $IncidentObj.id - Status = $IncidentObj.status - IncidentUrl = $IncidentObj.incidentWebUrl - RedirectId = $IncidentObj.redirectIncidentId - DisplayName = $IncidentObj.displayName - Created = $IncidentObj.createdDateTime - Updated = $IncidentObj.lastUpdateDateTime - AssignedTo = $IncidentObj.assignedTo - Classification = $IncidentObj.classification - Determination = $IncidentObj.determination - Severity = $IncidentObj.severity - Tags = ($IncidentObj.tags -join ', ') - Comments = @($IncidentObj.comments) - } - } - } - } -} -catch { - $StatusCode = [HttpStatusCode]::Forbidden - $body = $_.Exception.message -} -if (!$body) { - $StatusCode = [HttpStatusCode]::OK - $body = [PSCustomObject]@{ - MSResults = ($GraphRequest | Where-Object -Property id -NE $null) - } -} -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $Body - }) \ No newline at end of file diff --git a/ExecListAppId/function.json b/ExecListAppId/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecListAppId/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecListAppId/run.ps1 b/ExecListAppId/run.ps1 deleted file mode 100644 index d9683adb458a..000000000000 --- a/ExecListAppId/run.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$ResponseURL = "$(($Request.headers.'x-ms-original-url').replace('/api/ExecListAppId','/api/ExecSAMSetup'))" - -$Results = @{ - applicationId = $ENV:ApplicationID - tenantId = $ENV:TenantID - refreshUrl = "https://login.microsoftonline.com/$ENV:TenantID/oauth2/v2.0/authorize?client_id=$ENV:ApplicationID&response_type=code&redirect_uri=$ResponseURL&response_mode=query&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access+profile+openid&state=1&prompt=select_account" -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) \ No newline at end of file diff --git a/ExecMailboxMobileDevices/function.json b/ExecMailboxMobileDevices/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecMailboxMobileDevices/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecMailboxMobileDevices/run.ps1 b/ExecMailboxMobileDevices/run.ps1 deleted file mode 100644 index 5d14c16a87c1..000000000000 --- a/ExecMailboxMobileDevices/run.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - - -# Interact with query parameters or the body of the request. -Try { - $MobileResults = Set-CIPPMobileDevice -UserId $request.query.Userid -DeviceId $request.query.deviceid -Quarantine $request.query.Quarantine -tenantFilter $request.query.tenantfilter -APIName $APINAME -Delete $Request.query.Delete -ExecutingUser $request.headers.'x-ms-client-principal' - $Results = [pscustomobject]@{"Results" = $MobileResults } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed $($request.query.Userid): $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecMailboxRestore/function.json b/ExecMailboxRestore/function.json deleted file mode 100644 index bf6c3ef0c49a..000000000000 --- a/ExecMailboxRestore/function.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1", - "entryPoint": "Receive-CippHttpTrigger", - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ExecMaintenanceScripts/Scripts/Add-CippUser.ps1 b/ExecMaintenanceScripts/Scripts/Add-CippUser.ps1 deleted file mode 100644 index d04ad2358c9d..000000000000 --- a/ExecMaintenanceScripts/Scripts/Add-CippUser.ps1 +++ /dev/null @@ -1,67 +0,0 @@ -#requires -Version 7.2 - -[CmdletBinding(DefaultParameterSetName = 'interactive')] -Param( - [Parameter(Mandatory = $true, ParameterSetName = 'noninteractive')] - [ValidateSet('readonly', 'editor', 'admin')] - $Role, - [Parameter(Mandatory = $true, ParameterSetName = 'noninteractive')] - $SelectedUsers, - [Parameter(ParameterSetName = 'noninteractive')] - [Parameter(ParameterSetName = 'interactive')] - $ExpirationHours = 1 -) - -$ResourceGroup = '##RESOURCEGROUP##' -$Subscription = '##SUBSCRIPTION##' - -if (!(Get-Module -ListAvailable Microsoft.PowerShell.ConsoleGuiTools)) { - Install-Module Microsoft.PowerShell.ConsoleGuiTools -Force -} - -$Context = Get-AzContext -if (!$Context) { - Write-Host "`n- Connecting to Azure" - $Context = Connect-AzAccount -Subscription $Subscription -} -Write-Host "Connected to $($Context.Account)" - -$swa = Get-AzStaticWebApp -ResourceGroupName $ResourceGroup -$Domain = $swa.CustomDomain | Select-Object -First 1 -if ($Domain -eq $null) { $Domain = $swa.DefaultHostname } -Write-Host "CIPP SWA - $($swa.name)" - -if (!$Role) { - $Role = @('readonly', 'editor', 'admin') | Out-ConsoleGridView -OutputMode Single -Title 'Select CIPP Role' -} - -$CurrentUsers = Get-AzStaticWebAppUser -Name $swa.name -ResourceGroupName $ResourceGroup -AuthProvider all | Select-Object DisplayName, Role - -$AllUsers = Get-AzADUser -Filter "userType eq 'Member' and accountEnabled eq true" | Select-Object DisplayName, UserPrincipalName - - -$SelectedUsers = $AllUsers | Where-Object { $CurrentUsers.DisplayName -notcontains $_.UserPrincipalName } | Sort-Object -Property DisplayName | Out-ConsoleGridView -Title "Select users for role '$Role'" -Write-Host "Selected users: $($SelectedUsers.UserPrincipalName -join ', ')" - -Write-Host 'Generating invite links...' -$InviteList = foreach ($User in $SelectedUsers) { - $UserInvite = @{ - InputObject = $swa - Domain = $Domain - Provider = 'aad' - UserDetail = $User.UserPrincipalName - Role = $Role - NumHoursToExpiration = $ExpirationHours - } - $Invite = New-AzStaticWebAppUserRoleInvitationLink @UserInvite - - [PSCustomObject]@{ - User = $User.UserPrincipalName - Role = $Role - Link = $Invite.InvitationUrl - Expires = $Invite.ExpiresOn - } -} -$InviteList -$InviteList | Export-Csv -Path '.\cipp-invites.csv' -Append -Write-Host 'Invitations exported to .\cipp-invites.csv' diff --git a/ExecMaintenanceScripts/Scripts/Enable-FunctionAppGitHubActions.ps1 b/ExecMaintenanceScripts/Scripts/Enable-FunctionAppGitHubActions.ps1 deleted file mode 100644 index a47273afad9e..000000000000 --- a/ExecMaintenanceScripts/Scripts/Enable-FunctionAppGitHubActions.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -$ResourceGroup = '##RESOURCEGROUP##' -$Subscription = '##SUBSCRIPTION##' -$FunctionName = '##FUNCTIONAPP##' - -$Logo = @' - _____ _____ _____ _____ - / ____|_ _| __ \| __ \ - | | | | | |__) | |__) | - | | | | | ___/| ___/ - | |____ _| |_| | | | - \_____|_____|_| |_| - -'@ -Write-Host $Logo - -Write-Host '- Connecting to Azure' -Connect-AzAccount -Identity -Subscription $Subscription | Out-Null - -Write-Host 'Checking deployment settings' -$DeploymentSettings = & az functionapp deployment source show --resource-group $ResourceGroup --name $FunctionName | ConvertFrom-Json - -if (!($DeploymentSettings.isGitHubAction)) { - Write-Host 'Creating GitHub action, follow the prompts to log into GitHub' - $GitHubRepo = ([uri]$DeploymentSettings.repoUrl).LocalPath.TrimStart('/') - az functionapp deployment github-actions add --repo $GitHubRepo --branch $DeploymentSettings.branch --resource-group $ResourceGroup --name $FunctionName --login-with-github -} - -$DeploymentSettings = & az functionapp deployment source show --resource-group $ResourceGroup --name $FunctionName | ConvertFrom-Json -if ($DeploymentSettings.isGitHubAction) { - $cipp = Get-AzFunctionApp -ResourceGroupName $ResourceGroup - $cipp.ApplicationSettings['WEBSITE_RUN_FROM_PACKAGE'] = 1 - $cipp | Update-AzFunctionAppSetting -AppSetting $cipp.ApplicationSettings - - Write-Host "GitHub action created and project set to run from package, navigate to $($DeploymentSettings.repoUrl)/actions and run the 'Build and deploy Powershell project to Azure Function App'" -} -else { - Write-Host 'GitHub action not set up for deployment, try running the script again.' -} diff --git a/ExecMaintenanceScripts/Scripts/Grant-CippConditionalAccess.ps1 b/ExecMaintenanceScripts/Scripts/Grant-CippConditionalAccess.ps1 deleted file mode 100644 index b4460c2994d4..000000000000 --- a/ExecMaintenanceScripts/Scripts/Grant-CippConditionalAccess.ps1 +++ /dev/null @@ -1,122 +0,0 @@ -if (!(Get-Module -ListAvailable Microsoft.Graph)) { - Install-Module Microsoft.Graph -Confirm:$false -Force -AllowPrerelease -} - -$ResourceGroup = '##RESOURCEGROUP##' -$Subscription = '##SUBSCRIPTION##' -$FunctionName = '##FUNCTIONAPP##' -$TokenIP = '##TOKENIP##' - -$Logo = @' - _____ _____ _____ _____ - / ____|_ _| __ \| __ \ - | | | | | |__) | |__) | - | | | | | ___/| ___/ - | |____ _| |_| | | | - \_____|_____|_| |_| - -'@ -Write-Host $Logo - -Write-Host '=== Conditional Access Management ===' -if (Test-Path -Path '.\cipp-function-namedLocation.json') { - $UseCache = Read-Host -Prompt 'Used cached Named Location for CIPP? (Y/n)' - if ($UseCache -ne 'n') { - $ipNamedLocation = Get-Content -Path '.\cipp-function-namedLocation.json' | ConvertFrom-Json -AsHashtable - } -} - -if (!($ipNamedLocation)) { - Write-Host "`n- Connecting to Azure" - Connect-AzAccount -Identity -Subscription $Subscription | Out-Null - $Function = Get-AzFunctionApp -ResourceGroupName $ResourceGroup -Name $FunctionName - - Write-Host 'Getting Function App IP addresses' - # Get possible IPs from function app - $PossibleIpAddresses = (($Function | Select-Object -ExpandProperty PossibleOutboundIpAddress) + ',' + $TokenIP) -split ',' - - # Convert possible IP addresses to ipv4CidrRange list - $ipRanges = foreach ($Ip in $PossibleIpAddresses) { - $Cidr = '{0}/32' -f $Ip - @{ - '@odata.type' = '#microsoft.graph.iPv4CidrRange' - 'cidrAddress' = $Cidr - } - } - - # Return ipNamedLocation object - $ipNamedLocation = @{ - '@odata.type' = '#microsoft.graph.ipNamedLocation' - displayName = ('CyberDrain Improved Partner Portal - {0}' -f $FunctionName) - isTrusted = $true - ipRanges = $ipRanges - } - - $ipNamedLocation | ConvertTo-Json -Depth 10 | Out-File -Path '.\cipp-function-namedLocation.json' - Write-Host 'Named location policy created and saved to .\cipp-function-namedLocation.json' -} - -Write-Host "`n- Connecting to Customer Graph API, ensure you log in from a system that is allowed through the Conditional Access policy" -Select-MgProfile -Name 'beta' -$GraphOptions = @{ - Scopes = @('Policy.Read.All', 'Policy.ReadWrite.ConditionalAccess', 'Application.Read.All') - UseDeviceAuthentication = $true -} - -do { - Connect-MgGraph @GraphOptions - $Context = Get-MgContext - if ($Context) { - Write-Host "Connected as $($Context.Account) ($($Context.TenantId))" - $Switch = Read-Host -Prompt 'Switch Accounts? (y/N)' - if ($Switch -eq 'y') { - Disconnect-MgGraph | Out-Null - } - } -} -while (!(Get-MgContext)) - -Write-Host "`n- Getting existing policies" -$Policies = Get-MgIdentityConditionalAccessPolicy -Write-Host($Policies.displayName -join "`n") - -Write-Host "`n- Named Location Check" -$NamedLocations = Get-MgIdentityConditionalAccessNamedLocation -if ($NamedLocations.displayName -notcontains $ipNamedLocation.displayName) { - Write-Host "Creating Named Location: '$($ipNamedLocation.displayName)'" - $NamedLocation = New-MgIdentityConditionalAccessNamedLocation -BodyParameter $ipNamedLocation -} -else { - $NamedLocation = $NamedLocations | Where-Object { $_.displayName -eq $ipNamedLocation.displayName } - Write-Host "Named Location exists: '$($NamedLocation.displayName)'" - Update-MgIdentityConditionalAccessNamedLocation -NamedLocationId $NamedLocation.Id -BodyParameter $ipNamedLocation -} - -Write-Host "`n- Conditional access policy check" -$ConfigPolicy = Read-Host -Prompt 'Exclude CIPP from existing CA policies? (Y/n)' -if ($ConfigPolicy -ne 'n') { - foreach ($Policy in $Policies) { - Write-Host "- Policy: $($Policy.displayName)" - $Conditions = $Policy.Conditions - $ExcludeLocations = $Conditions.Locations.ExcludeLocations - $IncludeLocations = $Conditions.Locations.IncludeLocations - if ($ExcludeLocations -eq 'AllTrusted' -or $ExcludeLocations -contains $NamedLocation.Id) { - Write-Host 'Named location already excluded' - } - elseif ($IncludeLocations -eq 'AllTrusted' -or $IncludeLocations -contains $NamedLocation.Id) { - Write-Host 'Named location is already included' - } - else { - Write-Host 'Adding exclusion for named location' - $Locations = [system.collections.generic.list[string]]::new() - foreach ($Location in $ExcludeLocations) { - $Locations.Add($Location) | Out-Null - } - $Locations.Add($NamedLocation.Id) | Out-Null - $Conditions.Locations.ExcludeLocations = [string[]]$Locations - if (!($Conditions.Locations.IncludeLocations)) { $Conditions.Locations.IncludeLocations = 'All' } - Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $Policy.Id -Conditions $Conditions - } - } - Write-Host "`nDone." -} diff --git a/ExecMaintenanceScripts/function.json b/ExecMaintenanceScripts/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecMaintenanceScripts/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecMaintenanceScripts/run.ps1 b/ExecMaintenanceScripts/run.ps1 deleted file mode 100644 index e83bd46c96b1..000000000000 --- a/ExecMaintenanceScripts/run.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -try { - $GraphToken = Get-GraphToken -returnRefresh $true - $AccessTokenDetails = Read-JwtAccessDetails -Token $GraphToken.access_token - - $ReplacementStrings = @{ - '##TENANTID##' = $env:TenantID - '##RESOURCEGROUP##' = $env:WEBSITE_RESOURCE_GROUP - '##FUNCTIONAPP##' = $env:WEBSITE_SITE_NAME - '##SUBSCRIPTION##' = (($env:WEBSITE_OWNER_NAME).split('+') | Select-Object -First 1) - '##TOKENIP##' = $AccessTokenDetails.IPAddress - } -} -catch { Write-Host $_.Exception.Message } -#$ReplacementStrings | Format-Table - -try { - $ScriptFile = $Request.Query.ScriptFile - - try { - $Filename = Split-Path -Leaf $ScriptFile - } - catch {} - - if (!$ScriptFile -or [string]::IsNullOrEmpty($ScriptFile)) { - $ScriptFiles = Get-ChildItem .\ExecMaintenanceScripts\Scripts | Select-Object -ExpandProperty PSChildName - - $ScriptOptions = foreach ($ScriptFile in $ScriptFiles) { - @{label = $ScriptFile; value = $ScriptFile } - } - $Body = @{ ScriptFiles = @($ScriptOptions) } - } - elseif (!(Get-ChildItem .\ExecMaintenanceScripts\Scripts\$Filename -ErrorAction SilentlyContinue)) { - $Body = @{ Status = 'Script does not exist' } - } - else { - $Script = Get-Content -Raw .\ExecMaintenanceScripts\Scripts\$Filename - foreach ($i in $ReplacementStrings.Keys) { - $Script = $Script -replace $i, $ReplacementStrings.$i - } - - $ScriptContent = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Script)) - - if ($Request.Query.MakeLink) { - $Table = Get-CippTable -TableName 'MaintenanceScripts' - $LinkGuid = ([guid]::NewGuid()).ToString() - - $MaintenanceScriptRow = @{ - 'RowKey' = $LinkGuid - 'PartitionKey' = 'Maintenance' - 'ScriptContent' = $ScriptContent - } - Add-CIPPAzDataTableEntity @Table -Entity $MaintenanceScriptRow -Force - - $Body = @{ Link = "/api/PublicScripts?guid=$LinkGuid" } - } - else { - $Body = @{ ScriptContent = $ScriptContent } - } - } -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to retrieve maintenance scripts. Error: $($_.Exception.Message)" -Sev 'Error' - $Body = @{Status = "Failed to retrieve maintenance scripts $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) diff --git a/ExecNotificationConfig/function.json b/ExecNotificationConfig/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecNotificationConfig/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecNotificationConfig/run.ps1 b/ExecNotificationConfig/run.ps1 deleted file mode 100644 index 1d5238a72c59..000000000000 --- a/ExecNotificationConfig/run.ps1 +++ /dev/null @@ -1,47 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$sev = ([pscustomobject]$Request.body.Severity).value -join (',') -$results = try { - $Table = Get-CIPPTable -TableName SchedulerConfig - $SchedulerConfig = @{ - 'tenant' = 'Any' - 'tenantid' = 'TenantId' - 'type' = 'CIPPNotifications' - 'schedule' = 'Every 15 minutes' - 'Severity' = [string]$sev - 'email' = "$($Request.Body.Email)" - 'webhook' = "$($Request.Body.Webhook)" - 'onePerTenant' = [boolean]$Request.Body.onePerTenant - 'sendtoIntegration' = [boolean]$Request.Body.sendtoIntegration - 'includeTenantId' = [boolean]$Request.Body.includeTenantId - 'PartitionKey' = 'CippNotifications' - 'RowKey' = 'CippNotifications' - } - foreach ($logvalue in [pscustomobject]$Request.body.logsToInclude) { - $SchedulerConfig[([pscustomobject]$logvalue.value)] = $true - } - - Add-CIPPAzDataTableEntity @Table -Entity $SchedulerConfig -Force | Out-Null - 'Successfully set the configuration' -} -catch { - "Failed to set configuration: $($_.Exception.message)" -} - - -$body = [pscustomobject]@{'Results' = $Results } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecOffboardTenant/function.json b/ExecOffboardTenant/function.json deleted file mode 100644 index 4ee273331c44..000000000000 --- a/ExecOffboardTenant/function.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ExecOffboardTenant/run.ps1 b/ExecOffboardTenant/run.ps1 deleted file mode 100644 index a8be1ff1d1b5..000000000000 --- a/ExecOffboardTenant/run.ps1 +++ /dev/null @@ -1,108 +0,0 @@ -using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -try { - $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - $Tenantfilter = $request.body.tenantfilter - - $results = [System.Collections.ArrayList]@() - $errors = [System.Collections.ArrayList]@() - - if ($request.body.RemoveCSPGuestUsers) { - # Delete guest users who's domains match the CSP tenants - try { - try { - $domains = (New-GraphGETRequest -Uri "https://graph.microsoft.com/v1.0/domains?`$select=id" -tenantid $env:TenantID -NoAuthCheck:$true).id - $CSPGuestUsers = (New-GraphGETRequest -Uri "https://graph.microsoft.com/v1.0/users?`$select=id,mail&`$filter=userType eq 'Guest' and $(($domains | ForEach-Object { "endswith(mail, '$_')" }) -join " or ")&`$count=true" -tenantid $Tenantfilter -ComplexFilter) - } catch { - $errors.Add("Failed to retrieve guest users: $($_.Exception.message)") - } - - if ($CSPGuestUsers) { - [System.Collections.Generic.List[PSCustomObject]]$BulkRequests = @($CSPGuestUsers | ForEach-Object { - @{ - id = $($_.id) - method = "DELETE" - url = "/users/$($_.id)" - } - }) - - $BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantFilter - - $results.Add("Succesfully removed guest users") - } else { - $results.Add("No guest users found to remove") - } - } catch { - $errors.Add("Something went wrong while deleting guest users: $($_.Exception.message)") - } - } - - # All customer tenant specific actions ALWAYS have to be completed before this action! - if ($request.body.RemoveMultitenantApps) { - # Remove multi-tenant apps with the CSP tenant as origin - try { - $multitenantCSPApps = (New-GraphGETRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals?`$count=true&`$select=displayName,appId,id,appOwnerOrganizationId&`$filter=appOwnerOrganizationId eq $($env:TenantID)" -tenantid $Tenantfilter -ComplexFilter) - $sortedArray = $multitenantCSPApps | Sort-Object @{Expression = { if ($_.appId -eq $env:applicationid) { 1 } else { 0 } }; Ascending = $true } - $sortedArray | ForEach-Object { - try { - $delete = (New-GraphPostRequest -type "DELETE" -Uri "https://graph.microsoft.com/v1.0/serviceprincipals/$($_.id)" -tenantid $Tenantfilter) - $results.Add("Succesfully removed app $($_.displayName)") - } catch { - #$results.Add("Failed to removed app $($_.displayName)") - $errors.Add("Failed to removed app $($_.displayName)") - } - } - } catch { - #$results.Add("Failed to retrieve multitenant apps, no apps have been removed: $($_.Exception.message)") - $errors.Add("Failed to retrieve multitenant apps, no apps have been removed: $($_.Exception.message)") - } - } - - if ($request.body.TerminateGDAP) { - # Terminate GDAP relationships - try { - $delegatedAdminRelationships = (New-GraphGETRequest -Uri "https://graph.microsoft.com/v1.0/tenantRelationships/delegatedAdminRelationships?`$filter=(status eq 'active') AND (customer/tenantId eq '$TenantFilter')" -tenantid $env:TenantID) - $delegatedAdminRelationships | ForEach-Object { - try { - $terminate = (New-GraphPostRequest -type "POST" -Uri "https://graph.microsoft.com/v1.0/tenantRelationships/delegatedAdminRelationships/$($_.id)/requests" -body '{"action":"terminate"}' -ContentType "application/json" -tenantid $env:TenantID) - $results.Add("Succesfully terminated GDAP relationship $($_.displayName) from tenant $TenantFilter") - } catch { - #$results.Add("Failed to terminate GDAP relationship $($_.displayName): $($_.Exception.message)") - $errors.Add("Failed to terminate GDAP relationship $($_.displayName): $($_.Exception.message)") - } - } - } catch { - #$results.Add("Failed to retrieve GDAP relationships, no relationships have been terminated: $($_.Exception.message)") - $errors.Add("Failed to retrieve GDAP relationships, no relationships have been terminated: $($_.Exception.message)") - } - } - - if ($request.body.TerminateContract) { - # Terminate contract relationship - try { - $terminate = (New-GraphPostRequest -type "PATCH" -body '{ "relationshipToPartner": "none" }' -Uri "https://api.partnercenter.microsoft.com/v1/customers/$TenantFilter" -ContentType "application/json" -scope "https://api.partnercenter.microsoft.com/user_impersonation" -tenantid $env:TenantID) - $results.Add("Succesfully terminated contract relationship") - } - catch { - #$results.Add("Failed to terminate contract relationship: $($_.Exception.message)") - $errors.Add("Failed to terminate contract relationship: $($_.Exception.message)") - } - } - - $StatusCode = [HttpStatusCode]::OK - $body = [pscustomobject]@{ - "Results" = @($results) - "Errors" = @($errors) - } -} -catch { - $StatusCode = [HttpStatusCode]::OK - $body = $_.Exception.message -} -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $Body - }) \ No newline at end of file diff --git a/ExecOffboardUser/function.json b/ExecOffboardUser/function.json deleted file mode 100644 index f7988840965c..000000000000 --- a/ExecOffboardUser/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "offboardingmailbox" - } - ] -} diff --git a/ExecOffboardUser/run.ps1 b/ExecOffboardUser/run.ps1 deleted file mode 100644 index 53afa5424071..000000000000 --- a/ExecOffboardUser/run.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -try { - $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - $Username = $request.body.user - $Tenantfilter = $request.body.tenantfilter - $Results = if ($Request.body.Scheduled.enabled) { - $taskObject = [PSCustomObject]@{ - TenantFilter = $Tenantfilter - Name = "Offboarding: $Username" - Command = @{ - value = "Invoke-CIPPOffboardingJob" - } - Parameters = @{ - Username = $Username - APIName = "Scheduled Offboarding" - options = $request.body - } - ScheduledTime = $Request.body.scheduled.date - PostExecution = @{ - Webhook = [bool]$Request.Body.PostExecution.webhook - Email = [bool]$Request.Body.PostExecution.email - PSA = [bool]$Request.Body.PostExecution.psa - } - } - - Add-CIPPScheduledTask -Task $taskObject -hidden $false - } - else { - Invoke-CIPPOffboardingJob -Username $Username -TenantFilter $Tenantfilter -Options $Request.body -APIName $APIName -ExecutingUser $request.headers.'x-ms-client-principal' - } - $StatusCode = [HttpStatusCode]::OK - $body = [pscustomobject]@{"Results" = @($results) } -} -catch { - $StatusCode = [HttpStatusCode]::Forbidden - $body = $_.Exception.message -} -$Request.Body.PostExecution -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $Body - }) diff --git a/ExecOneDriveShortCut/function.json b/ExecOneDriveShortCut/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecOneDriveShortCut/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecOneDriveShortCut/run.ps1 b/ExecOneDriveShortCut/run.ps1 deleted file mode 100644 index ddb4566d600a..000000000000 --- a/ExecOneDriveShortCut/run.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -Try { - $MessageResult = New-CIPPOneDriveShortCut -username $Request.body.username -userid $Request.body.userid -TenantFilter $Request.Body.TenantFilter -URL $Request.body.input -ExecutingUser $request.headers.'x-ms-client-principal' - $Results = [pscustomobject]@{ "Results" = "$MessageResult" } -} -catch { - $Results = [pscustomobject]@{"Results" = "Onedrive Shortcut creation failed: $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecPasswordConfig/function.json b/ExecPasswordConfig/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecPasswordConfig/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecPasswordConfig/run.ps1 b/ExecPasswordConfig/run.ps1 deleted file mode 100644 index 104b9eaf1f7f..000000000000 --- a/ExecPasswordConfig/run.ps1 +++ /dev/null @@ -1,41 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -$Table = Get-CIPPTable -TableName Settings -$PasswordType = (Get-CIPPAzDataTableEntity @Table) - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -$results = try { - if ($Request.Query.List) { - @{ passwordType = $PasswordType.passwordType } - } - else { - $SchedulerConfig = @{ - 'passwordType' = "$($Request.Body.passwordType)" - 'passwordCount' = "12" - 'PartitionKey' = 'settings' - 'RowKey' = 'settings' - } - - Add-CIPPAzDataTableEntity @Table -Entity $SchedulerConfig -Force | Out-Null - 'Successfully set the configuration' - } -} -catch { - "Failed to set configuration: $($_.Exception.message)" -} - - -$body = [pscustomobject]@{'Results' = $Results } - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecQuarantineManagement/function.json b/ExecQuarantineManagement/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecQuarantineManagement/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecQuarantineManagement/run.ps1 b/ExecQuarantineManagement/run.ps1 deleted file mode 100644 index aa1bdb83a53d..000000000000 --- a/ExecQuarantineManagement/run.ps1 +++ /dev/null @@ -1,36 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - - -# Interact with query parameters or the body of the request. -Try { - $tenantfilter = $Request.Query.TenantFilter - $params = @{ - Identity = $request.query.ID; - AllowSender = [boolean]$Request.query.AllowSender - ReleasetoAll = [boolean]$Request.query.type - ActionType = $Request.query.type - } - Write-Host $params - New-ExoRequest -tenantid $TenantFilter -cmdlet "Release-QuarantineMessage" -cmdParams $Params - $Results = [pscustomobject]@{"Results" = "Successfully processed $($request.query.ID)" } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "$($request.query.id)" -Sev "Info" -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Quarantine Management failed: $($_.Exception.Message)" -Sev "Error" - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecResetMFA/function.json b/ExecResetMFA/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecResetMFA/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecResetPass/function.json b/ExecResetPass/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecResetPass/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecResetPass/run.ps1 b/ExecResetPass/run.ps1 deleted file mode 100644 index aef245f5248d..000000000000 --- a/ExecResetPass/run.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." -Write-Host "$($Request.query.ID)" -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$mustChange = [System.Convert]::ToBoolean($request.query.MustChange) - -try { - $Reset = Set-CIPPResetPassword -userid $Request.query.ID -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -forceChangePasswordNextSignIn $mustChange - $Results = [pscustomobject]@{"Results" = "$Reset" } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed to reset password for $($Request.query.displayName): $($_.Exception.Message)" } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to reset password for $($Request.query.displayName): $($_.Exception.Message)" -Sev "Error" - -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) \ No newline at end of file diff --git a/ExecRestoreBackup/function.json b/ExecRestoreBackup/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecRestoreBackup/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecRestoreBackup/run.ps1 b/ExecRestoreBackup/run.ps1 deleted file mode 100644 index 1b0a39db9893..000000000000 --- a/ExecRestoreBackup/run.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -try { - foreach ($line in ($Request.body | ConvertFrom-Json | Select-Object * -ExcludeProperty ETag)) { - Write-Host ($line) - $Table = Get-CippTable -tablename $line.table - $ht2 = @{} - $line.psobject.properties | ForEach-Object { $ht2[$_.Name] = [string]$_.Value } - $Table.Entity = $ht2 - Add-CIPPAzDataTableEntity @Table -Force - - } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created backup" -Sev "Debug" - - $body = [pscustomobject]@{ - "Results" = "Succesfully restored backup."; - } -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create backup: $($_.Exception.Message)" -Sev "Error" - $body = [pscustomobject]@{"Results" = "Backup Creation failed: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecRestoreDeleted/function.json b/ExecRestoreDeleted/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecRestoreDeleted/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecRestoreDeleted/run.ps1 b/ExecRestoreDeleted/run.ps1 deleted file mode 100644 index 3ebea4012d78..000000000000 --- a/ExecRestoreDeleted/run.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter - -try { - $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/directory/deletedItems/$($Request.query.ID)/restore" -tenantid $TenantFilter -type POST -body '{}' -verbose - $Results = [pscustomobject]@{"Results" = "Successfully completed request." } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) \ No newline at end of file diff --git a/ExecRevokeSessions/function.json b/ExecRevokeSessions/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecRevokeSessions/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecRevokeSessions/run.ps1 b/ExecRevokeSessions/run.ps1 deleted file mode 100644 index f3185f6de2fd..000000000000 --- a/ExecRevokeSessions/run.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $RevokeSessions = Revoke-CIPPSessions -userid $Request.Query.id -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' - $Results = [pscustomobject]@{"Results" = $RevokeSessions } -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) \ No newline at end of file diff --git a/ExecRunBackup/function.json b/ExecRunBackup/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecRunBackup/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecRunBackup/run.ps1 b/ExecRunBackup/run.ps1 deleted file mode 100644 index 32e6c1025d77..000000000000 --- a/ExecRunBackup/run.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -try { - if ($request.query.Selected) { - $BackupTables = $request.query.Selected -split ',' - } - else { - $BackupTables = @( - "bpa" - "Config" - "Domains" - "ExcludedLicenses" - "templates" - "standards" - "SchedulerConfig" - ) - } - $CSVfile = foreach ($CSVTable in $BackupTables) { - $Table = Get-CippTable -tablename $CSVTable - Get-CIPPAzDataTableEntity @Table | Select-Object *, @{l = 'table'; e = { $CSVTable } } - } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created backup" -Sev "Debug" - - $body = [pscustomobject]@{ - "Results" = "Created backup"; - backup = $CSVfile - } -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create backup: $($_.Exception.Message)" -Sev "Error" - $body = [pscustomobject]@{"Results" = "Backup Creation failed: $($_.Exception.Message)" } -} - - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecSAMSetup/function.json b/ExecSAMSetup/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecSAMSetup/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 deleted file mode 100644 index 898f4cd7c44d..000000000000 --- a/ExecSAMSetup/run.ps1 +++ /dev/null @@ -1,199 +0,0 @@ -using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$UserCreds = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($request.headers.'x-ms-client-principal')) | ConvertFrom-Json) - -if ($Request.query.error) { - Add-Type -AssemblyName System.Web - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - ContentType = 'text/html' - StatusCode = [HttpStatusCode]::Forbidden - Body = Get-normalizedError -Message [System.Web.HttpUtility]::UrlDecode($Request.Query.error_description) - }) - exit -} -if ("admin" -notin $UserCreds.userRoles) { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - ContentType = 'text/html' - StatusCode = [HttpStatusCode]::Forbidden - Body = 'Could not find an admin cookie in your browser. Make sure you do not have an adblocker active, use a Chromium browser, and allow cookies. If our automatic refresh does not work, try pressing the URL bar and hitting enter. We will try to refresh ourselves in 3 seconds.' - }) - exit -} - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -if ($env:MSI_SECRET) { - Disable-AzContextAutosave -Scope Process | Out-Null - $AzSession = Connect-AzAccount -Identity -} -if (!$ENV:SetFromProfile) { - Write-Host "We're reloading from KV" - Get-CIPPAuthentication -} - -$KV = $ENV:WEBSITE_DEPLOYMENT_ID -$Table = Get-CIPPTable -TableName SAMWizard -$Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) - -try { - if ($Request.query.count -lt 1 ) { $Results = "No authentication code found. Please go back to the wizard." } - - if ($request.body.setkeys) { - if ($request.body.tenantid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $request.body.tenantid -AsPlainText -Force) } - if ($request.body.RefreshToken) { Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $request.body.RefreshToken -AsPlainText -Force) } - if ($request.body.applicationid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $request.body.applicationid -AsPlainText -Force) } - if ($request.body.applicationsecret) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $request.body.applicationsecret -AsPlainText -Force) } - $Results = @{ Results = "The keys have been replaced. Please perform a permissions check." } - } - if ($Request.query.error -eq 'invalid_client') { $Results = "Client ID was not found in Azure. Try waiting 10 seconds to try again, if you have gotten this error after 5 minutes, please restart the process." } - if ($request.query.code) { - try { - $TenantId = $Rows.tenantid - if (!$TenantId) { $TenantId = $ENV:TenantId } - $AppID = $Rows.appid - if (!$AppID) { $appid = $env:ApplicationId } - $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 - $clientsecret = Get-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -AsPlainText - if (!$clientsecret) { $clientsecret = $ENV:ApplicationSecret } - $RefreshToken = Invoke-RestMethod -Method POST -Body "client_id=$appid&scope=https://graph.microsoft.com/.default+offline_access+openid+profile&code=$($request.query.code)&grant_type=authorization_code&redirect_uri=$($url)&client_secret=$clientsecret" -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" - Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $RefreshToken.refresh_token -AsPlainText -Force) - $Results = "Authentication is now complete. You may now close this window." - try { - $SetupPhase = $rows.validated = $true - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - } - catch { - #no need. - } - } - catch { - $Results = "Authentication failed. $($_.Exception.message)" - } - } - if ($request.query.CreateSAM) { - $Rows = @{ - RowKey = "setup" - PartitionKey = "setup" - validated = $false - SamSetup = "NotStarted" - partnersetup = $false - appid = "NotStarted" - tenantid = "NotStarted" - } - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) - - if ($Request.query.partnersetup) { - $SetupPhase = $Rows.partnersetup = $true - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - } - $step = 1 - $DeviceLogon = New-DeviceLogin -clientid "1b730954-1685-4b74-9bfd-dac224a7b894" -Scope 'https://graph.microsoft.com/.default' -FirstLogon - $SetupPhase = $rows.SamSetup = [string]($DeviceLogon | ConvertTo-Json) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - $Results = @{ message = "Your code is $($DeviceLogon.user_code). Enter the code" ; step = $step; url = $DeviceLogon.verification_uri } - } - if ($Request.query.CheckSetupProcess -and $request.query.step -eq 1) { - $SAMSetup = $Rows.SamSetup | ConvertFrom-Json -ErrorAction SilentlyContinue - $Token = (New-DeviceLogin -clientid "1b730954-1685-4b74-9bfd-dac224a7b894" -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) - if ($token.Access_Token) { - $step = 2 - $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 - $PartnerSetup = $Rows.partnersetup - $TenantId = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/organization" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method GET -ContentType 'application/json').value.id - $SetupPhase = $rows.tenantid = [string]($TenantId) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - if ($PartnerSetup) { - $app = Get-Content '.\Cache_SAMSetup\SAMManifest.json' | ConvertFrom-Json - $App.web.redirectUris = @($App.web.redirectUris + $URL) - $app = $app | ConvertTo-Json -Depth 15 - $AppId = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') - $rows.appid = [string]($AppId.appId) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - $attempt = 0 - do { - try { - try { - $SPNDefender = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"fc780465-2017-40d4-a0c5-307022471b92`" }" -ContentType 'application/json') - } - catch { - Write-Host "didn't deploy spn for defender, probably already there." - } - try { - $SPNTeams = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"48ac35b8-9aa8-4d74-927d-1f4a14a0b239`" }" -ContentType 'application/json') - } - catch { - Write-Host "didn't deploy spn for Teams, probably already there." - } - $SPN = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/servicePrincipals" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"$($AppId.appId)`" }" -ContentType 'application/json') - Start-Sleep 3 - $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method Get -ContentType 'application/json').value.id - Write-Host "Id is $GroupID" - $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN.id)`"}" -ContentType 'application/json') - Write-Host "Added to adminagents" - $attempt ++ - } - catch { - $attempt ++ - } - } until ($attempt -gt 5) - } - else { - $app = Get-Content '.\Cache_SAMSetup\SAMManifestNoPartner.json' - $AppId = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') - $rows.appid = [string]($AppId.appId) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - } - $AppPassword = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications/$($AppID.id)/addPassword" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body '{"passwordCredential":{"displayName":"CIPPInstall"}}' -ContentType 'application/json').secretText - Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $TenantId -AsPlainText -Force) - Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appid -AsPlainText -Force) - Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $AppPassword -AsPlainText -Force) - $Results = @{"message" = "Created application. Waiting 30 seconds for Azure propagation"; step = $step } - } - else { - $step = 1 - $Results = @{ message = "Your code is $($SAMSetup.user_code). Enter the code " ; step = $step; url = $SAMSetup.verification_uri } - } - - } - switch ($request.query.step) { - 2 { - $step = 2 - $TenantId = $Rows.tenantid - $AppID = $rows.appid - $PartnerSetup = $Rows.partnersetup - $SetupPhase = $rows.SamSetup = [string]($FirstLogonRefreshtoken | ConvertTo-Json) - Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null - $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 - $Validated = $Rows.validated - if ($Validated) { $step = 3 } - $Results = @{ message = "Give the next approval by clicking " ; step = $step; url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)" } - } - 3 { - - $step = 4 - $Results = @{"message" = "Received token."; step = $step } - - - } - 4 { - Remove-AzDataTableEntity @Table -Entity $Rows - - $step = 5 - $Results = @{"message" = "Installation completed."; step = $step - } - } - } - -} -catch { - $Results = [pscustomobject]@{"Results" = "Failed. $($_.InvocationInfo.ScriptLineNumber): $($_.Exception.message)" ; step = $step } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) diff --git a/ExecScheduledCommand/function.json b/ExecScheduledCommand/function.json deleted file mode 100644 index e4c27b23b985..000000000000 --- a/ExecScheduledCommand/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "scheduledcommandprocessor" - } - ] -} diff --git a/ExecSendOrgMessage/function.json b/ExecSendOrgMessage/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecSendOrgMessage/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecSendOrgMessage/run.ps1 b/ExecSendOrgMessage/run.ps1 deleted file mode 100644 index e42b4c988521..000000000000 --- a/ExecSendOrgMessage/run.ps1 +++ /dev/null @@ -1,116 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -$Device = $request.query.ID -try { - - $type = switch ($request.Query.type) { - 'taskbar' { - '844ec9d0-dd31-459c-a1e7-21fb1b39d5da' - $placementDetails = @(@{ - placement = 'default' - variants = @(@{ - variantId = "b3fce1ee-9658-4267-b174-23d4a1be148f" - localizedTexts = @(@{ - locale = "invariant" - text = @{ - "clickUrl" = $Request.query.URL - } - }) - }) - }) - } - 'notification' { - '1ff7c7e7-128c-4e18-a926-bdac4e906ea1' - $placementDetails = @(@{ - placement = 'default' - variants = @(@{ - variantId = "7a1419c9-9263-4202-9225-35b326b92792" - localizedTexts = @(@{ - locale = "invariant" - text = @{ - "clickUrl" = $Request.query.URL - } - }) - }) - }) - } - 'getStarted' { - $placementDetails = @(@{ - placement = 'card0' - variants = @(@{ - variantId = "ed0d0fa2-df72-42f4-9866-66cf3de1fafb" - localizedTexts = @(@{ - locale = "invariant" - text = @{ - "message" = "My Message Value" - "clickUrl" = "https://example.com/clickUrl/" - "title" = "This message" - "buttonText" = "PlzClick" - } - }) - }) - } - @{ - placement = 'card1' - variants = @(@{ - variantId = "ed0d0fa2-df72-42f4-9866-66cf3de1fafb" - localizedTexts = @(@{ - locale = "invariant" - text = @{ - "message" = "My Message Value" - "clickUrl" = "https://example.com/clickUrl/" - "title" = "This message" - "buttonText" = "PlzClick" - } - }) - }) - }) - } - - } - $freq = $request.query.freq - $object = [pscustomobject]@{ - startDateTime = (Get-Date).ToString("O") - endDateTime = (Get-Date).AddYears('1').ToString("O") - frequency = $freq - targeting = @{ - targetingType = 'aadGroup' - includeIds = @($Device) - } - content = @{ - "guidedContentId" = "$Type" - placementDetails = $placementDetails - logoInfo = @{ - contentType = "png" - logoCdnUrl = 'https://hulpnu.nl/tools/Red.jpg' - } - } - } - $tmpbody = ConvertTo-Json -Depth 15 -Compress -InputObject $object - Write-Host $tmpbody - - $GraphRequest = New-GraphPOSTRequest -noauthcheck $true -type "POST" -uri "https://graph.microsoft.com/beta/deviceManagement/organizationalMessageDetails" -tenantid $tenantfilter -body $tmpbody - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ExecSendPush/function.json b/ExecSendPush/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecSendPush/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecSendPush/run.ps1 b/ExecSendPush/run.ps1 deleted file mode 100644 index e5939f959b65..000000000000 --- a/ExecSendPush/run.ps1 +++ /dev/null @@ -1,118 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -$TenantFilter = $Request.Query.TenantFilter -$UserEmail = $Request.Query.UserEmail -$MFAAppID = '981f26a1-7f43-403b-a875-f8b09b8cd720' - -# Function to keep trying to get the access token while we wait for MS to actually set the temp password -function get-clientaccess { - param( - $uri, - $body, - $count = 1 - ) - try { - $ClientToken = Invoke-RestMethod -Method post -Uri $uri -Body $body -ea stop - } - catch { - if ($count -lt 20) { - - $count++ - Start-Sleep 1 - $ClientToken = get-clientaccess -uri $uri -body $body -count $count - } - else { - Throw "Could not get Client Token: $_" - } - } - return $ClientToken -} - - -# Get all service principals -$SPResult = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$top=999&`$select=id,appId" -tenantid $TenantFilter - -# Check if we have one for the MFA App -$SPID = ($SPResult | Where-Object { $_.appId -eq $MFAAppID }).id - -# Create a serivce principal if needed -if (!$SPID) { - - $SPBody = [pscustomobject]@{ - appId = $MFAAppID - } - $SPID = (New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/servicePrincipals" -tenantid $TenantFilter -type POST -body $SPBody -verbose).id -} - - -$PassReqBody = @{ - "passwordCredential" = @{ - "displayName" = "MFA Temporary Password" - "endDateTime" = $(((Get-Date).addminutes(5))) - "startDateTime" = $((Get-Date).addminutes(-5)) - } -} | ConvertTo-Json -Depth 5 - -$TempPass = (New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SPID/addPassword" -tenantid $TenantFilter -type POST -body $PassReqBody -verbose).secretText - -# Give it a chance to apply -#Start-Sleep 5 - -# Generate the XML for the push request -$XML = @" - -1.0 -$UserEmail -en-usOverrideVoiceOtpfalse69ff05bf-eb61-47f7-a70e-e7d77b6d47d0 -truetrueradiusUNKNOWN: -"@ - -# Request to get client token -$body = @{ - 'resource' = 'https://adnotifications.windowsazure.com/StrongAuthenticationService.svc/Connector' - 'client_id' = $MFAAppID - 'client_secret' = $TempPass - 'grant_type' = "client_credentials" - 'scope' = "openid" -} - -# Attempt to get a token using the temp password -$ClientUri = "https://login.microsoftonline.com/$TenantFilter/oauth2/token" -try { - $ClientToken = get-clientaccess -Uri $ClientUri -Body $body -} -catch { - $Body = "Failed to create temporary password" -} - -# If we got a token send a push -if ($ClientToken) { - - $ClientHeaders = @{ "Authorization" = "Bearer $($ClientToken.access_token)" } - - $obj = Invoke-RestMethod -Uri 'https://adnotifications.windowsazure.com/StrongAuthenticationService.svc/Connector//BeginTwoWayAuthentication' -Method POST -Headers $ClientHeaders -Body $XML -ContentType 'application/xml' - - if ($obj.BeginTwoWayAuthenticationResponse.result) { - $Body = "Received an MFA confirmation: $($obj.BeginTwoWayAuthenticationResponse.result.value | Out-String)" - } - if ($obj.BeginTwoWayAuthenticationResponse.AuthenticationResult -ne $true) { - $Body = "Authentication Failed! Does the user have Push/Phone call MFA configured? Errorcode: $($obj.BeginTwoWayAuthenticationResponse.result.value | Out-String)" - $colour = 'danger' - } - -} - -$Results = [pscustomobject]@{"Results" = $Body; colour = $colour } -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Sent push request to $UserEmail - Result: $($obj.BeginTwoWayAuthenticationResponse.result.value | Out-String)" -Sev "Info" - -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) - diff --git a/ExecSetMailboxQuota/function.json b/ExecSetMailboxQuota/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecSetMailboxQuota/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecSetOoO/function.json b/ExecSetOoO/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecSetOoO/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecSetSecurityAlert/function.json b/ExecSetSecurityAlert/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecSetSecurityAlert/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecSetSecurityAlert/run.ps1 b/ExecSetSecurityAlert/run.ps1 deleted file mode 100644 index 25d9ee4e7c66..000000000000 --- a/ExecSetSecurityAlert/run.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -# Interact with query parameters or the body of the request. -$tenantfilter = $Request.Query.TenantFilter -$AlertFilter = $Request.Query.GUID -$Status = $Request.Query.Status -$AssignBody = '{"status":"' + $Status + '","vendorInformation":{"provider":"' + $Request.query.provider + '","vendor":"' + $Request.query.vendor + '"}}' -try { - $GraphRequest = New-Graphpostrequest -uri "https://graph.microsoft.com/beta/security/alerts/$AlertFilter" -type PATCH -tenantid $TenantFilter -body $Assignbody - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Set alert $AlertFilter to status $Status" -Sev "Info" - $body = [pscustomobject]@{"Results" = "Set status for alert to $Status" } - -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to update alert $($AlertFilter): $($_.Exception.Message)" -Sev "Error" - $body = [pscustomobject]@{"Results" = "Failed to change status: $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecSetSecurityIncident/function.json b/ExecSetSecurityIncident/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecSetSecurityIncident/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecSetSecurityIncident/run.ps1 b/ExecSetSecurityIncident/run.ps1 deleted file mode 100644 index d12d10cc8df9..000000000000 --- a/ExecSetSecurityIncident/run.ps1 +++ /dev/null @@ -1,79 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - -$first='' -# Interact with query parameters or the body of the request. -$tenantfilter = $Request.Query.TenantFilter -$IncidentFilter = $Request.Query.GUID -$Status = $Request.Query.Status -$Assigned = $Request.Query.Assigned -$Classification = $Request.Query.Classification -$Determination = $Request.Query.Determination -$Redirected = $Request.Query.Redirected -as [int] -$BodyBuild -$AssignBody = '{' - -try { - # We won't update redirected incidents because the incident it is redirected to should instead be updated - if($Redirected -lt 1) - { - # Set received status - if($null -ne $Status) - { - $AssignBody += $first + '"status":"' + $Status + '"' - $BodyBuild += $first + "Set status for incident to " + $Status - $first = ', ' - } - - # Set received classification and determination - if($null -ne $Classification) - { - if($null -eq $Determination){ - # Maybe some poindexter tries to send a classification without a determination - throw - } - - $AssignBody += $first + '"classification":"' + $Classification + '", "determination":"' + $Determination + '"' - $BodyBuild += $first + "Set classification & determination for incident to " + $Classification + ' ' + $Determination - $first = ', ' - } - - # Set received asignee - if($null -ne $Assigned) - { - $AssignBody += $first + '"assignedTo":"' + $Assigned + '"' - if($null -eq $Status) - { - $BodyBuild += $first + "Set assigned for incident to " + $Assigned - } - $first = ', ' - } - - $AssignBody += '}' - - $ResponseBody = [pscustomobject]@{"Results" = $BodyBuild } - New-Graphpostrequest -uri "https://graph.microsoft.com/beta/security/incidents/$IncidentFilter" -type PATCH -tenantid $TenantFilter -body $Assignbody -asApp $true - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Update incident $IncidentFilter with values $Assignbody" -Sev "Info" - } - else { - $ResponseBody = [pscustomobject]@{"Results" = "Cannot update redirected incident" } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Refuse to pdate incident $IncidentFilter with values $Assignbody because it is redirected to another incident" -Sev "Info" - } - - $body = $ResponseBody -} -catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to update alert $($AlertFilter): $($_.Exception.Message)" -Sev "Error" - $body = [pscustomobject]@{"Results" = "Failed to update incident: $($_.Exception.Message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) diff --git a/ExecUniversalSearch/function.json b/ExecUniversalSearch/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/ExecUniversalSearch/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/ExecUniversalSearch/run.ps1 b/ExecUniversalSearch/run.ps1 deleted file mode 100644 index 2d7801a7b630..000000000000 --- a/ExecUniversalSearch/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" - - -# Write to the Azure Functions log stream. -Write-Host "PowerShell HTTP trigger function processed a request." - -# Interact with query parameters or the body of the request. - -try { - $tenantfilter = Get-Tenants - $payload = '{ "returnsPartialResults":true, "displayName":"getUsers", "target": { "allTenants":true }, "operationDefinition": { "values":["@sys.normalize([ConsistencyLevel: eventual GET /v1.0/users?$top=5&$search=\"userPrincipalName:' + $request.query.name + '\" OR \"displayName:' + $request.query.name + '\"])"] }, "aggregationDefinition": { "values":["@sys.append([/result],50)"] } }' - $GraphRequest = (New-GraphPOSTRequest -noauthcheck $true -type "POST" -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedTenantOperations" -tenantid $env:TenantID -body $payload).result.Results | ConvertFrom-Json | Where-Object { $_.'_TenantId' -in $tenantfilter.customerId } - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = "Could not connect to Azure Lighthouse API: $($ErrorMessage)" -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) diff --git a/ExecUserSettings/function.json b/ExecUserSettings/function.json deleted file mode 100644 index bf6c3ef0c49a..000000000000 --- a/ExecUserSettings/function.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1", - "entryPoint": "Receive-CippHttpTrigger", - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddGDAPRole.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddGDAPRole.ps1 new file mode 100644 index 000000000000..61701c1d51a9 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddGDAPRole.ps1 @@ -0,0 +1,66 @@ +using namespace System.Net + +Function Invoke-ExecAddGDAPRole { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Groups = $Request.body.gdapRoles + $CustomSuffix = $Request.body.customSuffix + $Table = Get-CIPPTable -TableName 'GDAPRoles' + + $Results = [System.Collections.Generic.List[string]]::new() + $ExistingGroups = New-GraphGetRequest -NoAuthCheck $True -uri 'https://graph.microsoft.com/beta/groups' -tenantid $env:TenantID + + $RoleMappings = foreach ($group in $Groups) { + if ($CustomSuffix) { + $GroupName = "M365 GDAP $($Group.Name) - $CustomSuffix" + $MailNickname = "M365GDAP$(($Group.Name).replace(' ',''))$($CustomSuffix)" + } else { + $GroupName = "M365 GDAP $($Group.Name)" + $MailNickname = "M365GDAP$(($Group.Name).replace(' ',''))" + } + try { + if ($GroupName -in $ExistingGroups.displayName) { + @{ + PartitionKey = 'Roles' + RowKey = ($ExistingGroups | Where-Object -Property displayName -EQ $GroupName).id + RoleName = $Group.Name + GroupName = $GroupName + GroupId = ($ExistingGroups | Where-Object -Property displayName -EQ $GroupName).id + roleDefinitionId = $group.ObjectId + } + $Results.Add("M365 GDAP $($Group.Name) already exists") + } else { + $BodyToship = [pscustomobject] @{'displayName' = $GroupName; 'description' = "This group is used to manage M365 partner tenants at the $($group.name) level."; securityEnabled = $true; mailEnabled = $false; mailNickname = $MailNickname } | ConvertTo-Json + $GraphRequest = New-GraphPostRequest -NoAuthCheck $True -uri 'https://graph.microsoft.com/beta/groups' -tenantid $env:TenantID -type POST -body $BodyToship -verbose + @{ + PartitionKey = 'Roles' + RowKey = $GraphRequest.Id + RoleName = $Group.Name + GroupName = $GroupName + GroupId = $GraphRequest.Id + roleDefinitionId = $group.ObjectId + } + $Results.Add("$GroupName added successfully") + } + } catch { + $Results.Add("Could not create GDAP group $($GroupName): $($_.Exception.Message)") + } + } + + Add-CIPPAzDataTableEntity @Table -Entity $RoleMappings -Force + + $body = @{Results = @($Results) } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddSPN.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddSPN.ps1 new file mode 100644 index 000000000000..de779bfe083d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAddSPN.ps1 @@ -0,0 +1,29 @@ +using namespace System.Net + +Function Invoke-ExecAddSPN { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $Body = if ($Request.Query.Enable) { '{"accountEnabled":"true"}' } else { '{"accountEnabled":"false"}' } + try { + $GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/v1.0/servicePrincipals' -tenantid $ENV:TenantID -type POST -Body "{ `"appId`": `"2832473f-ec63-45fb-976f-5d45a7d4bb91`" }" -NoAuthCheck $true + $Results = [pscustomobject]@{'Results' = "Successfully completed request. Add your GDAP migration permissions to your SAM application here: https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/CallAnAPI/appId/$($ENV:ApplicationID)/isMSAApp/ " } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed to add SPN. Please manually execute 'New-AzureADServicePrincipal -AppId 2832473f-ec63-45fb-976f-5d45a7d4bb91' The error was $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsList.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsList.ps1 new file mode 100644 index 000000000000..1b6141e1fcda --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsList.ps1 @@ -0,0 +1,108 @@ +using namespace System.Net + +Function Invoke-ExecAlertsList { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + function New-FlatArray ([Array]$arr) { + $arr | ForEach-Object { + if ($_ -is 'Array') { + New-FlatArray $_ + } else { $_ } + } + } + try { + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $GraphRequest = if ($TenantFilter -ne 'AllTenants') { + $Alerts = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/security/alerts' -tenantid $TenantFilter + $AlertsObj = foreach ($Alert In $alerts) { + @{ + Tenant = $TenantFilter + GUID = $GUID + Id = $alert.Id + Title = $alert.Title + Category = $alert.category + EventDateTime = $alert.eventDateTime + Severity = $alert.Severity + Status = $alert.Status + RawResult = $($Alerts | Where-Object { $_.Id -eq $alert.Id }) + InvolvedUsers = $($Alerts | Where-Object { $_.Id -eq $alert.Id }).userStates + } + } + + $DisplayableAlerts = New-FlatArray $AlertsObj | Where-Object { $_.Id -ne $null } | Sort-Object -Property EventDateTime -Descending + + [PSCustomObject]@{ + NewAlertsCount = $DisplayableAlerts | Where-Object { $_.Status -eq 'newAlert' } | Measure-Object | Select-Object -ExpandProperty Count + InProgressAlertsCount = $DisplayableAlerts | Where-Object { $_.Status -eq 'inProgress' } | Measure-Object | Select-Object -ExpandProperty Count + SeverityHighAlertsCount = ($DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'high' } | Measure-Object | Select-Object -ExpandProperty Count) + SeverityMediumAlertsCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'medium' } | Measure-Object | Select-Object -ExpandProperty Count + SeverityLowAlertsCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'low' } | Measure-Object | Select-Object -ExpandProperty Count + SeverityInformationalCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'informational' } | Measure-Object | Select-Object -ExpandProperty Count + MSResults = $DisplayableAlerts + } + } else { + $Table = Get-CIPPTable -TableName cachealertsandincidents + $Filter = "PartitionKey eq 'alert'" + $Rows = Get-CIPPAzDataTableEntity @Table -filter $Filter | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) + if (!$Rows) { + Push-OutputBinding -Name alertqueue -Value (Get-Date).ToString() + [PSCustomObject]@{ + Waiting = $true + } + } else { + $Alerts = $Rows + $AlertsObj = foreach ($Alert in $alerts) { + $AlertInfo = $Alert.Alert | ConvertFrom-Json + @{ + Tenant = $Alert.Tenant + GUID = $GUID + Id = $AlertInfo.Id + Title = $AlertInfo.Title + Category = $AlertInfo.category + EventDateTime = $AlertInfo.eventDateTime + Severity = $AlertInfo.Severity + Status = $AlertInfo.Status + RawResult = $AlertInfo + InvolvedUsers = $AlertInfo.userStates + } + } + $DisplayableAlerts = New-FlatArray $AlertsObj | Where-Object { $_.Id -ne $null } | Sort-Object -Property EventDateTime -Descending + [PSCustomObject]@{ + NewAlertsCount = $DisplayableAlerts | Where-Object { $_.Status -eq 'newAlert' } | Measure-Object | Select-Object -ExpandProperty Count + InProgressAlertsCount = $DisplayableAlerts | Where-Object { $_.Status -eq 'inProgress' } | Measure-Object | Select-Object -ExpandProperty Count + SeverityHighAlertsCount = ($DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'high' } | Measure-Object | Select-Object -ExpandProperty Count) + SeverityMediumAlertsCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'medium' } | Measure-Object | Select-Object -ExpandProperty Count + SeverityLowAlertsCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'low' } | Measure-Object | Select-Object -ExpandProperty Count + SeverityInformationalCount = $DisplayableAlerts | Where-Object { ($_.Status -eq 'inProgress') -or ($_.Status -eq 'newAlert') } | Where-Object { $_.Severity -eq 'informational' } | Measure-Object | Select-Object -ExpandProperty Count + MSResults = $DisplayableAlerts + } + } + } + + } catch { + $StatusCode = [HttpStatusCode]::Forbidden + $body = $_.Exception.message + } + if (!$body) { + $StatusCode = [HttpStatusCode]::OK + $body = $GraphRequest + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 new file mode 100644 index 000000000000..9393d5efff81 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 @@ -0,0 +1,59 @@ +using namespace System.Net + +Function Invoke-ExecAlertsListAllTenants { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + Get-Tenants | ForEach-Object -Parallel { + $domainName = $_.defaultDomainName + Import-Module '.\GraphHelper.psm1' + $Table = Get-CIPPTable -TableName 'cachealertsandincidents' + + try { + $Alerts = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/security/alerts' -tenantid $domainName + foreach ($Alert in $Alerts) { + $GUID = (New-Guid).Guid + $alertJson = $Alert | ConvertTo-Json + $GraphRequest = @{ + Alert = [string]$alertJson + RowKey = [string]$GUID + Tenant = $domainName + PartitionKey = 'alert' + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + + } + + } catch { + $GUID = (New-Guid).Guid + $AlertText = ConvertTo-Json -InputObject @{ + Title = "Could not connect to tenant to retrieve data: $($_.Exception.Message)" + Id = '' + Category = '' + EventDateTime = '' + Severity = '' + Status = '' + userStates = @('None') + vendorInformation = @{ + vendor = 'CIPP' + provider = 'CIPP' + } + } + $GraphRequest = @{ + Alert = [string]$AlertText + RowKey = [string]$GUID + PartitionKey = 'alert' + Tenant = $domainName + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + + + } + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAppApproval.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAppApproval.ps1 new file mode 100644 index 000000000000..561d060eb99b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAppApproval.ps1 @@ -0,0 +1,33 @@ +using namespace System.Net + +Function Invoke-ExecAppApproval { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + Write-Host "$($Request.query.ID)" + # Interact with query parameters or the body of the request. + + $applicationid = if ($request.query.applicationid) { $request.query.applicationid } else { $env:ApplicationID } + $Results = get-tenants | ForEach-Object { + [PSCustomObject]@{ + defaultDomainName = $_.defaultDomainName + link = "https://login.microsoftonline.com/$($_.customerId)/v2.0/adminconsent?client_id=$applicationid&scope=$applicationid/.default" + } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignApp.ps1 new file mode 100644 index 000000000000..4825fc8a9f81 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAssignApp.ps1 @@ -0,0 +1,59 @@ +using namespace System.Net + +Function Invoke-ExecAssignApp { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $tenantfilter = $Request.Query.TenantFilter + $appFilter = $Request.Query.ID + $AssignTo = $Request.Query.AssignTo + $AssignBody = switch ($AssignTo) { + + 'AllUsers' { + @' +{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allLicensedUsersAssignmentTarget"},"intent":"Required","settings":null}]} +'@ + } + + 'AllDevices' { + @' +{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"},"intent":"Required","settings":null}]} +'@ + } + + 'Both' { + @' +{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allLicensedUsersAssignmentTarget"},"intent":"Required","settings":null},{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"},"intent":"Required","settings":null}]} +'@ + } + + } + $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 -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Assigned $($appFilter) to $assignTo" -Sev 'Info' + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to assign app $($appFilter): $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to assign. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAutoExtendGDAP.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAutoExtendGDAP.ps1 new file mode 100644 index 000000000000..d0f1b5385e0c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAutoExtendGDAP.ps1 @@ -0,0 +1,23 @@ +using namespace System.Net + +Function Invoke-ExecAutoExtendGDAP { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $Results = Set-CIPPGDAPAutoExtend -RelationShipid $Request.query.ID + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ Results = $Results } + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECCheck.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECCheck.ps1 new file mode 100644 index 000000000000..7413636f7061 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECCheck.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-ExecBECCheck { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $body = if ($request.query.GUID) { + $Table = Get-CippTable -tablename 'cachebec' + $Filter = "PartitionKey eq 'bec' and RowKey eq '$($request.query.GUID)'" + $JSONOutput = Get-CIPPAzDataTableEntity @Table -Filter $Filter + if (!$JSONOutput) { + @{ Waiting = $true } + } else { + $JSONOutput.Results + } + } else { + $OrchRequest = [PSCustomObject]@{ + TenantFilter = $request.query.tenantfilter + UserID = $request.query.userid + userName = $request.query.userName + } + $InstanceId = Start-NewOrchestration -FunctionName 'Durable_BECRun' -InputObject $OrchRequest + @{ GUID = $request.query.userid } + } + + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECRemediate.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECRemediate.ps1 new file mode 100644 index 000000000000..b863aeac8f06 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBECRemediate.ps1 @@ -0,0 +1,51 @@ +using namespace System.Net + +Function Invoke-ExecBECRemediate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + Write-Host 'PowerShell HTTP trigger function processed a request.' + + $TenantFilter = $request.body.tenantfilter + $SuspectUser = $request.body.userid + $username = $request.body.username + Write-Host $TenantFilter + Write-Host $SuspectUser + $Results = try { + Set-CIPPResetPassword -userid $username -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + Set-CIPPSignInState -userid $username -AccountEnabled $false -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + Revoke-CIPPSessions -userid $SuspectUser -username $request.body.username -ExecutingUser $request.headers.'x-ms-client-principal' -APIName $APINAME -tenantFilter $TenantFilter + $RuleDisabled = 0 + New-ExoRequest -anchor $username -tenantid $TenantFilter -cmdlet 'get-inboxrule' -cmdParams @{Mailbox = $username } | ForEach-Object { + $null = New-ExoRequest -anchor $username -tenantid $TenantFilter -cmdlet 'Disable-InboxRule' -cmdParams @{Confirm = $false; Identity = $_.Identity } + "Disabled Inbox Rule $($_.Identity) for $username" + $RuleDisabled ++ + } + if ($RuleDisabled) { + "Disabled $RuleDisabled Inbox Rules for $username" + } else { + "No Inbox Rules found for $username. We have not disabled any rules." + } + + Write-LogMessage -API 'BECRemediate' -tenant $tenantfilter -message "Executed Remediation for $SuspectUser" -sev 'Info' + + } catch { + #Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to assign app $($appFilter): $($_.Exception.Message)" -Sev "Error" + $results = [pscustomobject]@{'Results' = "Failed to execute remediation. $($_.Exception.Message)" } + Write-LogMessage -API 'BECRemediate' -tenant $tenantfilter -message "Executed Remediation for $SuspectUser failed" -sev 'Error' + } + $results = [pscustomobject]@{'Results' = @($Results) } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBackendURLs.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBackendURLs.ps1 new file mode 100644 index 000000000000..4a29002ad122 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBackendURLs.ps1 @@ -0,0 +1,42 @@ +using namespace System.Net + +Function Invoke-ExecBackendURLs { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Subscription = ($ENV:WEBSITE_OWNER_NAME).split('+') | Select-Object -First 1 + $SWAName = $ENV:Website_SITE_NAME -replace 'cipp', 'CIPP-SWA-' + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + $results = [PSCustomObject]@{ + ResourceGroup = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/overview" + KeyVault = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.KeyVault/vaults/$($ENV:WEBSITE_SITE_NAME)/secrets" + FunctionApp = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/sites/$($ENV:WEBSITE_SITE_NAME)/appServices" + FunctionConfig = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/sites/$($ENV:WEBSITE_SITE_NAME)/configuration" + FunctionDeployment = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/sites/$($ENV:WEBSITE_SITE_NAME)/vstscd" + SWADomains = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/staticSites/$SWAName/customDomains" + SWARoles = "https://portal.azure.com/#@Go/resource/subscriptions/$Subscription/resourceGroups/$ENV:Website_Resource_Group/providers/Microsoft.Web/staticSites/$SWAName/roleManagement" + Subscription = $Subscription + RGName = $ENV:Website_Resource_Group + FunctionName = $ENV:WEBSITE_SITE_NAME + SWAName = $SWAName + } + + + $body = @{Results = $Results } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [httpstatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCPVPermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCPVPermissions.ps1 new file mode 100644 index 000000000000..7573e9fd209c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCPVPermissions.ps1 @@ -0,0 +1,51 @@ +using namespace System.Net + +Function Invoke-ExecCPVPermissions { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $TenantFilter = (get-tenants -IncludeAll -IncludeErrors | Where-Object -Property customerId -EQ $Request.query.Tenantfilter).defaultDomainName + Write-Host "Our Tenantfilter is $TenantFilter" + + $CPVConsentParams = @{ + Tenantfilter = $TenantFilter + } + if ($Request.Query.ResetSP -eq 'true') { + $CPVConsentParams.ResetSP = $true + } + + $GraphRequest = try { + Set-CIPPCPVConsent @CPVConsentParams + Add-CIPPApplicationPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $TenantFilter + Add-CIPPDelegatedPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $TenantFilter + $Success = $true + } catch { + "Failed to update permissions for $($TenantFilter): $($_.Exception.Message)" + $Success = $false + } + + $Tenant = Get-Tenants -IncludeAll -IncludeErrors | Where-Object -Property defaultDomainName -EQ $Tenantfilter + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ + Results = $GraphRequest + Metadata = @{ + Heading = 'CPV Permission - {0} ({1})' -f $Tenant.displayName, $Tenant.defaultDomainName + Success = $Success + } + } + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecClrImmId.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecClrImmId.ps1 new file mode 100644 index 000000000000..3506522f2c8c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecClrImmId.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-ExecClrImmId { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + Try { + $TenantFilter = $Request.Query.TenantFilter + $UserID = $Request.Query.ID + $Body = [pscustomobject] @{ + onPremisesImmutableId = $null + } | ConvertTo-Json + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$UserID" -tenantid $TenantFilter -type PATCH -body $Body + $Results = [pscustomobject]@{'Results' = 'Successfully Cleared ImmutableId' } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $_.Exception.Message"; colour = 'danger' } + $_.Exception + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecConverttoSharedMailbox.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecConverttoSharedMailbox.ps1 new file mode 100644 index 000000000000..cb16432e8937 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecConverttoSharedMailbox.ps1 @@ -0,0 +1,33 @@ +using namespace System.Net + +Function Invoke-ExecConverttoSharedMailbox { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + # Interact with query parameters or the body of the request. + Try { + $MailboxType = if ($request.query.ConvertToUser -eq 'true') { 'Regular' } else { 'Shared' } + $ConvertedMailbox = Set-CIPPMailboxType -userid $Request.query.id -tenantFilter $Request.query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -MailboxType $MailboxType + $Results = [pscustomobject]@{'Results' = "$ConvertedMailbox" } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed to convert $($request.query.id) - $($_.Exception.Message)" } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCopyForSent.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCopyForSent.ps1 new file mode 100644 index 000000000000..a039be57dfba --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCopyForSent.ps1 @@ -0,0 +1,34 @@ +using namespace System.Net + +Function Invoke-ExecCopyForSent { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + # Interact with query parameters or the body of the request. + Try { + $MessageCopyForSentAsEnabled = if ($request.query.MessageCopyForSentAsEnabled -eq 'false') { 'false' } else { 'true' } + $MessageResult = Set-CIPPMessageCopy -userid $Request.query.id -tenantFilter $Request.query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -MessageCopyForSentAsEnabled $MessageCopyForSentAsEnabled + $Results = [pscustomobject]@{'Results' = "$MessageResult" } + } catch { + $Results = [pscustomobject]@{'Results' = "set MessageCopyForSentAsEnabled to $MessageCopyForSentAsEnabled failed - $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCreateTAP.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCreateTAP.ps1 new file mode 100644 index 000000000000..9919b0564510 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecCreateTAP.ps1 @@ -0,0 +1,28 @@ +using namespace System.Net + +Function Invoke-ExecCreateTAP { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + try { + $TAP = New-CIPPTAP -userid $Request.query.ID -TenantFilter $Request.query.tenantfilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + $Results = [pscustomobject]@{'Results' = "$TAP" } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRelationship.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRelationship.ps1 new file mode 100644 index 000000000000..b51a86b0098d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRelationship.ps1 @@ -0,0 +1,31 @@ +using namespace System.Net + +Function Invoke-ExecDeleteGDAPRelationship { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $GDAPID = $request.query.GDAPId + try { + $DELETE = New-GraphPostRequest -NoAuthCheck $True -uri "https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web/v1/delegatedAdminRelationships/$($GDAPID)/requests" -type POST -body '{"action":"terminate"}' -tenantid $env:TenantID -scope 'https://api.partnercustomeradministration.microsoft.com/.default' + $Results = [pscustomobject]@{'Results' = "Success. GDAP relationship for $($GDAPID) been revoked" } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Success. GDAP relationship for $($GDAPID) been revoked" -Sev 'Info' + + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRoleMapping.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRoleMapping.ps1 new file mode 100644 index 000000000000..729c327b3528 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeleteGDAPRoleMapping.ps1 @@ -0,0 +1,33 @@ +using namespace System.Net + +Function Invoke-ExecDeleteGDAPRoleMapping { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Table = Get-CIPPTable -TableName 'GDAPRoles' + + Write-Host $Table + try { + $Filter = "PartitionKey eq 'Roles' and RowKey eq '{0}'" -f $Request.Query.GroupId + $Entity = Get-CIPPAzDataTableEntity @Table -Filter $Filter + Remove-AzDataTableEntity @Table -Entity $Entity + $Results = [pscustomobject]@{'Results' = 'Success. GDAP relationship mapping deleted' } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "GDAP relationship mapping deleted for $($Request.Query.GroupId)" -Sev 'Info' + + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceAction.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceAction.ps1 new file mode 100644 index 000000000000..90f9e86d4c6d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceAction.ps1 @@ -0,0 +1,34 @@ +using namespace System.Net + +Function Invoke-ExecDeviceAction { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + + + try { + if ($Request.Query.Action -eq 'setDeviceName') { + $ActionBody = @{ deviceName = $Request.Body.input } | ConvertTo-Json -Compress + } + $ActionResult = New-CIPPDeviceAction -Action $Request.Query.Action -ActionBody $ActionBody -DeviceFilter $Request.Query.GUID -TenantFilter $Request.Query.TenantFilter -ExecutingUser $request.headers.'x-ms-client-principal' -APINAME $APINAME + $body = [pscustomobject]@{'Results' = "$ActionResult" } + + } catch { + $body = [pscustomobject]@{'Results' = "Failed to queue action $action on $DeviceFilter $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/ExecDisableEmailForward/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 similarity index 70% rename from ExecDisableEmailForward/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 index 117c1715a92c..289e12df5215 100644 --- a/ExecDisableEmailForward/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 @@ -1,23 +1,26 @@ using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -try { +Function Invoke-ExecDisableEmailForward { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $Username = $request.body.user $Tenantfilter = $request.body.tenantfilter $Results = try { Set-CIPPForwarding -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -Forward $null -keepCopy $false -ForwardingSMTPAddress $null -Disable $true - } - catch { + } catch { "Could not disable forwarding message for $($username). Error: $($_.Exception.Message)" } - $body = [pscustomobject]@{"Results" = @($results) } -} -catch { - $body = [pscustomobject]@{"Results" = @("Could not disable forwarding user: $($_.Exception.message)") } + $body = [pscustomobject]@{'Results' = @($results) } +} catch { + $body = [pscustomobject]@{'Results' = @("Could not disable forwarding user: $($_.Exception.message)") } } # Associate values to output bindings by calling 'Push-OutputBinding'. @@ -25,3 +28,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableUser.ps1 new file mode 100644 index 000000000000..3e32878e726d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableUser.ps1 @@ -0,0 +1,26 @@ +using namespace System.Net + +Function Invoke-ExecDisableUser { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + try { + ([System.Convert]::ToBoolean($Request.Query.Enable)) + $State = Set-CIPPSignInState -userid $Request.query.ID -TenantFilter $Request.Query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -AccountEnabled ([System.Convert]::ToBoolean($Request.Query.Enable)) + $Results = [pscustomobject]@{'Results' = "$State" } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDnsConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDnsConfig.ps1 new file mode 100644 index 000000000000..067da939c2ca --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDnsConfig.ps1 @@ -0,0 +1,113 @@ +using namespace System.Net + +Function Invoke-ExecDnsConfig { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # List of supported resolvers + $ValidResolvers = @( + 'Google' + 'Cloudflare' + 'Quad9' + ) + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + $StatusCode = [HttpStatusCode]::OK + try { + $ConfigTable = Get-CippTable -tablename Config + $Filter = "PartitionKey eq 'Domains' and RowKey eq 'Domains'" + $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter $Filter + + $DomainTable = Get-CippTable -tablename 'Domains' + + if ($ValidResolvers -notcontains $Config.Resolver) { + $Config = @{ + PartitionKey = 'Domains' + RowKey = 'Domains' + Resolver = 'Google' + } + Add-CIPPAzDataTableEntity @ConfigTable -Entity $Config -Force + } + + $updated = $false + + switch ($Request.Query.Action) { + 'SetConfig' { + if ($Request.Query.Resolver) { + $Resolver = $Request.Query.Resolver + if ($ValidResolvers -contains $Resolver) { + try { + $Config.Resolver = $Resolver + } catch { + $Config = @{ + Resolver = $Resolver + } + } + $updated = $true + } + } + if ($updated) { + Add-CIPPAzDataTableEntity @ConfigTable -Entity $Config -Force + Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'DNS configuration updated' -Sev 'Info' + $body = [pscustomobject]@{'Results' = 'Success: DNS configuration updated.' } + } else { + $StatusCode = [HttpStatusCode]::BadRequest + $body = [pscustomobject]@{'Results' = 'Error: No DNS resolver provided.' } + } + } + 'SetDkimConfig' { + $Domain = $Request.Query.Domain + $Selector = ($Request.Query.Selector).trim() -split '\s*,\s*' + $DomainTable = Get-CIPPTable -Table 'Domains' + $Filter = "RowKey eq '{0}'" -f $Domain + $DomainInfo = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter + $DkimSelectors = [string]($Selector | ConvertTo-Json -Compress) + if ($DomainInfo) { + $DomainInfo.DkimSelectors = $DkimSelectors + } else { + $DomainInfo = @{ + 'RowKey' = $Request.Query.Domain + 'PartitionKey' = 'ManualEntry' + 'TenantId' = 'NoTenant' + 'MailProviders' = '' + 'TenantDetails' = '' + 'DomainAnalyser' = '' + 'DkimSelectors' = $DkimSelectors + } + } + Add-CIPPAzDataTableEntity @DomainTable -Entity $DomainInfo -Force + } + 'GetConfig' { + $body = [pscustomobject]$Config + Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'Retrieved DNS configuration' -Sev 'Info' + } + 'RemoveDomain' { + $Filter = "RowKey eq '{0}'" -f $Request.Query.Domain + $DomainRow = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @DomainTable -Entity $DomainRow + Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message "Removed Domain - $($Request.Query.Domain) " -Sev 'Info' + $body = [pscustomobject]@{ 'Results' = "Domain removed - $($Request.Query.Domain)" } + } + } + } catch { + Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "DNS Config API failed. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + $StatusCode = [HttpStatusCode]::BadRequest + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditCalendarPermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditCalendarPermissions.ps1 new file mode 100644 index 000000000000..f9737f7bb8ad --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditCalendarPermissions.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-ExecEditCalendarPermissions { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $UserID = ($request.query.UserID) + $UserToGetPermissions = $Request.query.UserToGetPermissions + $Tenantfilter = $request.Query.tenantfilter + $Permissions = @($Request.query.permissions) + $folderName = $Request.query.folderName + + + try { + if ($Request.query.removeaccess) { + $result = Set-CIPPCalenderPermission -UserID $UserID -folderName $folderName -RemoveAccess $Request.query.removeaccess -TenantFilter $TenantFilter + } else { + $result = Set-CIPPCalenderPermission -UserID $UserID -folderName $folderName -TenantFilter $Tenantfilter -UserToGetPermissions $UserToGetPermissions -Permissions $Permissions + $Result = "Successfully set permissions on folder $($CalParam.Identity). The user $UserToGetPermissions now has $Permissions permissions on this folder." + } + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception + $Result = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{Results = $Result } + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditMailboxPermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditMailboxPermissions.ps1 new file mode 100644 index 000000000000..97200abfc763 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditMailboxPermissions.ps1 @@ -0,0 +1,116 @@ +using namespace System.Net + +Function Invoke-ExecEditMailboxPermissions { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message 'Accessed this API' -Sev 'Debug' + $Username = $request.body.userID + $Tenantfilter = $request.body.tenantfilter + if ($username -eq $null) { exit } + $userid = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($username)" -tenantid $Tenantfilter).id + $Results = [System.Collections.ArrayList]@() + + $RemoveFullAccess = ($Request.body.RemoveFullAccess).value + foreach ($RemoveUser in $RemoveFullAccess) { + try { + $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-mailboxpermission' -cmdParams @{Identity = $userid; user = $RemoveUser; accessRights = @('FullAccess'); } + $results.add("Removed $($removeuser) from $($username) Shared Mailbox permissions") + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Removed $($RemoveUser) from $($username) Shared Mailbox permission" -Sev 'Info' -tenant $TenantFilter + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not remove mailbox permissions for $($removeuser) on $($username)" -Sev 'Error' -tenant $TenantFilter + $results.add("Could not remove $($removeuser) shared mailbox permissions for $($username). Error: $($_.Exception.Message)") + } + } + $AddFullAccess = ($Request.body.AddFullAccess).value + + foreach ($UserAutomap in $AddFullAccess) { + try { + $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Add-MailboxPermission' -cmdParams @{Identity = $userid; user = $UserAutomap; accessRights = @('FullAccess'); automapping = $true } + $results.add( "Granted $($UserAutomap) access to $($username) Mailbox with automapping") + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Granted $($UserAutomap) access to $($username) Mailbox with automapping" -Sev 'Info' -tenant $TenantFilter + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not add mailbox permissions for $($UserAutomap) on $($username)" -Sev 'Error' -tenant $TenantFilter + $results.add( "Could not add $($UserAutomap) shared mailbox permissions for $($username). Error: $($_.Exception.Message)") + } + } + $AddFullAccessNoAutoMap = ($Request.body.AddFullAccessNoAutoMap).value + + foreach ($UserNoAutomap in $AddFullAccessNoAutoMap) { + try { + $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Add-MailboxPermission' -cmdParams @{Identity = $userid; user = $UserNoAutomap; accessRights = @('FullAccess'); automapping = $false } + $results.add( "Granted $UserNoAutomap access to $($username) Mailbox without automapping") + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Granted $UserNoAutomap access to $($username) Mailbox without automapping" -Sev 'Info' -tenant $TenantFilter + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not add mailbox permissions for $($UserNoAutomap) on $($username)" -Sev 'Error' -tenant $TenantFilter + $results.add("Could not add $($UserNoAutomap) shared mailbox permissions for $($username). Error: $($_.Exception.Message)") + } + } + + $AddSendAS = ($Request.body.AddSendAs).value + + foreach ($UserSendAs in $AddSendAS) { + try { + $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Add-RecipientPermission' -cmdParams @{Identity = $userid; Trustee = $UserSendAs; accessRights = @('SendAs') } + $results.add( "Granted $UserSendAs access to $($username) with Send As permissions") + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Granted $UserSendAs access to $($username) with Send As permissions" -Sev 'Info' -tenant $TenantFilter + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not add mailbox permissions for $($UserSendAs) on $($username)" -Sev 'Error' -tenant $TenantFilter + $results.add("Could not add $($UserSendAs) send-as permissions for $($username). Error: $($_.Exception.Message)") + } + } + + $RemoveSendAs = ($Request.body.RemoveSendAs).value + + foreach ($UserSendAs in $RemoveSendAs) { + try { + $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-RecipientPermission' -cmdParams @{Identity = $userid; Trustee = $UserSendAs; accessRights = @('SendAs') } + $results.add( "Removed $UserSendAs from $($username) with Send As permissions") + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Removed $UserSendAs from $($username) with Send As permissions" -Sev 'Info' -tenant $TenantFilter + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not remove mailbox permissions for $($UserSendAs) on $($username)" -Sev 'Error' -tenant $TenantFilter + $results.add("Could not remove $($UserSendAs) send-as permissions for $($username). Error: $($_.Exception.Message)") + } + } + + $AddSendOnBehalf = ($Request.body.AddSendOnBehalf).value + + foreach ($UserSendOnBehalf in $AddSendOnBehalf) { + try { + $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $userid; GrantSendonBehalfTo = @{'@odata.type' = '#Exchange.GenericHashTable'; add = $UserSendOnBehalf }; } + $results.add( "Granted $UserSendOnBehalf access to $($username) with Send On Behalf Permissions") + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Granted $UserSendOnBehalf access to $($username) with Send On Behalf Permissions" -Sev 'Info' -tenant $TenantFilter + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not add send on behalf permissions for $($UserSendOnBehalf) on $($username)" -Sev 'Error' -tenant $TenantFilter + $results.add("Could not add $($UserSendOnBehalf) send on behalf permissions for $($username). Error: $($_.Exception.Message)") + } + } + + $RemoveSendOnBehalf = ($Request.body.RemoveSendOnBehalf).value + + foreach ($UserSendOnBehalf in $RemoveSendOnBehalf) { + try { + $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $userid; GrantSendonBehalfTo = @{'@odata.type' = '#Exchange.GenericHashTable'; remove = $UserSendOnBehalf }; } + $results.add( "Removed $UserSendOnBehalf from $($username) Send on Behalf Permissions") + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Removed $UserSendOnBehalf from $($username) Send on Behalf Permissions" -Sev 'Info' -tenant $TenantFilter + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME-message "Could not Remove send on behalf permissions for $($UserSendOnBehalf) on $($username)" -Sev 'Error' -tenant $TenantFilter + $results.add("Could not remove $($UserSendOnBehalf) send on behalf permissions for $($username). Error: $($_.Exception.Message)") + } + } + + $body = [pscustomobject]@{'Results' = @($results) } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditTemplate.ps1 new file mode 100644 index 000000000000..1be43b61462d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEditTemplate.ps1 @@ -0,0 +1,49 @@ +using namespace System.Net + +Function Invoke-ExecEditTemplate { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + try { + $Table = Get-CippTable -tablename 'templates' + $Table.Force = $true + $guid = $request.body.guid + $JSON = $request.body | Select-Object * -ExcludeProperty GUID | ConvertTo-Json + $Type = $request.Query.Type + + if ($Type -eq 'IntuneTemplate') { + Write-Host 'Intune Template' + Write-Host '' + $RawJSON = $request.body | Select-Object * -ExcludeProperty displayName, description, type, GUID | ConvertTo-Json -Depth 10 -Compress + Set-CIPPIntuneTemplate -RawJSON $RawJSON -GUID $GUID -DisplayName $Request.body.displayName -Description $Request.body.description -templateType $Request.body.type + } else { + Add-CIPPAzDataTableEntity @Table -Entity @{ + JSON = "$JSON" + RowKey = "$GUID" + PartitionKey = "$Type" + GUID = "$GUID" + } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Edited template $($Request.body.name) with GUID $GUID" -Sev 'Debug' + } + $body = [pscustomobject]@{ 'Results' = 'Successfully saved the template' } + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to edit template: $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Editing template failed: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEmailForward.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEmailForward.ps1 new file mode 100644 index 000000000000..70b27c14bfb0 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEmailForward.ps1 @@ -0,0 +1,72 @@ +using namespace System.Net + +Function Invoke-ExecEmailForward { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $Tenantfilter = $request.body.tenantfilter + $ForwardingAddress = $request.body.ForwardInternal.value + $ForwardingSMTPAddress = $request.body.ForwardExternal + $DisableForwarding = $request.body.disableForwarding + $APIName = $TriggerMetadata.FunctionName + + if ($ForwardingAddress) { + try { + New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-mailbox' -cmdParams @{Identity = $Username; ForwardingAddress = $ForwardingAddress ; DeliverToMailboxAndForward = [bool]$request.body.keepCopy } -Anchor $username + if (-not $request.body.KeepCopy) { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set Forwarding for $($username) to $($ForwardingAddress) and not keeping a copy" -Sev 'Info' -tenant $TenantFilter + $results = "Forwarding all email for $($username) to $($ForwardingAddress) and not keeping a copy" + } elseif ($request.body.KeepCopy) { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set Forwarding for $($username) to $($ForwardingAddress) and keeping a copy" -Sev 'Info' -tenant $TenantFilter + $results = "Forwarding all email for $($username) to $($ForwardingAddress) and keeping a copy" + } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add forwarding for $($username)" -Sev 'Error' -tenant $TenantFilter + $results = "Could not add forwarding for $($username). Error: $($_.Exception.Message)" + + } + } + + elseif ($ForwardingSMTPAddress) { + try { + New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-mailbox' -cmdParams @{Identity = $Username; ForwardingSMTPAddress = $ForwardingSMTPAddress ; DeliverToMailboxAndForward = [bool]$request.body.keepCopy } -Anchor $username + if (-not $request.body.KeepCopy) { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set forwarding for $($username) to $($ForwardingSMTPAddress) and not keeping a copy" -Sev 'Info' -tenant $TenantFilter + $results = "Forwarding all email for $($username) to $($ForwardingSMTPAddress) and not keeping a copy" + } elseif ($request.body.KeepCopy) { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set forwarding for $($username) to $($ForwardingSMTPAddress) and keeping a copy" -Sev 'Info' -tenant $TenantFilter + $results = "Forwarding all email for $($username) to $($ForwardingSMTPAddress) and keeping a copy" + } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add forwarding for $($username)" -Sev 'Error' -tenant $TenantFilter + $results = "Could not add forwarding for $($username). Error: $($_.Exception.Message)" + + } + + } + + elseif ($DisableForwarding -eq 'True') { + try { + New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; ForwardingAddress = $null; ForwardingSMTPAddress = $null; DeliverToMailboxAndForward = $false } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Disabled Email forwarding for $($username)" -Sev 'Info' -tenant $TenantFilter + $results = "Disabled Email Forwarding for $($username)" + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not disable Email forwarding for $($username)" -Sev 'Error' -tenant $TenantFilter + $results = "Could not disable Email forwarding for $($username). Error: $($_.Exception.Message)" + + } + } + + $Body = @{'Results' = @($results) } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEnableArchive.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEnableArchive.ps1 new file mode 100644 index 000000000000..ac4cf5f8686b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecEnableArchive.ps1 @@ -0,0 +1,32 @@ +using namespace System.Net + +Function Invoke-ExecEnableArchive { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + # Interact with query parameters or the body of the request. + Try { + $ResultsArch = Set-CIPPMailboxArchive -userid $Request.query.id -tenantFilter $Request.query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -ArchiveEnabled $true + $Results = [pscustomobject]@{'Results' = "$ResultsArch" } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeLicenses.ps1 new file mode 100644 index 000000000000..c20795dcb97c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeLicenses.ps1 @@ -0,0 +1,71 @@ +using namespace System.Net + +Function Invoke-ExecExcludeLicenses { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $Table = Get-CIPPTable -TableName ExcludedLicenses + try { + + if ($Request.Query.List) { + $Rows = Get-CIPPAzDataTableEntity @Table + if ($Rows.Count -lt 1) { + $TableBaseData = '[{"GUID":"16ddbbfc-09ea-4de2-b1d7-312db6112d70","Product_Display_Name":"MICROSOFT TEAMS (FREE)"},{"GUID":"1f2f344a-700d-42c9-9427-5cea1d5d7ba6","Product_Display_Name":"MICROSOFT STREAM"},{"GUID":"338148b6-1b11-4102-afb9-f92b6cdc0f8d","Product_Display_Name":"DYNAMICS 365 P1 TRIAL FOR INFORMATION WORKERS"},{"GUID":"606b54a9-78d8-4298-ad8b-df6ef4481c80","Product_Display_Name":"Power Virtual Agents Viral Trial"},{"GUID":"61e6bd70-fbdb-4deb-82ea-912842f39431","Product_Display_Name":"Dynamics 365 Customer Service Insights Trial"},{"GUID":"6470687e-a428-4b7a-bef2-8a291ad947c9","Product_Display_Name":"WINDOWS STORE FOR BUSINESS"},{"GUID":"710779e8-3d4a-4c88-adb9-386c958d1fdf","Product_Display_Name":"MICROSOFT TEAMS EXPLORATORY"},{"GUID":"74fbf1bb-47c6-4796-9623-77dc7371723b","Product_Display_Name":"Microsoft Teams Trial"},{"GUID":"90d8b3f8-712e-4f7b-aa1e-62e7ae6cbe96","Product_Display_Name":"Business Apps (free)"},{"GUID":"a403ebcc-fae0-4ca2-8c8c-7a907fd6c235","Product_Display_Name":"Power BI (free)"},{"GUID":"bc946dac-7877-4271-b2f7-99d2db13cd2c","Product_Display_Name":"Dynamics 365 Customer Voice Trial"},{"GUID":"dcb1a3ae-b33f-4487-846a-a640262fadf4","Product_Display_Name":"Microsoft Power Apps Plan 2 Trial"},{"GUID":"f30db892-07e9-47e9-837c-80727f46fd3d","Product_Display_Name":"MICROSOFT FLOW FREE"},{"GUID":"fcecd1f9-a91e-488d-a918-a96cdb6ce2b0","Product_Display_Name":"Microsoft Dynamics AX7 User Trial"}]' | ConvertFrom-Json -AsHashtable -Depth 10 + $TableRows = foreach ($Row in $TableBaseData) { + $Row.PartitionKey = 'License' + $Row.RowKey = $Row.GUID + + Add-CIPPAzDataTableEntity @Table -Entity ([pscustomobject]$Row) -Force | Out-Null + } + + $Rows = Get-CIPPAzDataTableEntity @Table + + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded licenses list' -Sev 'Info' + } + $body = @($Rows) + } + + # Interact with query parameters or the body of the request. + $name = $Request.Query.TenantFilter + if ($Request.Query.AddExclusion) { + $AddObject = @{ + PartitionKey = 'License' + RowKey = $Request.body.GUID + 'GUID' = $Request.body.GUID + 'Product_Display_Name' = $request.body.SKUName + } + Add-CIPPAzDataTableEntity @Table -Entity $AddObject -Force + + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Added exclusion $($request.body.SKUName)" -Sev 'Info' + $body = [pscustomobject]@{'Results' = "Success. We've added $($request.body.SKUName) to the excluded list." } + } + + if ($Request.Query.RemoveExclusion) { + $Filter = "RowKey eq '{0}' and PartitionKey eq 'License'" -f $Request.Query.Guid + $Entity = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey + Remove-AzDataTableEntity @Table -Entity $Entity + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Removed exclusion $($Request.Query.GUID)" -Sev 'Info' + $body = [pscustomobject]@{'Results' = "Success. We've removed $($Request.query.guid) from the excluded list." } + } + } catch { + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "Exclusion API failed. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeTenant.ps1 new file mode 100644 index 000000000000..ca63f9f20afa --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExcludeTenant.ps1 @@ -0,0 +1,71 @@ +using namespace System.Net + +Function Invoke-ExecExcludeTenant { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $user = $request.headers.'x-ms-client-principal' + $username = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($user)) | ConvertFrom-Json).userDetails + $date = (Get-Date).tostring('yyyy-MM-dd') + $TenantsTable = Get-CippTable -tablename Tenants + + if ($Request.Query.List) { + $ExcludedFilter = "PartitionKey eq 'Tenants' and Excluded eq true" + $ExcludedTenants = Get-CIPPAzDataTableEntity @TenantsTable -Filter $ExcludedFilter + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded tenants list' -Sev 'Info' + $body = @($ExcludedTenants) + } elseif ($Request.query.ListAll) { + $ExcludedTenants = Get-CIPPAzDataTableEntity @TenantsTable -filter "PartitionKey eq 'Tenants'" + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message 'got excluded tenants list' -Sev 'Info' + $body = @($ExcludedTenants) + } + try { + # Interact with query parameters or the body of the request. + $name = $Request.Query.TenantFilter + if ($Request.Query.AddExclusion) { + $Tenants = Get-Tenants -IncludeAll | Where-Object { $Request.body.value -contains $_.customerId } + + $Excluded = foreach ($Tenant in $Tenants) { + $Tenant.Excluded = $true + $Tenant.ExcludeUser = $username + $Tenant.ExcludeDate = $date + $Tenant + } + Write-Host ($Excluded | ConvertTo-Json) + Update-AzDataTableEntity @TenantsTable -Entity ([pscustomobject]$Excluded) + #Remove-CIPPCache + Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Added exclusion for customer(s): $($Excluded.defaultDomainName -join ',')" -Sev 'Info' + $body = [pscustomobject]@{'Results' = "Success. Added exclusions for customer(s): $($Excluded.defaultDomainName -join ',')" } + } + + if ($Request.Query.RemoveExclusion) { + $Filter = "PartitionKey eq 'Tenants' and defaultDomainName eq '{0}'" -f $name + $Tenant = Get-CIPPAzDataTableEntity @TenantsTable -Filter $Filter + $Tenant.Excluded = $false + $Tenant.ExcludeUser = '' + $Tenant.ExcludeDate = '' + Update-AzDataTableEntity @TenantsTable -Entity $Tenant + #Remove-CIPPCache + Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Removed exclusion for customer $($name)" -Sev 'Info' + $body = [pscustomobject]@{'Results' = "Success. We've removed $name from the excluded tenants." } + } + } catch { + Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Exclusion API failed. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + if (!$body) { $body = @() } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/ExecExtensionMapping/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 similarity index 90% rename from ExecExtensionMapping/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 index 2709f64de294..1e4c8c8677e3 100644 --- a/ExecExtensionMapping/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-ExecExtensionMapping { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' @@ -72,3 +77,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionNinjaOneQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionNinjaOneQueue.ps1 new file mode 100644 index 000000000000..3f78672c9894 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionNinjaOneQueue.ps1 @@ -0,0 +1,19 @@ +using namespace System.Net + +Function Invoke-ExecExtensionNinjaOneQueue { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + + Switch ($QueueItem.NinjaAction) { + 'StartAutoMapping' { Invoke-NinjaOneOrgMapping } + 'AutoMapTenant' { Invoke-NinjaOneOrgMappingTenant -QueueItem $QueueItem } + 'SyncTenant' { Invoke-NinjaOneTenantSync -QueueItem $QueueItem } + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 new file mode 100644 index 000000000000..38f050341aed --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 @@ -0,0 +1,76 @@ +using namespace System.Net + +Function Invoke-ExecExtensionSync { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + if ($Request.Query.Extension -eq 'Gradient') { + try { + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message 'Starting billing processing.' -sev Info + $Table = Get-CIPPTable -TableName Extensionsconfig + $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 + foreach ($ConfigItem in $Configuration.psobject.properties.name) { + switch ($ConfigItem) { + 'Gradient' { + If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { + Push-OutputBinding -Name gradientqueue -Value 'LetsGo' + $Results = [pscustomobject]@{'Results' = 'Succesfully started Gradient Sync' } + } + } + } + } + } catch { + $Results = [pscustomobject]@{'Results' = "Could not start Gradient Sync: $($_.Exception.Message)" } + + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start billing processing $($_.Exception.Message)" -sev Error + } + } + + if ($Request.Query.Extension -eq 'NinjaOne') { + try { + $Table = Get-CIPPTable -TableName NinjaOneSettings + + $CIPPMapping = Get-CIPPTable -TableName CippMapping + $Filter = "PartitionKey eq 'NinjaOrgsMapping'" + $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } + + foreach ($Tenant in $TenantsToProcess) { + Push-OutputBinding -Name NinjaProcess -Value @{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + } + + } + + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaLastRunTime' + 'SettingValue' = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffK') + } + + Add-AzDataTableEntity @Table -Entity $AddObject -Force + + Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' + + $Results = [pscustomobject]@{'Results' = "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" } + } catch { + $Results = [pscustomobject]@{'Results' = "Could not start NinjaOne Sync: $($_.Exception.Message)" } + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start NinjaOne Sync $($_.Exception.Message)" -sev Error + } + + } + + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) -clobber + +} diff --git a/ExecExtensionTest/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionTest.ps1 similarity index 89% rename from ExecExtensionTest/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionTest.ps1 index bbac07c87fb2..afbdfacc402c 100644 --- a/ExecExtensionTest/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionTest.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-ExecExtensionTest { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" $Table = Get-CIPPTable -TableName Extensionsconfig $Configuration = ((Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json) @@ -40,4 +45,6 @@ catch { Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Results - }) \ No newline at end of file + }) + + } diff --git a/ExecExtensionsConfig/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionsConfig.ps1 similarity index 92% rename from ExecExtensionsConfig/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionsConfig.ps1 index 7b127bbedcfe..fd9725fe5ff9 100644 --- a/ExecExtensionsConfig/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionsConfig.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-ExecExtensionsConfig { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' #Connect-AzAccount -UseDeviceAuthentication @@ -62,3 +67,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body }) + + } diff --git a/ExecGDAPInvite/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 similarity index 92% rename from ExecGDAPInvite/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 index a7b48f56a7d2..050a162ebd6f 100644 --- a/ExecGDAPInvite/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInvite.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-ExecGDAPInvite { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $RoleMappings = $Request.body.gdapRoles @@ -75,4 +80,6 @@ $body = @{ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $body - }) \ No newline at end of file + }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteApproved.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteApproved.ps1 new file mode 100644 index 000000000000..23565a19602e --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteApproved.ps1 @@ -0,0 +1,22 @@ +using namespace System.Net + +Function Invoke-ExecGDAPInviteApproved { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + Set-CIPPGDAPInviteGroups + + $body = @{Results = @('Processing recently activated GDAP relationships') } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteQueue.ps1 new file mode 100644 index 000000000000..60d7ddbb29d2 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPInviteQueue.ps1 @@ -0,0 +1,42 @@ +using namespace System.Net + +Function Invoke-ExecGDAPInviteQueue { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + #$TenantFilter = $env:TenantID + + $Table = Get-CIPPTable -TableName 'GDAPInvites' + $Invite = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$QueueItem'" + $APINAME = 'GDAPInvites' + $RoleMappings = $Invite.RoleMappings | ConvertFrom-Json + Write-Host ($Invite | ConvertTo-Json -Compress) + + foreach ($role in $RoleMappings) { + try { + $Mappingbody = ConvertTo-Json -Depth 10 -InputObject @{ + 'accessContainer' = @{ + 'accessContainerId' = "$($Role.GroupId)" + 'accessContainerType' = 'securityGroup' + } + 'accessDetails' = @{ + 'unifiedRoles' = @(@{ + 'roleDefinitionId' = "$($Role.roleDefinitionId)" + }) + } + } + New-GraphPostRequest -NoAuthCheck $True -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$($QueueItem)/accessAssignments" -tenantid $env:TenantID -type POST -body $MappingBody -verbose + Start-Sleep -Milliseconds 100 + } catch { + Write-LogMessage -API $APINAME -message "GDAP Group mapping failed - $($role.GroupId): $($_.Exception.Message)" -Sev Error + exit 1 + } + Write-LogMessage -API $APINAME -message "Groups mapped for GDAP Relationship: $($GdapInvite.RowKey)" -Sev Info + } + Remove-AzDataTableEntity @Table -Entity $Invite + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigration.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigration.ps1 new file mode 100644 index 000000000000..7b5ce0a31a1d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigration.ps1 @@ -0,0 +1,33 @@ +using namespace System.Net + +Function Invoke-ExecGDAPMigration { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Groups = $Request.body.gdapRoles + $Tenants = $Request.body.selectedTenants + $Results = [System.Collections.ArrayList]@() + + foreach ($Tenant in $Tenants) { + $obj = [PSCustomObject]@{ + tenant = $Tenant + gdapRoles = $Groups + } + Push-OutputBinding -Name gdapqueue -Value $obj + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Started GDAP Migration for $($tenant.displayName)" -Sev 'Debug' + $results.add("Started GDAP Migration for $($tenant.displayName)") | Out-Null + } + $body = @{Results = @($Results) } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigrationQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigrationQueue.ps1 new file mode 100644 index 000000000000..bb59a42fed9c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGDAPMigrationQueue.ps1 @@ -0,0 +1,99 @@ + using namespace System.Net + + Function Invoke-ExecGDAPMigrationQueue { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + #$TenantFilter = $env:TenantID +$RoleMappings = $QueueItem.gdapRoles +$tenant = $queueitem.tenant +$Table = Get-CIPPTable -TableName 'gdapmigration' +Write-Host ($QueueItem.tenant | ConvertTo-Json -Compress) +$logRequest = @{ + status = 'Started migration' + tenant = "$($tenant.displayName)" + RowKey = "$($tenant.customerId)" + PartitionKey = 'alert' + startAt = "$((Get-Date).ToString('s'))" +} + +Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null + +if ($RoleMappings) { + $LogRequest['status'] = 'Step 2: Roles selected, creating new GDAP relationship.' + Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null +} +else { + $LogRequest['status'] = 'Migration failed at Step 2: No role mappings created.' + Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null + exit 1 +} +try { + $JSONBody = @{ + 'displayName' = "$((New-Guid).GUID)" + 'partner' = @{ + 'tenantId' = "$env:tenantid" + } + + 'customer' = @{ + 'displayName' = "$($tenant.displayName)" + 'tenantId' = "$($tenant.customerId)" + } + 'accessDetails' = @{ + 'unifiedRoles' = @($RoleMappings | Select-Object roleDefinitionId) + } + 'duration' = 'P730D' + } | ConvertTo-Json -Depth 5 -Compress + Write-Host $JSONBody + $MigrateRequest = New-GraphPostRequest -NoAuthCheck $True -uri 'https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web//v1/delegatedAdminRelationships/migrate' -type POST -body $JSONBody -verbose -tenantid $env:TenantID -scope 'https://api.partnercustomeradministration.microsoft.com/.default' + Start-Sleep -Milliseconds 100 + do { + $CheckActive = New-GraphGetRequest -NoAuthCheck $True -uri "https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web//v1/delegatedAdminRelationships/$($MigrateRequest.id)" -tenantid $env:TenantID -scope 'https://api.partnercustomeradministration.microsoft.com/.default' + Start-Sleep -Milliseconds 200 + } until ($CheckActive.status -eq 'Active') +} +catch { + $LogRequest['status'] = "Migration Failed. Could not create relationship: $($_.Exception.Message)" + Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null +} + + +if ($CheckActive.status -eq 'Active') { + $LogRequest['status'] = 'Step 3: GDAP Relationship active. Mapping groups.' + Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null + foreach ($role in $RoleMappings) { + try { + $Mappingbody = ConvertTo-Json -Depth 10 -InputObject @{ + 'accessContainer' = @{ + 'accessContainerId' = "$($Role.GroupId)" + 'accessContainerType' = 'securityGroup' + } + 'accessDetails' = @{ + 'unifiedRoles' = @(@{ + 'roleDefinitionId' = "$($Role.roleDefinitionId)" + }) + } + } + $RoleActiveID = New-GraphPostRequest -NoAuthCheck $True -uri "https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web//v1/delegatedAdminRelationships/$($MigrateRequest.id)/accessAssignments" -tenantid $env:TenantID -type POST -body $MappingBody -verbose -scope 'https://api.partnercustomeradministration.microsoft.com/.default' + Start-Sleep -Milliseconds 400 + $LogRequest['status'] = "Step 3: GDAP Relationship active. Mapping group: $($Role.GroupId)" + Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null + } + catch { + $LogRequest['status'] = "Migration Failed. Could not create group mapping for group $($role.GroupId): $($_.Exception.Message)" + Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null + exit 1 + } + #$CheckActiveRole = New-GraphGetRequest -NoAuthCheck $True -uri "https://traf-pcsvcadmin-prod.trafficmanager.net/CustomerServiceAdminApi/Web//v1/delegatedAdminRelationships/$($MigrateRequest.id)/accessAssignments/$($RoleActiveID.id)" -tenantid $env:TenantId -scope 'https://api.partnercustomeradministration.microsoft.com/.default' + } + $LogRequest['status'] = 'Migration Complete' + Add-CIPPAzDataTableEntity @Table -Entity $logRequest -Force | Out-Null + +} + + + } diff --git a/ExecGeoIPLookup/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGeoIPLookup.ps1 similarity index 79% rename from ExecGeoIPLookup/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGeoIPLookup.ps1 index 27ec9a354887..ebd5cab52f88 100644 --- a/ExecGeoIPLookup/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGeoIPLookup.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-ExecGeoIPLookup { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' Write-Host $Request.Query.IP $location = Get-CIPPGeoIPLocation -IP $Request.query.IP @@ -22,3 +27,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $LocationInfo }) + + } diff --git a/ExecGetLocalAdminPassword/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetLocalAdminPassword.ps1 similarity index 71% rename from ExecGetLocalAdminPassword/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetLocalAdminPassword.ps1 index 3050065feaec..c58267717c20 100644 --- a/ExecGetLocalAdminPassword/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetLocalAdminPassword.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-ExecGetLocalAdminPassword { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName try { $GraphRequest = Get-CIPPLapsPassword -device $($request.query.guid) -tenantFilter $Request.Query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' @@ -21,3 +26,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetRecoveryKey.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetRecoveryKey.ps1 new file mode 100644 index 000000000000..7190225450a5 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGetRecoveryKey.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-ExecGetRecoveryKey { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $GraphRequest = Get-CIPPBitlockerKey -device $Request.query.GUID -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + $Body = [pscustomobject]@{'Results' = $GraphRequest } + + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $Body = [pscustomobject]@{'Results' = "Failed. $ErrorMessage" } + + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + +} diff --git a/ExecGraphRequest/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 similarity index 95% rename from ExecGraphRequest/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 index a8cd2c301a2c..e6f21c221a53 100644 --- a/ExecGraphRequest/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 @@ -1,9 +1,14 @@ -using namespace System.Net + using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) + Function Invoke-ExecGraphRequest { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName + $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' Function ConvertTo-FlatObject { @@ -110,3 +115,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode Body = @($GraphRequest) }) + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDelete.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDelete.ps1 new file mode 100644 index 000000000000..c4f5be9d67f7 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDelete.ps1 @@ -0,0 +1,32 @@ +using namespace System.Net + +Function Invoke-ExecGroupsDelete { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + # Interact with query parameters or the body of the request. + Try { + $RemoveResults = Remove-CIPPGroup -ID $Request.query.id -GroupType $Request.query.GroupType -tenantFilter $Request.query.TenantFilter -displayName $Request.query.displayName -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + $Results = [pscustomobject]@{'Results' = $RemoveResults } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDeliveryManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDeliveryManagement.ps1 new file mode 100644 index 000000000000..d77c8a56d6e7 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsDeliveryManagement.ps1 @@ -0,0 +1,33 @@ +using namespace System.Net + +Function Invoke-ExecGroupsDeliveryManagement { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + # Interact with query parameters or the body of the request. + Try { + $SetResults = Set-CIPPGroupAuthentication -ID $Request.query.id -GroupType $Request.query.GroupType -OnlyAllowInternalString $Request.query.OnlyAllowInternal -tenantFilter $Request.query.TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + $Results = [pscustomobject]@{'Results' = $SetResults } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Delivery Management failed: $($_.Exception.Message)" -Sev 'Error' + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsHideFromGAL.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsHideFromGAL.ps1 new file mode 100644 index 000000000000..6307f2ef898d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGroupsHideFromGAL.ps1 @@ -0,0 +1,28 @@ +using namespace System.Net + +Function Invoke-ExecGroupsHideFromGAL { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + Try { + $GroupStatus = Set-CIPPGroupGAL -Id $Request.query.id -tenantFilter $Request.query.TenantFilter -GroupType $Request.query.groupType -HiddenString $Request.query.HidefromGAL -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + $Results = [pscustomobject]@{'Results' = $GroupStatus } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Hide/UnHide from GAL failed: $($_.Exception.Message)" -Sev 'Error' + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecHideFromGAL.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecHideFromGAL.ps1 new file mode 100644 index 000000000000..787bdd798e36 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecHideFromGAL.ps1 @@ -0,0 +1,31 @@ +using namespace System.Net + +Function Invoke-ExecHideFromGAL { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + $TenantFilter = $request.query.tenantfilter + Try { + $Hidden = [System.Convert]::ToBoolean($Request.query.HideFromGal) + $HideResults = Set-CIPPHideFromGAL -tenantFilter $tenantFilter -userid $Request.query.ID -HideFromGAL $Hidden -ExecutingUser $request.headers.'x-ms-client-principal' -APIName 'ExecOffboardUser' + $Results = [pscustomobject]@{'Results' = $HideResults } + + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Hide/UnHide from GAL failed: $($_.Exception.Message)" -Sev 'Error' + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsList.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsList.ps1 new file mode 100644 index 000000000000..e3bb573809ef --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsList.ps1 @@ -0,0 +1,84 @@ +using namespace System.Net + +Function Invoke-ExecIncidentsList { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + try { + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $GraphRequest = if ($TenantFilter -ne 'AllTenants') { + $incidents = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/security/incidents' -tenantid $Request.Query.TenantFilter -AsApp $true + + foreach ($incident in $incidents) { + [PSCustomObject]@{ + Tenant = $Request.Query.TenantFilter + Id = $incident.id + Status = $incident.status + IncidentUrl = $incident.incidentWebUrl + RedirectId = $incident.redirectIncidentId + DisplayName = $incident.displayName + Created = $incident.createdDateTime + Updated = $incident.lastUpdateDateTime + AssignedTo = $incident.assignedTo + Classification = $incident.classification + Determination = $incident.determination + Severity = $incident.severity + Tags = ($IncidentObj.tags -join ', ') + Comments = $incident.comments + } + } + } else { + $Table = Get-CIPPTable -TableName cachealertsandincidents + $Filter = "PartitionKey eq 'Incident'" + $Rows = Get-CIPPAzDataTableEntity @Table -filter $Filter | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) + if (!$Rows) { + Push-OutputBinding -Name incidentqueue -Value (Get-Date).ToString() + [PSCustomObject]@{ + Waiting = $true + } + } else { + $incidents = $Rows + foreach ($incident in $incidents) { + $IncidentObj = $incident.Incident | ConvertFrom-Json + [PSCustomObject]@{ + Tenant = $incident.Tenant + Id = $IncidentObj.id + Status = $IncidentObj.status + IncidentUrl = $IncidentObj.incidentWebUrl + RedirectId = $IncidentObj.redirectIncidentId + DisplayName = $IncidentObj.displayName + Created = $IncidentObj.createdDateTime + Updated = $IncidentObj.lastUpdateDateTime + AssignedTo = $IncidentObj.assignedTo + Classification = $IncidentObj.classification + Determination = $IncidentObj.determination + Severity = $IncidentObj.severity + Tags = ($IncidentObj.tags -join ', ') + Comments = @($IncidentObj.comments) + } + } + } + } + } catch { + $StatusCode = [HttpStatusCode]::Forbidden + $body = $_.Exception.message + } + if (!$body) { + $StatusCode = [HttpStatusCode]::OK + $body = [PSCustomObject]@{ + MSResults = ($GraphRequest | Where-Object -Property id -NE $null) + } + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 new file mode 100644 index 000000000000..278fb273fc2b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 @@ -0,0 +1,56 @@ +using namespace System.Net + +Function Invoke-ExecIncidentsListAllTenants { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + Get-Tenants | ForEach-Object -Parallel { + $domainName = $_.defaultDomainName + Import-Module '.\GraphHelper.psm1' + $Table = Get-CIPPTable -TableName 'cachealertsandincidents' + + try { + $incidents = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/security/incidents' -tenantid $domainName -AsApp $true + $GraphRequest = foreach ($incident in $incidents) { + $GUID = (New-Guid).Guid + $GraphRequest = @{ + Incident = [string]($incident | ConvertTo-Json -Depth 10) + RowKey = [string]$GUID + PartitionKey = 'Incident' + Tenant = [string]$domainName + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + } + + } catch { + $GUID = (New-Guid).Guid + $AlertText = ConvertTo-Json -InputObject @{ + Tenant = $domainName + displayName = "Could not connect to Tenant: $($_.Exception.Message)" + comments = @{ + createdDateTime = (Get-Date).ToString('s') + createdbyDisplayName = 'CIPP' + comment = 'Could not connect' + } + classification = 'Unknown' + determination = 'Unknown' + severity = 'CIPP' + } + $GraphRequest = @{ + Incident = [string]$AlertText + RowKey = [string]$GUID + PartitionKey = 'Incident' + Tenant = [string]$domainName + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + + + } + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecListAppId.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecListAppId.ps1 new file mode 100644 index 000000000000..d3695a00742f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecListAppId.ps1 @@ -0,0 +1,26 @@ +using namespace System.Net + +Function Invoke-ExecListAppId { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $ResponseURL = "$(($Request.headers.'x-ms-original-url').replace('/api/ExecListAppId','/api/ExecSAMSetup'))" + + $Results = @{ + applicationId = $ENV:ApplicationID + tenantId = $ENV:TenantID + refreshUrl = "https://login.microsoftonline.com/$ENV:TenantID/oauth2/v2.0/authorize?client_id=$ENV:ApplicationID&response_type=code&redirect_uri=$ResponseURL&response_mode=query&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access+profile+openid&state=1&prompt=select_account" + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMailboxMobileDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMailboxMobileDevices.ps1 new file mode 100644 index 000000000000..15f96d33503a --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMailboxMobileDevices.ps1 @@ -0,0 +1,33 @@ +using namespace System.Net + +Function Invoke-ExecMailboxMobileDevices { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + # Interact with query parameters or the body of the request. + Try { + $MobileResults = Set-CIPPMobileDevice -UserId $request.query.Userid -DeviceId $request.query.deviceid -Quarantine $request.query.Quarantine -tenantFilter $request.query.tenantfilter -APIName $APINAME -Delete $Request.query.Delete -ExecutingUser $request.headers.'x-ms-client-principal' + $Results = [pscustomobject]@{'Results' = $MobileResults } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed $($request.query.Userid): $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 new file mode 100644 index 000000000000..80b7eef7f36a --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 @@ -0,0 +1,79 @@ +using namespace System.Net + +Function Invoke-ExecMaintenanceScripts { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + try { + $GraphToken = Get-GraphToken -returnRefresh $true + $AccessTokenDetails = Read-JwtAccessDetails -Token $GraphToken.access_token + + $ReplacementStrings = @{ + '##TENANTID##' = $env:TenantID + '##RESOURCEGROUP##' = $env:WEBSITE_RESOURCE_GROUP + '##FUNCTIONAPP##' = $env:WEBSITE_SITE_NAME + '##SUBSCRIPTION##' = (($env:WEBSITE_OWNER_NAME).split('+') | Select-Object -First 1) + '##TOKENIP##' = $AccessTokenDetails.IPAddress + } + } catch { Write-Host $_.Exception.Message } + #$ReplacementStrings | Format-Table + + try { + $ScriptFile = $Request.Query.ScriptFile + + try { + $Filename = Split-Path -Leaf $ScriptFile + } catch {} + + if (!$ScriptFile -or [string]::IsNullOrEmpty($ScriptFile)) { + $ScriptFiles = Get-ChildItem .\ExecMaintenanceScripts\Scripts | Select-Object -ExpandProperty PSChildName + + $ScriptOptions = foreach ($ScriptFile in $ScriptFiles) { + @{label = $ScriptFile; value = $ScriptFile } + } + $Body = @{ ScriptFiles = @($ScriptOptions) } + } elseif (!(Get-ChildItem .\ExecMaintenanceScripts\Scripts\$Filename -ErrorAction SilentlyContinue)) { + $Body = @{ Status = 'Script does not exist' } + } else { + $Script = Get-Content -Raw .\ExecMaintenanceScripts\Scripts\$Filename + foreach ($i in $ReplacementStrings.Keys) { + $Script = $Script -replace $i, $ReplacementStrings.$i + } + + $ScriptContent = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Script)) + + if ($Request.Query.MakeLink) { + $Table = Get-CippTable -TableName 'MaintenanceScripts' + $LinkGuid = ([guid]::NewGuid()).ToString() + + $MaintenanceScriptRow = @{ + 'RowKey' = $LinkGuid + 'PartitionKey' = 'Maintenance' + 'ScriptContent' = $ScriptContent + } + Add-CIPPAzDataTableEntity @Table -Entity $MaintenanceScriptRow -Force + + $Body = @{ Link = "/api/PublicScripts?guid=$LinkGuid" } + } else { + $Body = @{ ScriptContent = $ScriptContent } + } + } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to retrieve maintenance scripts. Error: $($_.Exception.Message)" -Sev 'Error' + $Body = @{Status = "Failed to retrieve maintenance scripts $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecNotificationConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecNotificationConfig.ps1 new file mode 100644 index 000000000000..24df02441efd --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecNotificationConfig.ps1 @@ -0,0 +1,53 @@ +using namespace System.Net + +Function Invoke-ExecNotificationConfig { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $sev = ([pscustomobject]$Request.body.Severity).value -join (',') + $results = try { + $Table = Get-CIPPTable -TableName SchedulerConfig + $SchedulerConfig = @{ + 'tenant' = 'Any' + 'tenantid' = 'TenantId' + 'type' = 'CIPPNotifications' + 'schedule' = 'Every 15 minutes' + 'Severity' = [string]$sev + 'email' = "$($Request.Body.Email)" + 'webhook' = "$($Request.Body.Webhook)" + 'onePerTenant' = [boolean]$Request.Body.onePerTenant + 'sendtoIntegration' = [boolean]$Request.Body.sendtoIntegration + 'includeTenantId' = [boolean]$Request.Body.includeTenantId + 'PartitionKey' = 'CippNotifications' + 'RowKey' = 'CippNotifications' + } + foreach ($logvalue in [pscustomobject]$Request.body.logsToInclude) { + $SchedulerConfig[([pscustomobject]$logvalue.value)] = $true + } + + Add-CIPPAzDataTableEntity @Table -Entity $SchedulerConfig -Force | Out-Null + 'Successfully set the configuration' + } catch { + "Failed to set configuration: $($_.Exception.message)" + } + + + $body = [pscustomobject]@{'Results' = $Results } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 new file mode 100644 index 000000000000..5383a20c2087 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardTenant.ps1 @@ -0,0 +1,113 @@ +using namespace System.Net + +Function Invoke-ExecOffboardTenant { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + try { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Tenantfilter = $request.body.tenantfilter + + $results = [System.Collections.ArrayList]@() + $errors = [System.Collections.ArrayList]@() + + if ($request.body.RemoveCSPGuestUsers) { + # Delete guest users who's domains match the CSP tenants + try { + try { + $domains = (New-GraphGETRequest -Uri "https://graph.microsoft.com/v1.0/domains?`$select=id" -tenantid $env:TenantID -NoAuthCheck:$true).id + $CSPGuestUsers = (New-GraphGETRequest -Uri "https://graph.microsoft.com/v1.0/users?`$select=id,mail&`$filter=userType eq 'Guest' and $(($domains | ForEach-Object { "endswith(mail, '$_')" }) -join ' or ')&`$count=true" -tenantid $Tenantfilter -ComplexFilter) + } catch { + $errors.Add("Failed to retrieve guest users: $($_.Exception.message)") + } + + if ($CSPGuestUsers) { + [System.Collections.Generic.List[PSCustomObject]]$BulkRequests = @($CSPGuestUsers | ForEach-Object { + @{ + id = $($_.id) + method = 'DELETE' + url = "/users/$($_.id)" + } + }) + + $BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantFilter + + $results.Add('Succesfully removed guest users') + } else { + $results.Add('No guest users found to remove') + } + } catch { + $errors.Add("Something went wrong while deleting guest users: $($_.Exception.message)") + } + } + + # All customer tenant specific actions ALWAYS have to be completed before this action! + if ($request.body.RemoveMultitenantApps) { + # Remove multi-tenant apps with the CSP tenant as origin + try { + $multitenantCSPApps = (New-GraphGETRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals?`$count=true&`$select=displayName,appId,id,appOwnerOrganizationId&`$filter=appOwnerOrganizationId eq $($env:TenantID)" -tenantid $Tenantfilter -ComplexFilter) + $sortedArray = $multitenantCSPApps | Sort-Object @{Expression = { if ($_.appId -eq $env:applicationid) { 1 } else { 0 } }; Ascending = $true } + $sortedArray | ForEach-Object { + try { + $delete = (New-GraphPostRequest -type 'DELETE' -Uri "https://graph.microsoft.com/v1.0/serviceprincipals/$($_.id)" -tenantid $Tenantfilter) + $results.Add("Succesfully removed app $($_.displayName)") + } catch { + #$results.Add("Failed to removed app $($_.displayName)") + $errors.Add("Failed to removed app $($_.displayName)") + } + } + } catch { + #$results.Add("Failed to retrieve multitenant apps, no apps have been removed: $($_.Exception.message)") + $errors.Add("Failed to retrieve multitenant apps, no apps have been removed: $($_.Exception.message)") + } + } + + if ($request.body.TerminateGDAP) { + # Terminate GDAP relationships + try { + $delegatedAdminRelationships = (New-GraphGETRequest -Uri "https://graph.microsoft.com/v1.0/tenantRelationships/delegatedAdminRelationships?`$filter=(status eq 'active') AND (customer/tenantId eq '$TenantFilter')" -tenantid $env:TenantID) + $delegatedAdminRelationships | ForEach-Object { + try { + $terminate = (New-GraphPostRequest -type 'POST' -Uri "https://graph.microsoft.com/v1.0/tenantRelationships/delegatedAdminRelationships/$($_.id)/requests" -body '{"action":"terminate"}' -ContentType 'application/json' -tenantid $env:TenantID) + $results.Add("Succesfully terminated GDAP relationship $($_.displayName) from tenant $TenantFilter") + } catch { + #$results.Add("Failed to terminate GDAP relationship $($_.displayName): $($_.Exception.message)") + $errors.Add("Failed to terminate GDAP relationship $($_.displayName): $($_.Exception.message)") + } + } + } catch { + #$results.Add("Failed to retrieve GDAP relationships, no relationships have been terminated: $($_.Exception.message)") + $errors.Add("Failed to retrieve GDAP relationships, no relationships have been terminated: $($_.Exception.message)") + } + } + + if ($request.body.TerminateContract) { + # Terminate contract relationship + try { + $terminate = (New-GraphPostRequest -type 'PATCH' -body '{ "relationshipToPartner": "none" }' -Uri "https://api.partnercenter.microsoft.com/v1/customers/$TenantFilter" -ContentType 'application/json' -scope 'https://api.partnercenter.microsoft.com/user_impersonation' -tenantid $env:TenantID) + $results.Add('Succesfully terminated contract relationship') + } catch { + #$results.Add("Failed to terminate contract relationship: $($_.Exception.message)") + $errors.Add("Failed to terminate contract relationship: $($_.Exception.message)") + } + } + + $StatusCode = [HttpStatusCode]::OK + $body = [pscustomobject]@{ + 'Results' = @($results) + 'Errors' = @($errors) + } + } catch { + $StatusCode = [HttpStatusCode]::OK + $body = $_.Exception.message + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardUser.ps1 new file mode 100644 index 000000000000..6e6e9970f39a --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboardUser.ps1 @@ -0,0 +1,50 @@ +using namespace System.Net + +Function Invoke-ExecOffboardUser { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + try { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Username = $request.body.user + $Tenantfilter = $request.body.tenantfilter + $Results = if ($Request.body.Scheduled.enabled) { + $taskObject = [PSCustomObject]@{ + TenantFilter = $Tenantfilter + Name = "Offboarding: $Username" + Command = @{ + value = 'Invoke-CIPPOffboardingJob' + } + Parameters = @{ + Username = $Username + APIName = 'Scheduled Offboarding' + options = $request.body + } + ScheduledTime = $Request.body.scheduled.date + PostExecution = @{ + Webhook = [bool]$Request.Body.PostExecution.webhook + Email = [bool]$Request.Body.PostExecution.email + PSA = [bool]$Request.Body.PostExecution.psa + } + } + + Add-CIPPScheduledTask -Task $taskObject -hidden $false + } else { + Invoke-CIPPOffboardingJob -Username $Username -TenantFilter $Tenantfilter -Options $Request.body -APIName $APIName -ExecutingUser $request.headers.'x-ms-client-principal' + } + $StatusCode = [HttpStatusCode]::OK + $body = [pscustomobject]@{'Results' = @($results) } + } catch { + $StatusCode = [HttpStatusCode]::Forbidden + $body = $_.Exception.message + } + $Request.Body.PostExecution + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboard_Mailboxpermissions.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboard_Mailboxpermissions.ps1 new file mode 100644 index 000000000000..18e70672635d --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOffboard_Mailboxpermissions.ps1 @@ -0,0 +1,15 @@ +using namespace System.Net + +Function Invoke-ExecOffboard_Mailboxpermissions { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + foreach ($Mailbox in $Mailboxes) { + Remove-CIPPMailboxPermissions -PermissionsLevel @('FullAccess', 'SendAs', 'SendOnBehalf') -userid $Mailbox.UserPrincipalName -AccessUser $QueueItem.User -TenantFilter $QueueItem.TenantFilter -APIName $APINAME -ExecutingUser $QueueItem.ExecutingUser + } + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOneDriveShortCut.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOneDriveShortCut.ps1 new file mode 100644 index 000000000000..2f820ab4a068 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOneDriveShortCut.ps1 @@ -0,0 +1,27 @@ +using namespace System.Net + +Function Invoke-ExecOneDriveShortCut { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + Try { + $MessageResult = New-CIPPOneDriveShortCut -username $Request.body.username -userid $Request.body.userid -TenantFilter $Request.Body.TenantFilter -URL $Request.body.input -ExecutingUser $request.headers.'x-ms-client-principal' + $Results = [pscustomobject]@{ 'Results' = "$MessageResult" } + } catch { + $Results = [pscustomobject]@{'Results' = "Onedrive Shortcut creation failed: $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecPasswordConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecPasswordConfig.ps1 new file mode 100644 index 000000000000..78e27e7f01f3 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecPasswordConfig.ps1 @@ -0,0 +1,46 @@ +using namespace System.Net + +Function Invoke-ExecPasswordConfig { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $Table = Get-CIPPTable -TableName Settings + $PasswordType = (Get-CIPPAzDataTableEntity @Table) + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $results = try { + if ($Request.Query.List) { + @{ passwordType = $PasswordType.passwordType } + } else { + $SchedulerConfig = @{ + 'passwordType' = "$($Request.Body.passwordType)" + 'passwordCount' = '12' + 'PartitionKey' = 'settings' + 'RowKey' = 'settings' + } + + Add-CIPPAzDataTableEntity @Table -Entity $SchedulerConfig -Force | Out-Null + 'Successfully set the configuration' + } + } catch { + "Failed to set configuration: $($_.Exception.message)" + } + + + $body = [pscustomobject]@{'Results' = $Results } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecQuarantineManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecQuarantineManagement.ps1 new file mode 100644 index 000000000000..7f85df715207 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecQuarantineManagement.ps1 @@ -0,0 +1,42 @@ +using namespace System.Net + +Function Invoke-ExecQuarantineManagement { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + + # Interact with query parameters or the body of the request. + Try { + $tenantfilter = $Request.Query.TenantFilter + $params = @{ + Identity = $request.query.ID + AllowSender = [boolean]$Request.query.AllowSender + ReleasetoAll = [boolean]$Request.query.type + ActionType = $Request.query.type + } + Write-Host $params + New-ExoRequest -tenantid $TenantFilter -cmdlet 'Release-QuarantineMessage' -cmdParams $Params + $Results = [pscustomobject]@{'Results' = "Successfully processed $($request.query.ID)" } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "$($request.query.id)" -Sev 'Info' + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Quarantine Management failed: $($_.Exception.Message)" -Sev 'Error' + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/ExecResetMFA/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetMFA.ps1 similarity index 71% rename from ExecResetMFA/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetMFA.ps1 index bfe6da2efe2d..457e8abcba1c 100644 --- a/ExecResetMFA/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetMFA.ps1 @@ -1,41 +1,47 @@ using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) +Function Invoke-ExecResetMFA { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' -Write-Host "$($Request.query.ID)" -# Interact with query parameters or the body of the request. -$TenantFilter = $Request.Query.TenantFilter -try { - $AADGraphtoken = (Get-GraphToken -scope 'https://graph.windows.net/.default') - $tenantid = (Get-Tenants | Where-Object -Property defaultDomainName -EQ $TenantFilter).customerId - $TrackingGuid = (New-Guid).GUID - $LogonPost = @" + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + Write-Host "$($Request.query.ID)" + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $AADGraphtoken = (Get-GraphToken -scope 'https://graph.windows.net/.default') + $tenantid = (Get-Tenants | Where-Object -Property defaultDomainName -EQ $TenantFilter).customerId + $TrackingGuid = (New-Guid).GUID + $LogonPost = @" http://provisioning.microsoftonline.com/IProvisioningWebService/MsolConnecturn:uuid:$TrackingGuidhttp://www.w3.org/2005/08/addressing/anonymous$($AADGraphtoken['Authorization'])50afce61-c917-435b-8c6d-60aa5a8b8aa71.2.183.57Version47$($TrackingGuid)https://provisioningapi.microsoftonline.com/provisioningwebservice.svcVersion4 "@ - $DataBlob = (Invoke-RestMethod -Method POST -Uri 'https://provisioningapi.microsoftonline.com/provisioningwebservice.svc' -ContentType 'application/soap+xml; charset=utf-8' -Body $LogonPost).envelope.header.BecContext.DataBlob.'#text' - $MSOLXML = @" + $DataBlob = (Invoke-RestMethod -Method POST -Uri 'https://provisioningapi.microsoftonline.com/provisioningwebservice.svc' -ContentType 'application/soap+xml; charset=utf-8' -Body $LogonPost).envelope.header.BecContext.DataBlob.'#text' + $MSOLXML = @" http://provisioning.microsoftonline.com/IProvisioningWebService/SetUserurn:uuid:$TrackingGuidhttp://www.w3.org/2005/08/addressing/anonymous$($AADGraphtoken['Authorization'])$($DataBlob)9450afce61-c917-435b-8c6d-60aa5a8b8aa71.2.183.57Version47$TrackingGuidhttps://provisioningapi.microsoftonline.com/provisioningwebservice.svcVersion16$($tenantid)$($Request.query.id)*0001-01-01T00:00:00Enabled "@ - $SetMFA = (Invoke-RestMethod -Uri 'https://provisioningapi.microsoftonline.com/provisioningwebservice.svc' -Method post -Body $MSOLXML -ContentType 'application/soap+xml; charset=utf-8') + $SetMFA = (Invoke-RestMethod -Uri 'https://provisioningapi.microsoftonline.com/provisioningwebservice.svc' -Method post -Body $MSOLXML -ContentType 'application/soap+xml; charset=utf-8') - $Results = [pscustomobject]@{'Results' = 'Successfully completed request. User must supply MFA at next logon' } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Reset Multi factor authentication settings for $($Request.query.id)" -Sev 'Info' -} -catch { - $Results = [pscustomobject]@{'Results' = "Failed to reset MFA methods for $($Request.query.id): $($_.Exception.Message)" } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to reset MFA: $($_.Exception.Message)" -Sev 'Error' + $Results = [pscustomobject]@{'Results' = 'Successfully completed request. User must supply MFA at next logon' } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Reset Multi factor authentication settings for $($Request.query.id)" -Sev 'Info' + } catch { + $Results = [pscustomobject]@{'Results' = "Failed to reset MFA methods for $($Request.query.id): $($_.Exception.Message)" } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to reset MFA: $($_.Exception.Message)" -Sev 'Error' -} + } -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) \ No newline at end of file + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetPass.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetPass.ps1 new file mode 100644 index 000000000000..0d16795f9104 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecResetPass.ps1 @@ -0,0 +1,37 @@ +using namespace System.Net + +Function Invoke-ExecResetPass { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + Write-Host "$($Request.query.ID)" + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $mustChange = [System.Convert]::ToBoolean($request.query.MustChange) + + try { + $Reset = Set-CIPPResetPassword -userid $Request.query.ID -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -forceChangePasswordNextSignIn $mustChange + $Results = [pscustomobject]@{'Results' = "$Reset" } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed to reset password for $($Request.query.displayName): $($_.Exception.Message)" } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to reset password for $($Request.query.displayName): $($_.Exception.Message)" -Sev 'Error' + + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreBackup.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreBackup.ps1 new file mode 100644 index 000000000000..0342d10f68c5 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreBackup.ps1 @@ -0,0 +1,40 @@ +using namespace System.Net + +Function Invoke-ExecRestoreBackup { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + try { + foreach ($line in ($Request.body | ConvertFrom-Json | Select-Object * -ExcludeProperty ETag)) { + Write-Host ($line) + $Table = Get-CippTable -tablename $line.table + $ht2 = @{} + $line.psobject.properties | ForEach-Object { $ht2[$_.Name] = [string]$_.Value } + $Table.Entity = $ht2 + Add-CIPPAzDataTableEntity @Table -Force + + } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Created backup' -Sev 'Debug' + + $body = [pscustomobject]@{ + 'Results' = 'Succesfully restored backup.' + } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create backup: $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Backup Creation failed: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreDeleted.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreDeleted.ps1 new file mode 100644 index 000000000000..a78905f8386a --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRestoreDeleted.ps1 @@ -0,0 +1,30 @@ +using namespace System.Net + +Function Invoke-ExecRestoreDeleted { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + + try { + $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/directory/deletedItems/$($Request.query.ID)/restore" -tenantid $TenantFilter -type POST -body '{}' -verbose + $Results = [pscustomobject]@{'Results' = 'Successfully completed request.' } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRevokeSessions.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRevokeSessions.ps1 new file mode 100644 index 000000000000..bab48ef09239 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRevokeSessions.ps1 @@ -0,0 +1,29 @@ +using namespace System.Net + +Function Invoke-ExecRevokeSessions { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + try { + $RevokeSessions = Revoke-CIPPSessions -userid $Request.Query.id -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' + $Results = [pscustomobject]@{'Results' = $RevokeSessions } + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRunBackup.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRunBackup.ps1 new file mode 100644 index 000000000000..a237798e8e01 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecRunBackup.ps1 @@ -0,0 +1,49 @@ +using namespace System.Net + +Function Invoke-ExecRunBackup { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + try { + if ($request.query.Selected) { + $BackupTables = $request.query.Selected -split ',' + } else { + $BackupTables = @( + 'bpa' + 'Config' + 'Domains' + 'ExcludedLicenses' + 'templates' + 'standards' + 'SchedulerConfig' + ) + } + $CSVfile = foreach ($CSVTable in $BackupTables) { + $Table = Get-CippTable -tablename $CSVTable + Get-CIPPAzDataTableEntity @Table | Select-Object *, @{l = 'table'; e = { $CSVTable } } + } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Created backup' -Sev 'Debug' + + $body = [pscustomobject]@{ + 'Results' = 'Created backup' + backup = $CSVfile + } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create backup: $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Backup Creation failed: $($_.Exception.Message)" } + } + + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 new file mode 100644 index 000000000000..9fca990f8f0c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 @@ -0,0 +1,198 @@ +using namespace System.Net + +Function Invoke-ExecSAMSetup { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + + if ($Request.query.error) { + Add-Type -AssemblyName System.Web + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + ContentType = 'text/html' + StatusCode = [HttpStatusCode]::Forbidden + Body = Get-normalizedError -Message [System.Web.HttpUtility]::UrlDecode($Request.Query.error_description) + }) + exit + } + if ('admin' -notin $UserCreds.userRoles) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + ContentType = 'text/html' + StatusCode = [HttpStatusCode]::Forbidden + Body = 'Could not find an admin cookie in your browser. Make sure you do not have an adblocker active, use a Chromium browser, and allow cookies. If our automatic refresh does not work, try pressing the URL bar and hitting enter. We will try to refresh ourselves in 3 seconds.' + }) + exit + } + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + if ($env:MSI_SECRET) { + Disable-AzContextAutosave -Scope Process | Out-Null + $AzSession = Connect-AzAccount -Identity + } + if (!$ENV:SetFromProfile) { + Write-Host "We're reloading from KV" + Get-CIPPAuthentication + } + + $KV = $ENV:WEBSITE_DEPLOYMENT_ID + $Table = Get-CIPPTable -TableName SAMWizard + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) + + try { + if ($Request.query.count -lt 1 ) { $Results = 'No authentication code found. Please go back to the wizard.' } + + if ($request.body.setkeys) { + if ($request.body.tenantid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $request.body.tenantid -AsPlainText -Force) } + if ($request.body.RefreshToken) { Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $request.body.RefreshToken -AsPlainText -Force) } + if ($request.body.applicationid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $request.body.applicationid -AsPlainText -Force) } + if ($request.body.applicationsecret) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $request.body.applicationsecret -AsPlainText -Force) } + $Results = @{ Results = 'The keys have been replaced. Please perform a permissions check.' } + } + if ($Request.query.error -eq 'invalid_client') { $Results = 'Client ID was not found in Azure. Try waiting 10 seconds to try again, if you have gotten this error after 5 minutes, please restart the process.' } + if ($request.query.code) { + try { + $TenantId = $Rows.tenantid + if (!$TenantId) { $TenantId = $ENV:TenantId } + $AppID = $Rows.appid + if (!$AppID) { $appid = $env:ApplicationId } + $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 + $clientsecret = Get-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -AsPlainText + if (!$clientsecret) { $clientsecret = $ENV:ApplicationSecret } + $RefreshToken = Invoke-RestMethod -Method POST -Body "client_id=$appid&scope=https://graph.microsoft.com/.default+offline_access+openid+profile&code=$($request.query.code)&grant_type=authorization_code&redirect_uri=$($url)&client_secret=$clientsecret" -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" + Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $RefreshToken.refresh_token -AsPlainText -Force) + $Results = 'Authentication is now complete. You may now close this window.' + try { + $SetupPhase = $rows.validated = $true + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + } catch { + #no need. + } + } catch { + $Results = "Authentication failed. $($_.Exception.message)" + } + } + if ($request.query.CreateSAM) { + $Rows = @{ + RowKey = 'setup' + PartitionKey = 'setup' + validated = $false + SamSetup = 'NotStarted' + partnersetup = $false + appid = 'NotStarted' + tenantid = 'NotStarted' + } + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) + + if ($Request.query.partnersetup) { + $SetupPhase = $Rows.partnersetup = $true + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + } + $step = 1 + $DeviceLogon = New-DeviceLogin -clientid '1b730954-1685-4b74-9bfd-dac224a7b894' -Scope 'https://graph.microsoft.com/.default' -FirstLogon + $SetupPhase = $rows.SamSetup = [string]($DeviceLogon | ConvertTo-Json) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + $Results = @{ message = "Your code is $($DeviceLogon.user_code). Enter the code" ; step = $step; url = $DeviceLogon.verification_uri } + } + if ($Request.query.CheckSetupProcess -and $request.query.step -eq 1) { + $SAMSetup = $Rows.SamSetup | ConvertFrom-Json -ErrorAction SilentlyContinue + $Token = (New-DeviceLogin -clientid '1b730954-1685-4b74-9bfd-dac224a7b894' -Scope 'https://graph.microsoft.com/.default' -device_code $SAMSetup.device_code) + if ($token.Access_Token) { + $step = 2 + $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 + $PartnerSetup = $Rows.partnersetup + $TenantId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/organization' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method GET -ContentType 'application/json').value.id + $SetupPhase = $rows.tenantid = [string]($TenantId) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + if ($PartnerSetup) { + $app = Get-Content '.\Cache_SAMSetup\SAMManifest.json' | ConvertFrom-Json + $App.web.redirectUris = @($App.web.redirectUris + $URL) + $app = $app | ConvertTo-Json -Depth 15 + $AppId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/applications' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') + $rows.appid = [string]($AppId.appId) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + $attempt = 0 + do { + try { + try { + $SPNDefender = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"fc780465-2017-40d4-a0c5-307022471b92`" }" -ContentType 'application/json') + } catch { + Write-Host "didn't deploy spn for defender, probably already there." + } + try { + $SPNTeams = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"48ac35b8-9aa8-4d74-927d-1f4a14a0b239`" }" -ContentType 'application/json') + } catch { + Write-Host "didn't deploy spn for Teams, probably already there." + } + $SPN = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/servicePrincipals' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"appId`": `"$($AppId.appId)`" }" -ContentType 'application/json') + Start-Sleep 3 + $GroupID = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups?`$filter=startswith(displayName,'AdminAgents')" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method Get -ContentType 'application/json').value.id + Write-Host "Id is $GroupID" + $AddingToAdminAgent = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/groups/$($GroupID)/members/`$ref" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body "{ `"@odata.id`": `"https://graph.microsoft.com/v1.0/directoryObjects/$($SPN.id)`"}" -ContentType 'application/json') + Write-Host 'Added to adminagents' + $attempt ++ + } catch { + $attempt ++ + } + } until ($attempt -gt 5) + } else { + $app = Get-Content '.\Cache_SAMSetup\SAMManifestNoPartner.json' + $AppId = (Invoke-RestMethod 'https://graph.microsoft.com/v1.0/applications' -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body $app -ContentType 'application/json') + $rows.appid = [string]($AppId.appId) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + } + $AppPassword = (Invoke-RestMethod "https://graph.microsoft.com/v1.0/applications/$($AppID.id)/addPassword" -Headers @{ authorization = "Bearer $($Token.Access_Token)" } -Method POST -Body '{"passwordCredential":{"displayName":"CIPPInstall"}}' -ContentType 'application/json').secretText + Set-AzKeyVaultSecret -VaultName $kv -Name 'tenantid' -SecretValue (ConvertTo-SecureString -String $TenantId -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $Appid.appid -AsPlainText -Force) + Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $AppPassword -AsPlainText -Force) + $Results = @{'message' = 'Created application. Waiting 30 seconds for Azure propagation'; step = $step } + } else { + $step = 1 + $Results = @{ message = "Your code is $($SAMSetup.user_code). Enter the code " ; step = $step; url = $SAMSetup.verification_uri } + } + + } + switch ($request.query.step) { + 2 { + $step = 2 + $TenantId = $Rows.tenantid + $AppID = $rows.appid + $PartnerSetup = $Rows.partnersetup + $SetupPhase = $rows.SamSetup = [string]($FirstLogonRefreshtoken | ConvertTo-Json) + Add-CIPPAzDataTableEntity @Table -Entity $Rows -Force | Out-Null + $URL = ($Request.headers.'x-ms-original-url').split('?') | Select-Object -First 1 + $Validated = $Rows.validated + if ($Validated) { $step = 3 } + $Results = @{ message = 'Give the next approval by clicking ' ; step = $step; url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/.default+offline_access+openid+profile&response_type=code&client_id=$($appid)&redirect_uri=$($url)" } + } + 3 { + + $step = 4 + $Results = @{'message' = 'Received token.'; step = $step } + + + } + 4 { + Remove-AzDataTableEntity @Table -Entity $Rows + + $step = 5 + $Results = @{'message' = 'Installation completed.'; step = $step + } + } + } + + } catch { + $Results = [pscustomobject]@{'Results' = "Failed. $($_.InvocationInfo.ScriptLineNumber): $($_.Exception.message)" ; step = $step } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + +} diff --git a/ExecScheduledCommand/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecScheduledCommand.ps1 similarity index 93% rename from ExecScheduledCommand/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecScheduledCommand.ps1 index 615b6b8471cf..ce73476b5c97 100644 --- a/ExecScheduledCommand/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecScheduledCommand.ps1 @@ -1,9 +1,14 @@ -# Input bindings are passed in via param block. -param($QueueItem, $TriggerMetadata) + using namespace System.Net -$Table = Get-CippTable -tablename 'ScheduledTasks' -$task = $QueueItem.TaskInfo -$commandParameters = $QueueItem.Parameters + Function Invoke-ExecScheduledCommand { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $commandParameters = $QueueItem.Parameters $tenant = $QueueItem.Parameters['TenantFilter'] Write-Host 'started task' @@ -83,4 +88,6 @@ else { ScheduledTime = "$nextRunUnixTime" } } -Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Successfully executed task: $($task.name)" -sev Info \ No newline at end of file +Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Successfully executed task: $($task.name)" -sev Info + + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSchedulerBillingRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSchedulerBillingRun.ps1 new file mode 100644 index 000000000000..cd272b0b0851 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSchedulerBillingRun.ps1 @@ -0,0 +1,28 @@ +using namespace System.Net + +Function Invoke-ExecSchedulerBillingRun { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message 'Starting billing processing.' -sev Info + + $Table = Get-CIPPTable -TableName Extensionsconfig + $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 + foreach ($ConfigItem in $Configuration.psobject.properties.name) { + switch ($ConfigItem) { + 'Gradient' { + If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { + New-GradientServiceSyncRun + } + } + } + } +} catch { + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start billing processing $($_.Exception.Message)" -sev Error +} + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendOrgMessage.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendOrgMessage.ps1 new file mode 100644 index 000000000000..b70f200aae55 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendOrgMessage.ps1 @@ -0,0 +1,122 @@ +using namespace System.Net + +Function Invoke-ExecSendOrgMessage { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + $Device = $request.query.ID + try { + + $type = switch ($request.Query.type) { + 'taskbar' { + '844ec9d0-dd31-459c-a1e7-21fb1b39d5da' + $placementDetails = @(@{ + placement = 'default' + variants = @(@{ + variantId = 'b3fce1ee-9658-4267-b174-23d4a1be148f' + localizedTexts = @(@{ + locale = 'invariant' + text = @{ + 'clickUrl' = $Request.query.URL + } + }) + }) + }) + } + 'notification' { + '1ff7c7e7-128c-4e18-a926-bdac4e906ea1' + $placementDetails = @(@{ + placement = 'default' + variants = @(@{ + variantId = '7a1419c9-9263-4202-9225-35b326b92792' + localizedTexts = @(@{ + locale = 'invariant' + text = @{ + 'clickUrl' = $Request.query.URL + } + }) + }) + }) + } + 'getStarted' { + $placementDetails = @(@{ + placement = 'card0' + variants = @(@{ + variantId = 'ed0d0fa2-df72-42f4-9866-66cf3de1fafb' + localizedTexts = @(@{ + locale = 'invariant' + text = @{ + 'message' = 'My Message Value' + 'clickUrl' = 'https://example.com/clickUrl/' + 'title' = 'This message' + 'buttonText' = 'PlzClick' + } + }) + }) + } + @{ + placement = 'card1' + variants = @(@{ + variantId = 'ed0d0fa2-df72-42f4-9866-66cf3de1fafb' + localizedTexts = @(@{ + locale = 'invariant' + text = @{ + 'message' = 'My Message Value' + 'clickUrl' = 'https://example.com/clickUrl/' + 'title' = 'This message' + 'buttonText' = 'PlzClick' + } + }) + }) + }) + } + + } + $freq = $request.query.freq + $object = [pscustomobject]@{ + startDateTime = (Get-Date).ToString('O') + endDateTime = (Get-Date).AddYears('1').ToString('O') + frequency = $freq + targeting = @{ + targetingType = 'aadGroup' + includeIds = @($Device) + } + content = @{ + 'guidedContentId' = "$Type" + placementDetails = $placementDetails + logoInfo = @{ + contentType = 'png' + logoCdnUrl = 'https://hulpnu.nl/tools/Red.jpg' + } + } + } + $tmpbody = ConvertTo-Json -Depth 15 -Compress -InputObject $object + Write-Host $tmpbody + + $GraphRequest = New-GraphPOSTRequest -noauthcheck $true -type 'POST' -uri 'https://graph.microsoft.com/beta/deviceManagement/organizationalMessageDetails' -tenantid $tenantfilter -body $tmpbody + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendPush.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendPush.ps1 new file mode 100644 index 000000000000..aeeb61d4f9c8 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSendPush.ps1 @@ -0,0 +1,122 @@ +using namespace System.Net + +Function Invoke-ExecSendPush { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $TenantFilter = $Request.Query.TenantFilter + $UserEmail = $Request.Query.UserEmail + $MFAAppID = '981f26a1-7f43-403b-a875-f8b09b8cd720' + + # Function to keep trying to get the access token while we wait for MS to actually set the temp password + function get-clientaccess { + param( + $uri, + $body, + $count = 1 + ) + try { + $ClientToken = Invoke-RestMethod -Method post -Uri $uri -Body $body -ea stop + } catch { + if ($count -lt 20) { + + $count++ + Start-Sleep 1 + $ClientToken = get-clientaccess -uri $uri -body $body -count $count + } else { + Throw "Could not get Client Token: $_" + } + } + return $ClientToken + } + + + # Get all service principals + $SPResult = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$top=999&`$select=id,appId" -tenantid $TenantFilter + + # Check if we have one for the MFA App + $SPID = ($SPResult | Where-Object { $_.appId -eq $MFAAppID }).id + + # Create a serivce principal if needed + if (!$SPID) { + + $SPBody = [pscustomobject]@{ + appId = $MFAAppID + } + $SPID = (New-GraphPostRequest -uri 'https://graph.microsoft.com/v1.0/servicePrincipals' -tenantid $TenantFilter -type POST -body $SPBody -verbose).id + } + + + $PassReqBody = @{ + 'passwordCredential' = @{ + 'displayName' = 'MFA Temporary Password' + 'endDateTime' = $(((Get-Date).addminutes(5))) + 'startDateTime' = $((Get-Date).addminutes(-5)) + } + } | ConvertTo-Json -Depth 5 + + $TempPass = (New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SPID/addPassword" -tenantid $TenantFilter -type POST -body $PassReqBody -verbose).secretText + + # Give it a chance to apply + #Start-Sleep 5 + + # Generate the XML for the push request + $XML = @" + +1.0 +$UserEmail +en-usOverrideVoiceOtpfalse69ff05bf-eb61-47f7-a70e-e7d77b6d47d0 +truetrueradiusUNKNOWN: +"@ + + # Request to get client token + $body = @{ + 'resource' = 'https://adnotifications.windowsazure.com/StrongAuthenticationService.svc/Connector' + 'client_id' = $MFAAppID + 'client_secret' = $TempPass + 'grant_type' = 'client_credentials' + 'scope' = 'openid' + } + + # Attempt to get a token using the temp password + $ClientUri = "https://login.microsoftonline.com/$TenantFilter/oauth2/token" + try { + $ClientToken = get-clientaccess -Uri $ClientUri -Body $body + } catch { + $Body = 'Failed to create temporary password' + } + + # If we got a token send a push + if ($ClientToken) { + + $ClientHeaders = @{ 'Authorization' = "Bearer $($ClientToken.access_token)" } + + $obj = Invoke-RestMethod -Uri 'https://adnotifications.windowsazure.com/StrongAuthenticationService.svc/Connector//BeginTwoWayAuthentication' -Method POST -Headers $ClientHeaders -Body $XML -ContentType 'application/xml' + + if ($obj.BeginTwoWayAuthenticationResponse.result) { + $Body = "Received an MFA confirmation: $($obj.BeginTwoWayAuthenticationResponse.result.value | Out-String)" + } + if ($obj.BeginTwoWayAuthenticationResponse.AuthenticationResult -ne $true) { + $Body = "Authentication Failed! Does the user have Push/Phone call MFA configured? Errorcode: $($obj.BeginTwoWayAuthenticationResponse.result.value | Out-String)" + $colour = 'danger' + } + + } + + $Results = [pscustomobject]@{'Results' = $Body; colour = $colour } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Sent push request to $UserEmail - Result: $($obj.BeginTwoWayAuthenticationResponse.result.value | Out-String)" -Sev 'Info' + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + + +} diff --git a/ExecSetMailboxQuota/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 similarity index 59% rename from ExecSetMailboxQuota/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 index 1d348cc5f9cd..e7c7cfa12b4a 100644 --- a/ExecSetMailboxQuota/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 @@ -1,39 +1,42 @@ using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -try { +Function Invoke-ExecSetMailboxQuota { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $Username = $request.body.user $Tenantfilter = $request.body.tenantfilter $quota = $Request.body.input $Results = try { if ($Request.Body.ProhibitSendQuota) { - $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-Mailbox" -cmdParams @{Identity = $Username; ProhibitSendQuota = $quota } + $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; ProhibitSendQuota = $quota } "Changed ProhibitSendQuota for $username - $($message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed ProhibitSendQuota for $username - $($message)" -Sev "Info" -tenant $TenantFilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed ProhibitSendQuota for $username - $($message)" -Sev 'Info' -tenant $TenantFilter } if ($Request.Body.ProhibitSendReceiveQuota) { - $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-Mailbox" -cmdParams @{Identity = $Username; ProhibitSendReceiveQuota = $quota } + $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; ProhibitSendReceiveQuota = $quota } "Changed ProhibitSendReceiveQuota for $username - $($message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed ProhibitSendReceiveQuota for $username - $($message)" -Sev "Info" -tenant $TenantFilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed ProhibitSendReceiveQuota for $username - $($message)" -Sev 'Info' -tenant $TenantFilter } if ($Request.Body.IssueWarningQuota) { - $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-Mailbox" -cmdParams @{Identity = $Username; IssueWarningQuota = $quota } + $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; IssueWarningQuota = $quota } "Changed IssueWarningQuota for $username - $($message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed IssueWarningQuota for $username - $($message)" -Sev "Info" -tenant $TenantFilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed IssueWarningQuota for $username - $($message)" -Sev 'Info' -tenant $TenantFilter } - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add OOO for $($username)" -Sev "Error" -tenant $TenantFilter + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add OOO for $($username)" -Sev 'Error' -tenant $TenantFilter "Could not add out of office message for $($username). Error: $($_.Exception.Message)" } - $body = [pscustomobject]@{"Results" = @($results) } -} -catch { - $body = [pscustomobject]@{"Results" = @("Could not set Out of Office user: $($_.Exception.message)") } + $body = [pscustomobject]@{'Results' = @($results) } +} catch { + $body = [pscustomobject]@{'Results' = @("Could not set Out of Office user: $($_.Exception.message)") } } # Associate values to output bindings by calling 'Push-OutputBinding'. @@ -41,3 +44,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + +} diff --git a/ExecSetOoO/run.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 similarity index 75% rename from ExecSetOoO/run.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 index 2382c99b661c..56dd98757832 100644 --- a/ExecSetOoO/run.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 @@ -1,17 +1,21 @@ using namespace System.Net -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) -try { +Function Invoke-ExecSetOoO { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $Username = $request.body.user $Tenantfilter = $request.body.tenantfilter if ($Request.body.input) { $InternalMessage = $Request.body.input $ExternalMessage = $Request.body.input - } - else { + } else { $InternalMessage = $Request.body.InternalMessage $ExternalMessage = $Request.body.ExternalMessage } @@ -19,21 +23,18 @@ try { $EndTime = $Request.body.EndTime $Results = try { - if ($Request.Body.AutoReplyState -ne "Scheduled") { + if ($Request.Body.AutoReplyState -ne 'Scheduled') { Set-CIPPOutOfOffice -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -InternalMessage $InternalMessage -ExternalMessage $ExternalMessage -State $Request.Body.AutoReplyState - } - else { + } else { Set-CIPPOutOfOffice -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -InternalMessage $InternalMessage -ExternalMessage $ExternalMessage -StartTime $StartTime -EndTime $EndTime -State $Request.Body.AutoReplyState } - } - catch { + } catch { "Could not add out of office message for $($username). Error: $($_.Exception.Message)" } - $body = [pscustomobject]@{"Results" = $($results) } -} -catch { - $body = [pscustomobject]@{"Results" = "Could not set Out of Office user: $($_.Exception.message)"} + $body = [pscustomobject]@{'Results' = $($results) } +} catch { + $body = [pscustomobject]@{'Results' = "Could not set Out of Office user: $($_.Exception.message)" } } # Associate values to output bindings by calling 'Push-OutputBinding'. @@ -41,3 +42,5 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityAlert.ps1 new file mode 100644 index 000000000000..1a13261e7c03 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityAlert.ps1 @@ -0,0 +1,35 @@ +using namespace System.Net + +Function Invoke-ExecSetSecurityAlert { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Interact with query parameters or the body of the request. + $tenantfilter = $Request.Query.TenantFilter + $AlertFilter = $Request.Query.GUID + $Status = $Request.Query.Status + $AssignBody = '{"status":"' + $Status + '","vendorInformation":{"provider":"' + $Request.query.provider + '","vendor":"' + $Request.query.vendor + '"}}' + try { + $GraphRequest = New-Graphpostrequest -uri "https://graph.microsoft.com/beta/security/alerts/$AlertFilter" -type PATCH -tenantid $TenantFilter -body $Assignbody + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Set alert $AlertFilter to status $Status" -Sev 'Info' + $body = [pscustomobject]@{'Results' = "Set status for alert to $Status" } + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to update alert $($AlertFilter): $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to change status: $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityIncident.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityIncident.ps1 new file mode 100644 index 000000000000..64b2ad0b6bcf --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetSecurityIncident.ps1 @@ -0,0 +1,79 @@ +using namespace System.Net + +Function Invoke-ExecSetSecurityIncident { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + $first = '' + # Interact with query parameters or the body of the request. + $tenantfilter = $Request.Query.TenantFilter + $IncidentFilter = $Request.Query.GUID + $Status = $Request.Query.Status + $Assigned = $Request.Query.Assigned + $Classification = $Request.Query.Classification + $Determination = $Request.Query.Determination + $Redirected = $Request.Query.Redirected -as [int] + $BodyBuild + $AssignBody = '{' + + try { + # We won't update redirected incidents because the incident it is redirected to should instead be updated + if ($Redirected -lt 1) { + # Set received status + if ($null -ne $Status) { + $AssignBody += $first + '"status":"' + $Status + '"' + $BodyBuild += $first + 'Set status for incident to ' + $Status + $first = ', ' + } + + # Set received classification and determination + if ($null -ne $Classification) { + if ($null -eq $Determination) { + # Maybe some poindexter tries to send a classification without a determination + throw + } + + $AssignBody += $first + '"classification":"' + $Classification + '", "determination":"' + $Determination + '"' + $BodyBuild += $first + 'Set classification & determination for incident to ' + $Classification + ' ' + $Determination + $first = ', ' + } + + # Set received asignee + if ($null -ne $Assigned) { + $AssignBody += $first + '"assignedTo":"' + $Assigned + '"' + if ($null -eq $Status) { + $BodyBuild += $first + 'Set assigned for incident to ' + $Assigned + } + $first = ', ' + } + + $AssignBody += '}' + + $ResponseBody = [pscustomobject]@{'Results' = $BodyBuild } + New-Graphpostrequest -uri "https://graph.microsoft.com/beta/security/incidents/$IncidentFilter" -type PATCH -tenantid $TenantFilter -body $Assignbody -asApp $true + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Update incident $IncidentFilter with values $Assignbody" -Sev 'Info' + } else { + $ResponseBody = [pscustomobject]@{'Results' = 'Cannot update redirected incident' } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Refuse to pdate incident $IncidentFilter with values $Assignbody because it is redirected to another incident" -Sev 'Info' + } + + $body = $ResponseBody + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantfilter) -message "Failed to update alert $($AlertFilter): $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed to update incident: $($_.Exception.Message)" } + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUniversalSearch.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUniversalSearch.ps1 new file mode 100644 index 000000000000..838f23ee3cd6 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUniversalSearch.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +Function Invoke-ExecUniversalSearch { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Interact with query parameters or the body of the request. + + try { + $tenantfilter = Get-Tenants + $payload = '{ "returnsPartialResults":true, "displayName":"getUsers", "target": { "allTenants":true }, "operationDefinition": { "values":["@sys.normalize([ConsistencyLevel: eventual GET /v1.0/users?$top=5&$search=\"userPrincipalName:' + $request.query.name + '\" OR \"displayName:' + $request.query.name + '\"])"] }, "aggregationDefinition": { "values":["@sys.append([/result],50)"] } }' + $GraphRequest = (New-GraphPOSTRequest -noauthcheck $true -type 'POST' -uri 'https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedTenantOperations' -tenantid $env:TenantID -body $payload).result.Results | ConvertFrom-Json | Where-Object { $_.'_TenantId' -in $tenantfilter.customerId } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = "Could not connect to Azure Lighthouse API: $($ErrorMessage)" + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) + +} diff --git a/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 index 4232a71adaf4..7da2b800b3c9 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 @@ -14,8 +14,8 @@ function Invoke-CIPPOffboardingJob { } $userid = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($username)" -tenantid $Tenantfilter).id $Return = switch ($Options) { - { $_."ConvertToShared" -eq 'true' } { - Set-CIPPMailboxType -ExecutingUser $ExecutingUser -tenantFilter $tenantFilter -userid $username -username $username -MailboxType "Shared" -APIName $APIName + { $_.'ConvertToShared' -eq 'true' } { + Set-CIPPMailboxType -ExecutingUser $ExecutingUser -tenantFilter $tenantFilter -userid $username -username $username -MailboxType 'Shared' -APIName $APIName } { $_.RevokeSessions -eq 'true' } { Revoke-CIPPSessions -tenantFilter $tenantFilter -username $username -userid $userid -ExecutingUser $ExecutingUser -APIName $APIName @@ -27,57 +27,56 @@ function Invoke-CIPPOffboardingJob { Remove-CIPPGroups -userid $userid -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName -Username "$Username" } - { $_."HideFromGAL" -eq 'true' } { + { $_.'HideFromGAL' -eq 'true' } { Set-CIPPHideFromGAL -tenantFilter $tenantFilter -userid $username -HideFromGAL $true -ExecutingUser $ExecutingUser -APIName $APIName } - { $_."DisableSignIn" -eq 'true' } { + { $_.'DisableSignIn' -eq 'true' } { Set-CIPPSignInState -TenantFilter $tenantFilter -userid $username -AccountEnabled $false -ExecutingUser $ExecutingUser -APIName $APIName } - { $_."OnedriveAccess" -ne "" } { + { $_.'OnedriveAccess' -ne '' } { $Options.OnedriveAccess | ForEach-Object { Set-CIPPOnedriveAccess -tenantFilter $tenantFilter -userid $username -OnedriveAccessUser $_.value -ExecutingUser $ExecutingUser -APIName $APIName } } - { $_."AccessNoAutomap" -ne "" } { - $Options.AccessNoAutomap | ForEach-Object { Set-CIPPMailboxAccess -tenantFilter $tenantFilter -userid $username -AccessUser $_.value -Automap $false -AccessRights @("FullAccess") -ExecutingUser $ExecutingUser -APIName $APIName } + { $_.'AccessNoAutomap' -ne '' } { + $Options.AccessNoAutomap | ForEach-Object { Set-CIPPMailboxAccess -tenantFilter $tenantFilter -userid $username -AccessUser $_.value -Automap $false -AccessRights @('FullAccess') -ExecutingUser $ExecutingUser -APIName $APIName } } - { $_."AccessAutomap" -ne "" } { - $Options.AccessAutomap | ForEach-Object { Set-CIPPMailboxAccess -tenantFilter $tenantFilter -userid $username -AccessUser $_.value -Automap $true -AccessRights @("FullAccess") -ExecutingUser $ExecutingUser -APIName $APIName } + { $_.'AccessAutomap' -ne '' } { + $Options.AccessAutomap | ForEach-Object { Set-CIPPMailboxAccess -tenantFilter $tenantFilter -userid $username -AccessUser $_.value -Automap $true -AccessRights @('FullAccess') -ExecutingUser $ExecutingUser -APIName $APIName } } - { $_."OOO" -ne "" } { - Set-CIPPOutOfOffice -tenantFilter $tenantFilter -userid $username -InternalMessage $Options.OOO -ExternalMessage $Options.OOO -ExecutingUser $ExecutingUser -APIName $APIName -state "Enabled" + { $_.'OOO' -ne '' } { + Set-CIPPOutOfOffice -tenantFilter $tenantFilter -userid $username -InternalMessage $Options.OOO -ExternalMessage $Options.OOO -ExecutingUser $ExecutingUser -APIName $APIName -state 'Enabled' } - { $_."forward" -ne "" } { + { $_.'forward' -ne '' } { Set-CIPPForwarding -userid $userid -username $username -tenantFilter $Tenantfilter -Forward $Options.forward -KeepCopy [bool]$Options.keepCopy -ExecutingUser $ExecutingUser -APIName $APIName } - { $_."RemoveLicenses" -eq 'true' } { + { $_.'RemoveLicenses' -eq 'true' } { Remove-CIPPLicense -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName } - { $_."Deleteuser" -eq 'true' } { + { $_.'Deleteuser' -eq 'true' } { Remove-CIPPUser -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName } - { $_."RemoveRules" -eq 'true' } { + { $_.'RemoveRules' -eq 'true' } { Remove-CIPPRules -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName } - { $_."RemoveMobile" -eq 'true' } { + { $_.'RemoveMobile' -eq 'true' } { Remove-CIPPMobileDevice -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName } - { $_."RemovePermissions" } { + { $_.'RemovePermissions' } { if ($RunScheduled) { - Remove-CIPPMailboxPermissions -PermissionsLevel @("FullAccess", "SendAs", "SendOnBehalf") -userid "AllUsers" -AccessUser $UserName -TenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $ExecutingUser + Remove-CIPPMailboxPermissions -PermissionsLevel @('FullAccess', 'SendAs', 'SendOnBehalf') -userid 'AllUsers' -AccessUser $UserName -TenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $ExecutingUser - } - else { + } else { $object = [PSCustomObject]@{ TenantFilter = $tenantFilter User = $username executingUser = $ExecutingUser } - Push-OutputBinding -Name Msg -Value $object + Push-OutputBinding -Name offboardingmailbox -Value $object "Removal of permissions queued. This task will run in the background and send it's results to the logbook." } } diff --git a/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 b/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 index e136536cd328..ca159b6611d5 100644 --- a/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 @@ -11,7 +11,7 @@ function Set-CIPPGDAPInviteGroups { foreach ($Activation in $Activations) { if ($InviteList.RowKey -contains $Activation.id) { Write-Host "Mapping groups for GDAP relationship: $($Activation.id)" - Push-OutputBinding -Name Msg -Value $Activation.id + Push-OutputBinding -Name gdapinvitequeue -Value $Activation.id } } } diff --git a/Z_CIPPHttpTrigger/function.json b/Z_CIPPHttpTrigger/function.json index d1a8f9c684ad..90c8dc9ea6af 100644 --- a/Z_CIPPHttpTrigger/function.json +++ b/Z_CIPPHttpTrigger/function.json @@ -57,6 +57,54 @@ "name": "listusers", "queueName": "generalAllTenantQueue" }, + { + "type": "queue", + "direction": "out", + "name": "gradientqueue", + "queueName": "billqueue" + }, + { + "type": "queue", + "direction": "out", + "name": "NinjaProcess", + "queueName": "NinjaOneQueue" + }, + { + "type": "queue", + "direction": "out", + "name": "alertqueue", + "queueName": "alertqueue" + }, + { + "type": "queue", + "direction": "out", + "name": "gdapinvitequeue", + "queueName": "gdapinvitequeue" + }, + { + "type": "queue", + "direction": "out", + "name": "gdapqueue", + "queueName": "gdapqueue" + }, + { + "type": "queue", + "direction": "out", + "name": "incidentqueue", + "queueName": "incidentqueue" + }, + { + "type": "queue", + "direction": "out", + "name": "offboardingmailbox", + "queueName": "offboardingmailbox" + }, + { + "type": "queue", + "direction": "out", + "name": "QueueWebhook", + "queueName": "webhooksqueue" + }, { "name": "starter", "type": "durableClient", From bdf65d7d663f6f702dabf182305a7eff59731720 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 20:57:31 +0100 Subject: [PATCH 57/77] Fix issues with imports --- .../Invoke-ExecDisableEmailForward.ps1 | 34 +++++----- .../Invoke-ExecSchedulerBillingRun.ps1 | 24 +++---- .../Invoke-ExecSetMailboxQuota.ps1 | 66 +++++++++---------- .../Public/Entrypoints/Invoke-ExecSetOoO.ps1 | 60 ++++++++--------- 4 files changed, 92 insertions(+), 92 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 index 289e12df5215..12c57868e68d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDisableEmailForward.ps1 @@ -7,26 +7,26 @@ Function Invoke-ExecDisableEmailForward { #> [CmdletBinding()] param($Request, $TriggerMetadata) + try { + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Username = $request.body.user + $Tenantfilter = $request.body.tenantfilter + $Results = try { + Set-CIPPForwarding -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -Forward $null -keepCopy $false -ForwardingSMTPAddress $null -Disable $true + } catch { + "Could not disable forwarding message for $($username). Error: $($_.Exception.Message)" + } - $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $Username = $request.body.user - $Tenantfilter = $request.body.tenantfilter - $Results = try { - Set-CIPPForwarding -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -Forward $null -keepCopy $false -ForwardingSMTPAddress $null -Disable $true + $body = [pscustomobject]@{'Results' = @($results) } } catch { - "Could not disable forwarding message for $($username). Error: $($_.Exception.Message)" + $body = [pscustomobject]@{'Results' = @("Could not disable forwarding user: $($_.Exception.message)") } } - $body = [pscustomobject]@{'Results' = @($results) } -} catch { - $body = [pscustomobject]@{'Results' = @("Could not disable forwarding user: $($_.Exception.message)") } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSchedulerBillingRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSchedulerBillingRun.ps1 index cd272b0b0851..2b4fd7d70190 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSchedulerBillingRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSchedulerBillingRun.ps1 @@ -7,22 +7,22 @@ Function Invoke-ExecSchedulerBillingRun { #> [CmdletBinding()] param($Request, $TriggerMetadata) + try { + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message 'Starting billing processing.' -sev Info - Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message 'Starting billing processing.' -sev Info - - $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 - foreach ($ConfigItem in $Configuration.psobject.properties.name) { - switch ($ConfigItem) { - 'Gradient' { - If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { - New-GradientServiceSyncRun + $Table = Get-CIPPTable -TableName Extensionsconfig + $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 + foreach ($ConfigItem in $Configuration.psobject.properties.name) { + switch ($ConfigItem) { + 'Gradient' { + If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { + New-GradientServiceSyncRun + } } } } + } catch { + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start billing processing $($_.Exception.Message)" -sev Error } -} catch { - Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start billing processing $($_.Exception.Message)" -sev Error -} } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 index e7c7cfa12b4a..ceee9e54b069 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetMailboxQuota.ps1 @@ -7,42 +7,42 @@ Function Invoke-ExecSetMailboxQuota { #> [CmdletBinding()] param($Request, $TriggerMetadata) - - $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $Username = $request.body.user - $Tenantfilter = $request.body.tenantfilter - $quota = $Request.body.input - $Results = try { - if ($Request.Body.ProhibitSendQuota) { - $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; ProhibitSendQuota = $quota } - "Changed ProhibitSendQuota for $username - $($message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed ProhibitSendQuota for $username - $($message)" -Sev 'Info' -tenant $TenantFilter - } - if ($Request.Body.ProhibitSendReceiveQuota) { - $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; ProhibitSendReceiveQuota = $quota } - "Changed ProhibitSendReceiveQuota for $username - $($message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed ProhibitSendReceiveQuota for $username - $($message)" -Sev 'Info' -tenant $TenantFilter - } - if ($Request.Body.IssueWarningQuota) { - $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; IssueWarningQuota = $quota } - "Changed IssueWarningQuota for $username - $($message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed IssueWarningQuota for $username - $($message)" -Sev 'Info' -tenant $TenantFilter + try { + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Username = $request.body.user + $Tenantfilter = $request.body.tenantfilter + $quota = $Request.body.input + $Results = try { + if ($Request.Body.ProhibitSendQuota) { + $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; ProhibitSendQuota = $quota } + "Changed ProhibitSendQuota for $username - $($message)" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed ProhibitSendQuota for $username - $($message)" -Sev 'Info' -tenant $TenantFilter + } + if ($Request.Body.ProhibitSendReceiveQuota) { + $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; ProhibitSendReceiveQuota = $quota } + "Changed ProhibitSendReceiveQuota for $username - $($message)" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed ProhibitSendReceiveQuota for $username - $($message)" -Sev 'Info' -tenant $TenantFilter + } + if ($Request.Body.IssueWarningQuota) { + $quota = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $Username; IssueWarningQuota = $quota } + "Changed IssueWarningQuota for $username - $($message)" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Changed IssueWarningQuota for $username - $($message)" -Sev 'Info' -tenant $TenantFilter + } + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add OOO for $($username)" -Sev 'Error' -tenant $TenantFilter + "Could not add out of office message for $($username). Error: $($_.Exception.Message)" } + + $body = [pscustomobject]@{'Results' = @($results) } } catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add OOO for $($username)" -Sev 'Error' -tenant $TenantFilter - "Could not add out of office message for $($username). Error: $($_.Exception.Message)" + $body = [pscustomobject]@{'Results' = @("Could not set Out of Office user: $($_.Exception.message)") } } - $body = [pscustomobject]@{'Results' = @($results) } -} catch { - $body = [pscustomobject]@{'Results' = @("Could not set Out of Office user: $($_.Exception.message)") } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 index 56dd98757832..128df28ca6a9 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSetOoO.ps1 @@ -7,40 +7,40 @@ Function Invoke-ExecSetOoO { #> [CmdletBinding()] param($Request, $TriggerMetadata) - - $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $Username = $request.body.user - $Tenantfilter = $request.body.tenantfilter - if ($Request.body.input) { - $InternalMessage = $Request.body.input - $ExternalMessage = $Request.body.input - } else { - $InternalMessage = $Request.body.InternalMessage - $ExternalMessage = $Request.body.ExternalMessage - } - $StartTime = $Request.body.StartTime - $EndTime = $Request.body.EndTime - - $Results = try { - if ($Request.Body.AutoReplyState -ne 'Scheduled') { - Set-CIPPOutOfOffice -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -InternalMessage $InternalMessage -ExternalMessage $ExternalMessage -State $Request.Body.AutoReplyState + try { + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $Username = $request.body.user + $Tenantfilter = $request.body.tenantfilter + if ($Request.body.input) { + $InternalMessage = $Request.body.input + $ExternalMessage = $Request.body.input } else { - Set-CIPPOutOfOffice -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -InternalMessage $InternalMessage -ExternalMessage $ExternalMessage -StartTime $StartTime -EndTime $EndTime -State $Request.Body.AutoReplyState + $InternalMessage = $Request.body.InternalMessage + $ExternalMessage = $Request.body.ExternalMessage + } + $StartTime = $Request.body.StartTime + $EndTime = $Request.body.EndTime + + $Results = try { + if ($Request.Body.AutoReplyState -ne 'Scheduled') { + Set-CIPPOutOfOffice -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -InternalMessage $InternalMessage -ExternalMessage $ExternalMessage -State $Request.Body.AutoReplyState + } else { + Set-CIPPOutOfOffice -userid $Request.body.user -tenantFilter $TenantFilter -APIName $APINAME -ExecutingUser $request.headers.'x-ms-client-principal' -InternalMessage $InternalMessage -ExternalMessage $ExternalMessage -StartTime $StartTime -EndTime $EndTime -State $Request.Body.AutoReplyState + } + } catch { + "Could not add out of office message for $($username). Error: $($_.Exception.Message)" } + + $body = [pscustomobject]@{'Results' = $($results) } } catch { - "Could not add out of office message for $($username). Error: $($_.Exception.Message)" + $body = [pscustomobject]@{'Results' = "Could not set Out of Office user: $($_.Exception.message)" } } - $body = [pscustomobject]@{'Results' = $($results) } -} catch { - $body = [pscustomobject]@{'Results' = "Could not set Out of Office user: $($_.Exception.message)" } -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body - }) + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) } From 85a735a54cf8413ec8ae97d8d8f9bf257802f04b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 20:59:31 +0100 Subject: [PATCH 58/77] resolve path errors --- Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 | 3 --- 1 file changed, 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 index f10b68fc0b03..9a5e25e55df5 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-GetCippAlerts.ps1 @@ -14,9 +14,6 @@ Function Invoke-GetCippAlerts { $Filter = "PartitionKey eq '{0}'" -f $PartitionKey $Rows = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Sort-Object TableTimestamp -Descending | Select-Object -First 10 - - - Set-Location (Get-Item $PSScriptRoot).Parent.FullName $APIVersion = Get-Content 'version_latest.txt' | Out-String $CIPPVersion = $request.query.localversion From 8387e6f42bc14d617b60b55b5efb548236ef9f96 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 21:28:11 +0100 Subject: [PATCH 59/77] module imports parallel processing --- .../Invoke-ExecAlertsListAllTenants.ps1 | 2 + .../Entrypoints/Invoke-ExecGraphRequest.ps1 | 189 +++++++++--------- .../Invoke-ExecIncidentsListAllTenants.ps1 | 1 + .../Invoke-ListBasicAuthAllTenants.ps1 | 2 + .../Invoke-ListGenericAllTenants.ps1 | 2 + .../Invoke-ListLicensesAllTenants.ps1 | 1 + .../Invoke-ListMFAUsersAllTenants.ps1 | 2 + .../Invoke-ListMailboxRulesAllTenants.ps1 | 5 + .../Entrypoints/Invoke-ListServiceHealth.ps1 | 2 + .../GraphRequests/Get-GraphRequestList.ps1 | 3 + Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 | 20 +- .../Private/New-GradientServiceSyncRun.ps1 | 23 +-- 12 files changed, 131 insertions(+), 121 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 index 9393d5efff81..d03fa1b01208 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 @@ -11,6 +11,8 @@ Function Invoke-ExecAlertsListAllTenants { Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName + Import-Module '.\Modules\AzBobbyTables' + Import-Module cippcore Import-Module '.\GraphHelper.psm1' $Table = Get-CIPPTable -TableName 'cachealertsandincidents' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 index e6f21c221a53..eb3f6e30ddce 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 @@ -1,119 +1,112 @@ - using namespace System.Net +using namespace System.Net - Function Invoke-ExecGraphRequest { - <# +Function Invoke-ExecGraphRequest { + <# .FUNCTIONALITY Entrypoint #> - [CmdletBinding()] - param($Request, $TriggerMetadata) + [CmdletBinding()] + param($Request, $TriggerMetadata) $APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -Function ConvertTo-FlatObject { - # https://evotec.xyz/powershell-converting-advanced-object-to-flat-object/ - MIT License - [CmdletBinding()] - Param ( - [Parameter(ValueFromPipeLine)][Object[]]$Objects, - [String]$Separator = '.', - [ValidateSet('', 0, 1)]$Base = 1, - [int]$Depth = 5, - [Parameter(DontShow)][String[]]$Path, - [Parameter(DontShow)][System.Collections.IDictionary] $OutputObject - ) - Begin { - $InputObjects = [System.Collections.Generic.List[Object]]::new() - } - Process { - foreach ($O in $Objects) { - $InputObjects.Add($O) + Function ConvertTo-FlatObject { + # https://evotec.xyz/powershell-converting-advanced-object-to-flat-object/ - MIT License + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeLine)][Object[]]$Objects, + [String]$Separator = '.', + [ValidateSet('', 0, 1)]$Base = 1, + [int]$Depth = 5, + [Parameter(DontShow)][String[]]$Path, + [Parameter(DontShow)][System.Collections.IDictionary] $OutputObject + ) + Begin { + $InputObjects = [System.Collections.Generic.List[Object]]::new() } - } - End { - If ($PSBoundParameters.ContainsKey('OutputObject')) { - $Object = $InputObjects[0] - $Iterate = [ordered] @{} - if ($null -eq $Object) { - #Write-Verbose -Message "ConvertTo-FlatObject - Object is null" - } - elseif ($Object.GetType().Name -in 'String', 'DateTime', 'TimeSpan', 'Version', 'Enum') { - $Object = $Object.ToString() + Process { + foreach ($O in $Objects) { + $InputObjects.Add($O) } - elseif ($Depth) { - $Depth-- - If ($Object -is [System.Collections.IDictionary]) { - $Iterate = $Object - } - elseif ($Object -is [Array] -or $Object -is [System.Collections.IEnumerable]) { - $i = $Base - foreach ($Item in $Object.GetEnumerator()) { - $Iterate["$i"] = $Item - $i += 1 + } + End { + If ($PSBoundParameters.ContainsKey('OutputObject')) { + $Object = $InputObjects[0] + $Iterate = [ordered] @{} + if ($null -eq $Object) { + #Write-Verbose -Message "ConvertTo-FlatObject - Object is null" + } elseif ($Object.GetType().Name -in 'String', 'DateTime', 'TimeSpan', 'Version', 'Enum') { + $Object = $Object.ToString() + } elseif ($Depth) { + $Depth-- + If ($Object -is [System.Collections.IDictionary]) { + $Iterate = $Object + } elseif ($Object -is [Array] -or $Object -is [System.Collections.IEnumerable]) { + $i = $Base + foreach ($Item in $Object.GetEnumerator()) { + $Iterate["$i"] = $Item + $i += 1 + } + } else { + foreach ($Prop in $Object.PSObject.Properties) { + if ($Prop.IsGettable) { + $Iterate["$($Prop.Name)"] = $Object.$($Prop.Name) + } + } } } - else { - foreach ($Prop in $Object.PSObject.Properties) { - if ($Prop.IsGettable) { - $Iterate["$($Prop.Name)"] = $Object.$($Prop.Name) - } + If ($Iterate.Keys.Count) { + foreach ($Key in $Iterate.Keys) { + ConvertTo-FlatObject -Objects @(, $Iterate["$Key"]) -Separator $Separator -Base $Base -Depth $Depth -Path ($Path + $Key) -OutputObject $OutputObject } + } else { + $Property = $Path -Join $Separator + $OutputObject[$Property] = $Object } - } - If ($Iterate.Keys.Count) { - foreach ($Key in $Iterate.Keys) { - ConvertTo-FlatObject -Objects @(, $Iterate["$Key"]) -Separator $Separator -Base $Base -Depth $Depth -Path ($Path + $Key) -OutputObject $OutputObject + } elseif ($InputObjects.Count -gt 0) { + foreach ($ItemObject in $InputObjects) { + $OutputObject = [ordered]@{} + ConvertTo-FlatObject -Objects @(, $ItemObject) -Separator $Separator -Base $Base -Depth $Depth -Path $Path -OutputObject $OutputObject + [PSCustomObject] $OutputObject } } - else { - $Property = $Path -Join $Separator - $OutputObject[$Property] = $Object - } } - elseif ($InputObjects.Count -gt 0) { - foreach ($ItemObject in $InputObjects) { - $OutputObject = [ordered]@{} - ConvertTo-FlatObject -Objects @(, $ItemObject) -Separator $Separator -Base $Base -Depth $Depth -Path $Path -OutputObject $OutputObject - [PSCustomObject] $OutputObject - } - } - } -} -$TenantFilter = $Request.Query.TenantFilter -try { - if ($TenantFilter -ne 'AllTenants') { - $RawGraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/$($Request.Query.Endpoint)" -tenantid $TenantFilter -NoPagination [boolean]$Request.query.DisablePagination -ComplexFilter } - else { - $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { - Import-Module .\GraphHelper.psm1 - try { - $DefaultDomainName = $_.defaultDomainName - $TenantName = $_.displayName - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/$($using:Request.Query.Endpoint)" -tenantid $DefaultDomainName -NoPagination [boolean]$using:Request.query.DisablePagination -ComplexFilter | Select-Object @{ - label = 'Tenant' - expression = { $TenantName } - }, * - } - catch { - continue - } - } + $TenantFilter = $Request.Query.TenantFilter + try { + if ($TenantFilter -ne 'AllTenants') { + $RawGraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/$($Request.Query.Endpoint)" -tenantid $TenantFilter -NoPagination [boolean]$Request.query.DisablePagination -ComplexFilter + } else { + $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { + Import-Module '.\Modules\AzBobbyTables' + Import-Module .\GraphHelper.psm1 + try { + $DefaultDomainName = $_.defaultDomainName + $TenantName = $_.displayName + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/$($using:Request.Query.Endpoint)" -tenantid $DefaultDomainName -NoPagination [boolean]$using:Request.query.DisablePagination -ComplexFilter | Select-Object @{ + label = 'Tenant' + expression = { $TenantName } + }, * + } catch { + continue + } + } + + } + $GraphRequest = $RawGraphRequest | Where-Object -Property '@odata.context' -EQ $null | ConvertTo-FlatObject + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $GraphRequest = $ErrorMessage } - $GraphRequest = $RawGraphRequest | Where-Object -Property '@odata.context' -EQ $null | ConvertTo-FlatObject - $StatusCode = [HttpStatusCode]::OK -} -catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($GraphRequest) + }) - } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 index 278fb273fc2b..62a1cfbd74d1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 @@ -11,6 +11,7 @@ Function Invoke-ExecIncidentsListAllTenants { Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName + Import-Module '.\Modules\AzBobbyTables' Import-Module '.\GraphHelper.psm1' $Table = Get-CIPPTable -TableName 'cachealertsandincidents' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 index c1c379e7b65a..8b004ca232d7 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 @@ -12,6 +12,8 @@ Function Invoke-ListBasicAuthAllTenants { Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' + $currentTime = Get-Date -Format 'yyyy-MM-ddTHH:MM:ss' $ts = (Get-Date).AddDays(-30) $endTime = $ts.ToString('yyyy-MM-ddTHH:MM:ss') diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 index c94a79f1f0a9..a22146af195c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 @@ -17,6 +17,8 @@ Function Invoke-ListGenericAllTenants { $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\GraphHelper.psm1' try { Write-Host $using:fullUrl diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 index 1aec2d4ec6ce..84264636823a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 @@ -11,6 +11,7 @@ Function Invoke-ListLicensesAllTenants { $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName + Import-Module '.\GraphHelper.psm1' Import-Module '.\Modules\AzBobbyTables' Import-Module '.\Modules\CIPPCore' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 index e052448aad8e..5a6a68ee54df 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 @@ -20,6 +20,8 @@ Function Invoke-ListMFAUsersAllTenants { $domainName = $_.defaultDomainName Import-Module '.\GraphHelper.psm1' Import-Module '.\modules\CippCore' + Import-Module '.\Modules\AzBobbyTables' + $Table = Get-CIPPTable -TableName cachemfa Try { $GraphRequest = Get-CIPPMFAState -TenantFilter $domainName -ErrorAction Stop diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 index 0a8130fa7245..d8b6eea1c094 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 @@ -19,9 +19,14 @@ Function Invoke-ListMailboxRulesAllTenants { $domainName = $_.defaultDomainName Import-Module '.\GraphHelper.psm1' Import-Module '.\Modules\CIPPcore' + Import-Module '.\Modules\AzBobbyTables' + try { $Rules = New-ExoRequest -tenantid $domainName -cmdlet 'Get-Mailbox' | ForEach-Object -Parallel { + Import-Module CIPPCore + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\GraphHelper.psm1' New-ExoRequest -Anchor $_.UserPrincipalName -tenantid $domainName -cmdlet 'Get-InboxRule' -cmdParams @{Mailbox = $_.GUID } } foreach ($Rule in $Rules) { diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListServiceHealth.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListServiceHealth.ps1 index 4e99ab610e70..bb25ad57b542 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListServiceHealth.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListServiceHealth.ps1 @@ -16,6 +16,8 @@ Function Invoke-ListServiceHealth { $ResultHealthSummary = Get-Tenants | ForEach-Object -Parallel { Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' $tenantname = $_.displayName Write-Host $tenantname $prop = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/admin/serviceAnnouncement/issues?`$filter=endDateTime eq null" -tenantid $_.defaultDomainName diff --git a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 index c93ebbb06c80..a1399bc5233d 100644 --- a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 +++ b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 @@ -118,6 +118,9 @@ function Get-GraphRequestList { if ($SkipCache) { Get-Tenants -IncludeErrors | ForEach-Object -Parallel { Import-Module .\GraphHelper.psm1 + Import-Module '.\Modules\AzBobbyTables' + + $GraphRequestParams = @{ TenantFilter = $_.defaultDomainName Endpoint = $using:Endpoint diff --git a/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 b/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 index d94d71b2b611..7b1c522d585e 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 @@ -3,7 +3,7 @@ function Remove-CIPPGroups { param( $Username, $tenantFilter, - $APIName = "Remove From Groups", + $APIName = 'Remove From Groups', $ExecutingUser, $userid ) @@ -16,29 +16,27 @@ function Remove-CIPPGroups { $Returnval = (New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/GetMemberGroups" -tenantid $tenantFilter -type POST -body '{"securityEnabledOnly": false}').value | ForEach-Object -Parallel { Import-Module CIPPCore Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' $group = $_ try { $Groupname = ($using:AllGroups | Where-Object -Property id -EQ $group).displayName $IsMailEnabled = ($using:AllGroups | Where-Object -Property id -EQ $group).mailEnabled - $IsM365Group = ($using:AllGroups | Where-Object { $_.id -eq $group -and $_.groupTypes -contains "Unified" }) -ne $null + $IsM365Group = ($using:AllGroups | Where-Object { $_.id -eq $group -and $_.groupTypes -contains 'Unified' }) -ne $null if ($IsM365Group) { $RemoveRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose - } - elseif (-not $IsMailEnabled) { + } elseif (-not $IsMailEnabled) { $RemoveRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose - } - elseif ($IsMailEnabled) { + } elseif ($IsMailEnabled) { $Params = @{ Identity = $Groupname; Member = $using:userid ; BypassSecurityGroupManagerCheck = $true } - New-ExoRequest -tenantid $using:tenantFilter -cmdlet "Remove-DistributionGroupMember" -cmdParams $params -UseSystemMailbox $true + New-ExoRequest -tenantid $using:tenantFilter -cmdlet 'Remove-DistributionGroupMember' -cmdParams $params -UseSystemMailbox $true } - Write-LogMessage -user $using:ExecutingUser -API $($using:APIName) -message "Removed $($using:Username) from $groupname" -Sev "Info" -tenant $using:TenantFilter + Write-LogMessage -user $using:ExecutingUser -API $($using:APIName) -message "Removed $($using:Username) from $groupname" -Sev 'Info' -tenant $using:TenantFilter "Successfully removed $($using:Username) from group $Groupname" - } - catch { - Write-LogMessage -user $using:ExecutingUser -API $($using:APIName) -message "Could not remove $($using:Username) from group $groupname" -Sev "Error" -tenant $using:TenantFilter + } catch { + Write-LogMessage -user $using:ExecutingUser -API $($using:APIName) -message "Could not remove $($using:Username) from group $groupname" -Sev 'Error' -tenant $using:TenantFilter "Could not remove $($using:Username) from group $($Groupname): $($_.Exception.Message). This is likely because its a Dynamic Group or synched with active directory" } } diff --git a/Modules/CippExtensions/Private/New-GradientServiceSyncRun.ps1 b/Modules/CippExtensions/Private/New-GradientServiceSyncRun.ps1 index b16197fa3183..38637ebe9ffd 100644 --- a/Modules/CippExtensions/Private/New-GradientServiceSyncRun.ps1 +++ b/Modules/CippExtensions/Private/New-GradientServiceSyncRun.ps1 @@ -21,12 +21,11 @@ function New-GradientServiceSyncRun { if ($NewAccounts) { Invoke-RestMethod -Uri 'https://app.usegradient.com/api/vendor-api/organization/accounts' -Method POST -Headers $GradientToken -Body $NewAccounts -ContentType 'application/json' } #setting the integration to active $ExistingIntegrations = (Invoke-RestMethod -Uri 'https://app.usegradient.com/api/vendor-api/organization' -Method GET -Headers $GradientToken) - if ($ExistingIntegrations.Status -ne "active") { + if ($ExistingIntegrations.Status -ne 'active') { $ActivateRequest = Invoke-RestMethod -Uri 'https://app.usegradient.com/api/vendor-api/organization/status/active' -Method PATCH -Headers $GradientToken } - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create tenants in Gradient API. Error: $($_.Exception.Message)" -Sev "Error" -tenant "GradientAPI" + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create tenants in Gradient API. Error: $($_.Exception.Message)" -Sev 'Error' -tenant 'GradientAPI' } @@ -39,6 +38,8 @@ function New-GradientServiceSyncRun { $RawGraphRequest = $Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' Write-Host "Doing $domainName" try { $Licrequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $_.defaultDomainName -ErrorAction Stop @@ -46,13 +47,12 @@ function New-GradientServiceSyncRun { Tenant = $domainName Licenses = $Licrequest } - } - catch { + } catch { [PSCustomObject]@{ Tenant = $domainName Licenses = @{ skuid = "Could not connect to client: $($_.Exception.Message)" - skuPartNumber = "Could not connect to client" + skuPartNumber = 'Could not connect to client' consumedUnits = 0 prepaidUnits = { Enabled = 0 } } @@ -63,11 +63,11 @@ function New-GradientServiceSyncRun { $skuid = $singlereq.Licenses foreach ($sku in $skuid) { try { - if ($sku.skuId -eq "Could not connect to client") { continue } + if ($sku.skuId -eq 'Could not connect to client') { continue } $PrettyName = ($ConvertTable | Where-Object { $_.guid -eq $sku.skuid }).'Product_Display_Name' | Select-Object -Last 1 if (!$PrettyName) { $PrettyName = $sku.skuPartNumber } #Check if serviceID exists by SKUID in gradient - $ExistingService = (Invoke-RestMethod -Uri "https://app.usegradient.com/api/vendor-api" -Method GET -Headers $GradientToken).data.skus | Where-Object name -EQ $PrettyName + $ExistingService = (Invoke-RestMethod -Uri 'https://app.usegradient.com/api/vendor-api' -Method GET -Headers $GradientToken).data.skus | Where-Object name -EQ $PrettyName Write-Host "New service: $($ExistingService.name) ID: $($ExistingService.id)" if (!$ExistingService) { #Create service @@ -85,9 +85,8 @@ function New-GradientServiceSyncRun { unitCount = $sku.prepaidUnits.enabled } | ConvertTo-Json -Depth 10 $Results = Invoke-RestMethod -Uri "https://app.usegradient.com/api/vendor-api/service/$($ExistingService.id)/count" -Method POST -Headers $GradientToken -Body $ServiceBody -ContentType 'application/json' - } - catch { - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create license in Gradient API. Error: $($_). $results" -Sev "Error" -tenant $singlereq.tenant + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to create license in Gradient API. Error: $($_). $results" -Sev 'Error' -tenant $singlereq.tenant } } From 5f361c72a2a9af45eb407a3130962b9f1320a761 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 21:30:16 +0100 Subject: [PATCH 60/77] imports --- .../Invoke-ExecAlertsListAllTenants.ps1 | 4 +- .../Entrypoints/Invoke-ExecGraphRequest.ps1 | 4 +- .../Invoke-ExecIncidentsListAllTenants.ps1 | 3 +- .../Invoke-ListBasicAuthAllTenants.ps1 | 1 + .../Invoke-ListGenericAllTenants.ps1 | 4 +- .../Invoke-ListMailboxRulesAllTenants.ps1 | 4 +- .../GraphRequests/Get-GraphRequestList.ps1 | 5 ++- Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 | 2 +- .../Public/Remove-CIPPMailboxPermissions.ps1 | 42 +++++++++---------- 9 files changed, 35 insertions(+), 34 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 index d03fa1b01208..7601d884a0e6 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecAlertsListAllTenants.ps1 @@ -11,9 +11,9 @@ Function Invoke-ExecAlertsListAllTenants { Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName - Import-Module '.\Modules\AzBobbyTables' - Import-Module cippcore Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' $Table = Get-CIPPTable -TableName 'cachealertsandincidents' try { diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 index eb3f6e30ddce..684d6f15e987 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecGraphRequest.ps1 @@ -79,9 +79,9 @@ Function Invoke-ExecGraphRequest { $RawGraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/$($Request.Query.Endpoint)" -tenantid $TenantFilter -NoPagination [boolean]$Request.query.DisablePagination -ComplexFilter } else { $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { + Import-Module '.\GraphHelper.psm1' Import-Module '.\Modules\AzBobbyTables' - - Import-Module .\GraphHelper.psm1 + Import-Module '.\Modules\CIPPCore' try { $DefaultDomainName = $_.defaultDomainName $TenantName = $_.displayName diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 index 62a1cfbd74d1..af1f94efd727 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecIncidentsListAllTenants.ps1 @@ -11,8 +11,9 @@ Function Invoke-ExecIncidentsListAllTenants { Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName - Import-Module '.\Modules\AzBobbyTables' Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' $Table = Get-CIPPTable -TableName 'cachealertsandincidents' try { diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 index 8b004ca232d7..f5a00e9231d2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBasicAuthAllTenants.ps1 @@ -13,6 +13,7 @@ Function Invoke-ListBasicAuthAllTenants { $domainName = $_.defaultDomainName Import-Module '.\GraphHelper.psm1' Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' $currentTime = Get-Date -Format 'yyyy-MM-ddTHH:MM:ss' $ts = (Get-Date).AddDays(-30) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 index a22146af195c..c71b75dd3106 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGenericAllTenants.ps1 @@ -17,9 +17,9 @@ Function Invoke-ListGenericAllTenants { $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { $domainName = $_.defaultDomainName - Import-Module '.\Modules\AzBobbyTables' - Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' try { Write-Host $using:fullUrl New-GraphGetRequest -uri $using:fullUrl -tenantid $_.defaultDomainName -ComplexFilter -ErrorAction Stop | Select-Object *, @{l = 'Tenant'; e = { $domainName } }, @{l = 'CippStatus'; e = { 'Good' } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 index d8b6eea1c094..f173ab9375be 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 @@ -24,9 +24,9 @@ Function Invoke-ListMailboxRulesAllTenants { try { $Rules = New-ExoRequest -tenantid $domainName -cmdlet 'Get-Mailbox' | ForEach-Object -Parallel { - Import-Module CIPPCore - Import-Module '.\Modules\AzBobbyTables' Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' New-ExoRequest -Anchor $_.UserPrincipalName -tenantid $domainName -cmdlet 'Get-InboxRule' -cmdParams @{Mailbox = $_.GUID } } foreach ($Rule in $Rules) { diff --git a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 index a1399bc5233d..fc9235272edb 100644 --- a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 +++ b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 @@ -117,10 +117,11 @@ function Get-GraphRequestList { 'AllTenants' { if ($SkipCache) { Get-Tenants -IncludeErrors | ForEach-Object -Parallel { - Import-Module .\GraphHelper.psm1 + Import-Module '.\GraphHelper.psm1' Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' + - $GraphRequestParams = @{ TenantFilter = $_.defaultDomainName Endpoint = $using:Endpoint diff --git a/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 b/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 index 7b1c522d585e..14aa12d54089 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 @@ -14,9 +14,9 @@ function Remove-CIPPGroups { $AllGroups = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/?`$select=displayName,mailEnabled,id,groupTypes" -tenantid $tenantFilter) $Returnval = (New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/GetMemberGroups" -tenantid $tenantFilter -type POST -body '{"securityEnabledOnly": false}').value | ForEach-Object -Parallel { - Import-Module CIPPCore Import-Module '.\GraphHelper.psm1' Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' $group = $_ try { diff --git a/Modules/CIPPCore/Public/Remove-CIPPMailboxPermissions.ps1 b/Modules/CIPPCore/Public/Remove-CIPPMailboxPermissions.ps1 index c9e480445056..7495e989b44d 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPMailboxPermissions.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPMailboxPermissions.ps1 @@ -5,42 +5,41 @@ function Remove-CIPPMailboxPermissions { $AccessUser, $TenantFilter, $PermissionsLevel, - $APIName = "Manage Shared Mailbox Access", + $APIName = 'Manage Shared Mailbox Access', $ExecutingUser ) try { - if ($userid -eq "AllUsers") { - $Mailboxes = New-ExoRequest -tenantid $TenantFilter -cmdlet "get-mailbox" + if ($userid -eq 'AllUsers') { + $Mailboxes = New-ExoRequest -tenantid $TenantFilter -cmdlet 'get-mailbox' $Mailboxes | ForEach-Object -Parallel { - Import-Module ".\Modules\CIPPCore" - import-module ".\GraphHelper.psm1" - Import-Module ".\Modules\AzBobbyTables" + Import-Module '.\GraphHelper.psm1' + Import-Module '.\Modules\AzBobbyTables' + Import-Module '.\Modules\CIPPCore' Write-Host "Removing permissions from mailbox $($_.UserPrincipalName)" - Remove-CIPPMailboxPermissions -PermissionsLevel @("FullAccess", "SendAs", "SendOnBehalf") -userid $_.UserPrincipalName -AccessUser $using:AccessUser -TenantFilter $using:TenantFilter -APIName $using:APINAME -ExecutingUser $using:ExecutingUser + Remove-CIPPMailboxPermissions -PermissionsLevel @('FullAccess', 'SendAs', 'SendOnBehalf') -userid $_.UserPrincipalName -AccessUser $using:AccessUser -TenantFilter $using:TenantFilter -APIName $using:APINAME -ExecutingUser $using:ExecutingUser } -ThrottleLimit 10 - } - else { + } else { $Results = $PermissionsLevel | ForEach-Object { switch ($_) { - "SendOnBehalf" { - $MailboxPerms = New-ExoRequest -Anchor $UserId -tenantid $Tenantfilter -cmdlet "Set-Mailbox" -cmdParams @{Identity = $userid; GrantSendonBehalfTo = @{'@odata.type' = '#Exchange.GenericHashTable'; remove = $AccessUser }; } - if ($MailboxPerms -notlike "*completed successfully but no settings of*") { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Removed SendOnBehalf permissions for $($AccessUser) from $($userid)'s mailbox." -Sev "Info" -tenant $TenantFilter + 'SendOnBehalf' { + $MailboxPerms = New-ExoRequest -Anchor $UserId -tenantid $Tenantfilter -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $userid; GrantSendonBehalfTo = @{'@odata.type' = '#Exchange.GenericHashTable'; remove = $AccessUser }; } + if ($MailboxPerms -notlike '*completed successfully but no settings of*') { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Removed SendOnBehalf permissions for $($AccessUser) from $($userid)'s mailbox." -Sev 'Info' -tenant $TenantFilter "Removed SendOnBehalf permissions for $($AccessUser) from $($userid)'s mailbox." } } - "SendAS" { - $MailboxPerms = New-ExoRequest -Anchor $userId -tenantid $Tenantfilter -cmdlet "Remove-RecipientPermission" -cmdParams @{Identity = $userid; Trustee = $AccessUser; accessRights = @("SendAs") } + 'SendAS' { + $MailboxPerms = New-ExoRequest -Anchor $userId -tenantid $Tenantfilter -cmdlet 'Remove-RecipientPermission' -cmdParams @{Identity = $userid; Trustee = $AccessUser; accessRights = @('SendAs') } if ($MailboxPerms -notlike "*because the ACE isn't present*") { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Removed SendAs permissions for $($AccessUser) from $($userid)'s mailbox." -Sev "Info" -tenant $TenantFilter + Write-LogMessage -user $ExecutingUser -API $APIName -message "Removed SendAs permissions for $($AccessUser) from $($userid)'s mailbox." -Sev 'Info' -tenant $TenantFilter "Removed SendAs permissions for $($AccessUser) from $($userid)'s mailbox." } } - "FullAccess" { - $permissions = New-ExoRequest -tenantid $TenantFilter -cmdlet "Remove-MailboxPermission" -cmdParams @{Identity = $userid; user = $AccessUser; accessRights = @("FullAccess") } -Anchor $userid + 'FullAccess' { + $permissions = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{Identity = $userid; user = $AccessUser; accessRights = @('FullAccess') } -Anchor $userid if ($permissions -notlike "*because the ACE doesn't exist on the object.*") { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Removed FullAccess permissions for $($AccessUser) from $($userid)'s mailbox." -Sev "Info" -tenant $TenantFilter + Write-LogMessage -user $ExecutingUser -API $APIName -message "Removed FullAccess permissions for $($AccessUser) from $($userid)'s mailbox." -Sev 'Info' -tenant $TenantFilter "Removed FullAccess permissions for $($AccessUser) from $($userid)'s mailbox." } } @@ -48,9 +47,8 @@ function Remove-CIPPMailboxPermissions { } } return $Results - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not remove mailbox permissions for $($userid). Error: $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter + } catch { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not remove mailbox permissions for $($userid). Error: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter return "Could not remove mailbox permissions for $($userid). Error: $($_.Exception.Message)" } } From 6a2f45aa727db3150dc5acddad9ced6a269a4762 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 Nov 2023 23:56:38 +0100 Subject: [PATCH 61/77] add scheduled item fix --- .../Entrypoints/Invoke-AddScheduledItem.ps1 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 index 3c7406d35503..22be136d7623 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddScheduledItem.ps1 @@ -1,16 +1,17 @@ - using namespace System.Net +using namespace System.Net - Function Invoke-AddScheduledItem { +Function Invoke-AddScheduledItem { <# .FUNCTIONALITY Entrypoint #> [CmdletBinding()] param($Request, $TriggerMetadata) + $Result = Add-CIPPScheduledTask -Task $Request.body -hidden $false - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = @{ Results = $Result } - }) + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ Results = $Result } + }) - } +} From 205b400794d9837a140909e8bb1b9d96d37c5449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 27 Nov 2023 00:13:29 +0100 Subject: [PATCH 62/77] Add App consent admin requests standard And apparently some whitespaces in other places --- Standards_DisableGuests/run.ps1 | 6 ++-- .../function.json | 9 +++++ Standards_EnableAppConsentRequests/run.ps1 | 36 +++++++++++++++++++ Standards_allowOTPTokens/run.ps1 | 4 +-- 4 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 Standards_EnableAppConsentRequests/function.json create mode 100644 Standards_EnableAppConsentRequests/run.ps1 diff --git a/Standards_DisableGuests/run.ps1 b/Standards_DisableGuests/run.ps1 index deefc9445601..abf43e7bfb11 100644 --- a/Standards_DisableGuests/run.ps1 +++ b/Standards_DisableGuests/run.ps1 @@ -5,10 +5,10 @@ try { $GraphRequest = New-GraphgetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=(signInActivity/lastSignInDateTime le $lookup)&`$select=id,UserPrincipalName,signInActivity,mail,userType,accountEnabled" -scope "https://graph.microsoft.com/.default" -tenantid $Tenant | Where-Object { $_.userType -EQ 'Guest' -and $_.AccountEnabled -EQ $true } foreach ($guest in $GraphRequest) { New-GraphPostRequest -type Patch -tenantid $tenant -uri "https://graph.microsoft.com/beta/users/$($guest.id)" -body '{"accountEnabled":"false"}' - Write-LogMessage -API "Standards" -tenant $tenant -message "Disabling guest $($guest.UserPrincipalName) ($($guest.id))" -sev Info + Write-LogMessage -API "Standards" -tenant $tenant -message "Disabling guest $($guest.UserPrincipalName) ($($guest.id))" -sev Info } - Write-LogMessage -API "Standards" -tenant $tenant -message "Disabled guests accounts with a login longer than 90 days ago." -sev Info + Write-LogMessage -API "Standards" -tenant $tenant -message "Disabled guests accounts with a login longer than 90 days ago." -sev Info } catch { - Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable guests older than 90 days: $($_.exception.message)" -sev Error + Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable guests older than 90 days: $($_.exception.message)" -sev Error } \ No newline at end of file diff --git a/Standards_EnableAppConsentRequests/function.json b/Standards_EnableAppConsentRequests/function.json new file mode 100644 index 000000000000..2d4ea9094b24 --- /dev/null +++ b/Standards_EnableAppConsentRequests/function.json @@ -0,0 +1,9 @@ +{ + "bindings": [ + { + "name": "tenant", + "direction": "in", + "type": "activityTrigger" + } + ] +} \ No newline at end of file diff --git a/Standards_EnableAppConsentRequests/run.ps1 b/Standards_EnableAppConsentRequests/run.ps1 new file mode 100644 index 000000000000..9075a12143c3 --- /dev/null +++ b/Standards_EnableAppConsentRequests/run.ps1 @@ -0,0 +1,36 @@ +param($tenant) + +try { + # Get current state + $CurrentInfo = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy" -tenantid $Tenant + + # Change state to enabled with default settings + $CurrentInfo.isEnabled = "true" + $CurrentInfo.notifyReviewers = "true" + $CurrentInfo.remindersEnabled = "true" + $CurrentInfo.requestDurationInDays = 30 + + # Get Global Admin role ID TODO: change to be able to chose role + $Role = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleDefinitions?`$filter=(displayName eq 'Global Administrator')&`$select=displayName,id" -tenantid $Tenant + # System.Array is required to make the query work + $RoleReviewers = [System.Array]@{ + query = "/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq `'$($Role.id)`'" + queryType = 'MicrosoftGraph' + queryRoot = 'null' + } + # Set reviewers to Global Admins if not already set, this avoids overwriting existing reviewers and duplication of reviewers objects + if ($CurrentInfo.reviewers.query -notlike "*$($Role.id)*") { + $CurrentInfo.reviewers += $RoleReviewers + } + + # Convert info object to JSON + $body = ($CurrentInfo | ConvertTo-Json -Depth 10) + (New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy" -Type put -Body $body -ContentType "application/json") + + + Write-LogMessage -API "Standards" -tenant $tenant -message "Enabled App consent admin requests" -sev Info + +} +catch { + Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable App consent admin requests. Error: $($_.exception.message)" -sev Error +} diff --git a/Standards_allowOTPTokens/run.ps1 b/Standards_allowOTPTokens/run.ps1 index c9d138129cce..6344508da742 100644 --- a/Standards_allowOTPTokens/run.ps1 +++ b/Standards_allowOTPTokens/run.ps1 @@ -8,8 +8,8 @@ try { $body = ($CurrentInfo | ConvertTo-Json -Depth 10) (New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator" -Type patch -Body $body -ContentType "application/json") - Write-LogMessage -API "Standards" -tenant $tenant -message "Enabled MS authenticator OTP/oAuth tokens" -sev Info + Write-LogMessage -API "Standards" -tenant $tenant -message "Enabled MS authenticator OTP/oAuth tokens" -sev Info } catch { - Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable MS authenticator OTP/oAuth tokens. Error: $($_.exception.message)" -sev "Error" + Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable MS authenticator OTP/oAuth tokens. Error: $($_.exception.message)" -sev Error } \ No newline at end of file From 19f891ab80e87aafe130aeceaa23e2184da65c35 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 00:19:23 +0100 Subject: [PATCH 63/77] moved files --- ...b46-4680-a035-9250bc675446.CATemplate.json | 41 ---- ...41e6-86a5-f78cdcff5069.IntuneTemplate.json | 7 - ...4b3a-b84b-850d4b69f494.IntuneTemplate.json | 7 - ...460c-a4bd-391944908007.IntuneTemplate.json | 7 - ...4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json | 7 - ...4aae-89be-d83c44b5799f.IntuneTemplate.json | 7 - ...b1-126720645ac6.TransportRuleTemplate.json | 34 ---- Config/CIPPDefaultTable.BPATemplate.json | 192 ------------------ Config/CIPPDefaultTenantPage.BPATemplate.json | 155 -------------- Config/ExcludeSkuList.JSON | 186 ----------------- Config/SharePoint.BPATemplate.json | 70 ------- ...2b-71f3af402b6c.TransportRuleTemplate.json | 35 ---- ...eb-b388f565418b.TransportRuleTemplate.json | 33 --- ...4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json | 7 - ...3b7-4c50-88be-ee80f74cbeac.CATemplate.json | 36 ---- ...d4-896e0958493b.TransportRuleTemplate.json | 32 --- ...419-40a8-a739-714bf5deff90.CATemplate.json | 36 ---- .../CIPPCore/Public/version_latest.txt | 0 18 files changed, 892 deletions(-) delete mode 100644 Config/49a8069e-3b46-4680-a035-9250bc675446.CATemplate.json delete mode 100644 Config/4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json delete mode 100644 Config/59bd753c-4204-4b3a-b84b-850d4b69f494.IntuneTemplate.json delete mode 100644 Config/7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json delete mode 100644 Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json delete mode 100644 Config/7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json delete mode 100644 Config/8d57edc3-071d-42e7-86b1-126720645ac6.TransportRuleTemplate.json delete mode 100644 Config/CIPPDefaultTable.BPATemplate.json delete mode 100644 Config/CIPPDefaultTenantPage.BPATemplate.json delete mode 100644 Config/ExcludeSkuList.JSON delete mode 100644 Config/SharePoint.BPATemplate.json delete mode 100644 Config/adf9f6d1-36fb-438b-a82b-71f3af402b6c.TransportRuleTemplate.json delete mode 100644 Config/b39b8d85-1531-420e-baeb-b388f565418b.TransportRuleTemplate.json delete mode 100644 Config/b79d0123-3105-4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json delete mode 100644 Config/cba836bb-33b7-4c50-88be-ee80f74cbeac.CATemplate.json delete mode 100644 Config/e82dd7d8-3f13-43cd-bdd4-896e0958493b.TransportRuleTemplate.json delete mode 100644 Config/f8be7e58-2419-40a8-a739-714bf5deff90.CATemplate.json rename version_latest.txt => Modules/CIPPCore/Public/version_latest.txt (100%) diff --git a/Config/49a8069e-3b46-4680-a035-9250bc675446.CATemplate.json b/Config/49a8069e-3b46-4680-a035-9250bc675446.CATemplate.json deleted file mode 100644 index c3fda26a115a..000000000000 --- a/Config/49a8069e-3b46-4680-a035-9250bc675446.CATemplate.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "state": "enabled", - "grantControls": { - "builtInControls": ["mfa"], - "operator": "OR", - "termsOfUse": [], - "customAuthenticationFactors": [] - }, - "conditions": { - "times": null, - "locations": null, - "signInRiskLevels": [], - "devices": null, - "deviceStates": null, - "users": { - "excludeRoles": [], - "excludeUsers": [], - "excludeGroups": [], - "includeUsers": ["All"], - "includeRoles": [], - "includeGroups": [] - }, - "servicePrincipalRiskLevels": [], - "userRiskLevels": [], - "clientAppTypes": [ - "exchangeActiveSync", - "browser", - "mobileAppsAndDesktopClients", - "other" - ], - "platforms": null, - "clientApplications": null, - "applications": { - "includeApplications": ["All"], - "includeUserActions": [], - "includeAuthenticationContextClassReferences": [], - "excludeApplications": [] - } - }, - "displayName": "Enforce Multi factor authentication for each application" -} diff --git a/Config/4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json b/Config/4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json deleted file mode 100644 index 3fd6ff12fb5b..000000000000 --- a/Config/4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Displayname": "CIPP Default: Set screen lock time to 5 minutes", - "Description": "Sets the screen to lock after 5 minutes of inactivity.", - "RAWJson": "{\"name\":\"Set Screen Lockout to 5 minutes\",\"description\":\"\",\"platforms\":\"windows10\",\"technologies\":\"mdm\",\"roleScopeTagIds\":[\"0\"],\"settings\":[{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationSetting\",\"settingInstance\":{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance\",\"settingDefinitionId\":\"device_vendor_msft_policy_config_localpoliciessecurityoptions_interactivelogon_machineinactivitylimit_v2\",\"simpleSettingValue\":{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationIntegerSettingValue\",\"value\":300}}}]}", - "Type": "Catalog", - "GUID": "4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json" -} diff --git a/Config/59bd753c-4204-4b3a-b84b-850d4b69f494.IntuneTemplate.json b/Config/59bd753c-4204-4b3a-b84b-850d4b69f494.IntuneTemplate.json deleted file mode 100644 index c55886283a8a..000000000000 --- a/Config/59bd753c-4204-4b3a-b84b-850d4b69f494.IntuneTemplate.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Displayname": "LAPS", - "Description": "", - "RAWJson": "{\r\n \"name\": \"LAPS\",\r\n \"description\": \"\",\r\n \"settings\": [\r\n {\r\n \"id\": \"0\",\r\n \"settingInstance\": {\r\n \"@odata.type\": \"#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance\",\r\n \"settingDefinitionId\": \"device_vendor_msft_laps_policies_backupdirectory\",\r\n \"settingInstanceTemplateReference\": {\r\n \"settingInstanceTemplateId\": \"a3270f64-e493-499d-8900-90290f61ed8a\"\r\n },\r\n \"choiceSettingValue\": {\r\n \"value\": \"device_vendor_msft_laps_policies_backupdirectory_1\",\r\n \"settingValueTemplateReference\": {\r\n \"settingValueTemplateId\": \"4d90f03d-e14c-43c4-86da-681da96a2f92\",\r\n \"useTemplateDefault\": false\r\n },\r\n \"children\": [\r\n {\r\n \"@odata.type\": \"#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance\",\r\n \"settingDefinitionId\": \"device_vendor_msft_laps_policies_passwordagedays_aad\",\r\n \"settingInstanceTemplateReference\": null,\r\n \"simpleSettingValue\": {\r\n \"@odata.type\": \"#microsoft.graph.deviceManagementConfigurationIntegerSettingValue\",\r\n \"settingValueTemplateReference\": null,\r\n \"value\": 30\r\n }\r\n }\r\n ]\r\n }\r\n }\r\n },\r\n {\r\n \"id\": \"1\",\r\n \"settingInstance\": {\r\n \"@odata.type\": \"#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance\",\r\n \"settingDefinitionId\": \"device_vendor_msft_laps_policies_passwordcomplexity\",\r\n \"settingInstanceTemplateReference\": {\r\n \"settingInstanceTemplateId\": \"8a7459e8-1d1c-458a-8906-7b27d216de52\"\r\n },\r\n \"choiceSettingValue\": {\r\n \"value\": \"device_vendor_msft_laps_policies_passwordcomplexity_4\",\r\n \"settingValueTemplateReference\": {\r\n \"settingValueTemplateId\": \"aa883ab5-625e-4e3b-b830-a37a4bb8ce01\",\r\n \"useTemplateDefault\": false\r\n },\r\n \"children\": []\r\n }\r\n }\r\n }\r\n ],\r\n \"platforms\": \"windows10\",\r\n \"technologies\": \"mdm\",\r\n \"templateReference\": {\r\n \"templateId\": \"adc46e5a-f4aa-4ff6-aeff-4f27bc525796_1\",\r\n \"templateFamily\": \"endpointSecurityAccountProtection\",\r\n \"templateDisplayName\": \"Local admin password solution (Windows LAPS)\",\r\n \"templateDisplayVersion\": \"Version 1\"\r\n }\r\n}", - "Type": "Catalog", - "GUID": "59bd753c-4204-4b3a-b84b-850d4b69f494" -} diff --git a/Config/7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json b/Config/7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json deleted file mode 100644 index 9cf2094dae99..000000000000 --- a/Config/7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Displayname": "CIPP Default: Skip Autopilot User Setup Page", - "Description": "Skips the autopilot user setup page", - "RAWJson": "{\"id\":\"00000000-0000-0000-0000-000000000000\",\"displayName\":\"Skip Autopilot User Setup Page\",\"roleScopeTagIds\":[\"0\"],\"@odata.type\":\"#microsoft.graph.windows10CustomConfiguration\",\"omaSettings\":[{\"displayName\":\"SkipUserSetupPage\",\"omaUri\":\"./Device/Vendor/MSFT/DMClient/Provider/MS DM Server/FirstSyncStatus/SkipUserStatusPage\",\"@odata.type\":\"#microsoft.graph.omaSettingBoolean\",\"value\":\"true\"}]}", - "Type": "Device", - "GUID": "7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json" -} diff --git a/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json b/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json deleted file mode 100644 index b7e696cc9b81..000000000000 --- a/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Displayname": "CIPP Default: Enable Onedrive Silent Logon and Known Folder Move", - "Description": "This policy enables Onedrive Silent Logon and Known Folder move", - "RAWJson": "{\n\"added\":[\n{\n\"enabled\":true,\n\"presentationValues\":[],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('9a4db949-29e4-4e31-a129-bf2b88d8fa1b')\"\n},\n{\n\"enabled\":true,\n\"presentationValues\":[\n{\n\"@odata.type\":\"#microsoft.graph.groupPolicyPresentationValueText\",\n\"value\":\"%tenantid%\",\n\"presentation@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')/presentations('fbefbbdf-5382-477c-8b6c-71f4a06e2805')\"\n},\n{\n\"@odata.type\":\"#microsoft.graph.groupPolicyPresentationValueText\",\n\"value\":\"0\",\n\"presentation@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')/presentations('35c82072-a93b-4022-be14-8684c2f6fcc2')\"\n}\n],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')\"\n},\n{\n\"enabled\":true,\n\"presentationValues\":[],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('81c07ba0-7512-402d-b1f6-00856975cfab')\"\n},\n{\n\"enabled\":true,\n\"presentationValues\":[],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('61b07a01-7e60-4127-b086-f6b32458a5c5')\"\n},\n],\n\"updated\":[],\n\"deletedIds\":[]\n}", - "Type": "Admin", - "GUID": "7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json" -} diff --git a/Config/7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json b/Config/7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json deleted file mode 100644 index 3e7e82b550fb..000000000000 --- a/Config/7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Displayname": "CIPP Default: Enable Bitlocker Encryption for OS drives", - "Description": "Enables Bitlocker and stores the key in Azure AD for system Drives", - "RAWJson": "{\"id\":\"00000000-0000-0000-0000-000000000000\",\"displayName\":\"CIPP: Enable Bitlocker Encryption\",\"roleScopeTagIds\":[\"0\"],\"@odata.type\":\"#microsoft.graph.windows10EndpointProtectionConfiguration\",\"applicationGuardEnabledOptions\":\"notConfigured\",\"firewallCertificateRevocationListCheckMethod\":\"deviceDefault\",\"firewallPacketQueueingMethod\":\"deviceDefault\",\"deviceGuardLocalSystemAuthorityCredentialGuardSettings\":\"notConfigured\",\"defenderSecurityCenterNotificationsFromApp\":\"notConfigured\",\"windowsDefenderTamperProtection\":\"notConfigured\",\"defenderSecurityCenterITContactDisplay\":\"notConfigured\",\"xboxServicesAccessoryManagementServiceStartupMode\":\"manual\",\"xboxServicesLiveAuthManagerServiceStartupMode\":\"manual\",\"xboxServicesLiveGameSaveServiceStartupMode\":\"manual\",\"xboxServicesLiveNetworkingServiceStartupMode\":\"manual\",\"applicationGuardBlockClipboardSharing\":\"notConfigured\",\"defenderPreventCredentialStealingType\":\"notConfigured\",\"defenderAdobeReaderLaunchChildProcess\":\"notConfigured\",\"defenderOfficeCommunicationAppsLaunchChildProcess\":\"notConfigured\",\"defenderAdvancedRansomewareProtectionType\":\"notConfigured\",\"defenderNetworkProtectionType\":\"notConfigured\",\"localSecurityOptionsFormatAndEjectOfRemovableMediaAllowedUser\":\"notConfigured\",\"localSecurityOptionsSmartCardRemovalBehavior\":\"lockWorkstation\",\"localSecurityOptionsInformationDisplayedOnLockScreen\":\"notConfigured\",\"localSecurityOptionsMinimumSessionSecurityForNtlmSspBasedClients\":\"none\",\"localSecurityOptionsMinimumSessionSecurityForNtlmSspBasedServers\":\"none\",\"lanManagerAuthenticationLevel\":\"lmAndNltm\",\"localSecurityOptionsAdministratorElevationPromptBehavior\":\"notConfigured\",\"localSecurityOptionsStandardUserElevationPromptBehavior\":\"notConfigured\",\"userRightsAccessCredentialManagerAsTrustedCaller\":null,\"userRightsLocalLogOn\":null,\"userRightsAllowAccessFromNetwork\":null,\"userRightsActAsPartOfTheOperatingSystem\":null,\"userRightsBackupData\":null,\"userRightsChangeSystemTime\":null,\"userRightsCreateGlobalObjects\":null,\"userRightsCreatePageFile\":null,\"userRightsCreatePermanentSharedObjects\":null,\"userRightsCreateSymbolicLinks\":null,\"userRightsCreateToken\":null,\"userRightsDebugPrograms\":null,\"userRightsBlockAccessFromNetwork\":null,\"userRightsDenyLocalLogOn\":null,\"userRightsRemoteDesktopServicesLogOn\":null,\"userRightsDelegation\":null,\"userRightsGenerateSecurityAudits\":null,\"userRightsImpersonateClient\":null,\"userRightsIncreaseSchedulingPriority\":null,\"userRightsLoadUnloadDrivers\":null,\"userRightsLockMemory\":null,\"userRightsManageAuditingAndSecurityLogs\":null,\"userRightsManageVolumes\":null,\"userRightsModifyFirmwareEnvironment\":null,\"userRightsModifyObjectLabels\":null,\"userRightsProfileSingleProcess\":null,\"userRightsRemoteShutdown\":null,\"userRightsRestoreData\":null,\"userRightsTakeOwnership\":null,\"bitLockerRecoveryPasswordRotation\":\"notConfigured\",\"bitLockerPrebootRecoveryMsgURLOption\":\"default\",\"bitLockerEncryptDevice\":true,\"bitLockerDisableWarningForOtherDiskEncryption\":true,\"bitLockerAllowStandardUserEncryption\":true,\"bitLockerSyntheticSystemDrivePolicybitLockerDriveRecovery\":true,\"applicationGuardAllowPrintToPDF\":false,\"applicationGuardAllowPrintToXPS\":false,\"applicationGuardAllowPrintToLocalPrinters\":false,\"applicationGuardAllowPrintToNetworkPrinters\":false,\"bitLockerFixedDrivePolicy\":{\"requireEncryptionForWriteAccess\":false,\"recoveryOptions\":null,\"encryptionMethod\":null},\"bitLockerRemovableDrivePolicy\":{\"requireEncryptionForWriteAccess\":false,\"encryptionMethod\":null},\"bitLockerSystemDrivePolicy\":{\"startupAuthenticationRequired\":true,\"startupAuthenticationTpmUsage\":\"allowed\",\"startupAuthenticationTpmPinUsage\":\"allowed\",\"startupAuthenticationTpmKeyUsage\":\"allowed\",\"startupAuthenticationTpmPinAndKeyUsage\":\"allowed\",\"startupAuthenticationBlockWithoutTpmChip\":false,\"minimumPinLength\":null,\"recoveryOptions\":{\"blockDataRecoveryAgent\":false,\"recoveryPasswordUsage\":\"allowed\",\"recoveryKeyUsage\":\"allowed\",\"enableRecoveryInformationSaveToStore\":true,\"recoveryInformationToStore\":\"passwordAndKey\",\"enableBitLockerAfterRecoveryInformationToStore\":true},\"prebootRecoveryEnableMessageAndUrl\":false,\"encryptionMethod\":null},\"firewallProfileDomain\":null,\"firewallProfilePrivate\":null,\"firewallProfilePublic\":null,\"deviceGuardEnableVirtualizationBasedSecurity\":false,\"deviceGuardEnableSecureBootWithDMA\":false}", - "Type": "Device", - "GUID": "7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json" -} diff --git a/Config/8d57edc3-071d-42e7-86b1-126720645ac6.TransportRuleTemplate.json b/Config/8d57edc3-071d-42e7-86b1-126720645ac6.TransportRuleTemplate.json deleted file mode 100644 index f6d9ccbac6ff..000000000000 --- a/Config/8d57edc3-071d-42e7-86b1-126720645ac6.TransportRuleTemplate.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "Block Specific email addresses with a hard rejection", - "applyome": false, - "attachmenthasexecutablecontent": false, - "attachmentispasswordprotected": false, - "attachmentisunsupported": false, - "attachmentprocessinglimitexceeded": false, - "comments": "\n", - "deletemessage": false, - "exceptifattachmenthasexecutablecontent": false, - "exceptifattachmentispasswordprotected": false, - "exceptifattachmentisunsupported": false, - "exceptifattachmentprocessinglimitexceeded": false, - "exceptifhasnoclassification": false, - "exceptifhassenderoverride": false, - "from": ["SomeSpammer@spam.com"], - "hasnoclassification": false, - "hassenderoverride": false, - "mode": "enforce", - "moderatemessagebymanager": false, - "quarantine": false, - "recipientaddresstype": "resolved", - "rejectmessageenhancedstatuscode": "5.7.1", - "rejectmessagereasontext": "Your email has been rejected.", - "removeome": false, - "removeomev2": false, - "removermsattachmentencryption": false, - "routemessageoutboundrequiretls": false, - "ruleerroraction": "ignore", - "rulesubtype": "none", - "senderaddresslocation": "header", - "stopruleprocessing": false, - "uselegacyregex": false -} diff --git a/Config/CIPPDefaultTable.BPATemplate.json b/Config/CIPPDefaultTable.BPATemplate.json deleted file mode 100644 index 9cdf9c2f5f9d..000000000000 --- a/Config/CIPPDefaultTable.BPATemplate.json +++ /dev/null @@ -1,192 +0,0 @@ -{ - "name": "CIPP Best Practices v1.0 - Table view", - "style": "Table", - "Fields": [ - { - "name": "PasswordNeverExpires", - "API": "Graph", - "URL": "https://graph.microsoft.com/beta/domains", - "ExtractFields": ["passwordValidityPeriodInDays"], - "where": "$_.passwordValidityPeriodInDays -eq 2147483647", - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "Password Never Expires", - "value": "PasswordNeverExpires", - "formatter": "bool" - } - ] - }, - { - "name": "OAuthAppConsent", - "API": "Graph", - "URL": "https://graph.microsoft.com/v1.0/policies/authorizationPolicy?$select=defaultUserRolePermissions", - "ExtractFields": ["defaultuserrolepermissions"], - "where": "'ManagePermissionGrantsForSelf.microsoft-user-default-legacy' -notin $_.defaultuserrolepermissions.permissionGrantPoliciesAssigned", - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "OAuth App Consent", - "value": "OAuthAppConsent", - "formatter": "bool" - } - ] - }, - { - "name": "UnifiedAuditLog", - "API": "Exchange", - "Command": "Get-AdminAuditLogConfig", - "ExtractFields": ["UnifiedAuditLogIngestionEnabled"], - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "Unified Audit Log", - "value": "UnifiedAuditLog", - "formatter": "bool" - } - ] - }, - { - "name": "MFANudgeState", - "API": "Graph", - "URL": "https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy", - "ExtractFields": ["registrationEnforcement"], - "StoreAs": "bool", - "where": "$_.registrationEnforcement.authenticationMethodsRegistrationCampaign.state -eq 'Enabled'", - "FrontendFields": [ - { - "name": "MFA Registration Campaign Enabled", - "value": "MFANudgeState", - "formatter": "bool" - } - ] - }, - { - "name": "TAPEnabled", - "API": "Graph", - "URL": "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass", - "ExtractFields": ["State"], - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "Temporary Access Pass Enabled", - "value": "TAPEnabled", - "formatter": "bool" - } - ] - }, - { - "name": "SecureDefaultState", - "API": "Graph", - "URL": "https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy", - "ExtractFields": ["IsEnabled"], - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "Secure Defaults State Enabled", - "value": "SecureDefaultState", - "formatter": "warnBool" - } - ] - }, - { - "name": "AnonymousPrivacyReports", - "API": "Graph", - "URL": "https://graph.microsoft.com/beta/admin/reportSettings", - "ExtractFields": ["displayConcealedNames"], - "StoreAs": "bool", - "where": "$_.displayConcealedNames -eq $false", - "FrontendFields": [ - { - "name": "Anonymous Privacy Reports", - "value": "AnonymousPrivacyReports", - "formatter": "reverseBool" - } - ] - }, - { - "name": "MessageCopyforSentAsDisabled", - "API": "Exchange", - "Command": "Get-Mailbox", - "Parameters": { - "RecipientTypeDetails": ["SharedMailbox", "UserMailbox"] - }, - "where": "$_.MessageCopyForSentAsEnabled -eq $false", - "ExtractFields": ["userprincipalname", "messageCopyForSentAsEnabled"], - "StoreAs": "JSON", - "FrontendFields": [ - { - "name": "Message Copy for Sent-As Disabled", - "formatter": "table", - "value": "MessageCopyforSentAsDisabled" - } - ] - }, - { - "name": "SharedMailboxeswithenabledusers", - "API": "Exchange", - "Command": "Get-Mailbox", - "Parameters": { - "RecipientTypeDetails": "SharedMailbox" - }, - "where": "$_.accountDisabled -eq $false", - "ExtractFields": ["userprincipalname", "accountDisabled"], - "StoreAs": "JSON", - "FrontendFields": [ - { - "name": "Shared Mailboxes with enabled users", - "formatter": "table", - "value": "SharedMailboxeswithenabledusers" - } - ] - }, - { - "name": "Unusedlicenses", - "API": "CIPPFunction", - "Command": "Get-CIPPLicenseOverview", - "ExtractFields": [ - "License", - "TotalLicenses", - "availableUnits", - "CountUsed" - ], - "StoreAs": "JSON", - "where": "$_.availableUnits -gt 0", - "FrontendFields": [ - { - "name": "Unused licenses", - "formatter": "table", - "value": "Unusedlicenses" - } - ] - }, - { - "name": "CurrentSecureScore", - "API": "Graph", - "URL": "https://graph.microsoft.com/beta/security/secureScores?$top=1", - "Parameters": { - "Nopagination": true - }, - "ExtractFields": ["currentScore", "maxScore", "averageComparativeScores"], - "StoreAs": "JSON", - "FrontendFields": [ - { - "name": "Current Secure Score", - "value": "CurrentSecureScore.currentScore" - }, - { - "name": "Max Secure Score", - "value": "CurrentSecureScore.maxScore" - }, - { - "name": "Average Comparative Score (All Tenants)", - "value": "CurrentSecureScore.averageComparativeScores[0].averageScore" - }, - { - "name": "Average Comparative Score (Similiar Size Tenants)", - "value": "CurrentSecureScore.averageComparativeScores[1].averageScore" - } - ] - } - ] -} diff --git a/Config/CIPPDefaultTenantPage.BPATemplate.json b/Config/CIPPDefaultTenantPage.BPATemplate.json deleted file mode 100644 index 48c62139c647..000000000000 --- a/Config/CIPPDefaultTenantPage.BPATemplate.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "name": "CIPP Best Practices v1.0 - Tenant view", - "style": "Tenant", - "Fields": [ - { - "name": "PasswordNeverExpires", - "UseExistingInfo": true, - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "Password Never Expires", - "value": "PasswordNeverExpires", - "formatter": "bool", - "desc": "This setting shows if your environment has enabled the password never expires setting. This setting is expected to be set to 'No'" - } - ] - }, - { - "name": "OAuthAppConsent", - "UseExistingInfo": true, - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "OAuth App Consent", - "value": "OAuthAppConsent", - "formatter": "bool", - "desc": "This setting shows if your environment has enabled OAuth App Consent. This setting is expected to be set to 'Yes'" - } - ] - }, - { - "name": "UnifiedAuditLog", - "UseExistingInfo": true, - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "Unified Audit Log", - "value": "UnifiedAuditLog", - "formatter": "bool", - "desc": "This setting shows if your environment has enabled the unified audit log. This setting is expected to be set to 'Yes'" - } - ] - }, - { - "name": "MFANudgeState", - "UseExistingInfo": true, - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "MFA Registration Campaign Enabled", - "value": "MFANudgeState", - "formatter": "bool", - "desc": "This setting shows if your environment has enabled the MFA registration campaign, also known as the MFA Nudge. This setting is recommended to be set to 'Yes'" - } - ] - }, - { - "name": "TAPEnabled", - "UseExistingInfo": true, - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "Temporary Access Pass Enabled", - "value": "TAPEnabled", - "formatter": "bool", - "desc": "This setting shows if your environment has enabled the temporary access pass feature." - } - ] - }, - { - "name": "SecureDefaultState", - "UseExistingInfo": true, - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "Secure Defaults State Enabled", - "value": "SecureDefaultState", - "formatter": "warnBool", - "desc": "This setting shows if your environment has enabled the secure defaults state. If you are using Conditional Access this setting may be set to `No`" - } - ] - }, - { - "name": "AnonymousPrivacyReports", - "UseExistingInfo": true, - "StoreAs": "bool", - "FrontendFields": [ - { - "name": "Anonymous Privacy Reports", - "value": "AnonymousPrivacyReports", - "formatter": "reverseBool", - "desc": "This setting shows if your environment has enabled the anonymous privacy reports, these will need to be disabled to be able to view mailboxes and onedrive reports" - } - ] - }, - { - "name": "MessageCopyforSentAsDisabled", - "UseExistingInfo": true, - "StoreAs": "JSON", - "FrontendFields": [ - { - "name": "Message Copy for Sent-As Disabled", - "formatter": "table", - "value": "MessageCopyforSentAsDisabled", - "desc": "These are the mailboxes that have the MessageCopyForSentAsDisabled setting enabled." - } - ] - }, - { - "name": "SharedMailboxeswithenabledusers", - "UseExistingInfo": true, - "StoreAs": "JSON", - "FrontendFields": [ - { - "name": "Shared Mailboxes with enabled users", - "formatter": "table", - "value": "SharedMailboxeswithenabledusers", - "desc": "These are the shared mailboxes that have enabled users." - } - ] - }, - { - "name": "Unusedlicenses", - "UseExistingInfo": true, - "StoreAs": "JSON", - "FrontendFields": [ - { - "name": "Unused licenses", - "formatter": "table", - "value": "Unusedlicenses", - "desc": "These are the licenses that are not assigned to an user, but have been purchased." - } - ] - }, - { - "name": "CurrentSecureScore", - "UseExistingInfo": true, - "StoreAs": "JSON", - "FrontendFields": [ - { - "name": "Current Secure Score", - "value": "CurrentSecureScore.currentScore", - "desc": "The current Secure Score for this tenant. This is the sum of all the individual controls that have been implemented.", - "formatter": "number" - }, - { - "name": "Max Secure Score", - "value": "CurrentSecureScore.maxScore", - "desc": "The maximum Secure Score for this tenant. This is the sum of all the individual controls that can be implemented.", - "formatter": "number" - } - ] - } - ] -} diff --git a/Config/ExcludeSkuList.JSON b/Config/ExcludeSkuList.JSON deleted file mode 100644 index e2c80e82a1e0..000000000000 --- a/Config/ExcludeSkuList.JSON +++ /dev/null @@ -1,186 +0,0 @@ -[ - { - "GUID": "90d8b3f8-712e-4f7b-aa1e-62e7ae6cbe96", - "Product_Display_Name": "Business Apps (free)" - }, - { - "GUID": "90d8b3f8-712e-4f7b-aa1e-62e7ae6cbe96", - "Product_Display_Name": "Business Apps (free)" - }, - { - "GUID": "f30db892-07e9-47e9-837c-80727f46fd3d", - "Product_Display_Name": "MICROSOFT FLOW FREE" - }, - { - "GUID": "f30db892-07e9-47e9-837c-80727f46fd3d", - "Product_Display_Name": "MICROSOFT FLOW FREE" - }, - { - "GUID": "f30db892-07e9-47e9-837c-80727f46fd3d", - "Product_Display_Name": "MICROSOFT FLOW FREE" - }, - { - "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", - "Product_Display_Name": "MICROSOFT TEAMS (FREE)" - }, - { - "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", - "Product_Display_Name": "MICROSOFT TEAMS (FREE)" - }, - { - "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", - "Product_Display_Name": "MICROSOFT TEAMS (FREE)" - }, - { - "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", - "Product_Display_Name": "MICROSOFT TEAMS (FREE)" - }, - { - "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", - "Product_Display_Name": "MICROSOFT TEAMS (FREE)" - }, - { - "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", - "Product_Display_Name": "MICROSOFT TEAMS (FREE)" - }, - { - "GUID": "a403ebcc-fae0-4ca2-8c8c-7a907fd6c235", - "Product_Display_Name": "Power BI (free)" - }, - { - "GUID": "a403ebcc-fae0-4ca2-8c8c-7a907fd6c235", - "Product_Display_Name": "Power BI (free)" - }, - { - "GUID": "61e6bd70-fbdb-4deb-82ea-912842f39431", - "Product_Display_Name": "Dynamics 365 Customer Service Insights Trial" - }, - { - "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", - "Product_Display_Name": "Dynamics 365 Customer Voice Trial" - }, - { - "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", - "Product_Display_Name": "Dynamics 365 Customer Voice Trial" - }, - { - "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", - "Product_Display_Name": "Dynamics 365 Customer Voice Trial" - }, - { - "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", - "Product_Display_Name": "Dynamics 365 Customer Voice Trial" - }, - { - "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", - "Product_Display_Name": "Dynamics 365 Customer Voice Trial" - }, - { - "GUID": "338148b6-1b11-4102-afb9-f92b6cdc0f8d", - "Product_Display_Name": "DYNAMICS 365 P1 TRIAL FOR INFORMATION WORKERS" - }, - { - "GUID": "338148b6-1b11-4102-afb9-f92b6cdc0f8d", - "Product_Display_Name": "DYNAMICS 365 P1 TRIAL FOR INFORMATION WORKERS" - }, - { - "GUID": "fcecd1f9-a91e-488d-a918-a96cdb6ce2b0", - "Product_Display_Name": "Microsoft Dynamics AX7 User Trial" - }, - { - "GUID": "fcecd1f9-a91e-488d-a918-a96cdb6ce2b0", - "Product_Display_Name": "Microsoft Dynamics AX7 User Trial" - }, - { - "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", - "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" - }, - { - "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", - "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" - }, - { - "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", - "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" - }, - { - "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", - "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" - }, - { - "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", - "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", - "Product_Display_Name": "Microsoft Teams Trial" - }, - { - "GUID": "606b54a9-78d8-4298-ad8b-df6ef4481c80", - "Product_Display_Name": "Power Virtual Agents Viral Trial" - }, - { - "GUID": "606b54a9-78d8-4298-ad8b-df6ef4481c80", - "Product_Display_Name": "Power Virtual Agents Viral Trial" - }, - { - "GUID": "606b54a9-78d8-4298-ad8b-df6ef4481c80", - "Product_Display_Name": "Power Virtual Agents Viral Trial" - }, - { - "GUID": "1f2f344a-700d-42c9-9427-5cea1d5d7ba6", - "Product_Display_Name": "MICROSOFT STREAM" - }, - { - "GUID": "1f2f344a-700d-42c9-9427-5cea1d5d7ba6", - "Product_Display_Name": "MICROSOFT STREAM" - }, - { - "GUID": "6470687e-a428-4b7a-bef2-8a291ad947c9", - "Product_Display_Name": "WINDOWS STORE FOR BUSINESS" - }, - { - "GUID": "6470687e-a428-4b7a-bef2-8a291ad947c9", - "Product_Display_Name": "WINDOWS STORE FOR BUSINESS" - }, - { - "GUID": "710779e8-3d4a-4c88-adb9-386c958d1fdf", - "Product_Display_Name": "MICROSOFT TEAMS EXPLORATORY" - } -] diff --git a/Config/SharePoint.BPATemplate.json b/Config/SharePoint.BPATemplate.json deleted file mode 100644 index 99ba97e5caa7..000000000000 --- a/Config/SharePoint.BPATemplate.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "name": "CIPP SharePoint Report v1.0 - Table view", - "style": "Table", - "Fields": [ - { - "name": "SharepointSettings", - "API": "Graph", - "URL": "https://graph.microsoft.com/beta/admin/sharepoint/settings", - "Parameters": { - "asApp": "True" - }, - "ExtractFields": [ - "sharingCapability", - "isMacSyncAppEnabled", - "isResharingByExternalUsersEnabled", - "isUnmanagedSyncAppForTenantRestricted", - "isSiteCreationEnabled", - "deletedUserPersonalSiteRetentionPeriodInDays" - ], - "StoreAs": "JSON", - "FrontendFields": [ - { - "name": "Sharing capability", - "value": "SharepointSettings.sharingCapability", - "formatter": "string" - }, - { - "name": "Mac Sync Enabled", - "value": "SharepointSettings.isMacSyncAppEnabled", - "formatter": "warnBool" - }, - { - "name": "Resharing by external users", - "value": "isResharingByExternalUsersEnabled", - "formatter": "reverseBool" - }, - { - "name": "Allow users to sync from unmanaged devices", - "value": "SharepointSettings.isUnmanagedSyncAppForTenantRestricted", - "formatter": "bool" - }, - { - "name": "Site creation by standards users enabled", - "value": "SharepointSettings.isSiteCreationEnabled", - "formatter": "reverseBool" - }, - { - "name": "Deleted user data rention(days)", - "value": "SharepointSettings.deletedUserPersonalSiteRetentionPeriodInDays", - "formatter": "string" - } - ] - }, - { - "name": "WebtimeOut", - "API": "Graph", - "URL": "https://graph.microsoft.com/beta/policies/activityBasedTimeoutPolicies", - "ExtractFields": ["definition"], - "StoreAs": "bool", - "where": "$_.definition -like '*WebSessionIdleTimeout*'", - "FrontendFields": [ - { - "name": "Web Time-Out enabled", - "value": "WebtimeOut", - "formatter": "bool" - } - ] - } - ] -} diff --git a/Config/adf9f6d1-36fb-438b-a82b-71f3af402b6c.TransportRuleTemplate.json b/Config/adf9f6d1-36fb-438b-a82b-71f3af402b6c.TransportRuleTemplate.json deleted file mode 100644 index 42abed3204d2..000000000000 --- a/Config/adf9f6d1-36fb-438b-a82b-71f3af402b6c.TransportRuleTemplate.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Block External Auto-forwarding", - "applyome": false, - "attachmenthasexecutablecontent": false, - "attachmentispasswordprotected": false, - "attachmentisunsupported": false, - "attachmentprocessinglimitexceeded": false, - "deletemessage": false, - "exceptifattachmenthasexecutablecontent": false, - "exceptifattachmentispasswordprotected": false, - "exceptifattachmentisunsupported": false, - "exceptifattachmentprocessinglimitexceeded": false, - "exceptifhasnoclassification": false, - "exceptifhassenderoverride": false, - "fromscope": "inorganization", - "hasnoclassification": false, - "hassenderoverride": false, - "messagetypematches": "autoforward", - "mode": "enforce", - "moderatemessagebymanager": false, - "quarantine": false, - "recipientaddresstype": "resolved", - "rejectmessageenhancedstatuscode": "5.7.1", - "rejectmessagereasontext": "to improve security, auto-forwarding rules to external addresses has been disabled. please contact your Microsoft partner if you'd like to set up an exception.", - "removeome": false, - "removeomev2": false, - "removermsattachmentencryption": false, - "routemessageoutboundrequiretls": false, - "ruleerroraction": "ignore", - "rulesubtype": "none", - "senderaddresslocation": "header", - "senttoscope": "notinorganization", - "stopruleprocessing": false, - "uselegacyregex": false -} diff --git a/Config/b39b8d85-1531-420e-baeb-b388f565418b.TransportRuleTemplate.json b/Config/b39b8d85-1531-420e-baeb-b388f565418b.TransportRuleTemplate.json deleted file mode 100644 index 73995dab2028..000000000000 --- a/Config/b39b8d85-1531-420e-baeb-b388f565418b.TransportRuleTemplate.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "if a message subject contains \"encrypt:\" encrypt message.", - "applyome": false, - "applyrightsprotectiontemplate": "encrypt", - "attachmenthasexecutablecontent": false, - "attachmentispasswordprotected": false, - "attachmentisunsupported": false, - "attachmentprocessinglimitexceeded": false, - "comments": "\n", - "deletemessage": false, - "exceptifattachmenthasexecutablecontent": false, - "exceptifattachmentispasswordprotected": false, - "exceptifattachmentisunsupported": false, - "exceptifattachmentprocessinglimitexceeded": false, - "exceptifhasnoclassification": false, - "exceptifhassenderoverride": false, - "hasnoclassification": false, - "hassenderoverride": false, - "mode": "enforce", - "moderatemessagebymanager": false, - "quarantine": false, - "recipientaddresstype": "resolved", - "removeome": false, - "removeomev2": false, - "removermsattachmentencryption": false, - "routemessageoutboundrequiretls": false, - "ruleerroraction": "ignore", - "rulesubtype": "none", - "senderaddresslocation": "header", - "stopruleprocessing": false, - "subjectcontainswords": ["encrypt:"], - "uselegacyregex": false -} diff --git a/Config/b79d0123-3105-4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json b/Config/b79d0123-3105-4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json deleted file mode 100644 index 987c08d764dc..000000000000 --- a/Config/b79d0123-3105-4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Displayname": "CIPP Default: Automatic Configuration of Outlook", - "Description": "Configures the first profile on a device to always use the e-mail address of the currently logged on user.", - "RAWJson": "{\"name\":\"Automatic configuration of Outlook\",\"description\":\"\",\"platforms\":\"windows10\",\"technologies\":\"mdm\",\"roleScopeTagIds\":[\"0\"],\"settings\":[{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationSetting\",\"settingInstance\":{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance\",\"settingDefinitionId\":\"user_vendor_msft_policy_config_outlk16v2~policy~l_microsoftofficeoutlook~l_toolsaccounts~l_exchangesettings_l_automaticallyconfigureprofilebasedonactiveonce\",\"choiceSettingValue\":{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationChoiceSettingValue\",\"value\":\"user_vendor_msft_policy_config_outlk16v2~policy~l_microsoftofficeoutlook~l_toolsaccounts~l_exchangesettings_l_automaticallyconfigureprofilebasedonactiveonce_1\",\"children\":[]}}}]}", - "Type": "Catalog", - "GUID": "b79d0123-3105-4c5d-9f15-62cc7a7eb7e1" -} diff --git a/Config/cba836bb-33b7-4c50-88be-ee80f74cbeac.CATemplate.json b/Config/cba836bb-33b7-4c50-88be-ee80f74cbeac.CATemplate.json deleted file mode 100644 index 0bece075c9d2..000000000000 --- a/Config/cba836bb-33b7-4c50-88be-ee80f74cbeac.CATemplate.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "grantControls": { - "termsOfUse": [], - "builtInControls": ["mfa"], - "customAuthenticationFactors": [], - "operator": "OR" - }, - "state": "enabled", - "conditions": { - "servicePrincipalRiskLevels": [], - "userRiskLevels": [], - "deviceStates": null, - "signInRiskLevels": [], - "clientAppTypes": ["all"], - "devices": null, - "locations": null, - "applications": { - "includeApplications": ["d414ee2d-73e5-4e5b-bb16-03ef55fea597"], - "includeUserActions": [], - "includeAuthenticationContextClassReferences": [], - "excludeApplications": [] - }, - "users": { - "includeUsers": ["All"], - "excludeRoles": [], - "includeRoles": [], - "excludeUsers": [], - "includeGroups": [], - "excludeGroups": [] - }, - "platforms": null, - "times": null, - "clientApplications": null - }, - "displayName": "Enforce Multi-factor authentication for Static Web Apps" -} diff --git a/Config/e82dd7d8-3f13-43cd-bdd4-896e0958493b.TransportRuleTemplate.json b/Config/e82dd7d8-3f13-43cd-bdd4-896e0958493b.TransportRuleTemplate.json deleted file mode 100644 index 4109bd25294e..000000000000 --- a/Config/e82dd7d8-3f13-43cd-bdd4-896e0958493b.TransportRuleTemplate.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "Block emails silently based on their subject", - "applyome": false, - "attachmenthasexecutablecontent": false, - "attachmentispasswordprotected": false, - "attachmentisunsupported": false, - "attachmentprocessinglimitexceeded": false, - "comments": "\n", - "deletemessage": true, - "exceptifattachmenthasexecutablecontent": false, - "exceptifattachmentispasswordprotected": false, - "exceptifattachmentisunsupported": false, - "exceptifattachmentprocessinglimitexceeded": false, - "exceptifhasnoclassification": false, - "exceptifhassenderoverride": false, - "hasnoclassification": false, - "hassenderoverride": false, - "mode": "enforce", - "moderatemessagebymanager": false, - "quarantine": false, - "recipientaddresstype": "resolved", - "removeome": false, - "removeomev2": false, - "removermsattachmentencryption": false, - "routemessageoutboundrequiretls": false, - "ruleerroraction": "ignore", - "rulesubtype": "none", - "senderaddresslocation": "header", - "stopruleprocessing": false, - "subjectorbodycontainswords": ["This subject"], - "uselegacyregex": false -} diff --git a/Config/f8be7e58-2419-40a8-a739-714bf5deff90.CATemplate.json b/Config/f8be7e58-2419-40a8-a739-714bf5deff90.CATemplate.json deleted file mode 100644 index 966e83ecf3cb..000000000000 --- a/Config/f8be7e58-2419-40a8-a739-714bf5deff90.CATemplate.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "state": "enabled", - "grantControls": { - "builtInControls": ["block"], - "operator": "OR", - "termsOfUse": [], - "customAuthenticationFactors": [] - }, - "conditions": { - "times": null, - "locations": null, - "signInRiskLevels": [], - "devices": null, - "deviceStates": null, - "users": { - "excludeRoles": [], - "excludeUsers": [], - "excludeGroups": [], - "includeUsers": ["All"], - "includeRoles": [], - "includeGroups": [] - }, - "servicePrincipalRiskLevels": [], - "userRiskLevels": [], - "clientAppTypes": ["exchangeActiveSync", "other"], - "platforms": null, - "clientApplications": null, - "applications": { - "includeApplications": ["None"], - "includeUserActions": [], - "includeAuthenticationContextClassReferences": [], - "excludeApplications": [] - } - }, - "displayName": "Block Legacy Authentication" -} diff --git a/version_latest.txt b/Modules/CIPPCore/Public/version_latest.txt similarity index 100% rename from version_latest.txt rename to Modules/CIPPCore/Public/version_latest.txt From a25e4981eb5e55a3ebd9031acc948670a3bb8ca6 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 00:36:36 +0100 Subject: [PATCH 64/77] fix pathing issue --- .../Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 index 1c42c21d9470..939431563611 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 @@ -11,6 +11,7 @@ Function Invoke-ListTransportRulesTemplates { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $Table = Get-CippTable -tablename 'templates' + Set-Location (Get-Item $PSScriptRoot).Parent.FullName $Templates = Get-ChildItem 'Config\*.TransportRuleTemplate.json' | ForEach-Object { From 102b40e5e89b92b88ae3ff8e93b916f36fe4edee Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 00:51:57 +0100 Subject: [PATCH 65/77] restored choco app folder for intune files --- AddChocoApp/Choco.App.xml | 15 ++++ AddChocoApp/Choco.app.json | 65 ++++++++++++++++++ AddChocoApp/IntunePackage.intunewin | Bin 0 -> 28368 bytes AddChocoApp/IntunePackage/Install.ps1 | 61 ++++++++++++++++ .../IntunePackage/IntuneWinAppUtil.exe | Bin 0 -> 54160 bytes AddChocoApp/IntunePackage/Uninstall.ps1 | 9 +++ AddChocoApp/function.json | 24 +++++++ AddChocoApp/run.ps1 | 58 ++++++++++++++++ 8 files changed, 232 insertions(+) create mode 100644 AddChocoApp/Choco.App.xml create mode 100644 AddChocoApp/Choco.app.json create mode 100644 AddChocoApp/IntunePackage.intunewin create mode 100644 AddChocoApp/IntunePackage/Install.ps1 create mode 100644 AddChocoApp/IntunePackage/IntuneWinAppUtil.exe create mode 100644 AddChocoApp/IntunePackage/Uninstall.ps1 create mode 100644 AddChocoApp/function.json create mode 100644 AddChocoApp/run.ps1 diff --git a/AddChocoApp/Choco.App.xml b/AddChocoApp/Choco.App.xml new file mode 100644 index 000000000000..fb0aac775d73 --- /dev/null +++ b/AddChocoApp/Choco.App.xml @@ -0,0 +1,15 @@ + + Install.ps1 + 28319 + IntunePackage.intunewin + Install.ps1 + + bmoyHXFtIws7JrnXNDV4rjzap+Be+4ZJEDJkTfbVIL8= + xNh8ZUZ6TLsAtihUEAU/NHiRfutDzz+eSgEdpaXUo9Q= + 3aQFPhO8ywEC4Ojby1lR0w== + PXX+hj3DXEpzMEMYBDXmAIlSyDIGuAwmAHIQpZIt8hU= + ProfileVersion1 + fx41h3rGZYZO3Jux7JnPgatlmpMc2ZFIZS8ipF5VDDw= + SHA256 + + \ No newline at end of file diff --git a/AddChocoApp/Choco.app.json b/AddChocoApp/Choco.app.json new file mode 100644 index 000000000000..7ef3f82d640f --- /dev/null +++ b/AddChocoApp/Choco.app.json @@ -0,0 +1,65 @@ +{ + "displayName": "", + "installCommandLine": "", + "uninstallCommandLine": "", + "description": "", + "developer": " ", + "owner": " ", + "informationUrl": " ", + "privacyInformationUrl": " ", + "fileName": "IntunePackage.intunewin", + "@odata.type": "#microsoft.graph.win32LobApp", + "applicableArchitectures": "x86, x64", + + "installExperience": { + "runAsAccount": "user", + "deviceRestartBehavior": "allow", + "@odata.type": "microsoft.graph.win32LobAppInstallExperience" + }, + "detectionRules": [ + { + "@odata.type": "#microsoft.graph.win32LobAppFileSystemDetection", + "path": "%programfiles%\\7-zip", + "fileOrFolderName": "7z.exe", + "check32BitOn64System": false, + "detectionType": "exists" + } + ], + "returncode": [ + { + "returnCode": 0, + "type": "success", + "@odata.type": "#microsoft.graph.win32LobAppReturnCode" + }, + { + "returnCode": 1707, + "type": "Success", + "@odata.type": "#microsoft.graph.win32LobAppReturnCode" + }, + { + "returnCode": 1641, + "type": "hardReboot", + "@odata.type": "#microsoft.graph.win32LobAppReturnCode" + }, + { + "returnCode": 1618, + "type": "retry", + "@odata.type": "#microsoft.graph.win32LobAppReturnCode" + }, + { + "returnCode": 3010, + "type": "softReboot", + "@odata.type": "#microsoft.graph.win32LobAppReturnCode" + } + ], + "minimumNumberOfProcessors": "1", + "minimumFreeDiskSpaceInMB": "8", + "minimumCpuSpeedInMHz": "4", + "minimumSupportedOperatingSystem": { + "@odata.type": "microsoft.graph.windowsMinimumOperatingSystem", + "v10_1607": true + }, + "notes": "CIPP Uploaded application", + "minimumMemoryInMB": "1", + "setupFilePath": "install.ps1" +} diff --git a/AddChocoApp/IntunePackage.intunewin b/AddChocoApp/IntunePackage.intunewin new file mode 100644 index 0000000000000000000000000000000000000000..f388884134ed6934bbe64c6593dcda5236d9ad09 GIT binary patch literal 28368 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�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 Date: Mon, 27 Nov 2023 01:33:15 +0100 Subject: [PATCH 66/77] Remove old functions --- ListFunctionParameters/function.json | 18 ------------------ ListMailboxRestores/function.json | 18 ------------------ ListUserSettings/function.json | 18 ------------------ 3 files changed, 54 deletions(-) delete mode 100644 ListFunctionParameters/function.json delete mode 100644 ListMailboxRestores/function.json delete mode 100644 ListUserSettings/function.json diff --git a/ListFunctionParameters/function.json b/ListFunctionParameters/function.json deleted file mode 100644 index bf6c3ef0c49a..000000000000 --- a/ListFunctionParameters/function.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1", - "entryPoint": "Receive-CippHttpTrigger", - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ListMailboxRestores/function.json b/ListMailboxRestores/function.json deleted file mode 100644 index bf6c3ef0c49a..000000000000 --- a/ListMailboxRestores/function.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1", - "entryPoint": "Receive-CippHttpTrigger", - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} diff --git a/ListUserSettings/function.json b/ListUserSettings/function.json deleted file mode 100644 index bf6c3ef0c49a..000000000000 --- a/ListUserSettings/function.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1", - "entryPoint": "Receive-CippHttpTrigger", - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} From 0d0029d7c899084bc69c2533b115704700b71c65 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 10:27:06 +0100 Subject: [PATCH 67/77] fixes minor bugs --- Standards_EnableAppConsentRequests/run.ps1 | 36 ++++++++++------------ 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/Standards_EnableAppConsentRequests/run.ps1 b/Standards_EnableAppConsentRequests/run.ps1 index 9075a12143c3..8f5c751752bc 100644 --- a/Standards_EnableAppConsentRequests/run.ps1 +++ b/Standards_EnableAppConsentRequests/run.ps1 @@ -2,35 +2,31 @@ param($tenant) try { # Get current state - $CurrentInfo = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy" -tenantid $Tenant + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy' -tenantid $Tenant # Change state to enabled with default settings - $CurrentInfo.isEnabled = "true" - $CurrentInfo.notifyReviewers = "true" - $CurrentInfo.remindersEnabled = "true" + $CurrentInfo.isEnabled = 'true' + $CurrentInfo.notifyReviewers = 'true' + $CurrentInfo.remindersEnabled = 'true' $CurrentInfo.requestDurationInDays = 30 # Get Global Admin role ID TODO: change to be able to chose role $Role = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleDefinitions?`$filter=(displayName eq 'Global Administrator')&`$select=displayName,id" -tenantid $Tenant - # System.Array is required to make the query work - $RoleReviewers = [System.Array]@{ - query = "/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq `'$($Role.id)`'" - queryType = 'MicrosoftGraph' - queryRoot = 'null' - } + $RoleReviewers = @(@{ + query = "/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq `'$($Role.id)`'" + queryType = 'MicrosoftGraph' + queryRoot = 'null' + }) # Set reviewers to Global Admins if not already set, this avoids overwriting existing reviewers and duplication of reviewers objects - if ($CurrentInfo.reviewers.query -notlike "*$($Role.id)*") { - $CurrentInfo.reviewers += $RoleReviewers + $CurrentInfo.reviewers = if ($CurrentInfo.reviewers.query -notlike "*$($Role.id)*") { + $RoleReviewers } - - # Convert info object to JSON - $body = ($CurrentInfo | ConvertTo-Json -Depth 10) - (New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy" -Type put -Body $body -ContentType "application/json") + $body = (ConvertTo-Json -Depth 10 -InputObject $CurrentInfo) + (New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy' -Type put -Body $body -ContentType 'application/json') - Write-LogMessage -API "Standards" -tenant $tenant -message "Enabled App consent admin requests" -sev Info + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Enabled App consent admin requests' -sev Info -} -catch { - Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable App consent admin requests. Error: $($_.exception.message)" -sev Error +} catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable App consent admin requests. Error: $($_.exception.message)" -sev Error } From 4d300de6f3b05c64287b2335b3f1f28ba7203872 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 11:54:49 +0100 Subject: [PATCH 68/77] added empty device removal rule --- .../Public/Remove-CIPPMobileDevice.ps1 | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 b/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 index d5d6d6b73228..b89bcbd5e3e4 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 @@ -4,26 +4,24 @@ function Remove-CIPPMobileDevice { $userid, $tenantFilter, $username, - $APIName = "Remove Mobile", + $APIName = 'Remove Mobile', $ExecutingUser ) try { - $devices = New-ExoRequest -tenantid $tenantFilter -cmdlet "Get-MobileDevice" -Anchor $username -cmdParams @{mailbox = $username } | ForEach-Object { + $devices = New-ExoRequest -tenantid $tenantFilter -cmdlet 'Get-MobileDevice' -Anchor $username -cmdParams @{mailbox = $username } | ForEach-Object { try { - New-ExoRequest -tenantid $tenantFilter -cmdlet "Remove-MobileDevice" -Anchor $username -cmdParams @{Identity = $_.Identity } + New-ExoRequest -tenantid $tenantFilter -cmdlet 'Remove-MobileDevice' -Anchor $username -cmdParams @{Identity = $_.Identity } "Removed device: $($_.FriendlyName)" - } - catch { + } catch { "Could not remove device: $($_.FriendlyName)" } } - - Write-LogMessage -user $ExecutingUser -API $APIName -message "Deleted mobile devices for $($username)" -Sev "Info" -tenant $tenantFilter + if (!$Devices) { 'No mobile devices have been removed as we could not find any' } + Write-LogMessage -user $ExecutingUser -API $APIName -message "Deleted mobile devices for $($username)" -Sev 'Info' -tenant $tenantFilter return $devices - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not delete mobile devices for $($username): $($_.Exception.Message)" -Sev "Error" -tenant $tenantFilter + } catch { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not delete mobile devices for $($username): $($_.Exception.Message)" -Sev 'Error' -tenant $tenantFilter return "Could not delete mobile devices for $($username). Error: $($_.Exception.Message)" } } From bec55c79b20f135952e659f59a5e21f1de379d4d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 12:16:48 +0100 Subject: [PATCH 69/77] release push --- Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 b/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 index b89bcbd5e3e4..e5ae407bf35e 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 @@ -17,7 +17,7 @@ function Remove-CIPPMobileDevice { "Could not remove device: $($_.FriendlyName)" } } - if (!$Devices) { 'No mobile devices have been removed as we could not find any' } + if (!$Devices) { $Devices ='No mobile devices have been removed as we could not find any' } Write-LogMessage -user $ExecutingUser -API $APIName -message "Deleted mobile devices for $($username)" -Sev 'Info' -tenant $tenantFilter return $devices } catch { From 6f17be7f9ded1a1974aef780900c16f0041a03f5 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 14:13:13 +0100 Subject: [PATCH 70/77] include config, force set path to public --- .gitignore | 1 - ...b46-4680-a035-9250bc675446.CATemplate.json | 41 ++++ ...41e6-86a5-f78cdcff5069.IntuneTemplate.json | 7 + ...4b3a-b84b-850d4b69f494.IntuneTemplate.json | 7 + ...460c-a4bd-391944908007.IntuneTemplate.json | 7 + ...4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json | 7 + ...4aae-89be-d83c44b5799f.IntuneTemplate.json | 7 + ...b1-126720645ac6.TransportRuleTemplate.json | 34 ++++ Config/CIPPDefaultTable.BPATemplate.json | 192 ++++++++++++++++++ Config/CIPPDefaultTenantPage.BPATemplate.json | 155 ++++++++++++++ Config/ExcludeSkuList.JSON | 186 +++++++++++++++++ Config/SharePoint.BPATemplate.json | 70 +++++++ ...2b-71f3af402b6c.TransportRuleTemplate.json | 35 ++++ ...eb-b388f565418b.TransportRuleTemplate.json | 33 +++ ...4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json | 7 + ...3b7-4c50-88be-ee80f74cbeac.CATemplate.json | 36 ++++ ...d4-896e0958493b.TransportRuleTemplate.json | 32 +++ ...419-40a8-a739-714bf5deff90.CATemplate.json | 36 ++++ Modules/CippEntrypoints/CippEntrypoints.psm1 | 4 +- .../version_latest.txt => version_latest.txt | 0 20 files changed, 895 insertions(+), 2 deletions(-) create mode 100644 Config/49a8069e-3b46-4680-a035-9250bc675446.CATemplate.json create mode 100644 Config/4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json create mode 100644 Config/59bd753c-4204-4b3a-b84b-850d4b69f494.IntuneTemplate.json create mode 100644 Config/7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json create mode 100644 Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json create mode 100644 Config/7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json create mode 100644 Config/8d57edc3-071d-42e7-86b1-126720645ac6.TransportRuleTemplate.json create mode 100644 Config/CIPPDefaultTable.BPATemplate.json create mode 100644 Config/CIPPDefaultTenantPage.BPATemplate.json create mode 100644 Config/ExcludeSkuList.JSON create mode 100644 Config/SharePoint.BPATemplate.json create mode 100644 Config/adf9f6d1-36fb-438b-a82b-71f3af402b6c.TransportRuleTemplate.json create mode 100644 Config/b39b8d85-1531-420e-baeb-b388f565418b.TransportRuleTemplate.json create mode 100644 Config/b79d0123-3105-4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json create mode 100644 Config/cba836bb-33b7-4c50-88be-ee80f74cbeac.CATemplate.json create mode 100644 Config/e82dd7d8-3f13-43cd-bdd4-896e0958493b.TransportRuleTemplate.json create mode 100644 Config/f8be7e58-2419-40a8-a739-714bf5deff90.CATemplate.json rename Modules/CIPPCore/Public/version_latest.txt => version_latest.txt (100%) diff --git a/.gitignore b/.gitignore index e052ecc4c278..0311022a9306 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,4 @@ chocoapps.cache Cache_* Logs ExcludedTenants -Config SendNotifications/config.json diff --git a/Config/49a8069e-3b46-4680-a035-9250bc675446.CATemplate.json b/Config/49a8069e-3b46-4680-a035-9250bc675446.CATemplate.json new file mode 100644 index 000000000000..5e9afa63f143 --- /dev/null +++ b/Config/49a8069e-3b46-4680-a035-9250bc675446.CATemplate.json @@ -0,0 +1,41 @@ +{ + "state": "enabled", + "grantControls": { + "builtInControls": ["mfa"], + "operator": "OR", + "termsOfUse": [], + "customAuthenticationFactors": [] + }, + "conditions": { + "times": null, + "locations": null, + "signInRiskLevels": [], + "devices": null, + "deviceStates": null, + "users": { + "excludeRoles": [], + "excludeUsers": [], + "excludeGroups": [], + "includeUsers": ["All"], + "includeRoles": [], + "includeGroups": [] + }, + "servicePrincipalRiskLevels": [], + "userRiskLevels": [], + "clientAppTypes": [ + "exchangeActiveSync", + "browser", + "mobileAppsAndDesktopClients", + "other" + ], + "platforms": null, + "clientApplications": null, + "applications": { + "includeApplications": ["All"], + "includeUserActions": [], + "includeAuthenticationContextClassReferences": [], + "excludeApplications": [] + } + }, + "displayName": "Enforce Multi factor authentication for each application" +} diff --git a/Config/4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json b/Config/4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json new file mode 100644 index 000000000000..f5951d846e4c --- /dev/null +++ b/Config/4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json @@ -0,0 +1,7 @@ +{ + "Displayname": "CIPP Default: Set screen lock time to 5 minutes", + "Description": "Sets the screen to lock after 5 minutes of inactivity.", + "RAWJson": "{\"name\":\"Set Screen Lockout to 5 minutes\",\"description\":\"\",\"platforms\":\"windows10\",\"technologies\":\"mdm\",\"roleScopeTagIds\":[\"0\"],\"settings\":[{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationSetting\",\"settingInstance\":{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance\",\"settingDefinitionId\":\"device_vendor_msft_policy_config_localpoliciessecurityoptions_interactivelogon_machineinactivitylimit_v2\",\"simpleSettingValue\":{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationIntegerSettingValue\",\"value\":300}}}]}", + "Type": "Catalog", + "GUID": "4d9206b0-4f96-41e6-86a5-f78cdcff5069.IntuneTemplate.json" +} diff --git a/Config/59bd753c-4204-4b3a-b84b-850d4b69f494.IntuneTemplate.json b/Config/59bd753c-4204-4b3a-b84b-850d4b69f494.IntuneTemplate.json new file mode 100644 index 000000000000..c55886283a8a --- /dev/null +++ b/Config/59bd753c-4204-4b3a-b84b-850d4b69f494.IntuneTemplate.json @@ -0,0 +1,7 @@ +{ + "Displayname": "LAPS", + "Description": "", + "RAWJson": "{\r\n \"name\": \"LAPS\",\r\n \"description\": \"\",\r\n \"settings\": [\r\n {\r\n \"id\": \"0\",\r\n \"settingInstance\": {\r\n \"@odata.type\": \"#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance\",\r\n \"settingDefinitionId\": \"device_vendor_msft_laps_policies_backupdirectory\",\r\n \"settingInstanceTemplateReference\": {\r\n \"settingInstanceTemplateId\": \"a3270f64-e493-499d-8900-90290f61ed8a\"\r\n },\r\n \"choiceSettingValue\": {\r\n \"value\": \"device_vendor_msft_laps_policies_backupdirectory_1\",\r\n \"settingValueTemplateReference\": {\r\n \"settingValueTemplateId\": \"4d90f03d-e14c-43c4-86da-681da96a2f92\",\r\n \"useTemplateDefault\": false\r\n },\r\n \"children\": [\r\n {\r\n \"@odata.type\": \"#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance\",\r\n \"settingDefinitionId\": \"device_vendor_msft_laps_policies_passwordagedays_aad\",\r\n \"settingInstanceTemplateReference\": null,\r\n \"simpleSettingValue\": {\r\n \"@odata.type\": \"#microsoft.graph.deviceManagementConfigurationIntegerSettingValue\",\r\n \"settingValueTemplateReference\": null,\r\n \"value\": 30\r\n }\r\n }\r\n ]\r\n }\r\n }\r\n },\r\n {\r\n \"id\": \"1\",\r\n \"settingInstance\": {\r\n \"@odata.type\": \"#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance\",\r\n \"settingDefinitionId\": \"device_vendor_msft_laps_policies_passwordcomplexity\",\r\n \"settingInstanceTemplateReference\": {\r\n \"settingInstanceTemplateId\": \"8a7459e8-1d1c-458a-8906-7b27d216de52\"\r\n },\r\n \"choiceSettingValue\": {\r\n \"value\": \"device_vendor_msft_laps_policies_passwordcomplexity_4\",\r\n \"settingValueTemplateReference\": {\r\n \"settingValueTemplateId\": \"aa883ab5-625e-4e3b-b830-a37a4bb8ce01\",\r\n \"useTemplateDefault\": false\r\n },\r\n \"children\": []\r\n }\r\n }\r\n }\r\n ],\r\n \"platforms\": \"windows10\",\r\n \"technologies\": \"mdm\",\r\n \"templateReference\": {\r\n \"templateId\": \"adc46e5a-f4aa-4ff6-aeff-4f27bc525796_1\",\r\n \"templateFamily\": \"endpointSecurityAccountProtection\",\r\n \"templateDisplayName\": \"Local admin password solution (Windows LAPS)\",\r\n \"templateDisplayVersion\": \"Version 1\"\r\n }\r\n}", + "Type": "Catalog", + "GUID": "59bd753c-4204-4b3a-b84b-850d4b69f494" +} diff --git a/Config/7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json b/Config/7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json new file mode 100644 index 000000000000..fde8371a4af5 --- /dev/null +++ b/Config/7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json @@ -0,0 +1,7 @@ +{ + "Displayname": "CIPP Default: Skip Autopilot User Setup Page", + "Description": "Skips the autopilot user setup page", + "RAWJson": "{\"id\":\"00000000-0000-0000-0000-000000000000\",\"displayName\":\"Skip Autopilot User Setup Page\",\"roleScopeTagIds\":[\"0\"],\"@odata.type\":\"#microsoft.graph.windows10CustomConfiguration\",\"omaSettings\":[{\"displayName\":\"SkipUserSetupPage\",\"omaUri\":\"./Device/Vendor/MSFT/DMClient/Provider/MS DM Server/FirstSyncStatus/SkipUserStatusPage\",\"@odata.type\":\"#microsoft.graph.omaSettingBoolean\",\"value\":\"true\"}]}", + "Type": "Device", + "GUID": "7547f73c-3cb0-460c-a4bd-391944908007.IntuneTemplate.json" +} diff --git a/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json b/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json new file mode 100644 index 000000000000..2dabd1a426af --- /dev/null +++ b/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json @@ -0,0 +1,7 @@ +{ + "Displayname": "CIPP Default: Enable Onedrive Silent Logon and Known Folder Move", + "Description": "This policy enables Onedrive Silent Logon and Known Folder move", + "RAWJson": "{\n\"added\":[\n{\n\"enabled\":true,\n\"presentationValues\":[],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('9a4db949-29e4-4e31-a129-bf2b88d8fa1b')\"\n},\n{\n\"enabled\":true,\n\"presentationValues\":[\n{\n\"@odata.type\":\"#microsoft.graph.groupPolicyPresentationValueText\",\n\"value\":\"%tenantid%\",\n\"presentation@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')/presentations('fbefbbdf-5382-477c-8b6c-71f4a06e2805')\"\n},\n{\n\"@odata.type\":\"#microsoft.graph.groupPolicyPresentationValueText\",\n\"value\":\"0\",\n\"presentation@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')/presentations('35c82072-a93b-4022-be14-8684c2f6fcc2')\"\n}\n],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')\"\n},\n{\n\"enabled\":true,\n\"presentationValues\":[],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('81c07ba0-7512-402d-b1f6-00856975cfab')\"\n},\n{\n\"enabled\":true,\n\"presentationValues\":[],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('61b07a01-7e60-4127-b086-f6b32458a5c5')\"\n},\n],\n\"updated\":[],\n\"deletedIds\":[]\n}", + "Type": "Admin", + "GUID": "7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json" +} diff --git a/Config/7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json b/Config/7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json new file mode 100644 index 000000000000..cc0fa7def31d --- /dev/null +++ b/Config/7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json @@ -0,0 +1,7 @@ +{ + "Displayname": "CIPP Default: Enable Bitlocker Encryption for OS drives", + "Description": "Enables Bitlocker and stores the key in Azure AD for system Drives", + "RAWJson": "{\"id\":\"00000000-0000-0000-0000-000000000000\",\"displayName\":\"CIPP: Enable Bitlocker Encryption\",\"roleScopeTagIds\":[\"0\"],\"@odata.type\":\"#microsoft.graph.windows10EndpointProtectionConfiguration\",\"applicationGuardEnabledOptions\":\"notConfigured\",\"firewallCertificateRevocationListCheckMethod\":\"deviceDefault\",\"firewallPacketQueueingMethod\":\"deviceDefault\",\"deviceGuardLocalSystemAuthorityCredentialGuardSettings\":\"notConfigured\",\"defenderSecurityCenterNotificationsFromApp\":\"notConfigured\",\"windowsDefenderTamperProtection\":\"notConfigured\",\"defenderSecurityCenterITContactDisplay\":\"notConfigured\",\"xboxServicesAccessoryManagementServiceStartupMode\":\"manual\",\"xboxServicesLiveAuthManagerServiceStartupMode\":\"manual\",\"xboxServicesLiveGameSaveServiceStartupMode\":\"manual\",\"xboxServicesLiveNetworkingServiceStartupMode\":\"manual\",\"applicationGuardBlockClipboardSharing\":\"notConfigured\",\"defenderPreventCredentialStealingType\":\"notConfigured\",\"defenderAdobeReaderLaunchChildProcess\":\"notConfigured\",\"defenderOfficeCommunicationAppsLaunchChildProcess\":\"notConfigured\",\"defenderAdvancedRansomewareProtectionType\":\"notConfigured\",\"defenderNetworkProtectionType\":\"notConfigured\",\"localSecurityOptionsFormatAndEjectOfRemovableMediaAllowedUser\":\"notConfigured\",\"localSecurityOptionsSmartCardRemovalBehavior\":\"lockWorkstation\",\"localSecurityOptionsInformationDisplayedOnLockScreen\":\"notConfigured\",\"localSecurityOptionsMinimumSessionSecurityForNtlmSspBasedClients\":\"none\",\"localSecurityOptionsMinimumSessionSecurityForNtlmSspBasedServers\":\"none\",\"lanManagerAuthenticationLevel\":\"lmAndNltm\",\"localSecurityOptionsAdministratorElevationPromptBehavior\":\"notConfigured\",\"localSecurityOptionsStandardUserElevationPromptBehavior\":\"notConfigured\",\"userRightsAccessCredentialManagerAsTrustedCaller\":null,\"userRightsLocalLogOn\":null,\"userRightsAllowAccessFromNetwork\":null,\"userRightsActAsPartOfTheOperatingSystem\":null,\"userRightsBackupData\":null,\"userRightsChangeSystemTime\":null,\"userRightsCreateGlobalObjects\":null,\"userRightsCreatePageFile\":null,\"userRightsCreatePermanentSharedObjects\":null,\"userRightsCreateSymbolicLinks\":null,\"userRightsCreateToken\":null,\"userRightsDebugPrograms\":null,\"userRightsBlockAccessFromNetwork\":null,\"userRightsDenyLocalLogOn\":null,\"userRightsRemoteDesktopServicesLogOn\":null,\"userRightsDelegation\":null,\"userRightsGenerateSecurityAudits\":null,\"userRightsImpersonateClient\":null,\"userRightsIncreaseSchedulingPriority\":null,\"userRightsLoadUnloadDrivers\":null,\"userRightsLockMemory\":null,\"userRightsManageAuditingAndSecurityLogs\":null,\"userRightsManageVolumes\":null,\"userRightsModifyFirmwareEnvironment\":null,\"userRightsModifyObjectLabels\":null,\"userRightsProfileSingleProcess\":null,\"userRightsRemoteShutdown\":null,\"userRightsRestoreData\":null,\"userRightsTakeOwnership\":null,\"bitLockerRecoveryPasswordRotation\":\"notConfigured\",\"bitLockerPrebootRecoveryMsgURLOption\":\"default\",\"bitLockerEncryptDevice\":true,\"bitLockerDisableWarningForOtherDiskEncryption\":true,\"bitLockerAllowStandardUserEncryption\":true,\"bitLockerSyntheticSystemDrivePolicybitLockerDriveRecovery\":true,\"applicationGuardAllowPrintToPDF\":false,\"applicationGuardAllowPrintToXPS\":false,\"applicationGuardAllowPrintToLocalPrinters\":false,\"applicationGuardAllowPrintToNetworkPrinters\":false,\"bitLockerFixedDrivePolicy\":{\"requireEncryptionForWriteAccess\":false,\"recoveryOptions\":null,\"encryptionMethod\":null},\"bitLockerRemovableDrivePolicy\":{\"requireEncryptionForWriteAccess\":false,\"encryptionMethod\":null},\"bitLockerSystemDrivePolicy\":{\"startupAuthenticationRequired\":true,\"startupAuthenticationTpmUsage\":\"allowed\",\"startupAuthenticationTpmPinUsage\":\"allowed\",\"startupAuthenticationTpmKeyUsage\":\"allowed\",\"startupAuthenticationTpmPinAndKeyUsage\":\"allowed\",\"startupAuthenticationBlockWithoutTpmChip\":false,\"minimumPinLength\":null,\"recoveryOptions\":{\"blockDataRecoveryAgent\":false,\"recoveryPasswordUsage\":\"allowed\",\"recoveryKeyUsage\":\"allowed\",\"enableRecoveryInformationSaveToStore\":true,\"recoveryInformationToStore\":\"passwordAndKey\",\"enableBitLockerAfterRecoveryInformationToStore\":true},\"prebootRecoveryEnableMessageAndUrl\":false,\"encryptionMethod\":null},\"firewallProfileDomain\":null,\"firewallProfilePrivate\":null,\"firewallProfilePublic\":null,\"deviceGuardEnableVirtualizationBasedSecurity\":false,\"deviceGuardEnableSecureBootWithDMA\":false}", + "Type": "Device", + "GUID": "7e06b0de-0469-4aae-89be-d83c44b5799f.IntuneTemplate.json" +} diff --git a/Config/8d57edc3-071d-42e7-86b1-126720645ac6.TransportRuleTemplate.json b/Config/8d57edc3-071d-42e7-86b1-126720645ac6.TransportRuleTemplate.json new file mode 100644 index 000000000000..cb8508809c87 --- /dev/null +++ b/Config/8d57edc3-071d-42e7-86b1-126720645ac6.TransportRuleTemplate.json @@ -0,0 +1,34 @@ +{ + "name": "Block Specific email addresses with a hard rejection", + "applyome": false, + "attachmenthasexecutablecontent": false, + "attachmentispasswordprotected": false, + "attachmentisunsupported": false, + "attachmentprocessinglimitexceeded": false, + "comments": "\n", + "deletemessage": false, + "exceptifattachmenthasexecutablecontent": false, + "exceptifattachmentispasswordprotected": false, + "exceptifattachmentisunsupported": false, + "exceptifattachmentprocessinglimitexceeded": false, + "exceptifhasnoclassification": false, + "exceptifhassenderoverride": false, + "from": ["SomeSpammer@spam.com"], + "hasnoclassification": false, + "hassenderoverride": false, + "mode": "enforce", + "moderatemessagebymanager": false, + "quarantine": false, + "recipientaddresstype": "resolved", + "rejectmessageenhancedstatuscode": "5.7.1", + "rejectmessagereasontext": "Your email has been rejected.", + "removeome": false, + "removeomev2": false, + "removermsattachmentencryption": false, + "routemessageoutboundrequiretls": false, + "ruleerroraction": "ignore", + "rulesubtype": "none", + "senderaddresslocation": "header", + "stopruleprocessing": false, + "uselegacyregex": false +} diff --git a/Config/CIPPDefaultTable.BPATemplate.json b/Config/CIPPDefaultTable.BPATemplate.json new file mode 100644 index 000000000000..2f7f31ac326c --- /dev/null +++ b/Config/CIPPDefaultTable.BPATemplate.json @@ -0,0 +1,192 @@ +{ + "name": "CIPP Best Practices v1.0 - Table view", + "style": "Table", + "Fields": [ + { + "name": "PasswordNeverExpires", + "API": "Graph", + "URL": "https://graph.microsoft.com/beta/domains", + "ExtractFields": ["passwordValidityPeriodInDays"], + "where": "$_.passwordValidityPeriodInDays -eq 2147483647", + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "Password Never Expires", + "value": "PasswordNeverExpires", + "formatter": "bool" + } + ] + }, + { + "name": "OAuthAppConsent", + "API": "Graph", + "URL": "https://graph.microsoft.com/v1.0/policies/authorizationPolicy?$select=defaultUserRolePermissions", + "ExtractFields": ["defaultuserrolepermissions"], + "where": "'ManagePermissionGrantsForSelf.microsoft-user-default-legacy' -notin $_.defaultuserrolepermissions.permissionGrantPoliciesAssigned", + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "OAuth App Consent", + "value": "OAuthAppConsent", + "formatter": "bool" + } + ] + }, + { + "name": "UnifiedAuditLog", + "API": "Exchange", + "Command": "Get-AdminAuditLogConfig", + "ExtractFields": ["UnifiedAuditLogIngestionEnabled"], + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "Unified Audit Log", + "value": "UnifiedAuditLog", + "formatter": "bool" + } + ] + }, + { + "name": "MFANudgeState", + "API": "Graph", + "URL": "https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy", + "ExtractFields": ["registrationEnforcement"], + "StoreAs": "bool", + "where": "$_.registrationEnforcement.authenticationMethodsRegistrationCampaign.state -eq 'Enabled'", + "FrontendFields": [ + { + "name": "MFA Registration Campaign Enabled", + "value": "MFANudgeState", + "formatter": "bool" + } + ] + }, + { + "name": "TAPEnabled", + "API": "Graph", + "URL": "https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass", + "ExtractFields": ["State"], + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "Temporary Access Pass Enabled", + "value": "TAPEnabled", + "formatter": "bool" + } + ] + }, + { + "name": "SecureDefaultState", + "API": "Graph", + "URL": "https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy", + "ExtractFields": ["IsEnabled"], + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "Secure Defaults State Enabled", + "value": "SecureDefaultState", + "formatter": "warnBool" + } + ] + }, + { + "name": "AnonymousPrivacyReports", + "API": "Graph", + "URL": "https://graph.microsoft.com/beta/admin/reportSettings", + "ExtractFields": ["displayConcealedNames"], + "StoreAs": "bool", + "where": "$_.displayConcealedNames -eq $false", + "FrontendFields": [ + { + "name": "Anonymous Privacy Reports", + "value": "AnonymousPrivacyReports", + "formatter": "reverseBool" + } + ] + }, + { + "name": "MessageCopyforSentAsDisabled", + "API": "Exchange", + "Command": "Get-Mailbox", + "Parameters": { + "RecipientTypeDetails": ["SharedMailbox", "UserMailbox"] + }, + "where": "$_.MessageCopyForSentAsEnabled -eq $false", + "ExtractFields": ["userprincipalname", "messageCopyForSentAsEnabled"], + "StoreAs": "JSON", + "FrontendFields": [ + { + "name": "Message Copy for Sent-As Disabled", + "formatter": "table", + "value": "MessageCopyforSentAsDisabled" + } + ] + }, + { + "name": "SharedMailboxeswithenabledusers", + "API": "Exchange", + "Command": "Get-Mailbox", + "Parameters": { + "RecipientTypeDetails": "SharedMailbox" + }, + "where": "$_.accountDisabled -eq $false", + "ExtractFields": ["userprincipalname", "accountDisabled"], + "StoreAs": "JSON", + "FrontendFields": [ + { + "name": "Shared Mailboxes with enabled users", + "formatter": "table", + "value": "SharedMailboxeswithenabledusers" + } + ] + }, + { + "name": "Unusedlicenses", + "API": "CIPPFunction", + "Command": "Get-CIPPLicenseOverview", + "ExtractFields": [ + "License", + "TotalLicenses", + "availableUnits", + "CountUsed" + ], + "StoreAs": "JSON", + "where": "$_.availableUnits -gt 0", + "FrontendFields": [ + { + "name": "Unused licenses", + "formatter": "table", + "value": "Unusedlicenses" + } + ] + }, + { + "name": "CurrentSecureScore", + "API": "Graph", + "URL": "https://graph.microsoft.com/beta/security/secureScores?$top=1", + "Parameters": { + "Nopagination": true + }, + "ExtractFields": ["currentScore", "maxScore", "averageComparativeScores"], + "StoreAs": "JSON", + "FrontendFields": [ + { + "name": "Current Secure Score", + "value": "CurrentSecureScore.currentScore" + }, + { + "name": "Max Secure Score", + "value": "CurrentSecureScore.maxScore" + }, + { + "name": "Average Comparative Score (All Tenants)", + "value": "CurrentSecureScore.averageComparativeScores[0].averageScore" + }, + { + "name": "Average Comparative Score (Similiar Size Tenants)", + "value": "CurrentSecureScore.averageComparativeScores[1].averageScore" + } + ] + } + ] +} diff --git a/Config/CIPPDefaultTenantPage.BPATemplate.json b/Config/CIPPDefaultTenantPage.BPATemplate.json new file mode 100644 index 000000000000..aa706369f119 --- /dev/null +++ b/Config/CIPPDefaultTenantPage.BPATemplate.json @@ -0,0 +1,155 @@ +{ + "name": "CIPP Best Practices v1.0 - Tenant view", + "style": "Tenant", + "Fields": [ + { + "name": "PasswordNeverExpires", + "UseExistingInfo": true, + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "Password Never Expires", + "value": "PasswordNeverExpires", + "formatter": "bool", + "desc": "This setting shows if your environment has enabled the password never expires setting. This setting is expected to be set to 'No'" + } + ] + }, + { + "name": "OAuthAppConsent", + "UseExistingInfo": true, + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "OAuth App Consent", + "value": "OAuthAppConsent", + "formatter": "bool", + "desc": "This setting shows if your environment has enabled OAuth App Consent. This setting is expected to be set to 'Yes'" + } + ] + }, + { + "name": "UnifiedAuditLog", + "UseExistingInfo": true, + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "Unified Audit Log", + "value": "UnifiedAuditLog", + "formatter": "bool", + "desc": "This setting shows if your environment has enabled the unified audit log. This setting is expected to be set to 'Yes'" + } + ] + }, + { + "name": "MFANudgeState", + "UseExistingInfo": true, + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "MFA Registration Campaign Enabled", + "value": "MFANudgeState", + "formatter": "bool", + "desc": "This setting shows if your environment has enabled the MFA registration campaign, also known as the MFA Nudge. This setting is recommended to be set to 'Yes'" + } + ] + }, + { + "name": "TAPEnabled", + "UseExistingInfo": true, + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "Temporary Access Pass Enabled", + "value": "TAPEnabled", + "formatter": "bool", + "desc": "This setting shows if your environment has enabled the temporary access pass feature." + } + ] + }, + { + "name": "SecureDefaultState", + "UseExistingInfo": true, + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "Secure Defaults State Enabled", + "value": "SecureDefaultState", + "formatter": "warnBool", + "desc": "This setting shows if your environment has enabled the secure defaults state. If you are using Conditional Access this setting may be set to `No`" + } + ] + }, + { + "name": "AnonymousPrivacyReports", + "UseExistingInfo": true, + "StoreAs": "bool", + "FrontendFields": [ + { + "name": "Anonymous Privacy Reports", + "value": "AnonymousPrivacyReports", + "formatter": "reverseBool", + "desc": "This setting shows if your environment has enabled the anonymous privacy reports, these will need to be disabled to be able to view mailboxes and onedrive reports" + } + ] + }, + { + "name": "MessageCopyforSentAsDisabled", + "UseExistingInfo": true, + "StoreAs": "JSON", + "FrontendFields": [ + { + "name": "Message Copy for Sent-As Disabled", + "formatter": "table", + "value": "MessageCopyforSentAsDisabled", + "desc": "These are the mailboxes that have the MessageCopyForSentAsDisabled setting enabled." + } + ] + }, + { + "name": "SharedMailboxeswithenabledusers", + "UseExistingInfo": true, + "StoreAs": "JSON", + "FrontendFields": [ + { + "name": "Shared Mailboxes with enabled users", + "formatter": "table", + "value": "SharedMailboxeswithenabledusers", + "desc": "These are the shared mailboxes that have enabled users." + } + ] + }, + { + "name": "Unusedlicenses", + "UseExistingInfo": true, + "StoreAs": "JSON", + "FrontendFields": [ + { + "name": "Unused licenses", + "formatter": "table", + "value": "Unusedlicenses", + "desc": "These are the licenses that are not assigned to an user, but have been purchased." + } + ] + }, + { + "name": "CurrentSecureScore", + "UseExistingInfo": true, + "StoreAs": "JSON", + "FrontendFields": [ + { + "name": "Current Secure Score", + "value": "CurrentSecureScore.currentScore", + "desc": "The current Secure Score for this tenant. This is the sum of all the individual controls that have been implemented.", + "formatter": "number" + }, + { + "name": "Max Secure Score", + "value": "CurrentSecureScore.maxScore", + "desc": "The maximum Secure Score for this tenant. This is the sum of all the individual controls that can be implemented.", + "formatter": "number" + } + ] + } + ] +} diff --git a/Config/ExcludeSkuList.JSON b/Config/ExcludeSkuList.JSON new file mode 100644 index 000000000000..e2c80e82a1e0 --- /dev/null +++ b/Config/ExcludeSkuList.JSON @@ -0,0 +1,186 @@ +[ + { + "GUID": "90d8b3f8-712e-4f7b-aa1e-62e7ae6cbe96", + "Product_Display_Name": "Business Apps (free)" + }, + { + "GUID": "90d8b3f8-712e-4f7b-aa1e-62e7ae6cbe96", + "Product_Display_Name": "Business Apps (free)" + }, + { + "GUID": "f30db892-07e9-47e9-837c-80727f46fd3d", + "Product_Display_Name": "MICROSOFT FLOW FREE" + }, + { + "GUID": "f30db892-07e9-47e9-837c-80727f46fd3d", + "Product_Display_Name": "MICROSOFT FLOW FREE" + }, + { + "GUID": "f30db892-07e9-47e9-837c-80727f46fd3d", + "Product_Display_Name": "MICROSOFT FLOW FREE" + }, + { + "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", + "Product_Display_Name": "MICROSOFT TEAMS (FREE)" + }, + { + "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", + "Product_Display_Name": "MICROSOFT TEAMS (FREE)" + }, + { + "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", + "Product_Display_Name": "MICROSOFT TEAMS (FREE)" + }, + { + "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", + "Product_Display_Name": "MICROSOFT TEAMS (FREE)" + }, + { + "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", + "Product_Display_Name": "MICROSOFT TEAMS (FREE)" + }, + { + "GUID": "16ddbbfc-09ea-4de2-b1d7-312db6112d70", + "Product_Display_Name": "MICROSOFT TEAMS (FREE)" + }, + { + "GUID": "a403ebcc-fae0-4ca2-8c8c-7a907fd6c235", + "Product_Display_Name": "Power BI (free)" + }, + { + "GUID": "a403ebcc-fae0-4ca2-8c8c-7a907fd6c235", + "Product_Display_Name": "Power BI (free)" + }, + { + "GUID": "61e6bd70-fbdb-4deb-82ea-912842f39431", + "Product_Display_Name": "Dynamics 365 Customer Service Insights Trial" + }, + { + "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", + "Product_Display_Name": "Dynamics 365 Customer Voice Trial" + }, + { + "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", + "Product_Display_Name": "Dynamics 365 Customer Voice Trial" + }, + { + "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", + "Product_Display_Name": "Dynamics 365 Customer Voice Trial" + }, + { + "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", + "Product_Display_Name": "Dynamics 365 Customer Voice Trial" + }, + { + "GUID": "bc946dac-7877-4271-b2f7-99d2db13cd2c", + "Product_Display_Name": "Dynamics 365 Customer Voice Trial" + }, + { + "GUID": "338148b6-1b11-4102-afb9-f92b6cdc0f8d", + "Product_Display_Name": "DYNAMICS 365 P1 TRIAL FOR INFORMATION WORKERS" + }, + { + "GUID": "338148b6-1b11-4102-afb9-f92b6cdc0f8d", + "Product_Display_Name": "DYNAMICS 365 P1 TRIAL FOR INFORMATION WORKERS" + }, + { + "GUID": "fcecd1f9-a91e-488d-a918-a96cdb6ce2b0", + "Product_Display_Name": "Microsoft Dynamics AX7 User Trial" + }, + { + "GUID": "fcecd1f9-a91e-488d-a918-a96cdb6ce2b0", + "Product_Display_Name": "Microsoft Dynamics AX7 User Trial" + }, + { + "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", + "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" + }, + { + "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", + "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" + }, + { + "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", + "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" + }, + { + "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", + "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" + }, + { + "GUID": "dcb1a3ae-b33f-4487-846a-a640262fadf4", + "Product_Display_Name": "Microsoft Power Apps Plan 2 Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "74fbf1bb-47c6-4796-9623-77dc7371723b", + "Product_Display_Name": "Microsoft Teams Trial" + }, + { + "GUID": "606b54a9-78d8-4298-ad8b-df6ef4481c80", + "Product_Display_Name": "Power Virtual Agents Viral Trial" + }, + { + "GUID": "606b54a9-78d8-4298-ad8b-df6ef4481c80", + "Product_Display_Name": "Power Virtual Agents Viral Trial" + }, + { + "GUID": "606b54a9-78d8-4298-ad8b-df6ef4481c80", + "Product_Display_Name": "Power Virtual Agents Viral Trial" + }, + { + "GUID": "1f2f344a-700d-42c9-9427-5cea1d5d7ba6", + "Product_Display_Name": "MICROSOFT STREAM" + }, + { + "GUID": "1f2f344a-700d-42c9-9427-5cea1d5d7ba6", + "Product_Display_Name": "MICROSOFT STREAM" + }, + { + "GUID": "6470687e-a428-4b7a-bef2-8a291ad947c9", + "Product_Display_Name": "WINDOWS STORE FOR BUSINESS" + }, + { + "GUID": "6470687e-a428-4b7a-bef2-8a291ad947c9", + "Product_Display_Name": "WINDOWS STORE FOR BUSINESS" + }, + { + "GUID": "710779e8-3d4a-4c88-adb9-386c958d1fdf", + "Product_Display_Name": "MICROSOFT TEAMS EXPLORATORY" + } +] diff --git a/Config/SharePoint.BPATemplate.json b/Config/SharePoint.BPATemplate.json new file mode 100644 index 000000000000..da945a5d1f4f --- /dev/null +++ b/Config/SharePoint.BPATemplate.json @@ -0,0 +1,70 @@ +{ + "name": "CIPP SharePoint Report v1.0 - Table view", + "style": "Table", + "Fields": [ + { + "name": "SharepointSettings", + "API": "Graph", + "URL": "https://graph.microsoft.com/beta/admin/sharepoint/settings", + "Parameters": { + "asApp": "True" + }, + "ExtractFields": [ + "sharingCapability", + "isMacSyncAppEnabled", + "isResharingByExternalUsersEnabled", + "isUnmanagedSyncAppForTenantRestricted", + "isSiteCreationEnabled", + "deletedUserPersonalSiteRetentionPeriodInDays" + ], + "StoreAs": "JSON", + "FrontendFields": [ + { + "name": "Sharing capability", + "value": "SharepointSettings.sharingCapability", + "formatter": "string" + }, + { + "name": "Mac Sync Enabled", + "value": "SharepointSettings.isMacSyncAppEnabled", + "formatter": "warnBool" + }, + { + "name": "Resharing by external users", + "value": "isResharingByExternalUsersEnabled", + "formatter": "reverseBool" + }, + { + "name": "Allow users to sync from unmanaged devices", + "value": "SharepointSettings.isUnmanagedSyncAppForTenantRestricted", + "formatter": "bool" + }, + { + "name": "Site creation by standards users enabled", + "value": "SharepointSettings.isSiteCreationEnabled", + "formatter": "reverseBool" + }, + { + "name": "Deleted user data rention(days)", + "value": "SharepointSettings.deletedUserPersonalSiteRetentionPeriodInDays", + "formatter": "string" + } + ] + }, + { + "name": "WebtimeOut", + "API": "Graph", + "URL": "https://graph.microsoft.com/beta/policies/activityBasedTimeoutPolicies", + "ExtractFields": ["definition"], + "StoreAs": "bool", + "where": "$_.definition -like '*WebSessionIdleTimeout*'", + "FrontendFields": [ + { + "name": "Web Time-Out enabled", + "value": "WebtimeOut", + "formatter": "bool" + } + ] + } + ] +} diff --git a/Config/adf9f6d1-36fb-438b-a82b-71f3af402b6c.TransportRuleTemplate.json b/Config/adf9f6d1-36fb-438b-a82b-71f3af402b6c.TransportRuleTemplate.json new file mode 100644 index 000000000000..a92d2ae442d0 --- /dev/null +++ b/Config/adf9f6d1-36fb-438b-a82b-71f3af402b6c.TransportRuleTemplate.json @@ -0,0 +1,35 @@ +{ + "name": "Block External Auto-forwarding", + "applyome": false, + "attachmenthasexecutablecontent": false, + "attachmentispasswordprotected": false, + "attachmentisunsupported": false, + "attachmentprocessinglimitexceeded": false, + "deletemessage": false, + "exceptifattachmenthasexecutablecontent": false, + "exceptifattachmentispasswordprotected": false, + "exceptifattachmentisunsupported": false, + "exceptifattachmentprocessinglimitexceeded": false, + "exceptifhasnoclassification": false, + "exceptifhassenderoverride": false, + "fromscope": "inorganization", + "hasnoclassification": false, + "hassenderoverride": false, + "messagetypematches": "autoforward", + "mode": "enforce", + "moderatemessagebymanager": false, + "quarantine": false, + "recipientaddresstype": "resolved", + "rejectmessageenhancedstatuscode": "5.7.1", + "rejectmessagereasontext": "to improve security, auto-forwarding rules to external addresses has been disabled. please contact your Microsoft partner if you'd like to set up an exception.", + "removeome": false, + "removeomev2": false, + "removermsattachmentencryption": false, + "routemessageoutboundrequiretls": false, + "ruleerroraction": "ignore", + "rulesubtype": "none", + "senderaddresslocation": "header", + "senttoscope": "notinorganization", + "stopruleprocessing": false, + "uselegacyregex": false +} diff --git a/Config/b39b8d85-1531-420e-baeb-b388f565418b.TransportRuleTemplate.json b/Config/b39b8d85-1531-420e-baeb-b388f565418b.TransportRuleTemplate.json new file mode 100644 index 000000000000..0f0541e25af9 --- /dev/null +++ b/Config/b39b8d85-1531-420e-baeb-b388f565418b.TransportRuleTemplate.json @@ -0,0 +1,33 @@ +{ + "name": "if a message subject contains \"encrypt:\" encrypt message.", + "applyome": false, + "applyrightsprotectiontemplate": "encrypt", + "attachmenthasexecutablecontent": false, + "attachmentispasswordprotected": false, + "attachmentisunsupported": false, + "attachmentprocessinglimitexceeded": false, + "comments": "\n", + "deletemessage": false, + "exceptifattachmenthasexecutablecontent": false, + "exceptifattachmentispasswordprotected": false, + "exceptifattachmentisunsupported": false, + "exceptifattachmentprocessinglimitexceeded": false, + "exceptifhasnoclassification": false, + "exceptifhassenderoverride": false, + "hasnoclassification": false, + "hassenderoverride": false, + "mode": "enforce", + "moderatemessagebymanager": false, + "quarantine": false, + "recipientaddresstype": "resolved", + "removeome": false, + "removeomev2": false, + "removermsattachmentencryption": false, + "routemessageoutboundrequiretls": false, + "ruleerroraction": "ignore", + "rulesubtype": "none", + "senderaddresslocation": "header", + "stopruleprocessing": false, + "subjectcontainswords": ["encrypt:"], + "uselegacyregex": false +} diff --git a/Config/b79d0123-3105-4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json b/Config/b79d0123-3105-4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json new file mode 100644 index 000000000000..8994b7867849 --- /dev/null +++ b/Config/b79d0123-3105-4c5d-9f15-62cc7a7eb7e1.IntuneTemplate.json @@ -0,0 +1,7 @@ +{ + "Displayname": "CIPP Default: Automatic Configuration of Outlook", + "Description": "Configures the first profile on a device to always use the e-mail address of the currently logged on user.", + "RAWJson": "{\"name\":\"Automatic configuration of Outlook\",\"description\":\"\",\"platforms\":\"windows10\",\"technologies\":\"mdm\",\"roleScopeTagIds\":[\"0\"],\"settings\":[{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationSetting\",\"settingInstance\":{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance\",\"settingDefinitionId\":\"user_vendor_msft_policy_config_outlk16v2~policy~l_microsoftofficeoutlook~l_toolsaccounts~l_exchangesettings_l_automaticallyconfigureprofilebasedonactiveonce\",\"choiceSettingValue\":{\"@odata.type\":\"#microsoft.graph.deviceManagementConfigurationChoiceSettingValue\",\"value\":\"user_vendor_msft_policy_config_outlk16v2~policy~l_microsoftofficeoutlook~l_toolsaccounts~l_exchangesettings_l_automaticallyconfigureprofilebasedonactiveonce_1\",\"children\":[]}}}]}", + "Type": "Catalog", + "GUID": "b79d0123-3105-4c5d-9f15-62cc7a7eb7e1" +} diff --git a/Config/cba836bb-33b7-4c50-88be-ee80f74cbeac.CATemplate.json b/Config/cba836bb-33b7-4c50-88be-ee80f74cbeac.CATemplate.json new file mode 100644 index 000000000000..a454eecc7793 --- /dev/null +++ b/Config/cba836bb-33b7-4c50-88be-ee80f74cbeac.CATemplate.json @@ -0,0 +1,36 @@ +{ + "grantControls": { + "termsOfUse": [], + "builtInControls": ["mfa"], + "customAuthenticationFactors": [], + "operator": "OR" + }, + "state": "enabled", + "conditions": { + "servicePrincipalRiskLevels": [], + "userRiskLevels": [], + "deviceStates": null, + "signInRiskLevels": [], + "clientAppTypes": ["all"], + "devices": null, + "locations": null, + "applications": { + "includeApplications": ["d414ee2d-73e5-4e5b-bb16-03ef55fea597"], + "includeUserActions": [], + "includeAuthenticationContextClassReferences": [], + "excludeApplications": [] + }, + "users": { + "includeUsers": ["All"], + "excludeRoles": [], + "includeRoles": [], + "excludeUsers": [], + "includeGroups": [], + "excludeGroups": [] + }, + "platforms": null, + "times": null, + "clientApplications": null + }, + "displayName": "Enforce Multi-factor authentication for Static Web Apps" +} diff --git a/Config/e82dd7d8-3f13-43cd-bdd4-896e0958493b.TransportRuleTemplate.json b/Config/e82dd7d8-3f13-43cd-bdd4-896e0958493b.TransportRuleTemplate.json new file mode 100644 index 000000000000..a975c36fa432 --- /dev/null +++ b/Config/e82dd7d8-3f13-43cd-bdd4-896e0958493b.TransportRuleTemplate.json @@ -0,0 +1,32 @@ +{ + "name": "Block emails silently based on their subject", + "applyome": false, + "attachmenthasexecutablecontent": false, + "attachmentispasswordprotected": false, + "attachmentisunsupported": false, + "attachmentprocessinglimitexceeded": false, + "comments": "\n", + "deletemessage": true, + "exceptifattachmenthasexecutablecontent": false, + "exceptifattachmentispasswordprotected": false, + "exceptifattachmentisunsupported": false, + "exceptifattachmentprocessinglimitexceeded": false, + "exceptifhasnoclassification": false, + "exceptifhassenderoverride": false, + "hasnoclassification": false, + "hassenderoverride": false, + "mode": "enforce", + "moderatemessagebymanager": false, + "quarantine": false, + "recipientaddresstype": "resolved", + "removeome": false, + "removeomev2": false, + "removermsattachmentencryption": false, + "routemessageoutboundrequiretls": false, + "ruleerroraction": "ignore", + "rulesubtype": "none", + "senderaddresslocation": "header", + "stopruleprocessing": false, + "subjectorbodycontainswords": ["This subject"], + "uselegacyregex": false +} diff --git a/Config/f8be7e58-2419-40a8-a739-714bf5deff90.CATemplate.json b/Config/f8be7e58-2419-40a8-a739-714bf5deff90.CATemplate.json new file mode 100644 index 000000000000..392a69750b24 --- /dev/null +++ b/Config/f8be7e58-2419-40a8-a739-714bf5deff90.CATemplate.json @@ -0,0 +1,36 @@ +{ + "state": "enabled", + "grantControls": { + "builtInControls": ["block"], + "operator": "OR", + "termsOfUse": [], + "customAuthenticationFactors": [] + }, + "conditions": { + "times": null, + "locations": null, + "signInRiskLevels": [], + "devices": null, + "deviceStates": null, + "users": { + "excludeRoles": [], + "excludeUsers": [], + "excludeGroups": [], + "includeUsers": ["All"], + "includeRoles": [], + "includeGroups": [] + }, + "servicePrincipalRiskLevels": [], + "userRiskLevels": [], + "clientAppTypes": ["exchangeActiveSync", "other"], + "platforms": null, + "clientApplications": null, + "applications": { + "includeApplications": ["None"], + "includeUserActions": [], + "includeAuthenticationContextClassReferences": [], + "excludeApplications": [] + } + }, + "displayName": "Block Legacy Authentication" +} diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index ffea0fd0021d..44c7ebc5443c 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -2,7 +2,9 @@ using namespace System.Net function Receive-CippHttpTrigger { Param($Request, $TriggerMetadata) - + #force path to CIPP-API + Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName + Write-Host (Get-Item $PSScriptRoot).Parent.Parent.FullName $APIName = $TriggerMetadata.FunctionName $FunctionName = 'Invoke-{0}' -f $APIName diff --git a/Modules/CIPPCore/Public/version_latest.txt b/version_latest.txt similarity index 100% rename from Modules/CIPPCore/Public/version_latest.txt rename to version_latest.txt From 451066fdf947bb8938e066f17abff9efedb5510b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 14:18:27 +0100 Subject: [PATCH 71/77] remove set locations --- Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 | 1 - Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 | 1 - Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 | 1 - .../Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 | 1 - Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 | 1 - Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 | 1 - .../CIPPCore/Public/Entrypoints/Invoke-ListGroupTemplates.ps1 | 1 - .../CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 | 1 - .../Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 | 1 - Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 | 1 - 10 files changed, 10 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 index d57a4710ced6..033f7a909a6a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddChocoApp.ps1 @@ -10,7 +10,6 @@ $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -Set-Location (Get-Item $PSScriptRoot).Parent.FullName Write-Host "PowerShell HTTP trigger function processed a request." $ChocoApp = $request.body diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 index c920e5cfe32f..85c883333511 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddMSPApp.ps1 @@ -10,7 +10,6 @@ $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' -Set-Location (Get-Item $PSScriptRoot).Parent.FullName Write-Host 'PowerShell HTTP trigger function processed a request.' $RMMApp = $request.body diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 index 1dbc7e279c8c..be59d93845f4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddWinGetApp.ps1 @@ -10,7 +10,6 @@ $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -Set-Location (Get-Item $PSScriptRoot).Parent.FullName Write-Host "PowerShell HTTP trigger function processed a request." $WinGetApp = $request.body diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 index 80b7eef7f36a..567e6dc3a1c4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecMaintenanceScripts.ps1 @@ -10,7 +10,6 @@ Function Invoke-ExecMaintenanceScripts { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - Set-Location (Get-Item $PSScriptRoot).Parent.FullName try { $GraphToken = Get-GraphToken -returnRefresh $true $AccessTokenDetails = Read-JwtAccessDetails -Token $GraphToken.access_token diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 index 66c095f69d69..2185772864cd 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPATemplates.ps1 @@ -14,7 +14,6 @@ Function Invoke-ListBPATemplates { Write-Host 'PowerShell HTTP trigger function processed a request.' Write-Host $Request.query.id - Set-Location (Get-Item $PSScriptRoot).Parent.FullName $Templates = Get-ChildItem 'Config\*.BPATemplate.json' if ($Request.Query.RawJson) { diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 index 7604aa1b5961..14ec616c0616 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListCAtemplates.ps1 @@ -10,7 +10,6 @@ Function Invoke-ListCAtemplates { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - Set-Location (Get-Item $PSScriptRoot).Parent.FullName # Write to the Azure Functions log stream. Write-Host 'PowerShell HTTP trigger function processed a request.' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroupTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroupTemplates.ps1 index 97443c415144..205410cd9f92 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroupTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListGroupTemplates.ps1 @@ -10,7 +10,6 @@ Function Invoke-ListGroupTemplates { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - Set-Location (Get-Item $PSScriptRoot).Parent.FullName # Write to the Azure Functions log stream. Write-Host 'PowerShell HTTP trigger function processed a request.' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 index 95a56cc1106f..e3270a1834b0 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListIntuneTemplates.ps1 @@ -10,7 +10,6 @@ Function Invoke-ListIntuneTemplates { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - Set-Location (Get-Item $PSScriptRoot).Parent.FullName $Table = Get-CippTable -tablename 'templates' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 index 939431563611..1c42c21d9470 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListTransportRulesTemplates.ps1 @@ -11,7 +11,6 @@ Function Invoke-ListTransportRulesTemplates { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $Table = Get-CippTable -tablename 'templates' - Set-Location (Get-Item $PSScriptRoot).Parent.FullName $Templates = Get-ChildItem 'Config\*.TransportRuleTemplate.json' | ForEach-Object { diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 index 489df3ba15a0..961117510a0e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUsers.ps1 @@ -14,7 +14,6 @@ Function Invoke-ListUsers { # Write to the Azure Functions log stream. Write-Host 'PowerShell HTTP trigger function processed a request.' $ConvertTable = Import-Csv Conversiontable.csv | Sort-Object -Property 'guid' -Unique - Set-Location (Get-Item $PSScriptRoot).Parent.FullName # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.TenantFilter $GraphFilter = $Request.Query.graphFilter From 383a28a3c329e19c528a4d19707dfbbc4a429477 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 14:26:10 +0100 Subject: [PATCH 72/77] removing some set-locations --- Applications_Upload/run.ps1 | 73 +++++++++---------- BestPracticeAnalyser_GetQueue/run.ps1 | 1 - DomainAnalyser_List/run.ps1 | 7 +- .../CIPPCore/Public/Remove-CIPPLicense.ps1 | 12 ++- SendStats/run.ps1 | 4 +- 5 files changed, 43 insertions(+), 54 deletions(-) diff --git a/Applications_Upload/run.ps1 b/Applications_Upload/run.ps1 index c5cabfdcc96e..a92637a42882 100644 --- a/Applications_Upload/run.ps1 +++ b/Applications_Upload/run.ps1 @@ -4,41 +4,38 @@ $Filter = "PartitionKey eq 'apps' and RowKey eq '$name'" Set-Location (Get-Item $PSScriptRoot).Parent.FullName $ChocoApp = (Get-CIPPAzDataTableEntity @Table -filter $Filter).JSON | ConvertFrom-Json $intuneBody = $ChocoApp.IntuneBody -$tenants = if ($chocoapp.Tenant -eq "AllTenants") { +$tenants = if ($chocoapp.Tenant -eq 'AllTenants') { (Get-tenants).defaultDomainName -} -else { +} else { $chocoapp.Tenant } -if ($chocoApp.type -eq "MSPApp") { +if ($chocoApp.type -eq 'MSPApp') { [xml]$Intunexml = Get-Content "AddMSPApp\$($ChocoApp.MSPAppName).app.xml" $intunewinFilesize = (Get-Item "AddMSPApp\$($ChocoApp.MSPAppName).intunewin") $Infile = "AddMSPApp\$($ChocoApp.MSPAppName).intunewin" -} -else { - [xml]$Intunexml = Get-Content "AddChocoApp\choco.app.xml" - $intunewinFilesize = (Get-Item "AddChocoApp\IntunePackage.intunewin") +} else { + [xml]$Intunexml = Get-Content 'AddChocoApp\choco.app.xml' + $intunewinFilesize = (Get-Item 'AddChocoApp\IntunePackage.intunewin') $Infile = "AddChocoApp\$($intunexml.ApplicationInfo.FileName)" } $assignTo = $ChocoApp.AssignTo $AssignToIntent = $ChocoApp.InstallationIntent -$Baseuri = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps" +$Baseuri = 'https://graph.microsoft.com/beta/deviceAppManagement/mobileApps' $ContentBody = ConvertTo-Json @{ name = $intunexml.ApplicationInfo.FileName size = [int64]$intunexml.ApplicationInfo.UnencryptedContentSize sizeEncrypted = [int64]($intunewinFilesize).length } $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -$RemoveCacheFile = if ($chocoapp.Tenant -ne "AllTenants") { +$RemoveCacheFile = if ($chocoapp.Tenant -ne 'AllTenants') { Remove-AzDataTableEntity @Table -Entity $clearRow -} -else { +} else { $Table.Force = $true Add-CIPPAzDataTableEntity @Table -Entity @{ JSON = "$($ChocoApp | ConvertTo-Json)" RowKey = "$($ClearRow.RowKey)" - PartitionKey = "apps" - status = "Deployed" + PartitionKey = 'apps' + status = 'Deployed' } } $EncBody = @{ @@ -58,62 +55,60 @@ foreach ($tenant in $tenants) { $ApplicationList = (New-graphGetRequest -Uri $baseuri -tenantid $Tenant) | Where-Object { $_.DisplayName -eq $ChocoApp.ApplicationName } if ($ApplicationList.displayname.count -ge 1) { - Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) exists. Skipping this application" -Sev "Info" + Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) exists. Skipping this application" -Sev 'Info' continue } - if ($chocoApp.type -eq "WinGet") { - Write-Host "Winget!" + if ($chocoApp.type -eq 'WinGet') { + Write-Host 'Winget!' Write-Host ($intuneBody | ConvertTo-Json -Compress) $NewApp = New-GraphPostRequest -Uri $baseuri -Body ($intuneBody | ConvertTo-Json -Compress) -Type POST -tenantid $tenant Start-Sleep -Milliseconds 200 - Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) uploaded as WinGet app." -Sev "Info" - if ($AssignTo -ne "On") { + Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) uploaded as WinGet app." -Sev 'Info' + if ($AssignTo -ne 'On') { $intent = if ($AssignToIntent) { 'Uninstall' } else { 'Required' } - Set-CIPPAssignedApplication -ApplicationId $NewApp.Id -Intent $intent -TenantFilter $tenant -groupName "$AssignTo" -AppType "WinGet" + Set-CIPPAssignedApplication -ApplicationId $NewApp.Id -Intent $intent -TenantFilter $tenant -groupName "$AssignTo" -AppType 'WinGet' } - Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) Successfully created" -Sev "Info" + Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) Successfully created" -Sev 'Info' exit 0 - } - else { + } else { $NewApp = New-GraphPostRequest -Uri $baseuri -Body ($intuneBody | ConvertTo-Json) -Type POST -tenantid $tenant } $ContentReq = New-GraphPostRequest -Uri "$($BaseURI)/$($NewApp.id)/microsoft.graph.win32lobapp/contentVersions/1/files/" -Body $ContentBody -Type POST -tenantid $tenant do { - $AzFileUri = New-graphGetRequest -Uri "$($BaseURI)/$($NewApp.id)/microsoft.graph.win32lobapp/contentVersions/1/files/$($ContentReq.id)" -tenantid $tenant - if ($AZfileuri.uploadState -like "*fail*") { break } + $AzFileUri = New-graphGetRequest -Uri "$($BaseURI)/$($NewApp.id)/microsoft.graph.win32lobapp/contentVersions/1/files/$($ContentReq.id)" -tenantid $tenant + if ($AZfileuri.uploadState -like '*fail*') { break } Start-Sleep -Milliseconds 300 } while ($AzFileUri.AzureStorageUri -eq $null) $chunkSizeInBytes = 4mb [byte[]]$bytes = [System.IO.File]::ReadAllBytes($($intunewinFilesize.fullname)) - $chunks = [Math]::Ceiling($bytes.Length / $chunkSizeInBytes); - $id = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($chunks.ToString("0000"))) + $chunks = [Math]::Ceiling($bytes.Length / $chunkSizeInBytes) + $id = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($chunks.ToString('0000'))) #For anyone that reads this, The maximum chunk size is 100MB for blob storage, so we can upload it as one part and just give it the single ID. Easy :) - $Upload = Invoke-RestMethod -Uri "$($AzFileUri.azureStorageUri)&comp=block&blockid=$id" -Method Put -Headers @{'x-ms-blob-type' = 'BlockBlob' } -InFile $inFile -ContentType "application/octet-stream" + $Upload = Invoke-RestMethod -Uri "$($AzFileUri.azureStorageUri)&comp=block&blockid=$id" -Method Put -Headers @{'x-ms-blob-type' = 'BlockBlob' } -InFile $inFile -ContentType 'application/octet-stream' $ConfirmUpload = Invoke-RestMethod -Uri "$($AzFileUri.azureStorageUri)&comp=blocklist" -Method Put -Body "$id" - $CommitReq = New-graphPostRequest -Uri "$($BaseURI)/$($NewApp.id)/microsoft.graph.win32lobapp/contentVersions/1/files/$($ContentReq.id)/commit" -Body $EncBody -Type POST -tenantid $tenant + $CommitReq = New-graphPostRequest -Uri "$($BaseURI)/$($NewApp.id)/microsoft.graph.win32lobapp/contentVersions/1/files/$($ContentReq.id)/commit" -Body $EncBody -Type POST -tenantid $tenant do { $CommitStateReq = New-graphGetRequest -Uri "$($BaseURI)/$($NewApp.id)/microsoft.graph.win32lobapp/contentVersions/1/files/$($ContentReq.id)" -tenantid $tenant - if ($CommitStateReq.uploadState -like "*fail*") { - Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) Commit failed. Please check if app uploaded succesful" -Sev "Warning" + if ($CommitStateReq.uploadState -like '*fail*') { + Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) Commit failed. Please check if app uploaded succesful" -Sev 'Warning' break } Start-Sleep -Milliseconds 300 - } while ($CommitStateReq.uploadState -eq "commitFilePending") + } while ($CommitStateReq.uploadState -eq 'commitFilePending') $CommitFinalizeReq = New-graphPostRequest -Uri "$($BaseURI)/$($NewApp.id)" -tenantid $tenant -Body '{"@odata.type":"#microsoft.graph.win32lobapp","committedContentVersion":"1"}' -type PATCH - Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "Added Application $($chocoApp.ApplicationName)" -Sev "Info" - if ($AssignTo -ne "On") { + Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "Added Application $($chocoApp.ApplicationName)" -Sev 'Info' + if ($AssignTo -ne 'On') { $intent = if ($AssignToIntent) { 'Uninstall' } else { 'Required' } - Set-CIPPAssignedApplication -ApplicationId $NewApp.Id -Intent $intent -TenantFilter $tenant -groupName "$AssignTo" -AppType "Win32Lob" + Set-CIPPAssignedApplication -ApplicationId $NewApp.Id -Intent $intent -TenantFilter $tenant -groupName "$AssignTo" -AppType 'Win32Lob' } - Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "Successfully added Application" -Sev "Info" - } - catch { + Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message 'Successfully added Application' -Sev 'Info' + } catch { "Failed to add Application for $($Tenant): $($_.Exception.Message)" - Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "Failed adding Application $($ChocoApp.ApplicationName). Error: $($_.Exception.Message)" -Sev "Error" + Write-LogMessage -api 'AppUpload' -tenant $($Tenant) -message "Failed adding Application $($ChocoApp.ApplicationName). Error: $($_.Exception.Message)" -Sev 'Error' continue } diff --git a/BestPracticeAnalyser_GetQueue/run.ps1 b/BestPracticeAnalyser_GetQueue/run.ps1 index baca4447eea1..0e2792c8075c 100644 --- a/BestPracticeAnalyser_GetQueue/run.ps1 +++ b/BestPracticeAnalyser_GetQueue/run.ps1 @@ -1,5 +1,4 @@ param($name) -Set-Location (Get-Item $PSScriptRoot).Parent.FullName #$Skiplist = (Get-Content ExcludedTenants -ErrorAction SilentlyContinue | ConvertFrom-Csv -Delimiter "|" -Header "name", "date", "user").name $Tenants = Get-Tenants #Get-Content ".\tenants.cache.json" | ConvertFrom-Json | Where-Object {$Skiplist -notcontains $_.defaultDomainName} diff --git a/DomainAnalyser_List/run.ps1 b/DomainAnalyser_List/run.ps1 index 70a5bd281942..65a72671901f 100644 --- a/DomainAnalyser_List/run.ps1 +++ b/DomainAnalyser_List/run.ps1 @@ -2,7 +2,6 @@ using namespace System.Net # Input bindings are passed in via param block. param($Request, $TriggerMetadata) -Set-Location (Get-Item $PSScriptRoot).Parent.FullName $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' @@ -29,11 +28,9 @@ try { $Object } } - } - catch {} + } catch {} } -} -catch { +} catch { $Results = @() } diff --git a/Modules/CIPPCore/Public/Remove-CIPPLicense.ps1 b/Modules/CIPPCore/Public/Remove-CIPPLicense.ps1 index 6caa7fe02eba..fea6f737657c 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPLicense.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPLicense.ps1 @@ -4,23 +4,21 @@ function Remove-CIPPLicense { $ExecutingUser, $userid, $username, - $APIName = "Remove License", + $APIName = 'Remove License', $TenantFilter ) - Set-Location (Get-Item $PSScriptRoot).FullName $ConvertTable = Import-Csv Conversiontable.csv try { $CurrentLicenses = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userid)" -tenantid $tenantFilter).assignedlicenses.skuid $ConvertedLicense = $(($ConvertTable | Where-Object { $_.guid -in $CurrentLicenses }).'Product_Display_Name' | Sort-Object -Unique) -join ',' - $LicensesToRemove = if ($CurrentLicenses) { ConvertTo-Json @( $CurrentLicenses) } else { "[]" } + $LicensesToRemove = if ($CurrentLicenses) { ConvertTo-Json @( $CurrentLicenses) } else { '[]' } $LicenseBody = '{"addLicenses": [], "removeLicenses": ' + $LicensesToRemove + '}' $LicRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/assignlicense" -tenantid $tenantFilter -type POST -body $LicenseBody -verbose - Write-LogMessage -user $ExecutingUser -API $APIName -message "Removed license for $($username)" -Sev "Info" -tenant $TenantFilter + Write-LogMessage -user $ExecutingUser -API $APIName -message "Removed license for $($username)" -Sev 'Info' -tenant $TenantFilter Return "Removed current licenses: $ConvertedLicense" - } - catch { - Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not remove license for $username" -Sev "Error" -tenant $TenantFilter + } catch { + Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not remove license for $username" -Sev 'Error' -tenant $TenantFilter return "Could not remove license for $($username). Error: $($_.Exception.Message)" } } diff --git a/SendStats/run.ps1 b/SendStats/run.ps1 index 5e86e35e980f..b4427d230338 100644 --- a/SendStats/run.ps1 +++ b/SendStats/run.ps1 @@ -4,13 +4,13 @@ param($Timer) #These stats are sent to a central server to help us understand how many tenants are using the product, and how many are using the latest version, this information allows the CIPP team to make decisions about what features to support, and what features to deprecate. #We will never ship any data that is related to your instance, all we care about is the number of tenants, and the version of the API you are running, and if you completed setup. -if ($ENV:applicationid -ne "LongApplicationID") { +if ($ENV:applicationid -ne 'LongApplicationID') { $SetupComplete = $true } $TenantCount = (Get-Tenants).count Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$APIVersion = Get-Content "version_latest.txt" | Out-String +$APIVersion = Get-Content 'version_latest.txt' | Out-String $SendingObject = [PSCustomObject]@{ rgid = $env:WEBSITE_SITE_NAME From f3a0933ba270ac5857fed61a5913d3cd6aa325f3 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 27 Nov 2023 21:49:52 +0100 Subject: [PATCH 73/77] Fixed bug with new configs tenants --- Modules/CippExtensions/Private/Get-HaloToken.ps1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Modules/CippExtensions/Private/Get-HaloToken.ps1 b/Modules/CippExtensions/Private/Get-HaloToken.ps1 index bc75822a9875..0c26b909eec5 100644 --- a/Modules/CippExtensions/Private/Get-HaloToken.ps1 +++ b/Modules/CippExtensions/Private/Get-HaloToken.ps1 @@ -11,11 +11,10 @@ function Get-HaloToken { client_secret = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name 'HaloPSA' -AsPlainText) scope = 'all' } - if ($Configuration.Tenant -ne "None") { $Tenant = "?tenant=$($Configuration.tenant)" } + if ($Configuration.Tenant -ne 'None') { $Tenant = "?tenant=$($Configuration.Tenant)" } $token = Invoke-RestMethod -Uri "$($Configuration.AuthURL)/token$Tenant" -Method Post -Body $body -ContentType 'application/x-www-form-urlencoded' return $token - } - else { + } else { throw 'No Halo configuration' } } \ No newline at end of file From 2f2b8de52875a55c8beaee26fb3e46001c869ddf Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 28 Nov 2023 10:26:22 +0100 Subject: [PATCH 74/77] upped version --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index f4fa8fcb995d..1163055e28e8 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -4.6.1 \ No newline at end of file +4.7.0 \ No newline at end of file From a09e4518b6362189b2ecbae8a594d3cbb58f138f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 28 Nov 2023 12:08:51 +0100 Subject: [PATCH 75/77] fixes bpa path bug --- BestPracticeAnalyser_All/run.ps1 | 3 + .../Public/Entrypoints/Invoke-ListBPA.ps1 | 118 +++++++++--------- 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/BestPracticeAnalyser_All/run.ps1 b/BestPracticeAnalyser_All/run.ps1 index 71b679a9c66a..ddd92560ccda 100644 --- a/BestPracticeAnalyser_All/run.ps1 +++ b/BestPracticeAnalyser_All/run.ps1 @@ -92,6 +92,9 @@ $AddRow = foreach ($Template in $templates) { 'string' { $Result.Add($field.Name, [string]$FieldInfo) } + 'percentage' { + + } } } catch { Write-LogMessage -API 'BPA' -tenant $tenant -message "Error storing $($field.Name) for $($TenantName.displayName) with GUID $($TenantName.customerId). Error: $($_.Exception.Message)" -sev Error diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 index 1933e6776f2d..eea73a9c4b9e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListBPA.ps1 @@ -1,6 +1,6 @@ - using namespace System.Net +using namespace System.Net - Function Invoke-ListBPA { +Function Invoke-ListBPA { <# .FUNCTIONALITY Entrypoint @@ -8,78 +8,76 @@ [CmdletBinding()] param($Request, $TriggerMetadata) - $APIName = $TriggerMetadata.FunctionName -# Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" + $APIName = $TriggerMetadata.FunctionName + # Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -$Table = get-cipptable 'cachebpav2' -$name = $Request.query.Report -if ($name -eq $null) { $name = 'CIPP Best Practices v1.0 - Table view' } + $Table = get-cipptable 'cachebpav2' + $name = $Request.query.Report + if ($name -eq $null) { $name = 'CIPP Best Practices v1.0 - Table view' } -# Get all possible JSON files for reports, find the correct one, select the Columns -$JSONFields = @() -$Columns = $null -$CippRoot = (Get-Item $PSScriptRoot).Parent.FullName -(Get-ChildItem -Path "$CippRoot\Config\*.BPATemplate.json" -Recurse | Select-Object -ExpandProperty FullName | ForEach-Object { - $Template = $(Get-Content $_) | ConvertFrom-Json - if ($Template.Name -eq $NAME) { - $JSONFields = $Template.Fields | Where-Object { $_.StoreAs -eq 'JSON' } | ForEach-Object { $_.name } - $Columns = $Template.fields.FrontendFields | Where-Object -Property name -NE $null - $Style = $Template.Style - } -}) + # Get all possible JSON files for reports, find the correct one, select the Columns + $JSONFields = @() + $Columns = $null +(Get-ChildItem -Path 'Config\*.BPATemplate.json' -Recurse | Select-Object -ExpandProperty FullName | ForEach-Object { + $Template = $(Get-Content $_) | ConvertFrom-Json + if ($Template.Name -eq $NAME) { + $JSONFields = $Template.Fields | Where-Object { $_.StoreAs -eq 'JSON' } | ForEach-Object { $_.name } + $Columns = $Template.fields.FrontendFields | Where-Object -Property name -NE $null + $Style = $Template.Style + } + }) -if ($Request.query.tenantFilter -ne 'AllTenants' -and $Style -eq 'Tenant') { - $mergedObject = New-Object pscustomobject + if ($Request.query.tenantFilter -ne 'AllTenants' -and $Style -eq 'Tenant') { + $mergedObject = New-Object pscustomobject - $Data = (Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq '$($Request.query.tenantFilter)'") | ForEach-Object { - $row = $_ - $JSONFields | ForEach-Object { - $jsonContent = $row.$_ - if ($jsonContent -ne $null -and $jsonContent -ne 'FAILED') { - $row.$_ = $jsonContent | ConvertFrom-Json -Depth 15 + $Data = (Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq '$($Request.query.tenantFilter)'") | ForEach-Object { + $row = $_ + $JSONFields | ForEach-Object { + $jsonContent = $row.$_ + if ($jsonContent -ne $null -and $jsonContent -ne 'FAILED') { + $row.$_ = $jsonContent | ConvertFrom-Json -Depth 15 + } + } + $row.PSObject.Properties | ForEach-Object { + $mergedObject | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force } } - $row.PSObject.Properties | ForEach-Object { - $mergedObject | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force - } - } - $Data = $mergedObject -} -else { - $Tenants = Get-Tenants -IncludeErrors - $Data = (Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$NAME'") | ForEach-Object { - $row = $_ - $JSONFields | ForEach-Object { - $jsonContent = $row.$_ - if ($jsonContent -ne $null -and $jsonContent -ne 'FAILED') { - $row.$_ = $jsonContent | ConvertFrom-Json -Depth 15 + $Data = $mergedObject + } else { + $Tenants = Get-Tenants -IncludeErrors + $Data = (Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$NAME'") | ForEach-Object { + $row = $_ + $JSONFields | ForEach-Object { + $jsonContent = $row.$_ + if ($jsonContent -ne $null -and $jsonContent -ne 'FAILED') { + $row.$_ = $jsonContent | ConvertFrom-Json -Depth 15 + } } + $row | Where-Object -Property PartitionKey -In $Tenants.customerId } - $row | Where-Object -Property PartitionKey -In $Tenants.customerId - } -} + } -$Results = [PSCustomObject]@{ - Data = $Data - Columns = $Columns - Style = $Style -} + $Results = [PSCustomObject]@{ + Data = $Data + Columns = $Columns + Style = $Style + } -if (!$Results) { - $Results = @{ - Columns = @( value = 'Results'; name = 'Results') - Data = @(@{ Results = 'The BPA has not yet run.' }) + if (!$Results) { + $Results = @{ + Columns = @( value = 'Results'; name = 'Results') + Data = @(@{ Results = 'The BPA has not yet run.' }) + } } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = ($Results | ConvertTo-Json -Depth 15) - }) + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = ($Results | ConvertTo-Json -Depth 15) + }) - } +} From 24f4bf182fe0e47850bf5ad46a49a5db14aeea4e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 28 Nov 2023 12:45:54 +0100 Subject: [PATCH 76/77] fix bug with setup --- Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 index 9fca990f8f0c..1319ced9aa86 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecSAMSetup.ps1 @@ -8,7 +8,7 @@ Function Invoke-ExecSAMSetup { [CmdletBinding()] param($Request, $TriggerMetadata) - + $UserCreds = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($request.headers.'x-ms-client-principal')) | ConvertFrom-Json) if ($Request.query.error) { Add-Type -AssemblyName System.Web Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ From 51bff62a777a0e6cae32668eabffc2f7feaee7e3 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 28 Nov 2023 12:46:18 +0100 Subject: [PATCH 77/77] upped version --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index 1163055e28e8..cfacfe40809c 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -4.7.0 \ No newline at end of file +4.7.1 \ No newline at end of file