# Endpoints Endpoints are defined by their URI and HTTP method. Calls made to the Universal server that match your defined API endpoint and method execute the API endpoint script. ```powershell New-PSUEndpoint -Url '/endpoint' -Method 'GET' -Endpoint { "Hello, world!" } ``` To invoke the above method, you can use `Invoke-RestMethod`. ```powershell Invoke-RestMethod http://localhost:5000/endpoint ``` When defining endpoints in the management API, you can skip the `New-PSUEndpoint` call, as the admin console defines it. ![API Properties](https://1373299915-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8F6PrkNTG8Y34hADzKOL%2Fuploads%2Fgit-blob-632d5108b53fcbd362c47488410a013ad72fa8c0%2Fimage%20\(442\).png?alt=media) The only contents that you need to provide in the editor are the script you wish to call. ![API Content](https://1373299915-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8F6PrkNTG8Y34hADzKOL%2Fuploads%2Fgit-blob-d4878c92586d1f94974e20402ee3b699ca8727ca%2Fimage%20\(550\).png?alt=media) {% hint style="warning" %} Avoid using endpoint URLs that match internal PowerShell Universal Management API URLs, as this causes unexpected behavior. You can reference the [OpenAPI documentation](https://docs.powershelluniversal.com/openapi#management-api-documentation) for the [Management API](https://docs.powershelluniversal.com/config/management-api) to verify that none of the URLs match. {% endhint %} ## HTTP Methods Endpoints can have one or more HTTP methods defined. To determine which method is used by an endpoint, use the built-in `$Method` variable. ```powershell New-PSUEndpoint -Url '/user' -Method @('GET', 'POST') -Endpoint { if ($Method -eq 'GET') { Get-User } else { New-User } } ``` ## Variable URL URLs can contain variable segments. You can denote a variable segment using a colon (`:`). For example, the following URL would provide a variable for the ID of the user. The `$Id` variable will be defined within the endpoint when it is executed. Variables must be unique in the same endpoint URL. ```powershell New-PSUEndpoint -Url '/user/:id' -Method 'GET' -Endpoint { Get-User -Id $Id } ``` To call this API and specify the ID, do the following: ```powershell Invoke-RestMethod http://localhost:5000/user/123 ``` ## Query String Parameters Query string parameters are automatically passed into endpoints as variables that you can then access. For example, if you have an endpoint that expects an `$Id` variable, you can provide it in the query string. ```powershell New-PSUEndpoint -Url '/user' -Method 'GET' -Endpoint { Get-User -Id $Id } ``` The resulting `Invoke-RestMethod` call must then include the query string parameter. ```powershell Invoke-RestMethod http://localhost:5000/user?Id=123 ``` When using multiple query string parameters, ensure that your URL is surrounded by quotes so PowerShell translates it properly. Including an ampersand (&) without quotes will cause issues in both Windows PowerShell and PowerShell 7. ```powershell Invoke-RestMethod "http://localhost:5000/user?Id=123&name=tim" ``` ### Security Considerations When accepting input via Query String parameters you may be vulnerable to [CWE-914: Improper Control of Dynamically-Identified Variables](https://cwe.mitre.org/data/definitions/914.html). Consider using a `param` block to ensure that only valid parameters are provided to the endpoint. Below is an example of CWE-914. Include a `$IsChallengePassed` query string parameter to bypass the challenge. ```powershell New-PSUEndpoint -Url "/api/v1.0/CWE914Test" -Description "Vulnerable to CWE-914" -Endpoint { if($ChallengeInputData -eq "AcceptableInput") { $IsChallengePassed = $true } if($IsChallengePassed) { "Challenge passed. Here is Sensitive Information" } else { "Challenge not passed" } } ``` In order to avoid this particular issue, you can use a `param` block. ```powershell New-PSUEndpoint -Url "/api/v1.0/CWE914Test" -Description "Not Vulnerable to CWE-914" -Endpoint { Param( $ChallengeInputData ) if($ChallengeInputData -eq "AcceptableInput") { $IsChallengePassed = $true } if($IsChallengePassed) { "Challenge passed. Here is Sensitive Information" } else { "Challenge not passed" } } ``` ## Headers Request headers are available in APIs using the `$Headers` variable. The variable is a hashtable. To access a header, use the following syntax: ```powershell $Headers['Content-Type'] ``` ## Cookies Request cookies are available in APIs using the `$Cookies` variable. The variable is a hashtable. To access a cookie, use the following syntax: ```powershell $Cookies['Request-Cookie'] ``` Send back request cookies with the `New-PSUApiResponse` cmdlet. Use the `-Cookies` parameter with a supplied hashtable. ```powershell New-PSUApiResponse -StatusCode 200 -Cookies @{ ResponseCookie = '123' } ``` ## Body To access a request body, you will simply access the `$Body` variable. Universal `$Body` variable will be a string. If you expect JSON, you should use `ConvertFrom-Json`. ```powershell New-PSUEndpoint -Url '/user' -Method Post -Endpoint { $User = ConvertFrom-Json $Body New-User $User } ``` To call the above endpoint, specify the body of `Invoke-RestMethod`. ```powershell Invoke-RestMethod http://localhost:5000/user -Method Post -Body "{'username': 'adam'}" ``` ## Live Log You can view the live log information for any endpoint by clicking the log tab. Live logs include URL, HTTP method, source IP address, PowerShell streams, status code, return Content Type, and HTTP content length. You can write to the live log from within your endpoints with cmdlets like `Write-Host`.

Endpoint Live Log

## Testing You can use the Test tab in the Endpoint editor to test your APIs. Using this Test tool, you can adjust headers, the query string, and body. You can also adjust the Authentication and Authorization for the test.

Endpoint Test Tab

When using the test tab, any changes to the values of the test will result in an updated Code block that you can then use within PowerShell. Click the Code tab to view the test code. ```powershell Invoke-RestMethod -Uri 'http://localhost:5000/test-api?Page=1' -Headers @{'X-Custom-Header' = 'Value';} -Method 'POST' ``` Additionally, tests performed within the tester will be stored for 30 days to allow for retesting without having to reconfigure all the properties. Clicking the Apply button will setup the Test tool with the same properties.

Test History

## Form Data You can pass data to an endpoint as form data. Form data will pass into your endpoint as parameters. ```powershell New-PSUEndpoint -Url '/user' -Method Post -Endpoint { param([Parameter(Mandatory)]$userName, $FirstName, $LastName) New-User $UserName -FirstName $FirstName -LastName $LastName } ``` You can then use a hashtable with Invoke-RestMethod to pass form data. ```powershell Invoke-RestMethod http://localhost:5000/user -Method Post -Body @{ UserName = "adriscoll" FirstName = "Adam" LastName = "Driscoll" } ``` ## JSON Data You can pass JSON data to an endpoint and it will automatically bind to a param block. ```powershell New-PSUEndpoint -Url '/user' -Method Post -Endpoint { param([Parameter(Mandatory)]$userName, $FirstName, $LastName) New-User $UserName -FirstName $FirstName -LastName $LastName } ``` You can then send JSON data to the endpoint. ```powershell Invoke-RestMethod http://localhost:5000/user -Method Post -Body (@{ UserName = "adriscoll" FirstName = "Adam" LastName = "Driscoll" } | ConvertTo-Json) -ContentType 'application/json' ``` ## Param Block You can use a `param` block within your script to enforce mandatory parameters and provide default values for optional parameters such as query string parameters. Variables such as `$Body`, `$Headers` and `$User` are provided automatically. In the below example, the `$Name` parameter is mandatory and the `$Role` parameter has a default value of Default. ```powershell New-PSUEndpoint -Url '/user/:name' -Endpoint { param([Parameter(Mandatory)$Name, $Role = "Default") } ``` When using the `param` block with route parameters like the above example, you must include the route variable in your parameter. If it is not specified, you will not have access to that value. For example, the following `$Name` variable is always `$null`. The endpoint always returns false. ```powershell New-PSUEndpoint -Url '/user/:name' -Endpoint { param($Role = "Default") $Name -eq 'Adam' } ``` If you use the `CmdletBinding` or `Parameter` attribute within your param block, the endpoint will strictly enforce which parameters are allowed into the endpoint. For example, the following enforces that the name parameter is specified. ```powershell New-PSUEndpoint -Url '/user' -Endpoint { param([Parameter(Mandatory)$Name) } ``` That said, you cannot specify additional parameters to the endpoint. Doing the following will cause an error. ```powershell Invoke-RestMethod http://localhost:5000/user -Method Post -Body (@{ Name = "adriscoll" DisplayName = 'Adam' } | ConvertTo-Json) -ContentType 'application/json' ``` If you change your endpoint to avoid using the `Parameter` attribute, you can pass in any number of params and they will be bound as variables and not parameters to the endpoint. ```powershell New-PSUEndpoint -Url '/user' -Endpoint { param($Name) } ``` ### Method Parameter Sets You can define parameter sets using method parameters. By default, PowerShell Universal will inspect the param block to determine whether these `Get`, `Put`, `Post`, `Delete`, or other HTTP method names are specified and will include them automatically. When endpoints accept multiple methods, it may not be able to determine which parameter set to call based on the data provided. In the example below, both the Get and the Post accept the name parameter. There is also no way to call the Post without a name so validation could fail. To alleviate this, include `Post` and `Get` parameters that are part of their respective parameter set. PowerShell Universal will include this parameter to ensure the proper parameter set is called. {% code overflow="wrap" %} ```powershell New-PSUEndpoint -Url '/user' -Method @("Get", "Post") -Endpoint { param( [Parameter(ParameterSetName = "GET")] [Parameter(ParameterSetName = "POST", Mandatory)] $Name, [Parameter(ParameterSetName = "GET")] [Switch]$Get, [Parameter(ParameterSetName = "POST")] [Switch]$Post ) if ($Get) { # Get User } if ($Post) { # Create User } } ``` {% endcode %} ## Returning Data Data returned from endpoints is assumed to be JSON data. If you return an object from the endpoint script block, it is automatically serialized to JSON. If you want to return another type of data, you can return a string formatted however you chose. ## Processing Files ### Uploading Files You can process uploaded files by using the `$Data` parameter to access the byte array of data uploaded to the endpoint. ```powershell New-PSUEndpoint -Url '/file' -Method Post -Endpoint { $Data } PS C:\Users\adamr> iwr http://localhost:5000/file -method post -InFile '.\Desktop\add-dashboard.png' StatusCode : 200 StatusDescription : OK Content : [137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,2,17,0,0,1,92,8,2,0,0,0,249,210,123,106,0,0,0,1, 115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,0,9,112,72,89,115,0, 0,… ``` {% hint style="warning" %} `The multipart/form-data`content type is not supported for uploading files to APIs. {% endhint %} You can also save the file into a directory. ```powershell New-PSUEndpoint -Url '/file' -Method Post -Endpoint { [IO.File]::WriteAllBytes("tempfile.dat", $Data) } ``` ### Downloading Files You can send files down using the `New-PSUApiResponse` cmdlet. ```powershell New-PSUEndpoint -Url '/image' -Endpoint { $ImageData = [IO.File]::ReadAllBytes("image.jpeg") New-PSUApiResponse -ContentType 'image/jpg' -Data $ImageData } ``` ## Returning Custom Responses You can return custom responses from endpoints by using the `New-PSUApiResponse` cmdlet in your endpoint. This cmdlet allows you to set the status code, content type and even specify the byte\[] data for the content to be returned. ```powershell New-PSUEndpoint -Url '/file' -Method Get -Endpoint { New-PSUApiResponse -StatusCode 410 } ``` You can also return custom body data with the `-Body` parameter of `New-PSUApiResponse`. ```powershell New-PSUEndpoint -Url '/file' -Method Get -Endpoint { New-PSUApiResponse -Body "Not what you're looking for." -StatusCode 404 } ``` Invoking the REST method returns the custom error code. ```powershell PS C:\Users\adamr\Desktop> invoke-restmethod http://localhost:8080/file Invoke-RestMethod: Not what you're looking for. ``` You can control the content type of the returned data with the `-ContentType` parameter. ```powershell New-PSUEndpoint -Url '/file' -Method Get -Endpoint { New-PSUApiResponse -Body "12" -ContentType 'text/xml' } ``` You can control the response headers with a hashtable of values that you pass to the `-Headers`parameter. ```powershell New-PSUApiResponse -StatusCode 200 -Headers @{ "Referrer-Policy" = "no-referrer" } ``` ## Persistent Runspaces Persistent runspaces allow you to maintain runspace state between API calls. This is important for users that perform some sort of initialization within their endpoints that they do not want to execute on subsequent API calls. By default, runspaces are reset after each execution. This removes variables, modules and functions defined during the execution of the API. To enable persistent runspaces, you will need to configure an [environment ](https://docs.powershelluniversal.com/config/environments)for your API. Set the `-PersistentRunspace` parameter to enable this feature. This is configured in the `environments.ps1` script. ```powershell New-PSUEnvironment -Name 'Env' -Path 'powershell.exe' -PersistentRunspace ``` You can then assign the API environment in the `settings.ps1` script. ```powershell Set-PSUSetting -ApiEnvironment 'Env' ``` ## Timeout By default, endpoints will not time out. To set a timeout for your endpoints, you can use the `New-PSUEndpoint` `-Timeout` parameter. The timeout is set in the number of seconds. ## External Endpoint Content You can define the path to an external endpoint content file with the `-Path` parameter of `New-PSUEndpoint`. The path is relative to the `.universal` directory in Repository. The content of the `endpoints.ps1` file is then this: ```powershell New-PSUEndpoint -Url "/path" -Path "endpoint-path.ps1" ``` ## C# APIs C# APIs are enabled as a [plugin](https://docs.powershelluniversal.com/platform/plugins#c-api-environment). There is no UI for creating a C# API, so you need to do so using configuration files. First, create a `.cs` file that runs your API. You will have access to a `request` parameter that includes all the data about the API request. ```csharp public class ApiRequest { public long Id; public ICollection Variables; public IEnumerable Files { get; set; }; public string Url; public ICollection Headers; public byte[] Data; public int ErrorAction; public ICollection Parameters; public string Method; public ICollection Cookies; public string ClaimsPrincipal; public string ContentType; } ``` You will also have access to a `ServiceProvider` property that allows you to access services within PowerShell Universal. These are not currently well-documented, but below is an example of restarting a dashboard. ```csharp var dm = ServiceProvider.GetService(typeof(IDashboardManager)); var dashboard = dm.GetDashboard(1); dm.Restart(dashboard); ``` Some other useful services include: * IDatabase * IApiService * IConfigurationService * IJobService You can choose to return an `ApiResponse` from your endpoint. ```powershell return new ApiResponse { StatusCode = 404 }; ``` Once you have defined your C# endpoint file, you can add it by editing `endpoints.ps1`. ```powershell New-PSUEndpoint -Url /csharp -Path endpoint.cs -Environment 'C#' ``` The PowerShell Universal service automatically compiles and runs C# endpoints. ## API * [New-PSUEndpoint](https://github.com/ironmansoftware/universal-docs/blob/v5/cmdlets/New-PSUEndpoint.txt) * [Get-PSUEndpoint](https://github.com/ironmansoftware/universal-docs/blob/v5/cmdlets/Get-PSUEndpoint.txt) * [Remove-PSUEndpoint](https://github.com/ironmansoftware/universal-docs/blob/v5/cmdlets/Remove-PSUEndpoint.txt) * [New-PSUApiResponse](https://github.com/ironmansoftware/universal-docs/blob/v5/cmdlets/New-PSUApiResponse.txt) * [Set-PSUSetting](https://github.com/ironmansoftware/universal-docs/blob/v5/cmdlets/Set-PSUSetting.txt)