Public/Actions.ps1
# Actions Function New-TMAction { param( [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default', [Parameter(Mandatory = $true)][PSObject]$Action, [Parameter(Mandatory = $false)][PSObject]$Project, [Parameter(Mandatory = $false)][Switch]$Update, [Parameter(Mandatory = $false)][Switch]$Passthru ) $TMSession = Get-TMSession $TMSession ## Use the TM Session provided if ($TMSession.TMVersion -like '4.6*') { New-TMAction46 @PSBoundParameters } else { New-TMAction474 @PSBoundParameters } } Function New-TMAction46 { param( [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default', [Parameter(Mandatory = $true)][PSObject]$Action, [Parameter(Mandatory = $false)][PSObject]$Project, [Parameter(Mandatory = $false)][Switch]$Passthru ) ## Get Session Configuration $TMSession = Get-TMSession $TMSession if (-not $TMSession) { 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 $TMCertSettings = @{SkipCertificateCheck = $TMSession.AllowInsecureSSL } ## Check for existing Action $ActionCheck = Get-TMAction -Name $Action.name -TMSession $TMSession if ($ActionCheck) { if ($Passthru) { return $ActionCheck } else { return } } else { ## No Credential exists. Create it $Instance = $TMSession.TMServer.Replace('/tdstm', '') $instance = $instance.Replace('https://', '') $instance = $instance.Replace('http://', '') $uri = 'https://' $uri += $instance $uri += '/tdstm/ws/apiAction' ## Lookup Cross References if (-not $Project) { $Project = $Projects | Where-Object { $_.name -eq $Action.project.name } } $ProjectID = $Project.id $ProviderID = (Get-TMProvider -TMSession $TMSession | Where-Object { $_.name -eq $Action.provider.name }).id $CredentialID = (Get-TMCredential -TMSession $TMSession | Where-Object { $_.name -eq $Action.credential.name }).id ## Fix up the object $Action.PSObject.properties.Remove('id') $Action.actionType = $Action.actionType.id $Action.project.id = $ProjectID $Action.provider.id = $ProviderID $Action.credential.id = $CredentialID $Action.version = 1 $PostBodyJSON = $Action | ConvertTo-Json -Depth 100 Set-TMHeaderAccept 'JSON' -TMSession $TMSession Set-TMHeaderContentType 'JSON' -TMSession $TMSession try { $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSession.TMWebSession -Body $PostBodyJSON @TMCertSettings if ($response.StatusCode -eq 200) { $responseContent = $response.Content | ConvertFrom-Json if ($responseContent.status -eq 'success') { if ($Passthru) { return $responseContent.data } else { return } } } } catch { Write-Host 'Unable to create Action.' return $_ } } } Function New-TMAction474 { param( [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default', [Parameter(Mandatory = $true)][PSObject]$Action, [Parameter(Mandatory = $false)][PSObject]$Project, [Parameter(Mandatory = $false)][Switch]$Update, [Parameter(Mandatory = $false)][Switch]$Passthru ) ## Get Session Configuration $TMSession = Get-TMSession $TMSession if (-not $TMSession) { 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 $TMCertSettings = @{SkipCertificateCheck = $TMSession.AllowInsecureSSL } ## Check for existing Action $ActionCheck = Get-TMAction -Name $Action.name -TMSession $TMSession if ($ActionCheck -and -not $Update) { if ($PassThru) { return $ActionCheck } else { return } } else { ## No Action exists. Create it $Instance = $TMSession.TMServer.Replace('/tdstm', '') $instance = $instance.Replace('https://', '') $instance = $instance.Replace('http://', '') $uri = 'https://' $uri += $instance $uri += '/tdstm/ws/apiAction' ## Cleans the Action Name of characters we don't want $Action.name = $Action.name -replace '\\', '' -replace '\/', '' -replace '\:', '' -replace '>', '' -replace '<', '' -replace '\(', '' -replace '\)', '' -replace '\*', '' # $Action.name = $SafeActionName ## If The Existing action should be updated if ($ActionCheck -and $Update) { ## When the Action is an update, use important details from the current object. $Action.id = $ActionCheck.id $Action.dateCreated = $ActionCheck.dateCreated $Action.lastUpdated = $ActionCheck.lastUpdated # $Action.debugEnabled = $ActionCheck.debugEnabled $Action.version = $ActionCheck.version $action.project = $ActionCheck.project ## Set the HTTP call details $uri += '/' + $Action.id $HttpMethod = 'Put' } else { ## Process as a new object. $HttpMethod = 'Post' $Action.PSObject.Properties.Remove('id') } ## Lookup Provider and Credential IDs, Field Specs if ([String]::IsNullOrWhiteSpace($Action.provider.name)) { Write-Error -Message "Provider name is blank for Action: $($Action.name)" return } $ProviderID = (Get-TMProvider -Name $Action.provider.name -TMSession $TMSession).id if (!$ProviderID) { # Create the provider if it doesn't exist $NowFormatted = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ' -AsUTC).ToString() $Provider = [PSCustomObject]@{ id = $null name = $Action.provider.name description = '' comment = '' dateCreated = $NowFormatted lastUpdated = $NowFormatted } $ProviderID = (New-TMProvider -Provider $Provider -PassThru -TMSession $TMSession).id } $Action.provider.id = $ProviderID ## If the Credential name is provided, look up the proper credential and add the ID if ($Action.credential) { if ($Action.credential.name) { $CredentialID = (Get-TMCredential -Name $Action.credential.name -TMSession $TMSession ).id if ($CredentialID) { $Action.credential.id = $CredentialID } $Action.remoteCredentialMethod = 'SUPPLIED' } else { $Action.credential = $null $Action.remoteCredentialMethod = 'USER_PRIV' } } $FieldSettings = Get-TMFieldSpecs -TMSession $TMSession ## Fix the Parameters to the correct custom field name if ($Action.methodParams) { ## Unwrap if ($Action.methodParams.GetType().Name -eq 'String') { $Parameters = @($Action.methodParams | ConvertFrom-Json) } else { $Parameters = $Action.methodParams } for ($j = 0; $j -lt $Parameters.Count; $j++) { if ($Parameters[$j].context -in @('DEVICE', 'APPLICATION', 'STORAGE', 'DATABASE')) { ## Check if a fieldLabel param exists in the Parameters[$j]. ## This is added by the New-TMIntegrationPlugin command so the correct ## Custom fields can be assigned when the new Action is installed. if ($Parameters[$j].PSobject.Properties.name -match 'fieldLabel') { ## Check if this is a CustomN field if ($Parameters[$j].fieldName -eq 'customN') { ## Update the Project's assigned Field by associating the label to the current field list. $Parameters[$j].fieldName = ($FieldSettings.($Parameters[$j].context).fields | Where-Object { $_.label -eq $Parameters[$j].fieldLabel }).field } } } ## Remove the 'fieldLabel property as it's invalid column definition $Parameters[$j].PSObject.Properties.Remove('fieldLabel') } if ($Parameters) { $Action.methodParams = (ConvertTo-Array -InputObject $Parameters | ConvertTo-Json -Depth 100 -Compress ).toString() } else { $Action.PSObject.Properties.Remove('methodParams') } } ## Adjust the actionType for TM6.1 if ($TMSession.TMVersion -ge '6.1.0') { $Action.actionType = $Action.actionType.id ?? $Action.actionType } $PostBodyJSON = $Action | ConvertTo-Json Set-TMHeaderAccept 'JSON' -TMSession $TMSession Set-TMHeaderContentType 'JSON' -TMSession $TMSession try { $response = Invoke-WebRequest -Method $HttpMethod -Uri $uri -WebSession $TMSession.TMWebSession -Body $PostBodyJSON @TMCertSettings if ($response.StatusCode -eq 200) { $responseContent = $response.Content | ConvertFrom-Json if ($responseContent.status -ne 'success') { throw ($responseContent.errors -join ', ') } else { if ($PassThru) { return $responseContent.data } } } elseif ($response.StatusCode -eq 204) { return } } catch { Write-Host 'Unable to create Action.' return $_ } } } Function Get-TMAction { [CmdletBinding(DefaultParameterSetName = 'Default')] param( [Parameter(Mandatory = $false)] [PSObject]$TMSession = 'Default', [Parameter(Mandatory = $false, ParameterSetName = 'ByName')] [String[]]$Name, [Parameter(Mandatory = $false, ParameterSetName = 'ById')] [String[]]$Id, [Parameter(Mandatory = $false)] [String[]]$ProviderName, [Parameter(Mandatory = $false)] [Switch]$ResetIDs, [Parameter(Mandatory = $false)] [String]$SaveCodePath, [Parameter(Mandatory = $false)] [bool]$Api = $true, [Parameter(Mandatory = $false)] [Switch]$Passthru ) ## Get Session Configuration $TMSession = Get-TMSession $TMSession if (-not $TMSession) { 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 $TMCertSettings = @{SkipCertificateCheck = $TMSession.AllowInsecureSSL } $Instance = $TMSession.TMServer.Replace('/tdstm', '').Replace('https://', '').Replace('http://', '') if ($Api) { # Format the uri $uri = "https://$instance/tdstm/api/apiAction?project=$($TMSession.UserContext.project.id)" try { $response = Invoke-RestMethod -Method Get -Uri $uri -WebSession $TMSession.TMRestSession @TMCertSettings $Result = $response | Where-Object { $_.actionType.id -EQ 'POWER_SHELL' } } catch { throw $_ } } else { # Format the uri $uri = "https://$instance/tdstm/ws/apiAction" try { $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings } catch { throw $_ } if ($response.StatusCode -in @(200, 204)) { $Result = ($response.Content | ConvertFrom-Json).data $Result = $Result | Where-Object actionType -EQ 'POWER_SHELL' } else { throw 'Unable to collect Actions.' } } ## Return the details -- Filter based on passed parameters if ($ProviderName) { $Result = $Result | Where-Object { $_.provider.name -in $ProviderName } } elseif ($Name) { $Result = $Result | Where-Object { $_.name -in $Name } } elseif ($Id) { $Result = $Result | Where-Object { $_.id -in $Id } } ## 6.1 and forward, the endpoints do not supply the source code with the object if ($TMSession.TMVersion -ge '6.0.0') { if ($Api) { ## Update each of the objects that were returned with their full version $Result = $Result | ForEach-Object { # Format the uri $uri = "https://$instance/tdstm/api/apiAction/$($_.id)?project=$($TMSession.UserContext.project.id)" Invoke-RestMethod -Method Get -Uri $uri -WebSession $TMSession.TMRestSession @TMCertSettings } } else { ## Update each of the objects that were returned with their full version $Result = $Result | ForEach-Object { # Format the uri $uri = "https://$instance/tdstm/ws/apiAction/$($_.id)?project=$($TMSession.UserContext.project.id)" Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings try { $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings } catch { throw $_ } if ($response.StatusCode -in @(200, 204)) { ($response.Content | ConvertFrom-Json).data } else { throw 'Unable to collect Actions.' } } } } # Get Field / Label maps to translate the parameters $FieldToLabelMap = Get-TMFieldToLabelMap -TMSession $TMSession foreach ($action in $Result | Where-Object methodParams) { ## Fix the Parameters to the correct custom field name if ($action.methodParams -is [String]) { $Parameters = $action.methodParams | ConvertFrom-Json } foreach ($parameter in $Parameters | Where-Object Context -In 'DEVICE', 'APPLICATION', 'STORAGE', 'DATABASE' | Where-Object fieldName) { ## The custom column field identifer is lost when the IDs get reset. Add a fieldLabel node to put the custom field label. This will get replaced/updated by the Import $FieldLabel = $FieldToLabelMap[$parameter.context][$parameter.fieldName] $parameter | Add-Member -NotePropertyName 'fieldLabel' -NotePropertyValue $FieldLabel -Force ## Clear the Custom field number associated to the Parameter if ($ResetIDs -and ($parameter.fieldName -like 'custom*')) { $parameter.fieldName = 'customN' } } $action.methodParams = $Parameters ## Apply Package Format version 2 details Add-Member -InputObject $action -NotePropertyName 'actionType' -NotePropertyValue ($action.actionType.id ?? $action.actionType) -Force } if ($ResetIDs) { ## Clear pertinent data in each Action foreach ($action in $Result) { $action.id = $null $action.project.id = $null if ($action.credential) { $action.credential.id = $null } $action.provider.id = $null } } ## Save the Code Files to a folder if ($SaveCodePath) { ## Save Each of the Script Source Data foreach ($action in $Result) { ## Convert the 'Type' property to a string/enum $action.actionType = $action.actionType.id ?? $action.actionType ## Get a FileName safe version of the Provider Name $SafeProviderName = Get-FilenameSafeString $action.provider.name $SafeActionName = Get-FilenameSafeString $action.name ## Create the Provider Action Folder path $ProviderPath = Join-Path $SaveCodePath $SafeProviderName if (-not (Test-Path -Path $ProviderPath -PathType Container) ) { mkdir $ProviderPath } ## Create a File name for the Action $ProviderScriptConfigPath = Join-Path $ProviderPath ($SafeActionName + '.json') $ProviderScriptPath = Join-Path $ProviderPath ($SafeActionName + '.ps1') ## Split the script out of the Item, and save both files. $ScriptBlock = $action.script Set-Content -Path $ProviderScriptPath -Force -Value $ScriptBlock ## Remove the script from the JSON file $action.PSObject.Properties.Remove('script') ## Convert the Parameters to a TMActionParameters Class Object if ($action.methodParams) { $Parameters = $action.methodParams | ForEach-Object { [TMActionParameters]::new($_) } $action.methodParams = $Parameters } Set-Content -Path $ProviderScriptConfigPath -Force -Value ($action | ConvertTo-Json -Depth 10) } } if ($Passthru -or !$SaveCodePath) { ## Return an array even if it holds a single value - consistency return , $Result } } Function Read-TMActionScriptFile { param( [Parameter(Mandatory = $false)] [PSObject]$TMSession = 'Default', [Parameter(Mandatory = $true)] [String]$Path ) ## First order of business is to determine if this Script file has a counterpart Json file $ActionConfigJsonPath = $Path -Replace '.ps1', '.json' $ActionConfigFile = Get-Item -Path $ActionConfigJsonPath -ErrorAction 'SilentlyContinue' ## Check if there is a config JSON file, if so, get the Action from the 2 files if ($ActionConfigFile) { ## Get the Action Object that doesn't have the source code $TMAction = Get-Content -Path $ActionConfigFile | ConvertFrom-Json $FieldToLabelMap = Get-TMLabelToFieldMap -TMSession $TMSession ## If this Action has parameters, handle sorting and updating them if ($TMAction.methodParams) { ## Convert the methodParams to a String, as it should be switch ($TMAction.methodParams.GetType().Name) { ## Array of objects, each is a PSCustomObject of a TMActionParameter type 'Object[]' { ## Remove Offline properties that do not belong on the object. $TMAction.methodParams = $TMAction.methodParams | Sort-Object -Property 'order' | ForEach-Object { ## Add the correct TM Field Label if ($_.fieldName -eq 'customN') { $_.fieldName = $FieldToLabelMap.($_.context.toUpper()).($_.fieldLabel) } $_.PSObject.Properties.Remove('order') $_.PSObject.Properties.Remove('fieldLabel') $_ } | ConvertTo-Json -Depth 5 -Compress break } ## There might only be one Param (and was written without being forced to a list) 'PSCustomObject' { ## Add the correct TM Field Label if ($TMAction.methodParams.fieldName -eq 'customN') { $TMAction.methodParams.fieldName = $FieldToLabelMap.($TMAction.methodParams.context.toUpper()).($TMAction.methodParams.fieldLabel) } $TMAction.methodParams.PSObject.Properties.Remove('order') $TMAction.methodParams.PSObject.Properties.Remove('fieldLabel') $TMAction.methodParams = @(($TMAction.methodParams)) | ConvertTo-Json -Depth 5 -Compress break } ## For completeness, some Extensions have the literal JSON string data. If so, 'String' { ## Do nothing break } Default { ## If there was no value, or something else entirely, default to an empty set of parameters $TMAction.methodParams = '[]' } } } ## Read the Script Content and add it to the Action Object $ScriptContent = (Get-Content -Path $Path -Raw) ?? '' Add-Member -InputObject $TMAction -NotePropertyName script -NotePropertyValue ($ScriptContent.ToString() ?? '') -Force } ## Perform a read of the PS file to capture the ReferenceDesign formatted settings else { ## Name the Input File $Content = Get-Content -Path $Path -Raw ## Ignore Empty Files if (-Not $Content) { return } $ContentLines = Get-Content -Path $Path ## Create Automation Token Variables Parse the Script File New-Variable astTokens -Force New-Variable astErr -Force $ast = [System.Management.Automation.Language.Parser]::ParseInput($Content, [ref]$astTokens, [ref]$astErr) ## ## Assess the Script Parts to get delineating line numbers ## ## Locate the Delimiting line $ConfigBlockEndLine = $astTokens | ` Where-Object { $_.Text -like '## End of TM Configuration, Begin Script*' } |` Select-Object -First 1 | ` Select-Object -ExpandProperty Extent | ` Select-Object -ExpandProperty StartLineNumber ## Test to see if the Script is formatted output with Metadata if (-not $ConfigBlockEndLine) { ## There is no metadata, create the basic object with just the source code $ActionConfig = [pscustomobject]@{ ActionName = (Get-Item -Path $Path).BaseName Description = '' ProviderName = (Get-Item -Path $Path).Directory.BaseName } ## Create the objects the below constructor expect $ConfigBlockEndLine = -1 $Params = $null $TMActionParams = [System.Collections.ArrayList] @() } else { ## ## Read the Script Header to gather the configurations ## ## Get all of the lines in the header comment $TMConfigHeader = 0..$ConfigBlockEndLine | ForEach-Object { if ($astTokens[$_].kind -eq 'comment') { $astTokens[$_] } } | Select-Object -First 1 | Select-Object -ExpandProperty Text ## Create a Properties object that will store the values listed in the header of the script file $ActionConfig = [PSCustomObject]@{ } ## Process each line of the Header string $TMConfigHeader -split "`n" | ForEach-Object { ## Process each line of the comment if ($_ -like '*=*') { $k, $v = $_ -split '=' $k = $k.Trim() -replace "'", '' -replace '"', '' $v = $v.Trim() -replace "'", '' -replace '"', '' $ActionConfig | Add-Member -NotePropertyName $k -NotePropertyValue $v } } ## ## Read the Script Block ## ## Create a Text StrinBuilder to collect the Script into $ActionConfigStringBuilder = New-Object System.Text.StringBuilder ## For each line in the Code Block, add it to the Action Script Code StringBuilder 0..$ConfigBlockEndLine | ForEach-Object { $ActionConfigStringBuilder.AppendLine($ContentLines[$_]) | Out-Null } $ActionConfigScriptString = $ActionConfigStringBuilder.ToString() $ActionConfigScriptBlock = [scriptblock]::Create($ActionConfigScriptString) ## Invoke the Script Block to create the $Params Object in this scope ## this line populates the $Params object from the Action Script Invoke-Command -ScriptBlock $ActionConfigScriptBlock -NoNewScope ## Collect the Parameters $TMActionParams = [System.Collections.ArrayList] @() ## Action Parameter Class Definition # { # "desc": "", # "type": "string", # "value": "", # "context": "DEVICE", # "encoded": false, # "readonly": false, # "required": false, # "fieldName": null, # "paramName": "IPAddress", # "fieldLabel": "IP Address" # } ## Process the Parameters into Action Params foreach ($ParamLabel in $Params.Keys) { ## Create a new Params Object to load to the Action $NewParamConfig = [PSCustomObject]@{ type = 'string' value = '' description = '' context = '' fieldLabel = '' fieldName = '' required = $false encoded = $false readonly = $false } ## Read the existing Params configuration, assembling each additional Paramater option $ScriptParamConfig = $Params.$ParamLabel $ScriptParamConfig.Keys | ForEach-Object { switch ($_.toLower()) { 'value' { $NewParamConfig.value = $ScriptParamConfig[$_] break } 'type' { $NewParamConfig.type = $ScriptParamConfig[$_] break } 'description' { $NewParamConfig.description = $ScriptParamConfig[$_] break } 'desc' { $NewParamConfig.description = $ScriptParamConfig[$_] break } 'context' { $NewParamConfig.context = $ScriptParamConfig[$_] break } 'fieldlabel' { $NewParamConfig.fieldLabel = $ScriptParamConfig[$_] break } 'fieldName' { $NewParamConfig.fieldName = $ScriptParamConfig[$_] break } 'required' { $NewParamConfig.required = (ConvertTo-Boolean $ScriptParamConfig[$_]) break } } } ## Add the Parameter Name from the Configuration Object Add-Member -InputObject $NewParamConfig -NotePropertyName 'paramName' -NotePropertyValue $ParamLabel $TMActionParams.Add($NewParamConfig) | Out-Null } } ## Note where the Action Code is located $StartCodeBlockLine = $ConfigBlockEndLine + 1 $EndCodeBlockLine = $ast[-1].Extent.EndLineNumber ## Create a Text StrinBuilder to collect the Script into $ActionScriptStringBuilder = New-Object System.Text.StringBuilder ## For each line in the Code Block, add it to the Action Script Code StringBuilder $StartCodeBlockLine..$EndCodeBlockLine | ForEach-Object { $ActionScriptStringBuilder.AppendLine($ContentLines[$_]) | Out-Null } ## Convert the StringBuilder to a Multi-Line String $ActionScriptCode = $ActionScriptStringBuilder.ToString() ## If no Parameters were assembled, provide an empty array if (-not $TMActionParams) { $TMActionParams = [System.Collections.ArrayList] @() } # } ## Assemble the Action Object $TMAction = [pscustomobject]@{ id = $null name = $ActionConfig.ActionName description = $ActionConfig.Description debugEnabled = $false methodParams = ($TMActionParams | ConvertTo-Json -Compress) script = $ActionScriptCode reactionScripts = '{"PRE":"","ERROR":"// Put the task on hold and add a comment with the cause of the error\n task.error( response.stderr )","FINAL":"","FAILED":"","LAPSED":"","STATUS":"// Check the HTTP response code for a 200 OK \n if (response.status == SC.OK) { \n \t return SUCCESS \n } else { \n \t return ERROR \n}","DEFAULT":"// Put the task on hold and add a comment with the cause of the error\n task.error( response.stderr )\n","STALLED":"","SUCCESS":"// Update Asset Fields\nif(response?.data?.assetUpdates){\n\tfor (field in response.data.assetUpdates) {\n \t\tasset.\"${field.key}\" = field.value;\n\t}\n}\ntask.done()"}' provider = @{ id = $null name = $ActionConfig.ProviderName } project = @{ id = $null name = $null } remoteCredentialMethod = 'USER_PRIV' credential = $null asyncQueue = $null version = 1 dateCreated = Get-Date lastUpdated = Get-Date timeout = 0 commandLine = $null dictionaryMethodName = 'Select...' callbackMethod = $null connectorMethod = $null pollingStalledAfter = 0 pollingInterval = 0 pollingLapsedAfter = 0 defaultDataScript = $null useWithTask = 0 reactionScriptsValid = 1 docUrl = '' isRemote = $true actionType = 'POWER_SHELL' useWithAsset = 0 isPolling = 0 endpointUrl = '' apiCatalog = $null } } ## Return the Action Object return $TMAction } function Invoke-TMActionScript { <# .SYNOPSIS Runs the Script Code in the specified TransitionManager Action .DESCRIPTION Runs the Script Code in the specified TransitionManager Action .PARAMETER TMSession Name of an open TMSession. Use Get-TMSession to get open sessions .PARAMETER Name Name of the action in TM that will be invoked. .EXAMPLE Invoke-TMActionScript -Name 'Ping Remote Machine' .OUTPUTS This command does not output any content #> [CmdletBinding()] param( [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default', [Parameter(Mandatory = $true)][String]$Name, [Parameter(Mandatory = $false)][PSObject]$Project = $global:TMSessions[$TMSession].UserContext.Project, [Parameter(Mandatory = $false)][bool]$Api = $false ) ## Get Session Configuration $TMSession = Get-TMSession -Name $TMSession if (-not (Test-TMSession $TMSession)) { 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.' } ## Get the TM Action $TMAction = Get-TMAction @PSBoundParameters -Api $Api if (-Not $TMAction) { throw 'Unable to get the Action' } ## Try converting and running the script block from the Action try { ## Create a Temporary Filename $TempPsFile = New-TemporaryFile | Rename-Item -NewName { $_.FullName -replace '\.tmp', '.ps1' } -PassThru | Select-Object -ExpandProperty FullName $ActionScriptBlock = [scriptblock]::Create($TMAction.Script) Set-Content -Value $ActionScriptBlock.ToString() -Path $TempPsFile -Force -Confirm:$false ## Invoke the script block, No New Scope to use the available variables, ## and with Debug enabled to allow stepping into the file via debugging mode try { $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() & ($TempPsFile) $Stopwatch.Stop() Write-Host -Message "Task $($Name) completed in $(Get-TimeSpanString -Timespan $Stopwatch.Elapsed)" } catch { $Stopwatch.Stop() Write-Warning -Message "Task $($Name) completed (with errors) in $(Get-TimeSpanString -Timespan $Stopwatch.Elapsed)" -WarningAction Continue throw $_ } finally { Remove-Item $TempPsFile -Force } } catch { throw $_ } } |