Private/Association/Invoke-JCAssociation.ps1

Function Invoke-JCAssociation
{
    [CmdletBinding(DefaultParameterSetName = 'ById')]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 0)][ValidateNotNullOrEmpty()][ValidateSet('add', 'get', 'remove')][string]$Action
        , [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 1, HelpMessage = 'The type of the object.')][ValidateNotNullOrEmpty()][ValidateSet('command', 'ldap_server', 'policy', 'application', 'radius_server', 'system_group', 'system', 'user_group', 'user', 'g_suite', 'office_365')][Alias('TypeNameSingular')][string]$Type
        , [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 2, HelpMessage = 'Bypass user confirmation and ValidateSet when adding or removing associations.')][ValidateNotNullOrEmpty()][Switch]$Force
    )
    DynamicParam
    {
        # Build dynamic parameters
        $RuntimeParameterDictionary = Get-DynamicParamAssociation @PsBoundParameters
        Return $RuntimeParameterDictionary
    }
    Begin
    {
        # Debug message for parameter call
        Invoke-Command -ScriptBlock:($ScriptBlock_DefaultDebugMessageBegin) -ArgumentList:($MyInvocation, $PsBoundParameters, $PSCmdlet) -NoNewScope
        $Results = @()
    }
    Process
    {
        # For DynamicParam with a default value set that value and then convert the DynamicParam inputs into new variables for the script to use
        Invoke-Command -ScriptBlock:($ScriptBlock_DefaultDynamicParamProcess) -ArgumentList:($PsBoundParameters, $PSCmdlet, $RuntimeParameterDictionary) -NoNewScope
        Try
        {
            # All the bindings, recursive , both direct and indirect
            $URL_Template_Associations_MemberOf = '/api/v2/{0}/{1}/memberof' # $SourcePlural, $SourceId
            $URL_Template_Associations_Membership = '/api/v2/{0}/{1}/membership' # $SourcePlural (systemgroups,usergroups), $SourceId
            $URL_Template_Associations_TargetType = '/api/v2/{0}/{1}/{2}' # $SourcePlural, $SourceId, $TargetPlural
            # Only direct bindings and don’t traverse through groups
            $URL_Template_Associations_Targets = '/api/v2/{0}/{1}/associations?targets={2}' # $SourcePlural, $SourceId, $TargetSingular
            $URL_Template_Associations_Members = '/api/v2/{0}/{1}/members' # $SourcePlural, $SourceId
            # Determine to search by id or name but always prefer id
            If ($Id)
            {
                $SourceItemSearchByValue = $Id
                $SourceSearchBy = 'ById'
            }
            ElseIf ($Name)
            {
                $SourceItemSearchByValue = $Name
                $SourceSearchBy = 'ByName'
            }
            Else
            {
                Write-Error ('-Id or -Name parameter must be populated.') -ErrorAction:('Stop')
            }
            # Get SourceInfo
            $Source = Get-JCObject -Type:($Type) -SearchBy:($SourceSearchBy) -SearchByValue:($SourceItemSearchByValue)
            If ($Source)
            {
                ForEach ($SourceItem In $Source)
                {
                    $SourceItemId = $SourceItem.($SourceItem.ById)
                    $SourceItemName = $SourceItem.($SourceItem.ByName)
                    $SourceItemTypeName = $SourceItem.TypeName
                    $SourceItemTypeNameSingular = $SourceItemTypeName.TypeNameSingular
                    $SourceItemTypeNamePlural = $SourceItemTypeName.TypeNamePlural
                    $SourceItemTargets = $SourceItem.Targets |
                        Where-Object { $_.TargetSingular -in $TargetType -or $_.TargetPlural -in $TargetType }
                    ForEach ($SourceItemTarget In $SourceItemTargets)
                    {
                        $SourceItemTargetSingular = $SourceItemTarget.TargetSingular
                        $SourceItemTargetPlural = $SourceItemTarget.TargetPlural
                        # Build Url based upon source and target combinations
                        If (($SourceItemTypeNamePlural -eq 'systems' -and $SourceItemTargetPlural -eq 'systemgroups') -or ($SourceItemTypeNamePlural -eq 'users' -and $SourceItemTargetPlural -eq 'usergroups'))
                        {
                            $Uri_Associations_GET = $URL_Template_Associations_MemberOf -f $SourceItemTypeNamePlural, $SourceItemId
                        }
                        ElseIf (($SourceItemTypeNamePlural -eq 'systemgroups' -and $SourceItemTargetPlural -eq 'systems') -or ($SourceItemTypeNamePlural -eq 'usergroups' -and $SourceItemTargetPlural -eq 'users'))
                        {
                            $Uri_Associations_GET = $URL_Template_Associations_Membership -f $SourceItemTypeNamePlural, $SourceItemId
                        }
                        ElseIf (($SourceItemTypeNamePlural -eq 'activedirectories' -and $SourceItemTargetPlural -eq 'users') -or ($SourceItemTypeNamePlural -eq 'users' -and $SourceItemTargetPlural -eq 'activedirectories'))
                        {
                            $Uri_Associations_GET = $URL_Template_Associations_Targets -f $SourceItemTypeNamePlural, $SourceItemId, $SourceItemTargetSingular
                        }
                        Else
                        {
                            $Uri_Associations_GET = $URL_Template_Associations_TargetType -f $SourceItemTypeNamePlural, $SourceItemId, $SourceItemTargetPlural
                        }
                        # Call endpoint
                        If ($Action -eq 'get')
                        {
                            $AssociationOut = @()
                            # If switches are not passed in set them to be false so they can be used with Format-JCAssociation
                            If (!($IncludeInfo)) {$IncludeInfo = $false; }
                            If (!($IncludeNames)) {$IncludeNames = $false; }
                            If (!($IncludeVisualPath)) {$IncludeVisualPath = $false; }
                            If (!($Raw)) {$Raw = $false; }
                            # Get associations and format the output
                            $Association = Format-JCAssociation -Uri:($Uri_Associations_GET) -Method:('GET') -Source:($SourceItem) -IncludeInfo:($IncludeInfo) -IncludeNames:($IncludeNames) -IncludeVisualPath:($IncludeVisualPath) -Raw:($Raw)
                            If ($Direct -eq $true)
                            {
                                $AssociationOut += $Association | Where-Object {$_.associationType -eq 'direct' -or $_.associationType -eq "direct`/indirect"}
                            }
                            If ($Indirect -eq $true)
                            {
                                $AssociationOut += $Association | Where-Object {$_.associationType -eq 'indirect' -or $_.associationType -eq "direct`/indirect"}
                            }
                            If (!($Direct) -and !($Indirect))
                            {
                                $AssociationOut += $Association
                            }
                            If ($Raw)
                            {
                                $Result = $AssociationOut | Select-Object -Property:('*') -ExcludeProperty:('associationType', 'httpMetaData')
                                $Results += $Result
                            }
                            Else
                            {
                                $Result = $AssociationOut
                                $Results += $Result | Select-Object * `
                                    , @{Name = 'action'; Expression = {$Action}} `
                                    , @{Name = 'IsSuccessStatusCode'; Expression = {$Association.httpMetaData.BaseResponse.IsSuccessStatusCode | Select-Object -Unique}} `
                                    , @{Name = 'error'; Expression = {$null}}
                            }
                        }
                        Else
                        {
                            # For target determine to search by id or name but always prefer id
                            If ($TargetId)
                            {
                                $TargetSearchByValue = $TargetId
                                $TargetSearchBy = 'ById'
                            }
                            ElseIf ($TargetName)
                            {
                                $TargetSearchByValue = $TargetName
                                $TargetSearchBy = 'ByName'
                            }
                            Else
                            {
                                Write-Error ('-TargetId or -TargetName parameter must be populated.') -ErrorAction:('Stop')
                            }
                            If ($associationType -ne 'indirect')
                            {
                                # Get Target object
                                $Target = Get-JCObject -Type:($SourceItemTargetSingular) -SearchBy:($TargetSearchBy) -SearchByValue:($TargetSearchByValue)
                                If ($Target)
                                {
                                    ForEach ($TargetItem In $Target)
                                    {
                                        $TargetItemId = $TargetItem.($TargetItem.ById)
                                        $TargetItemName = $TargetItem.($TargetItem.ByName)
                                        $TargetItemTypeNameSingular = $TargetItem.TypeName.TypeNameSingular
                                        $TargetItemTypeNamePlural = $TargetItem.TypeName.TypeNamePlural
                                        # Build the attributes for the json body string
                                        $AttributesValue = If ($Action -eq 'add' -and $Attributes)
                                        {
                                            $Attributes | ConvertTo-Json -Depth:(100) -Compress
                                        }
                                        Else
                                        {
                                            'null'
                                        }
                                        # Validate that the association exists
                                        $TestAssociation = Format-JCAssociation -Uri:($Uri_Associations_GET) -Method:('GET') -Source:($SourceItem) -TargetId:($TargetItemId) -IncludeNames:($true)
                                        $IndirectAssociations = $TestAssociation  | Where-Object {$_.associationType -eq 'indirect'}
                                        $DirectAssociations = $TestAssociation  | Where-Object {$_.associationType -eq 'direct' -or $_.associationType -eq "direct`/indirect"}
                                        If ($DirectAssociations.associationType -eq "direct`/indirect") {$DirectAssociations.associationType = 'direct'}
                                        # If the target is not only an indirect association
                                        If ($TargetItemId -in $DirectAssociations.targetId -or $Action -eq 'add')
                                        {
                                            # Build uri and body
                                            If (($SourceItemTypeNamePlural -eq 'systems' -and $SourceItemTargetPlural -eq 'systemgroups') -or ($SourceItemTypeNamePlural -eq 'users' -and $SourceItemTargetPlural -eq 'usergroups'))
                                            {
                                                $Uri_Associations_POST = $URL_Template_Associations_Members -f $TargetItemTypeNamePlural, $TargetItemId
                                                $JsonBody = '{"op":"' + $Action + '","type":"' + $SourceItemTypeNameSingular + '","id":"' + $SourceItemId + '","attributes":' + $AttributesValue + '}'
                                            }
                                            ElseIf (($SourceItemTypeNamePlural -eq 'systemgroups' -and $SourceItemTargetPlural -eq 'systems') -or ($SourceItemTypeNamePlural -eq 'usergroups' -and $SourceItemTargetPlural -eq 'users'))
                                            {
                                                $Uri_Associations_POST = $URL_Template_Associations_Members -f $SourceItemTypeNamePlural, $SourceItemId
                                                $JsonBody = '{"op":"' + $Action + '","type":"' + $TargetItemTypeNameSingular + '","id":"' + $TargetItemId + '","attributes":' + $AttributesValue + '}'
                                            }
                                            Else
                                            {
                                                $Uri_Associations_POST = $URL_Template_Associations_Targets -f $SourceItemTypeNamePlural, $SourceItemId, $SourceItemTargetSingular
                                                $JsonBody = '{"op":"' + $Action + '","type":"' + $TargetItemTypeNameSingular + '","id":"' + $TargetItemId + '","attributes":' + $AttributesValue + '}'
                                            }
                                            # Send body to endpoint.
                                            Write-Verbose ('"' + $Action + '" the association between the "' + $SourceItemTypeNameSingular + '" "' + $SourceItemName + '" and the "' + $TargetItemTypeNameSingular + '" "' + $TargetItemName + '"')
                                            Write-Debug ('[UrlTemplate]:' + $Uri_Associations_POST + '; Body:' + $JsonBody + ';')
                                            If (!($Force))
                                            {
                                                Do
                                                {
                                                    $HostResponse = Read-Host -Prompt:('Are you sure you want to "' + $Action + '" the association between the "' + $SourceItemTypeNameSingular + '" called "' + $SourceItemName + '" and the "' + $TargetItemTypeNameSingular + '" called "' + $TargetItemName + '"?[Y/N]')
                                                }
                                                Until ($HostResponse -in ('y', 'n'))
                                            }
                                            If ($HostResponse -eq 'y' -or $Force)
                                            {
                                                Try
                                                {
                                                    $JCApi = Invoke-JCApi -Body:($JsonBody) -Method:('POST') -Url:($Uri_Associations_POST)
                                                    $ActionResult = $JCApi | Select-Object * `
                                                        , @{Name = 'IsSuccessStatusCode'; Expression = {$JCApi.httpMetaData.BaseResponse.IsSuccessStatusCode | Select-Object -Unique}} `
                                                        , @{Name = 'error'; Expression = {$null}}
                                                }
                                                Catch
                                                {
                                                    $ActionResult = [PSCustomObject]@{
                                                        'IsSuccessStatusCode' = $_.Exception.Response.IsSuccessStatusCode | Select-Object -Unique;
                                                        'error'               = $_;
                                                    }
                                                    Write-Error ($_)
                                                }
                                            }
                                            # Validate that the new association has been created
                                            If ($Action -eq 'add')
                                            {
                                                $AddAssociationValidation = Format-JCAssociation -Uri:($Uri_Associations_GET) -Method:('GET') -Source:($SourceItem) -TargetId:($TargetItemId) -IncludeNames:($true) | Where-Object {$_.TargetId -eq $TargetItemId}
                                                If ($AddAssociationValidation)
                                                {
                                                    $Result = $AddAssociationValidation
                                                }
                                                Else
                                                {
                                                    Write-Error ('Association not found. Unable to validate that the association between "' + $SourceItemTypeNameSingular + '" "' + $SourceItemSearchByValue + '" and "' + $TargetItemTypeNameSingular + '" "' + $TargetSearchByValue + '" was created.')
                                                }
                                            }
                                            # Validate that the old association has been removed
                                            If ($Action -eq 'remove')
                                            {
                                                $RemoveAssociationValidation = Format-JCAssociation -Uri:($Uri_Associations_GET) -Method:('GET') -Source:($SourceItem) -TargetId:($TargetItemId) -IncludeNames:($true)
                                                If (!($RemoveAssociationValidation) -or $RemoveAssociationValidation.associationType -eq 'indirect')
                                                {
                                                    $Result = $DirectAssociations
                                                }
                                                Else
                                                {
                                                    Write-Error ('Association found. Unable to validate that the association between "' + $SourceItemTypeNameSingular + '" "' + $SourceItemSearchByValue + '" and "' + $TargetItemTypeNameSingular + '" "' + $TargetSearchByValue + '" has been removed.')
                                                }
                                            }
                                            # Append record status
                                            $Results += If ($Result)
                                            {
                                                $Result | Select-Object * `
                                                    , @{Name = 'action'; Expression = {$Action}} `
                                                    , @{Name = 'IsSuccessStatusCode'; Expression = {$ActionResult.IsSuccessStatusCode | Select-Object -Unique}} `
                                                    , @{Name = 'error'; Expression = {$ActionResult.error}}
                                            }
                                        }
                                    }
                                }
                                Else
                                {
                                    Write-Error ('Unable to find the target "' + $SourceItemTargetSingular + '" called "' + $TargetSearchByValue + '".')
                                }
                            }
                            Else
                            {
                                Write-Verbose ('Association is ' + $associationType + ' between "' + $SourceItemTypeNameSingular + '" "' + $SourceItemSearchByValue + '" and "' + $TargetItemTypeNameSingular + '" "' + $TargetSearchByValue + '".')
                            }
                        }
                    }
                }
            }
            Else
            {
                Write-Error ('Unable to find the "' + $Type + '" called "' + $SourceItemSearchByValue + '".')
            }
        }
        Catch
        {
            Invoke-Command -ScriptBlock:($ScriptBlock_TryCatchError) -ArgumentList:($_, $true) -NoNewScope
        }
    }
    End
    {
        If ($Results)
        {
            $HiddenProperties = @('httpMetaData')
            Return Hide-ObjectProperty -Object:($Results) -HiddenProperties:($HiddenProperties)
        }
    }
}