functions/Invoke-RestApi.ps1
function Invoke-RestApi { <# .SYNOPSIS Invokes the Secret Server Rest API .DESCRIPTION Invokes the Thycotic Secret Server REST API .EXAMPLE Invoke-TssRestApi -Uri 'https://vault.company.com/api/v1/oauth2/token' -Method POST -Body $body Performs POST request to the URI specified, returning the output from the endpoint. .LINK https://thycotic-ps.github.io/thycotic.secretserver/commands/Invoke-TssRestApi #> [Cmdletbinding()] param( # Secret Server REST API URL [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [Alias('Url')] [uri] $Uri, # Valid Access Token issued by Secret Server [Parameter(ValueFromPipelineByPropertyName)] [Alias('PAT')] [string] $PersonalAccessToken, # Method used for the web request, supported by Secret Server [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('GET','DELETE', 'PATCH', 'POST', 'PUT')] [string] $Method, # Specifies the body of the request. [Parameter(ValueFromPipelineByPropertyName)] [Object] $Body, # Specifies the file path to write the content. [Parameter(ValueFromPipelineByPropertyName)] [string] $OutFile, # Specifies the content type of the web request. # If this parameter is omitted and the request method is POST, Invoke-RestMethod sets the content type to application/x-www-form-urlencoded. Otherwise, the content type is not specified in the call. [string] $ContentType = 'application/json', # Header of the web request. Enter a hash table or dictionary. [System.Collections.IDictionary] [Alias('Header')] $Headers, # Indicates using the credentials of the current user to send the web request (winauth). [Alias('UseDefaultCredential')] [switch] $UseDefaultCredentials, # Specifies that the cmdlet uses a proxy server for the request, rather than connecting directly to the Internet resource. Enter the URI of a network proxy server. [uri] $Proxy, # Specifies a user account that has permission to use the proxy server that is specified by the Proxy parameter. The default is the current user. # Type a user name, such as "User01" or "Domain01\User01", or enter a PSCredential object, such as one generated by the Get-Credential cmdlet. # This parameter is valid only when the Proxy parameter is also used in the command. You cannot use the ProxyCredential and ProxyUseDefaultCredentials parameters in the same command. [PSCredential] [Management.Automation.CredentialAttribute()] $ProxyCredential, # Indicates that the cmdlet uses the credentials of the current user to access the proxy server that is specified by the Proxy parameter. # This parameter is valid only when the Proxy parameter is also used in the command. You cannot use the ProxyCredential and ProxyUseDefaultCredentials parameters in the same command. [switch] $ProxyUseDefaultCredentials, # Output a custom type name for the results. [Parameter(ValueFromPipelineByPropertyName)] [string[]] $PSTypeName, # A set of additional properties to add to an object [Parameter(ValueFromPipelineByPropertyName)] [Collections.IDictionary] $Property, # A list of property names to remove from an object [string[]] $RemoveProperty, # Expand a given property from an object [string] $ExpandProperty ) process { #region Prepare Parameters $irmSplat = @{ } + $PSBoundParameters # First, copy PSBoundParameters and remove the parameters that aren't Invoke-RestMethod's $irmSplat.Remove('PersonalAccessToken') # * -PersonalAccessToken $irmSplat.Remove('PSTypeName') # * -PSTypeName $irmSplat.Remove('Property') # *-Property $irmSplat.Remove('RemoveProperty') # *-RemoveProperty $irmSplat.Remove('ExpandProperty') # *-ExpandProperty if ($PersonalAccessToken) { # If there was a personal access token, set the authorization header if ($Headers) { # (make sure not to step on other headers). $irmSplat.Headers.Authorization = "Bearer $PersonalAccessToken" } else { $irmSplat.Headers = @{ Authorization = "Bearer $PersonalAccessToken" } } } else { } if ($Body -and $Body -isnot [string]) { # If a body was passed, and it wasn't a string # $irmSplat.Body = $Body | ConvertTo-Json -Depth 100 # make it JSON. } if (-not $irmSplat.ContentType) { # If no content type was passed $irmSplat.ContentType = $ContentType # set it to the default. } #endregion Prepare Parameters #region Call Invoke-RestMethod # We call Invoke-RestMethod with the parameters we've passed in. # It will take care of converting the results from JSON. Invoke-RestMethod @irmSplat | & { process { $in = $_ # What it will not do is "unroll" them. if ($in -eq 'null') { return } if ($ExpandProperty) { if ($in.$ExpandProperty) { $in.$ExpandProperty } } elseif ($in.Value -and $in.Count) { # If that's what we're dealing with $_.Value # pass value down the pipe. } elseif ($in.code -like '*API_*') { $PSCmdlet.WriteError( [Management.Automation.ErrorRecord]::new( [Exception]::new("$($in.message)"),"$($in.code)","InvalidOperation",$in)) $PSCmdlet.WriteVerbose("$in") return } elseif ($in -notlike '*<html*') { # Otherwise, As long as the value doesn't look like HTML, $_ # pass it down the pipe. } else { # If it happened to look like HTML, write an error $PSCmdlet.WriteError( [Management.Automation.ErrorRecord]::new( [Exception]::new("Response was HTML, Request Failed."), "ResultWasHTML", "NotSpecified", $in)) $PSCmdlet.WriteVerbose("$in") # and write the full content to verbose. return } # Redirect standard error (2) to same place as standard output (1) } } 2>&1 | & { process { # One more step of the pipeline will unroll each of the values. if ($_ -is [string]) { return $_ } if ($null -ne $_.Count -and $_.Count -eq 0) { return } if ($PSTypeName -and # If we have a PSTypeName (to apply formatting) $_ -isnot [Management.Automation.ErrorRecord] # and it is not an error (which we do not want to format) ) { $_.PSTypeNames.Clear() # then clear the existing typenames and decorate the object. foreach ($t in $PSTypeName) { $_.PSTypeNames.add($T) } } if ($Property) { foreach ($propKeyValue in $Property.GetEnumerator()) { if ($_.PSObject.Properties[$propKeyValue.Key]) { $_.PSObject.Properties.Remove($propKeyValue.Key) } $_.PSObject.Properties.Add($( if ($propKeyValue.Value -as [ScriptBlock[]]) { [PSScriptProperty]::new.Invoke(@($propKeyValue.Key) + $propKeyValue.Value) } else { [PSNoteProperty]::new($propKeyValue.Key, $propKeyValue.Value) })) } } if ($RemoveProperty) { foreach ($propToRemove in $RemoveProperty) { $_.PSObject.Properties.Remove($propToRemove) } } return $_ # output the object and we're done. } } #endregion Call Invoke-RestMethod } } |