lib/FieldSettings.ps1
## TM Field Settings Function Get-TMNextSharedColumn { param( [Parameter(Mandatory = $false, ValueFromPipeline = $true)][PSObject]$FieldSpecs ) ## Get furthest Custom field number number in each for ($i = 1; $i -ne 100; $i++) { if ( (-Not ($FieldSpecs.APPLICATION.fields | Where-Object { $_.field -eq 'custom' + $i })) ` -and (-Not ($FieldSpecs.DEVICE.fields | Where-Object { $_.field -eq 'custom' + $i })) ` -and (-Not ($FieldSpecs.DATABASE.fields | Where-Object { $_.field -eq 'custom' + $i })) ` -and (-Not ($FieldSpecs.STORAGE.fields | Where-Object { $_.field -eq 'custom' + $i })) ) { return 'custom' + $i } } } Function Get-TMNextCustomColumn { param( [Parameter(Mandatory = $true, ValueFromPipeline = $true)][PSObject]$ClassFields ) for ($i = 1; $i -ne 100; $i++) { if (-Not ($ClassFields.fields | Where-Object { $_.field -eq 'custom' + $i })) { return 'custom' + $i } } } Function Get-TMFieldSpecs { param( [Parameter(Mandatory = $false)][String]$TMSession = 'Default', [Parameter(Mandatory = $false)][String]$Server = $global:TMSessions[$TMSession].TMServer, [Parameter(Mandatory = $false)]$AllowInsecureSSL = $global:TMSessions[$TMSession].AllowInsecureSSL, [Parameter(Mandatory = $false)][Switch]$ResetIDs, [Parameter(Mandatory = $false)][Switch]$CustomOnly, [Parameter(Mandatory = $false)][Switch]$Label ) ## Get Session Configuration $TMSessionConfig = $global:TMSessions[$TMSession] if (-not $TMSessionConfig) { Write-Host 'TMSession: [' -NoNewline Write-Host $TMSession -ForegroundColor Cyan Write-Host '] was not Found. Please use the New-TMSession command.' Throw 'TM Session Not Found. Use New-TMSession command before using features.' } #Honor SSL Settings if ($TMSessionConfig.AllowInsecureSSL) { $TMCertSettings = @{SkipCertificateCheck = $true } } else { $TMCertSettings = @{SkipCertificateCheck = $false } } $instance = $Server.Replace('/tdstm', '') $instance = $instance.Replace('https://', '') $instance = $instance.Replace('http://', '') $uri = 'https://' $uri += $instance $uri += '/tdstm/ws/customDomain/fieldSpec/ASSETS' try { $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSessionConfig.TMWebSession @TMCertSettings } catch { return $_ } if ($response.StatusCode -eq 200) { $Result = ($response.Content | ConvertFrom-Json) } else { return 'Unable to collect Field Settings.' } if ($CustomOnly) { foreach ($AssetClass in $Result.PSObject.Properties.Value) { $AssetClass.fields = $AssetClass.fields | Where-Object { $_.udf -ne '0' } } } if ($ResetIDs) { ## Read each domain field to reset the IDs foreach ($AssetClass in $Result.PSObject.Properties.Value) { ## Sort the fields by label and collect a new ordered list of the fields $AssetClass.fields = $AssetClass.fields | Sort-Object -Property 'label' ## Iterate over all of the fields for ($i = 0; $i -lt $AssetClass.fields.Count; $i++) { ## If the Field is a custom field. if ($AssetClass.fields[$i].field -like 'custom*') { ## Change the custom field number to be set on import $AssetClass.fields[$i].field = 'customN' } ## If the Field Control is a JSON, sets the JSONFieldID to Null. if ($AssetClass.fields[$i].control -eq 'JSON') { $AssetClass.fields[$i].jsonFieldId = $null } } } } return $Result } Function Update-TMFieldSpecs { param( [Parameter(Mandatory = $false)][String]$TMSession = 'Default', [Parameter(Mandatory = $false)][String]$Server = $global:TMSessions[$TMSession].TMServer, [Parameter(Mandatory = $false)]$AllowInsecureSSL = $global:TMSessions[$TMSession].AllowInsecureSSL, [Parameter(Mandatory = $true)][PSObject]$FieldSpecs ) ## Get Session Configuration $TMSessionConfig = $global:TMSessions[$TMSession] if (-not $TMSessionConfig) { Write-Host 'TMSession: [' -NoNewline Write-Host $TMSession -ForegroundColor Cyan Write-Host '] was not Found. Please use the New-TMSession command.' Throw 'TM Session Not Found. Use New-TMSession command before using features.' } #Honor SSL Settings if ($TMSessionConfig.AllowInsecureSSL) { $TMCertSettings = @{SkipCertificateCheck = $true } } else { $TMCertSettings = @{SkipCertificateCheck = $false } } $TMVersion = [Version]::parse($TMSessionConfig.TMVersion) ## Define the domain classes $DomainClasses = @('APPLICATION', 'DEVICE', 'DATABASE', 'STORAGE') # Get the Existing Field Spec $ServerFields = Get-TMFieldSpecs -TMSession $TMSession # Create the Updated FieldSpec Object and clear the fields $UpdateFields = $ServerFields | ConvertTo-Json -Depth 100 | ConvertFrom-Json -Depth 100 foreach ($DomainClass in $DomainClasses) { $UpdateFields.$DomainClass.fields = @() } # Collect the existing Asset Class Fields, updating them if they exist foreach ($DomainClass in $DomainClasses) { $ServerClassFields = $ServerFields.$DomainClass.fields $NewFields = $FieldSpecs.$DomainClass.fields foreach ($Field in $ServerClassFields) { ## Look for an existing field name $MatchingNewField = $NewFields | Where-Object { $_.label -eq $Field.label } if ($MatchingNewField) { ## Get the UPDATED Field $ReturnField = $MatchingNewField | ConvertTo-Json -Depth 100 -Compress | ConvertFrom-Json if ($ReturnField.Count -gt 1) { Throw "Duplicate Field Detected - $($Field.label)" } ## Update with the original Field ID to keep the existing column $ReturnField.field = $Field.field ## Set the Constraints iist if ($ReturnField.control -in @('List', 'YesNo')) { if ($TMVersion.Major -eq 6 -and $TMVersion -ge '6.1.0') { ## Ensure that the incomming data has the right constraint(s) property Add-Member -InputObject $ReturnField -NotePropertyName 'constraint' -NotePropertyValue ($ReturnField.constraint ?? $ReturnField.constraints) -Force ## Check to see if it's in the list ## For Each field that already exists, get the list of constraints $Field.constraint.PSObject.Properties.Name | ForEach-Object { ## If the ReturnField Constraints doesn't have the value, add it if ($ReturnField.constraint.PSObject.Properties.Name -notcontains $_) { $ReturnField.constraint.PSObject.Properties.Name += $_ } } ## Remove the Added field from the Incoming Field Spec object so it doesn't get added again $NewFields.PSObject.Properties.Remove($MatchingNewField) } else { ## Ensure that the incomming data has the right constraint(s) property Add-Member -InputObject $ReturnField -NotePropertyName 'constraints' -NotePropertyValue ($ReturnField.constraints ?? $ReturnField.constraint) -Force ## For Each field that already exists, get the list of constraint $Field.constraints.PSObject.Properties.Name | ForEach-Object { ## If the ReturnField Constraint doesn't have the value, add it if ($Field.constraints.PSObject.Properties.Name -notcontains $_) { $Field.constraints.PSObject.Properties.Name += $_ } } } } #### TODO: This is now no longe working against 61. This line has been moved iunto ## Remove the Added field from the Incoming Field Spec object so it doesn't get added again # $NewFields.PSObject.Properties.Remove($MatchingNewField) # $NewFields.PSObject.Properties.Remove($MatchingNewField) } else { ## There is no matching field, return the original Field $ReturnField = $Field } $UpdateFields.$DomainClass.fields += $ReturnField } } # Add Shared Fields ## This is only done on one asset class (0, which exists every time). Since shared fields go to all asset classes. $NewSharedFields = $FieldSpecs.$DomainClass[0].fields | Where-Object { $_.shared -eq 1 } foreach ($NewSharedField in $NewSharedFields) { ## Don't allow duplicates by label if ( ($UpdateFields.APPLICATION.fields | Where-Object { $_.label -eq $NewSharedField.label }) ` -or ($UpdateFields.DEVICE.fields | Where-Object { $_.label -eq $NewSharedField.label }) ` -or ($UpdateFields.DATABASE.fields | Where-Object { $_.label -eq $NewSharedField.label }) ` -or ($UpdateFields.STORAGE.fields | Where-Object { $_.label -eq $NewSharedField.label }) ` ) { ## Field Already Exists and has been updated # Write-Host "Shared Field Name:"$NewSharedField.label"is already in use." Continue } ## Field doesn't exist $ReturnField = $NewSharedField | ConvertTo-Json -Depth 100 -Compress | ConvertFrom-Json if ($ReturnField.field -eq 'customN') { $ReturnField.field = Get-TMNextSharedColumn $UpdateFields } $UpdateFields.APPLICATION.fields += $ReturnField $UpdateFields.DEVICE.fields += $ReturnField $UpdateFields.DATABASE.fields += $ReturnField $UpdateFields.STORAGE.fields += $ReturnField # Write-Host 'ADDING SHARED Field Name: ' $ReturnField.label } ## Add NON Shared fields, one for each Asset Class foreach ($DomainClass in $DomainClasses) { $NewNonSharedFields = $FieldSpecs.$DomainClass.fields | Where-Object { $_.shared -eq 0 } foreach ($Field in $NewNonSharedFields) { ## Don't allow duplicates by label if ($UpdateFields.$DomainClass.fields | Where-Object { $_.label -eq $Field.label }) { ## Field Already Exists and has been updated # Write-Host $DomainClass 'Field Name: ' $Field.label ' is already in use.' Continue } ## Field doesn't exist $ReturnField = $Field if ($ReturnField.field -eq 'customN') { $ReturnField.field = Get-TMNextCustomColumn $UpdateFields.$DomainClass } $UpdateFields.$DomainClass.fields += $ReturnField # Write-Host 'ADDING '$DomainClass 'Field Name: ' $Field.label } } ## Validate the number of fields being created foreach ($DomainClass in $DomainClasses) { ## Count the number of Custom Fields $CustomFieldCount = ($UpdateFields.$DomainClass.fields | Where-Object { $_.field -like 'custom*' }).Count ## Disallow an upload if there are more than 96 custom fields if ($CustomFieldCount -gt 96) { Throw "Adding these fields would result in a total of $CustomFieldCount fields in the $DomainClass class." } ## Adjust every domain field spec to the correct format if ($TMVersion.Major -eq 6 -and $TMVersion -ge '6.1.0') { ## Ensure that the incomming data has the right constraint(s) property $UpdateFields.$DomainClass.fields | ForEach-Object { Add-Member -InputObject $_ -NotePropertyName 'constraint' -NotePropertyValue ($_.constraint ?? $_.constraints) -Force if ($_.PSObject.Properties.Name.Contains('constraints')) { $_.PSObject.Properties.Remove('constraints') } Add-Member -InputObject $_ -NotePropertyName 'defaultValue' -NotePropertyValue ($_.defaultValue ?? $_.default) -Force if ($_.PSObject.Properties.Name.Contains('default')) { $_.PSObject.Properties.Remove('default') } } } else { $UpdateFields.$DomainClass.fields | ForEach-Object { ## Ensure that the incomming data has the right constraint(s) property Add-Member -InputObject $_ -NotePropertyName 'constraints' -NotePropertyValue ($_.constraint ?? $_.constraints) -Force if ($_.PSObject.Properties.Name.Contains('constraint')) { $_.PSObject.Properties.Remove('constraint') } Add-Member -InputObject $_ -NotePropertyName 'default' -NotePropertyValue ($_.default ?? $_.defaultValue) -Force if ($_.PSObject.Properties.Name.Contains('defaultValue')) { $_.PSObject.Properties.Remove('defaultValue') } } } } ## Build the URL to post back to $uri = 'https://' $uri += $Server $uri += '/tdstm/ws/customDomain/fieldSpec/ASSETS' ## Create the Field Settings body, differs for TM separate TM versions $TMVersion = [Version]::parse($TMSessionConfig.TMVersion) if ((($TMVersion.Major -eq 5) -and ($TMVersion -ge '5.0.4')) -or (($TMVersion.Major -eq 6 -and $TMVersion -ge '6.0.0.2'))) { ## Build an API post body with the Project ID $PostBody = [PSCustomObject]@{ fieldSettings = $UpdateFields projectId = $TMSessions[$TMsession].UserContext.project.id } | ConvertTo-Json -Depth 100 -Compress } else { $PostBody = $UpdateFields | ConvertTo-Json -Depth 100 -Compress } Set-TMHeaderContentType -ContentType 'JSON' -TMSession $TMSession try { $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSessionConfig.TMWebSession -Body $PostBody @TMCertSettings if ($response.StatusCode -eq 200) { $ResponseJson = $response.Content | ConvertFrom-Json -Depth 100 if ($ResponseJson.status -eq 'success') { # Write-Host 'Field Specs been updated.' return } else { throw $ResponseJson.errors } } elseif ($response.StatusCode -eq 204) { return } else { throw $_ } } catch { return $_ } } Function Set-TMFieldSpecs { param( [Parameter(Mandatory = $false)][String]$TMSession = 'Default', [Parameter(Mandatory = $false)][String]$Server = $global:TMSessions[$TMSession].TMServer, [Parameter(Mandatory = $false)]$AllowInsecureSSL = $global:TMSessions[$TMSession].AllowInsecureSSL, [Parameter(Mandatory = $true)][PSObject]$FieldSpecs, [Switch]$Force ) ## This command is potentially destructive if (-Not $Force) { Write-Host 'This command is used to overwrite an existing field spec object and should be used with care.' Write-Host 'The counterpart command ' -NoNewline Write-Host 'Update-TMFieldSpecs ' -NoNewline -ForegroundColor Yellow Write-Host 'might be more useful as it merges changes into an existing FieldSpec.' Write-Host 'To use this command to overwrite a FieldSpec, use of the -Force switch is required' throw 'You must use the force switch with this command' } ## Get Session Configuration $TMSessionConfig = $global:TMSessions[$TMSession] if (-not $TMSessionConfig) { Write-Host 'TMSession: [' -NoNewline Write-Host $TMSession -ForegroundColor Cyan Write-Host '] was not Found. Please use the New-TMSession command.' Throw 'TM Session Not Found. Use New-TMSession command before using features.' } #Honor SSL Settings if ($TMSessionConfig.AllowInsecureSSL) { $TMCertSettings = @{SkipCertificateCheck = $true } } else { $TMCertSettings = @{SkipCertificateCheck = $false } } ## Build the URL to post back to $uri = 'https://' $uri += $Server $uri += '/tdstm/ws/customDomain/fieldSpec/ASSETS' ## Create the Field Settings body, differs for TM separate TM versions $TMVersion = [Version]::parse($TMSessionConfig.TMVersion) if ((($TMVersion.Major -eq 5) -and ($TMVersion -ge '5.0.4')) -or (($TMVersion.Major -eq 6 -and $TMVersion -ge '6.0.0.2'))) { ## Build an API post body with the Project ID $PostBody = [PSCustomObject]@{ fieldSettings = $FieldSpecs projectId = $TMSessions[$TMsession].UserContext.project.id } | ConvertTo-Json -Depth 100 -Compress } else { $PostBody = $FieldSpecs | ConvertTo-Json -Depth 100 -Compress } Set-TMHeaderContentType -ContentType 'JSON' -TMSession $TMSession try { $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSessionConfig.TMWebSession -Body $PostBody @TMCertSettings if ($response.StatusCode -eq 200) { $ResponseJson = $response.Content | ConvertFrom-Json -Depth 100 if ($ResponseJson.status -eq 'success') { # Write-Host 'Field Specs been updated.' return } else { throw $ResponseJson.errors } } elseif ($response.StatusCode -eq 204) { return } else { throw $_ } } catch { return $_ } } Function Add-TMFieldListValues { param( [Parameter(Mandatory = $false)][String]$TMSession = 'Default', [Parameter(Mandatory = $false)][String]$Server = $global:TMSessions[$TMSession].TMServer, [Parameter(Mandatory = $false)]$AllowInsecureSSL = $global:TMSessions[$TMSession].AllowInsecureSSL, [Parameter(Mandatory = $true)][ValidateSet('Application', 'Database', 'Device', 'Storage')][String]$Domain, [Parameter(Mandatory = $true)][String]$FieldLabel, [Parameter(Mandatory = $true)][Array]$Values, [Parameter(Mandatory = $false)][ValidateSet('Asc', 'Desc')] [String]$SortConstraintList ) if ($Values.Count -eq 0) { Write-Verbose 'There were no updates to make to the field' return } ## Get Session Configuration $TMSessionConfig = $global:TMSessions[$TMSession] if (-not $TMSessionConfig) { Write-Host 'TMSession: [' -NoNewline Write-Host $TMSession -ForegroundColor Cyan Write-Host '] was not Found. Please use the New-TMSession command.' Throw 'TM Session Not Found. Use New-TMSession command before using features.' } #Honor SSL Settings if ($TMSessionConfig.AllowInsecureSSL) { $TMCertSettings = @{SkipCertificateCheck = $true } } else { $TMCertSettings = @{SkipCertificateCheck = $false } } $TMVersion = [Version]::parse($TMSessionConfig.TMVersion) ## Define the domain classes $DomainClasses = @{ Application = 'APPLICATION' Device = 'DEVICE' Database = 'DATABASE' Storage = 'STORAGE' } # Get the Existing Field Spec $FieldSpecs = Get-TMFieldSpecs -TMSession $TMSession ## Create the Updated FieldSpec Object and clear the fields $Field = $FieldSpecs.($DomainClasses[$Domain]).fields | Where-Object { $_.label -eq $FieldLabel } if (-Not $Field) { Write-Host "Field: $($FieldLabel) not found!" -ForegroundColor Red return } ## Only Add values to a List control if ($Field -and $Field.control -ne 'List') { Write-Verbose "Skipping update on $($Field.label) as it is a $($Field.control) type." return } foreach ($NewItem in $Values) { ## Version 6.0.2.1+ (and TODO version 6.1?, 5x?) ## Requires the class names to be lower case $CONSTRAINTS_PROPERTY_NAME = 'constraints' if ($TMVersion.Major -eq 6 -and $TMVersion -ge '6.1.0') { $CONSTRAINTS_PROPERTY_NAME = 'constraint' } ## Convert each Asset Class name to lowercase ## Check to see if it's in the list if (-Not $Field.$CONSTRAINTS_PROPERTY_NAME.values.Contains($NewItem)) { $Field.$CONSTRAINTS_PROPERTY_NAME.values += $NewItem } ## Sort the list if required if ($SortConstraintList -eq 'Asc') { $Field.$CONSTRAINTS_PROPERTY_NAME.values = $Field.$CONSTRAINTS_PROPERTY_NAME.values | Sort-Object } if ($SortConstraintList -eq 'Desc') { $Field.$CONSTRAINTS_PROPERTY_NAME.values = $Field.$CONSTRAINTS_PROPERTY_NAME.values | Sort-Object -Descending } } ## Put the data back $FieldSpecs.($DomainClasses[$Domain]).fields | Where-Object { $_.label -eq $FieldLabel } | ForEach-Object { $_ = $Field } ## Construct an update call $uri = "https://$($Server)/tdstm/ws/customDomain/fieldSpec/ASSETS" Set-TMHeaderContentType -ContentType 'JSON' -TMSession $TMSession ## Create the Field Settings body, differs for TM separate TM versions $TMVersion = [Version]::parse($TMSessionConfig.TMVersion) if ((($TMVersion.Major -eq 5) -and ($TMVersion -ge '5.0.4')) -or (($TMVersion.Major -eq 6 -and $TMVersion -ge '6.0.0.2'))) { ## Build an API post body with the Project ID $PostBody = [PSCustomObject]@{ fieldSettings = $FieldSpecs projectId = $TMSessions[$TMsession].UserContext.project.id } | ConvertTo-Json -Depth 100 -Compress } else { $PostBody = $FieldSpecs | ConvertTo-Json -Depth 100 -Compress } try { $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSessionConfig.TMWebSession -Body $PostBody @TMCertSettings if ($response.StatusCode -eq 200) { $ResponseJson = $response.Content | ConvertFrom-Json -Depth 100 if ($ResponseJson.status -ne 'success') { throw $ResponseJson.errors } } elseif ($response.StatusCode -eq 204) { return } else { ThrowError 'Unable to save Field Settings' } } catch { throw $_ } } function Get-TMFieldToLabelMap { param( [Parameter(Mandatory = $false)] [String]$TMSession = 'Default', [Parameter(Mandatory = $false)] [String]$Server = $global:TMSessions[$TMSession].TMServer, [Parameter(Mandatory = $false)] [Bool]$AllowInsecureSSL = $global:TMSessions[$TMSession].AllowInsecureSSL, [Parameter(Mandatory = $false)] [ValidateSet('APPLICATION', 'DEVICE', 'DATABASE', 'STORAGE')] [String]$Domain ) $AllFields = Get-TMFieldSpecs -TMSession $TMSession -Server $Server -AllowInsecureSSL $AllowInsecureSSL $FieldLabelMap = @{} foreach ($DomainName in @('APPLICATION', 'DEVICE', 'DATABASE', 'STORAGE')) { $FieldLabelMap.Add($DomainName, @{}) foreach ($Spec in $AllFields.$DomainName.fields) { # Add-Member -InputObject $FieldLabelMap.$DomainName -NotePropertyName $Spec.field -NotePropertyValue $Spec.label -Force Write-Verbose "Domain: $($DomainName), Field: $($Spec.field), Label: $($Spec.label)" $FieldLabelMap.$DomainName.Add($Spec.field, $Spec.label) } } if ($Domain) { $FieldLabelMap.$Domain } else { $FieldLabelMap } } function Get-TMLabelToFieldMap { param( [Parameter(Mandatory = $false)] [String]$TMSession = 'Default', [Parameter(Mandatory = $false)] [String]$Server = $global:TMSessions[$TMSession].TMServer, [Parameter(Mandatory = $false)] [Bool]$AllowInsecureSSL = $global:TMSessions[$TMSession].AllowInsecureSSL, [Parameter(Mandatory = $false)] [ValidateSet('APPLICATION', 'DEVICE', 'DATABASE', 'STORAGE')] [String]$Domain ) $AllFields = Get-TMFieldSpecs -TMSession $TMSession -Server $Server -AllowInsecureSSL $AllowInsecureSSL $LabelFieldMap = @{} foreach ($DomainName in @('APPLICATION', 'DEVICE', 'DATABASE', 'STORAGE')) { $LabelFieldMap.Add($DomainName, @{}) foreach ($Spec in $AllFields.$DomainName.fields) { $LabelFieldMap.$DomainName.Add($Spec.label, $Spec.field) } } if ($Domain) { $LabelFieldMap.$Domain } else { $LabelFieldMap } } |