Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Function Invoke-ExecExtensionMapping {
'HuduFields' {
$Result = Get-HuduFieldMapping -CIPPMapping $Table
}
'ITGlue' {
$Result = Get-ITGlueMapping -CIPPMapping $Table
}
'ITGlueFields' {
$Result = Get-ITGlueFieldMapping -CIPPMapping $Table
}
'Sherweb' {
$Result = Get-SherwebMapping -CIPPMapping $Table
}
Expand Down Expand Up @@ -76,6 +82,14 @@ Function Invoke-ExecExtensionMapping {
$Result = Set-ExtensionFieldMapping -CIPPMapping $Table -APIName $APIName -Request $Request -Extension 'Hudu'
Register-CIPPExtensionScheduledTasks
}
'ITGlue' {
$Result = Set-ITGlueMapping -CIPPMapping $Table -APIName $APIName -Request $Request
Register-CIPPExtensionScheduledTasks
}
'ITGlueFields' {
$Result = Set-ExtensionFieldMapping -CIPPMapping $Table -APIName $APIName -Request $Request -Extension 'ITGlue'
Register-CIPPExtensionScheduledTasks
}
}
}
$StatusCode = [HttpStatusCode]::OK
Expand Down Expand Up @@ -116,6 +130,23 @@ Function Invoke-ExecExtensionMapping {
$StatusCode = [HttpStatusCode]::InternalServerError
}

try {
if ($Request.Query.CreateCAType) {
switch ($Request.Query.CreateCAType) {
'ITGlue' {
$Result = New-ITGlueCAPolicyAssetType
}
}
}
$StatusCode = [HttpStatusCode]::OK
}
catch {
$ErrorMessage = Get-CippException -Exception $_
$Result = "Create CA Type API failed. $($ErrorMessage.NormalizedError)"
Write-LogMessage -API $APIName -headers $Headers -message $Result -Sev 'Error' -LogData $ErrorMessage
$StatusCode = [HttpStatusCode]::InternalServerError
}

return ([HttpResponseContext]@{
StatusCode = $StatusCode
Body = $Result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ Function Invoke-ExecExtensionSync {
Register-CIPPExtensionScheduledTasks -Reschedule -Extensions 'Hudu'
$Results = [pscustomobject]@{'Results' = 'Extension sync tasks have been rescheduled and will start within 15 minutes' }
}
'ITGlue' {
Register-CIPPExtensionScheduledTasks -Reschedule -Extensions 'ITGlue'
$Results = [pscustomobject]@{'Results' = 'ITGlue extension sync tasks have been rescheduled and will start within 15 minutes' }
}

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ Function Invoke-ExecExtensionTest {
$Results = [pscustomobject]@{'Results' = 'Failed to connect to Hudu, check your API credentials and try again.' }
}
}
'ITGlue' {
$Conn = Connect-ITGlueAPI -Configuration $Configuration
$Orgs = Invoke-ITGlueRequest -Method GET -Endpoint '/organizations' -Headers $Conn.Headers -BaseUrl $Conn.BaseUrl -PageSize 1 -FirstPageOnly
if ($Orgs) {
$Results = [pscustomobject]@{'Results' = "Successfully connected to ITGlue. Found $($Orgs.Count) organisation(s) on first page." }
} else {
$Results = [pscustomobject]@{'Results' = 'Failed to connect to ITGlue. Check your API key and region setting.' }
}
}
'Sherweb' {
$token = Get-SherwebAuthentication
if ($token) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ function Invoke-ExecExtensionsConfig {
$Body.Hudu.NextSync = ''
}

if ($Body.ITGlue.NextSync) {
#parse unixtime for addedtext
$Timestamp = [datetime]::UnixEpoch.AddSeconds([int]$Body.ITGlue.NextSync).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
Register-CIPPExtensionScheduledTasks -Reschedule -NextSync $Body.ITGlue.NextSync -Extensions 'ITGlue'
$AddedText = " Next sync will be at $Timestamp."
$Body.ITGlue.NextSync = ''
}

$Table = Get-CIPPTable -TableName Extensionsconfig
foreach ($APIKey in $Body.PSObject.Properties.Name) {
Write-Information "Working on $apikey"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
Function Invoke-ExecITGlueCreateFlexibleAssetType {
<#
.FUNCTIONALITY
Entrypoint
.ROLE
CIPP.Extension.ReadWrite
#>
[CmdletBinding()]
param($Request, $TriggerMetadata)

$APIName = $Request.Params.CIPPEndpoint
$Headers = $Request.Headers

try {
$AssetType = $Request.Body.AssetType

if ([string]::IsNullOrEmpty($AssetType)) {
throw "AssetType parameter is required"
}

# Only support ConditionalAccessPolicies for now
if ($AssetType -ne 'ConditionalAccessPolicies') {
throw "Unsupported asset type: $AssetType"
}

# Get ITGlue configuration
$Table = Get-CIPPTable -TableName Extensionsconfig
$Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -ErrorAction Stop
$Conn = Connect-ITGlueAPI -Configuration $Configuration

if (-not $Conn) {
throw "Failed to connect to ITGlue API. Please check your configuration."
}

# Search for existing type matching "Conditional Access"
$AllFlexibleAssetTypes = Invoke-ITGlueRequest -Method GET -Endpoint '/flexible_asset_types' -Headers $Conn.Headers -BaseUrl $Conn.BaseUrl
$ExistingCAPType = $AllFlexibleAssetTypes | Where-Object { $_.name -like '*Conditional Access*' } | Select-Object -First 1

if ($ExistingCAPType) {
$CAPTypeId = $ExistingCAPType.id
$Message = "Found existing Conditional Access flexible asset type: $($ExistingCAPType.name) (ID: $CAPTypeId)"
} else {
# Create flexible asset type with all fields using relationships structure
$NewTypeBody = @{
data = @{
type = 'flexible-asset-types'
attributes = @{
name = 'Conditional Access Policy'
description = 'Microsoft 365 Conditional Access Policies synced from CIPP'
icon = 'shield-alt'
enabled = $true
}
relationships = @{
'flexible-asset-fields' = @{
data = @(
@{
type = 'flexible-asset-fields'
attributes = @{
order = 1
name = 'Policy Name'
kind = 'Text'
required = $true
'show-in-list' = $true
}
}
@{
type = 'flexible-asset-fields'
attributes = @{
order = 2
name = 'Policy ID'
kind = 'Text'
required = $false
'show-in-list' = $false
}
}
@{
type = 'flexible-asset-fields'
attributes = @{
order = 3
name = 'State'
kind = 'Text'
required = $false
'show-in-list' = $true
}
}
@{
type = 'flexible-asset-fields'
attributes = @{
order = 4
name = 'Policy Details'
kind = 'Textbox'
required = $false
'show-in-list' = $false
}
}
@{
type = 'flexible-asset-fields'
attributes = @{
order = 5
name = 'Raw JSON'
kind = 'Textbox'
required = $false
'show-in-list' = $false
}
}
)
}
}
}
} | ConvertTo-Json -Depth 20 -Compress

$NewType = Invoke-RestMethod -Uri "$($Conn.BaseUrl)/flexible_asset_types" -Method POST -Headers $Conn.Headers -Body $NewTypeBody
$CAPTypeId = $NewType.data.id
$Message = "Created new Conditional Access Policy flexible asset type (ID: $CAPTypeId)"
}

# Save mapping to database
$MappingTable = Get-CIPPTable -TableName CippMapping
$AddMapping = @{
PartitionKey = 'ITGlueFieldMapping'
RowKey = 'ConditionalAccessPolicies'
IntegrationId = "$CAPTypeId"
IntegrationName = 'Conditional Access Policy'
}
Add-CIPPAzDataTableEntity @MappingTable -Entity $AddMapping -Force

Write-LogMessage -API $APIName -headers $Headers -message $Message -Sev 'Info'

$Result = @{
Success = $true
Message = $Message
TypeId = $CAPTypeId
}
$StatusCode = [HttpStatusCode]::OK
}
catch {
$ErrorMessage = Get-CippException -Exception $_
$Result = @{
Success = $false
Message = "Failed to create flexible asset type: $($ErrorMessage.NormalizedError)"
}
Write-LogMessage -API $APIName -headers $Headers -message $Result.Message -Sev 'Error' -LogData $ErrorMessage
$StatusCode = [HttpStatusCode]::InternalServerError
}

return ([HttpResponseContext]@{
StatusCode = $StatusCode
Body = $Result
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ function Invoke-ListMailboxForwarding {
continue
}

# External takes precedence when both are configured
$ForwardingType = if ($HasExternalForwarding) {
$ForwardingType = if ($HasExternalForwarding -and $HasInternalForwarding) {
'Both'
} elseif ($HasExternalForwarding) {
'External'
} else {
'Internal'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
function Add-ITGlueFlexibleAssetFields {
<#
.FUNCTIONALITY
Internal
.SYNOPSIS
Ensures required fields exist in an ITGlue Flexible Asset Type.
#>
param(
$TypeId,
[array]$FieldsToAdd, # Array of @{ Name = ''; Kind = 'Textbox'; ShowInList = $false }
$Conn
)

# GET type with its fields included (one call for all fields)
$TypeResponse = Invoke-RestMethod -Uri "$($Conn.BaseUrl)/flexible_asset_types/$TypeId`?include=flexible_asset_fields" -Method GET -Headers $Conn.Headers
$IncludedFields = $TypeResponse.included | Where-Object { $_.type -eq 'flexible-asset-fields' }
$ExistingNames = $IncludedFields | ForEach-Object { $_.attributes.name }

# Filter to only fields that don't exist
$NewFields = $FieldsToAdd | Where-Object { $_.Name -notin $ExistingNames }

if ($NewFields.Count -eq 0) {
return # All fields already exist
}

# Build complete field list: existing (with IDs) + new fields
$AllFields = [System.Collections.Generic.List[object]]::new()
foreach ($F in $IncludedFields) {
$AllFields.Add([ordered]@{
id = $F.id
name = $F.attributes.name
kind = $F.attributes.kind
required = $F.attributes.required
'show-in-list' = $F.attributes.'show-in-list'
position = $F.attributes.position
})
}

foreach ($NewField in $NewFields) {
$AllFields.Add([ordered]@{
name = $NewField.Name
kind = $NewField.Kind
required = $false
'show-in-list' = $NewField.ShowInList
})
}

$PatchBody = @{
data = @{
type = 'flexible-asset-types'
id = $TypeId
attributes = @{
'flexible-asset-fields' = @($AllFields)
}
}
} | ConvertTo-Json -Depth 20 -Compress

$null = Invoke-RestMethod -Uri "$($Conn.BaseUrl)/flexible_asset_types/$TypeId" -Method PATCH -Headers $Conn.Headers -Body $PatchBody
}
13 changes: 13 additions & 0 deletions Modules/CippExtensions/Private/ITGlue/Format-ITGlueCAPValue.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function Format-ITGlueCAPValue {
<#
.FUNCTIONALITY
Internal
.SYNOPSIS
Converts Out-String output (newline-separated) to comma-separated format.
Used for formatting CAP values from Invoke-ListConditionalAccessPolicies.
#>
param($Value)

if ([string]::IsNullOrWhiteSpace($Value)) { return '' }
($Value.Trim() -split "`n" | Where-Object { $_ -and $_.Trim() } | ForEach-Object { $_.Trim() }) -join ', '
}
Loading