Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 188 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

v5

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

API

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Automation

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Apps

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

What's New in v5?

New features in PowerShell Universal v5.

New Admin Console

The admin console has been rebuilt using Blazor for ASP.NET. The look and feel are the same but more tightly associated with the backend Universal platform.

Portal

The provides a simple-to-use access point for consumers of PSU resources. You can assign resources by role, and they will be grouped by tags in a searchable interface without the complexities of the admin console.

Pages and Widgets

Portal and provide easy-to-use UI components that you can visually position on pages, which can be assigned to roles. Widgets are built using Blazor and PowerShell accept parameters, and they are interactive.

PowerShell Universal Gallery

The PowerShell Universal Gallery is now integrated directly in PowerShell Universal. Access pre-built solutions for your PowerShell Universal environment.

You can view the .

Granular Permissions

Authorization within the platform is now configured via a that controls which users have access to which resources. This also includes new roles for specific feature groups, so administrators do not need to configure privileges for every scenario.

PostgreSQL Support

PostgreSQL is now supported as a persistence store. PostgreSQL is open source and free.

Updated Runtimes

PowerShell Universal v5 is built on .NET 9 and PowerShell 7.5.

gRPC Cmdlets

The Universal module now uses gRPC for all communication with the system. gRPC is an interprocess communication technology that is fast and runs over HTTP. By unifying on a single technology, the cmdlets now take advantage of all the granular privileges and help reduce technical debt in the platform.

Windows PowerShell 5.1 and PowerShell 7 Environments

The Agent environment has been replaced with Windows PowerShell 5.1 and PowerShell 7 environments. These environments host the PowerShell engine, but they allow for better control of assembly loading to ensure more modules are compatible with PowerShell Universal.

PowerShell Universal Portal
Pages
Widgets
Gallery repository here
granular permission system

Data Display

Data Visualization

Feedback

Supported Browsers

Universal uses a variety of modern web frameworks and can have issues with older browsers such as Internet Explorer.

Recent versions of the following web browsers are supported: Chrome, Firefox, Safari, and Microsoft Edge.

When considering a browser, you will need to understand that certain features are required. They include:

  • WebSocket

  • CSS Flexbox

About

Create web applications in PowerShell using PowerShell Universal Apps.

Apps are individual websites created with PowerShell Universal. They take advantage of a large set of pre-defined cmdlets for all kinds of components and interactive features. You can use apps to create dynamic websites that meet any need you can think of. Some common apps are:

  • Disparate Data Grouped into Tables and Data Grids

  • End User Onboarding Tools

  • UI Tools for System Management without Elevated Credentials

For a full list of real-life examples, .

Apps should be considered an advanced feature and require both knowledge of the PowerShell Universal app cmdlets as well as a decent knowledge of PowerShell itself. With that said, they provide the greatest level of customization for web apps in PowerShell.

About

The ultimate command center for your PowerShell environment.

PowerShell Universal provides a centralized location to store and run scripts, build modules, expose REST APIs and share them with end users via automatic or custom user interfaces, setup schedules and more.

APIs

Transform your PowerShell scripts into RESTful HTTP and WebSocket APIs for seamless integration across platforms. Leverage OpenAPI provide documentation and additional automation opportunities.

About Automation

Run and schedule scripts with automation

Run Scripts

You can run in PowerShell Universal. PowerShell Universal integrates deeply with the PowerShell host to provide a UI for param blocks, output rich objects, display progress and even allow the user to provide feedback.

Image

Image component for apps.

Image by URL

Display an image based on a URL. You can host URLs using .

Image by Path

Display an image based on a file local to the server.

Badge

Badges component for Universal Apps.

Basic Badge

Examples of badges containing text, using primary and secondary colors. The badge is applied to its children.

Error Boundary

Error boundary component for apps.

The New-UDErrorBoundary component is used for isolating portions of a dashboard to contain components that may throw an error. Many app components use the error boundary component internally.

If you'd like to isolate a portion of your app to prevent the entire page from failing to load, you can use the following syntax.

If any error is thrown from the content, you will see an error such as thing.

API

Fetch
click here

Uninstall

Uninstall PowerShell Universal

Application Files

Depending on how you installed PowerShell Universal, you will need to uninstall the application files.

ZIP Installation

If you installed using a provided ZIP file, you can simply stop the PowerShell Universal process or service and delete the folder you extracted to.

MSI Installation

If you installed with the Windows MSI, uninstall the application from Add\Remove Programs.

Module Installation

The Universal module installs the application files to the following locations by default.

Windows

  • %ProgramData%\PowerShellUniversal

Linux and Mac OS

  • %HOME%/.PowerShellUniversal

Configuration Files

Configuration files are stored in the repository folder. Once you have removed the application files, you can delete the configuration files. They are stored in the following locations by default:

Windows

  • %ProgramData%\PowerShellUniversal

  • %ProgramData%\UniversalAutomation

Linux

  • %HOME%/.PowerShellUniversal/

Mac OS

  • %HOME%/.PowerShellUniversal/

Desktop

  • %AppData%\PowerShellUniversal

Database

Removing the database depends on the database type used.

SQLite

SQLite databases are stored in a single file on the file system.

Windows

  • %ProgramData%\PowerShellUniversal\database.db

Linux and Mac OS

  • %HOME%/.PowerShellUniversal/database.db

PostgreSQL and SQL

PostgreSQL and SQL databases are stored on your SQL server and will require you to manually remove the database.

IIS

You need to remove the IIS App Pool and Website when removing PowerShell Universal. Note that App Pools can be shared amongst websites and caution should be taken when doing so.

Inputs

Input controls for Universal Dashboard

Utilities

Layout

Navigation

Surfaces

New-UDErrorBoundary -Content {
    throw "Oh no!"
}
New-UDErrorBoundary

Markdown

Markdown display for Universal Apps.

New-UDMarkdown accepts a markdown string and renders it as HTML elements within a dashboard.

New-UDMarkdown -Markdown "
   # Header
   - List Item 1
   - List Item 2
   
   ## Sub Header
"

Markdown in Paper

When using New-UDPaper, you will need to override the default display type of the paper to avoid formatting issues.

$m=@'
# Hello

## world
- a
- b
-c
'@

New-UDPaper -Elevation 7 -Children {
   New-UDMarkdown -markdown $m
} -Style @{
   display = 'block'
}

API

  • New-UDMarkdown

Protect Section

Protect sections based on roles.

The Protect-UDSection cmdlet hides it's content if a user does not have the specified roles.

Protect-UDSection -Role @("Administrator") -Content {
   New-UDTypography -Text 'Only Administrators see this'
}

Image Size

Change the size of the image using the -Width and -Height parameters.

Attributes

Apply additional attributes to the image.

API

  • New-UDImage

Published Folders
New-UDImage -Url "https://ironmansoftware.com/img/ps-logo.png"
New-UDImage -Path C:\users\adamr\Desktop\ps-logo.png
New-UDImage -Url "https://ironmansoftware.com/img/ps-logo.png" -Width 250 -Height 250
New-UDImage -Url "https://ironmansoftware.com/img/ps-logo.png" -Attributes @{
    alt = "Ironman Software Logo"
}

HTTP Endpoints

  • Custom Responses

  • Token-Based Authentication

  • Rate Limiting

  • Large File Support

  • Open API Documentation

  • File Hosting

  • Event Hubs

  • Automation

    Streamline your automation with an intuitive web interface for executing, scheduling, securing, and auditing PowerShell scripts. Effortlessly manage tasks and access in-browser terminals to maximize efficiency and control in your automation workflows.

    Run PowerShell Scripts
    • Scheduling

    • Run As Support

    • Multiple Environments

    • Automatic Form Generation

    Apps

    Create fully customizable, interactive web apps tailored for your internal users using PowerShell. With over 70 versatile controls and seamless integration to other PowerShell modules and scripts, you can build powerful interfaces to enhance productivity and collaboration.

    Universal Dashboard
    • Interactive Apps

    • Input Forms

    • Customizable Tables

    • Charts

    Hosting

    PowerShell Universal is cross-platform and can be hosted on-premises, in the cloud or even on a Raspberry Pi.

    Host in Azure
    • Windows, Linux and Mac

    • Docker Containers

    • Azure

    • IIS

    Security

    PowerShell Universal is a versatile, cross-platform solution that adapts to your hosting needs. Deploy seamlessly on AWS, Azure, IIS, or your on-premises infrastructure—even on compact devices like a Raspberry Pi. Flexibility meets power for automation anywhere.

    Security Settings
    • OpenID Connect

    • WS-Federation

    • Basic Authentication

    • Client Certificate

    Development

    PowerShell Universal delivers a seamless development experience with built-in tools like IntelliSense, syntax highlighting, error checking, formatting, and debugging—all accessible directly from your browser. Enhance productivity further with a dedicated VS Code extension and integrated Git support for streamlined version control.

    Development Tools in PowerShell Universal
    • Rich Editing Experience

    • Code-First Configuration

    • Debugging Tools

    • PowerShell Module

    Platform

    With support for PowerShell 7 and Windows PowerShell, seamless integration with PowerShell modules, and powerful tools for variable and secret management, it adapts to your needs. Enjoy multilingual support, compatibility with multiple database types (SQLite, SQL Server, PostgreSQL), and built-in high availability and load balancing for enterprise-grade scalability.

    Module Management
    • PowerShell 7 Support

    • Windows PowerShell Support

    • PowerShell Modules

    • Variables

    Community

    Join the thriving PowerShell Universal community and connect with like-minded professionals. Participate in our active forum for support and collaboration, explore our open-source gallery of pre-made solutions, and stay informed with our transparent roadmap and bug tracker.

    Community Forums
    • Forums

    • Issue Tracker

    • Discord

    Licensing

    Universal is licensed per server. Visit our website for more information on pricing.

    Many features of PowerShell Universal are free.

    PowerShell Universal Admin Console
    Execute PowerShell with HTTP
    Schedule Jobs

    You can schedule jobs to run continuously, at certain times or even when events happen within the PowerShell Universal platform.

    Schedules UI

    Ad-Hoc Commands

    Run ad-hoc commands in terminals in any of your configured environments and, optionally, as alternate credentials.

    In Browser Terminal
    scripts
    Script Output
    Color

    API

    • New-UDBadge

    Release Support Policy

    PowerShell Universal recently migrated from a static, two-year support policy for releases to a standard and long-term support policy based on the underlying frameworks that the platform is built upon.

    Support Policy

    Starting with version 6, PowerShell Universal will provide long term support options pinned to the underlying support policy of the .NET SDK. Microsoft provides support for .NET versions for 18 months on standard support releases and 36 months on long-term support releases. PowerShell Universal runs on a similar support policy.

    Version 5 is the last version on the historical, arbitrary support policy per major version. It will be supported until August 2026. Please see the below table for upcoming release support. Support is the same for each release type but varies based on the life time.

    Version
    Release Date
    End of Support
    Support Type

    About

    About PowerShell Universal REST APIs.

    Universal provides the ability to define REST API endpoints using PowerShell. When the endpoints are executed by a compatible HTTP client, the PowerShell script will execute and return the result to the end user.

    This feature is for developing custom APIs run by Universal. It is not required for managing Universal. Universal provides a set of management APIs that are included with the platform.

    Execution Environment

    The REST API execution environment runs in your default PowerShell version. Unlike Automation jobs, which can also be run via the Universal management API, APIs that you define are run in a single PowerShell process. Because the PowerShell process is not started and stopped for each call to the endpoint, the API is much faster.

    You can define the that runs the PowerShell Universal API process by specifying the -ApiEnvironment on Set-PSUSetting. Changing this setting will cause the API process to restart.

    Per Endpoint Environment

    You can also define the environment used by specifying the Environment on the endpoint itself.

    Performance

    Performance is relative to the hardware and network conditions that you are running Universal on. That said, in ideal conditions you can expect the Universal APIs to service about 500 requests per second. This is with an entirely empty endpoint so any script that you add to that endpoint will reduce the throughput. The reduction of throughput will depend on the cmdlets and script executed within the API endpoint. There is no hard limit.

    See for detailed information about benchmark tests on Universal APIs.

    Variables

    Variables are listed on the .

    API

    Date and Time

    Date and time component for Universal Apps.

    The New-UDDateTime component is used for formatting dates and times within the client's browser. By using the client's browser, you can format the time based on the local time zone and locale settings for the user.

    The output of New-UDDateTime cannot be used with components like New-UDHtml, New-UDMarkdown or Show-UDToast. The object returned by New-UDDateTime needs to run JavaScript within the browser and is not an actual DateTime object.

    The date and time component uses DayJS. For a full list of custom formatting options, visit the .

    Basic Formatting

    By default, the date and time will be formatted using the LLL localized formatting template.

    Resulting output: August 16, 2018 8:02 PM

    Custom Formatting

    You can specify custom formatting strings using the .

    Resulting output: 25/01/2019

    Locale

    You can specify the locale to display the date and time in.

    Resulting output: 13 de septiembre de 2022 7:30

    API

    List

    List component for Universal Apps.

    Lists are continuous, vertical indexes of text or images.

    Lists are a continuous group of text or images. They are composed of items containing primary and supplemental actions, which are represented by icons and text.

    List

    New-UDList -Content {
        New-UDListItem -Label 'Inbox' -Icon (New-UDIcon -Icon envelope -Size 3x) -SubTitle 'New Stuff'
        New-UDListItem -Label 'Drafts' -Icon (New-UDIcon -Icon edit -Size 3x) -SubTitle "Stuff I'm working on "
        New-UDListItem -Label 'Trash' -Icon (New-UDIcon -Icon trash -Size 3x) -SubTitle 'Stuff I deleted'
        New-UDListItem -Label 'Spam' -Icon (New-UDIcon -Icon bug -Size 3x) -SubTitle "Stuff I didn't want"
    }

    OnClick Event Handler

    You can define an action to take when an item is clicked by using the -OnClick parameter of New-UDListItem.

    API

    Progress

    Progress component for Universal Apps

    Circular Progress

    New-UDProgress -Circular -Color Blue

    Linear Indeterminate

    Linear Determinate

    API

    Date Picker

    Date Picker component for Universal Apps

    Date pickers pickers provide a simple way to select a single value from a pre-determined set.

    Date pickers can be used in Forms and Steppers.

    OnChange Event Handler

    The OnChange event handler is called when the date changes. You can access the current date by using the $Body variable.

    Variant

    You can customize how the date picker is shown. The default is the inline variant that displays the date picker popup in line with the input control. The static variant displays the date picker without having to click anything.

    Locale

    To set the locate of the date picker, specify the -Locale parameter.

    Minimum and Maximum

    By default, the user can select any date. To specify minimum and maximum dates, using the -Minimum and -Maximum parameters.

    Views

    You can limit which portions of the date picker are included by using the -Views parameter. For example, if you wanted to remove the year selector and limit to the current year, you could do the following.

    API

    Rating

    Rating input component.

    Basic Rating

    New-UDRating 

    OnChange

    Take action when the rating is changed.

    Maximum

    Change the maximum rating.

    Precision

    Change the precision for ratings.

    Size

    Change the size of the rating icons.

    Drawer

    Drawer component for Universal Apps

    Permanent Drawer

    A permanent drawer will be shown at all times. By default, it is show on the left side of the screen.

    New-UDDrawer -Variant 'permanent' -Content {
      New-UDList -Children {
            New-UDListItem -Label "Home"
            New-UDListItem -Label "Getting Started" -Children {
                New-UDListItem -Label "Installation" -OnClick {}
                New-UDListItem -Label "Usage" -OnClick {}
                New-UDListItem -Label "FAQs" -OnClick {}
                New-UDListItem -Label "System Requirements" -OnClick {}
                New-UDListItem -Label "Purchasing" -OnClick {}
            }
        }
    }
    Permanent Drawer

    API

    Checkbox

    Check component for Universal Apps

    Checkboxes allow the user to select one or more items from a set.

    Checkboxes

    Checkboxes can be disabled and checked by default

    Checkboxes with custom icon

    Create checkboxes that use any icon and style.

    Checkboxes with onChange script block

    Create checkboxes that fire script blocks when changed.

    Checkbox with custom label placement

    You can adjust where the label for the checkbox is placed.

    Get the value of a Checkbox

    You can use Get-UDElement to get the value of the checkbox. Get-UDElement will also return other properties of the checkbox component.

    The following example shows a toast message with the value of the checkbox.

    API

    Menu

    New-UDMenu component for Universal Apps.

    The menu component can be used to provide a drop down list of options for the user to select.

    Basic Menu

    Create a basic menu.

    Button Styles

    You can edit the style of the menu by adjusting the variant parameter.

    Values

    You can use the value parameter to define a value that differs from the text displayed.

    OnChange Event Handler

    Use the -OnChange parameter to specify a script block to call when a new value is selected. The value of the selected item will be available in $EventData.

    API

    Skeleton

    A skeleton component for PowerShell Universal Apps.

    A skeleton is a form of a loading component that can show a placeholder while data is received.

    Variants

    There are three variants that you can use for a skeleton. You can use a circle, text or a rectangle. You can also define the height and width of the skeleton.

    New-UDSkeleton
    New-UDSkeleton -Variant circle -Width 40 -Height 40
    New-UDSkeleton -Variant rect -Width 210 -Height 118
    Skeletons

    Animations

    Skeletons will use the pulsate animation by default. You can also disable animation or use a wave animation.

    API

    Hidden

    Quickly and responsively toggle the visibility value of components and more with the hidden utilities.

    How it works

    Hidden works with a range of breakpoints e.g. xsUp or mdDown, or one or more breakpoints e.g. -Only 'sm' or -Only @('md', 'xl'). Ranges and individual breakpoints can be used simultaneously to achieve very customized behavior. The ranges are inclusive of the specified breakpoints.

    Up

    Using any breakpoint -Up parameter, the given children will be hidden at or above the breakpoint.

    Down

    Using any breakpoint -Down parameter, the given children will be hidden at or below the breakpoint.

    Only

    Using the breakpoint -Only parameter, the given children will be hidden at the specified breakpoint(s).

    The -Only parameter can be used in two ways:

    • list a single breakpoint

    • list an array of breakpoints

    API

    Stack

    Stack components in one dimesion.

    The Stack component manages layout of immediate children along the vertical or horizontal axis with optional spacing and/or dividers between each child.

    Horizontal Stack

    Horizontally stacked items.

    New-UDStack -Content {
       New-UDPaper -Content { "Item 1" } -Elevation 3
       New-UDPaper -Content { "Item 2" } -Elevation 3
       New-UDPaper -Content { "Item 3" } -Elevation 3
    } -Spacing 2

    Vertical Stack

    Vertically stacked items.

    API

    Alert

    Alert component for Universal Apps.

    Alerts provide a simple way to communicate information to a user.

    Simple Alerts

    Alerts have four different severities and can include text or other content.

    New-UDAlert -Severity 'error' -Text 'This is an error alert — check it out!' 
    New-UDAlert -Severity 'warning' -Text 'This is an warning alert — check it out!'
    New-UDAlert -Severity 'info' -Text 'This is an error info — check it out!' 
    New-UDAlert -Severity 'success' -Text 'This is an success alert — check it out!'
    Alert Types

    Advanced Alerts

    Alerts can contain any component and also a title.

    API

    Typography

    Typography component for Universal Apps

    Use typography to present your design and content as clearly and efficiently as possible.

    Too many type sizes and styles at once can spoil any layout. A typographic scale has a limited set of type sizes that work well together along with the layout grid.

    All Typography Types

    @("h1", "h2", "h3", "h4", "h5", "h6", "subtitle1", "subtitle2", "body1", "body2", 
    "caption", "button", "overline", "srOnly", "inherit", 
    "display4", "display3", "display2", "display1", "headline", "title", "subheading") | ForEach-Object {
        New-UDTypography -Variant $_ -Text $_ -GutterBottom
        New-UDElement -Tag 'p' -Content {}
    }

    Colored Text

    You can use the -Style parameter to define colors for your text.

    Theme-based Styling

    You can use styling by using the -Sx parameter of New-UDTypography. For example, to apply the secondary text color, you can use the following syntax.

    API

    Time Picker

    Time picker component for Universal Apps

    Time pickers pickers provide a simple way to select a single value from a pre-determined set.

    Time Picker

    New-UDTimePicker

    Locale

    Specify the locale of the time picker.

    24-Hour Time

    You can use the -DisableAmPm parameter to use 24-hour time.

    API

    Floating Action Button

    Floating action button component for Universal Apps

    A floating action button (FAB) performs the primary, or most common, action on a screen.

    A floating action button appears in front of all screen content, typically as a circular shape with an icon in its center. FABs come in two types: regular, and extended.

    Only use a FAB if it is the most suitable way to present a screen’s primary action.

    Only one floating action button is recommended per screen to represent the most common action.

    Floating Action Button

    New-UDFloatingActionButton -Icon (New-UDIcon -Icon user) -Size Small
    New-UDFloatingActionButton -Icon (New-UDIcon -Icon user) -Size Medium
    New-UDFloatingActionButton -Icon (New-UDIcon -Icon user) -Size Large

    OnClick

    API

    Link

    Link component for Universal Apps.

    Create a hyper link in a dashboard.

    Basic Link

    Create a basic link that goes to a web page.

    New-UDLink -Text 'Ironman Software' -Url https://www.ironmansoftware.com

    Change Style

    Adjust the underline and text style.

    Open in a New Window

    Open the link a new window when clicked.

    OnClick Event Handler

    Execute a PowerShell script block when the link is clicked.

    API

    Downgrade

    Learn how to revert to a downgrade level of PowerShell Universal.

    In some scenarios it may be required to roll back the version of PowerShell Universal. This could be due to a feature change or bug that affects the system in a way too impactful to continue with the version. We validating a version in a development or quality assurance environment before upgrading in production to avoid having to perform a downgrade.

    Downgrading can be complicated and error prone. We recommend restoring from a backup or snapshot instead of downgrading.

    Event Hubs

    Receive client events from the PowerShell Universal server.

    Event Hubs require a .

    Event Hubs provide the ability to connect client to the PowerShell Universal server. Once connected, the PowerShell Universal server can send messages to the connected clients and they will run a local PowerShell script block.

    Creating an Event Hub

    Chip

    Chip component for Universal Apps.

    Chips are compact elements that represent an input, attribute, or action.

    Chips allow users to enter information, make selections, filter content, or trigger actions.

    While included here as a standalone component, the most common use will be in some form of input, so some of the behavior demonstrated here is not shown in context.

    Basic Chips

    Tooltip

    Tooltip component for PowerShell Universal Apps.

    Tooltips display informative text when users hover over an element.

    Basic Tooltip

    Placement

    Place the tooltip on

    Autocomplete

    Autocomplete component for Universal Apps

    The autocomplete is a normal text input enhanced by a panel of suggested options.

    Static List of Options

    Creates a basic autocomplete with a static list of options

    Tree View

    Tree view component for Universal Apps.

    New-UDTreeView allows you to create a tree of items and, optionally, dynamically expand the list when clicked.

    Basic Tree View

    Create a basic tree view by using the New-UDTreeNode cmdlet.

    Button

    Button component for Universal Apps

    Buttons allow users to take actions, and make choices, with a single tap.

    Contained Button

    Contained buttons are high-emphasis, distinguished by their use of elevation and fill. They contain actions that are primary to your app.

    Radio

    Radio component for Universal Apps

    Radio buttons allow the user to select one option from a set.

    Use radio buttons when the user needs to see all available options. If available options can be collapsed, consider using a dropdown menu because it uses less space.

    Radio buttons should have the most commonly used option selected by default.

    Simple Radio

    Terminals

    In-browser PowerShell terminals.

    Terminals require a .

    Terminals are in-browser PowerShell consoles that you can execute arbitrary commands within. Terminals are configured to target an environment that you select and can optionally us Run As credentials to run as other users. The history of terminals is maintained within the PowerShell Universal database. You can reconnect to disconnected terminals as long as they haven't timed out.

    Terminal configurations are stored in terminals.ps1

    Dynamic Regions

    Dynamic regions allow you control the reload of data within the region.

    New-UDDynamic allows you to define a dynamic region. Pages themselves are dynamic in nature. This means that every time a page is loaded, it runs the PowerShell for that page. Sometimes, you may want to reload a section of a page rather than the whole page itself. This is when you will want to use dynamic regions.

    Basic Dynamic Region

    This dynamic region reloads when the button is clicked.

    Backdrop

    Backdrop component for Universal Apps.

    The backdrop component places an overlay over the drop of the entire page. It's useful for displaying loading states.

    Basic Backdrop

    To create a basic backdrop, you can use the New-UDBackdrop cmdlet and include content to show within the backdrop. The content will be centered on the page. To show the backdrop, use the -Open switch parameter.

    Additional Resources

    Additional PowerShell Universal resources.

    The Ironman Software blog has articles about PowerShell Universal.

    Grid Layout

    Drag and drop layout designer.

    Grid Layout

    The Grid Layout component is useful for defining layouts in a visual manner. You can drag and drop components using the web interface to automatically define the layout as JSON.

    Switch

    Switch component for Universal Apps

    Switches toggle the state of a single setting on or off.

    Switches are the preferred way to adjust settings on mobile. The option that the switch controls, as well as the state it’s in, should be made clear from the corresponding inline label.

    Switch

    Create a basic switch.

    Slider

    Slider component for Universal Apps.

    Sliders allow users to make selections from a range of values.

    Sliders reflect a range of values along a bar, from which users may select a single value. They are ideal for adjusting settings such as volume, brightness, or applying image filters.

    Slider

    Tests

    PowerShell Universal automated test support.

    This feature requires a .

    PowerShell Universal integrates with the test framework to allow you to execute test suites and view results. Results are stored in the PowerShell Universal database so you can view historical results.

    You can work with tests by visiting Automation \ Tests.

    Rate Limiting

    Rate limiting options for Universal.

    Rate limiting requires a .

    PowerShell Universal lets you rate limit requests made to the web server. You can configure rate limiting per endpoint and per period. By default, the client IP address rate limits clients.

    Configuration data for rate limits are stored in the ratelimits.ps1 file.

    HTML

    Define static HTML using Univeral apps.

    You can define static HTML using New-UDHtml. This cmdlet does not create React components but rather allows you to define static HTML. Any valid HTML string is supported.

    The following creates an unordered list.

    Modifying the <head> tag

    You can use the New-UDHelmet component to add new tags to the <head> of the HTML document. This is useful for loading custom JavaScript or CSS libraries.

    Tabs

    Tab component for Universal Apps

    Tabs make it easy to explore and switch between different views.

    Tabs organize and allow navigation between groups of content that are related and at the same level of hierarchy.

    Tabs

    Expansion Panel

    Expansion Panel component for Universal Apps

    Expansion panels contain creation flows and allow lightweight editing of an element.

    An expansion panel is a lightweight container that may either stand alone or be connected to a larger surface, such as a card.

    Simple Expansion Panel

      New-UDBadge -BadgeContent { 4 } -Children {
          New-UDIcon -Icon Envelope -Size 2x
      } -Color primary
    New-UDBadge -BadgeContent { 4 } -Children {
        New-UDIcon -Icon Envelope -Size 2x
    } -Color secondary
    New-UDBadge -BadgeContent { 4 } -Children {
        New-UDIcon -Icon Envelope -Size 2x
    } -Color success
    New-UDDatePicker
    New-UDDatePicker -OnChange {
        Show-UDToast -Message $body
    }
    New-UDRating -OnChange {
        Show-UDToast $EventData
    }
    New-UDCheckBox
    New-UDCheckBox -Disabled
    New-UDCheckBox -Checked $true
    New-UDCheckBox -Checked $true -Disabled
    New-UDMenu -Content {
       New-UDMenuItem -Text 'Item 1'
       New-UDMenuItem -Text 'Item 1'
       New-UDMenuItem -Text 'Item 1'
    }
    innerWidth  |xs      sm       md       lg       xl
                |--------|--------|--------|--------|-------->
    width       |   xs   |   sm   |   md   |   lg   |   xl
    
    smUp        |   show | hide
    mdDown      |                     hide | show
    Demo instance of PowerShell Universal.

    Discord

    Chat with other PowerShell Universal users.

    Downloads

    Download the latest version of PowerShell Universal.

    Forums

    Connect with the PowerShell Universal community.

    Issue Tracker

    File a bug report or feature request for PowerShell Universal.

    Pricing

    Purchase a license for the features of PowerShell Universal.

    Scripts

    Examples and full solutions for PowerShell Universal.

    Blog
    Demo
    Feedback Integration
    Pipeline Output
    Event Triggers
    Concurrency Controls
    Ad-Hoc Terminals
    Pester Test Support
    Dynamic Regions
    Steppers (Wizards)
    Transitions
    Integration with Scripts and APIs
    Extensible Platform
    Custom Styling and Branding
    Windows Service
    HTTPS
    SAML2
    Windows Authentication
    Script-Based Authorization
    Claims-Based Authorization
    Script Access Controls
    Custom and Built-In Roles
    Management API
    Visual Studio Code Extension
    Performance Profiler
    Password and Secret Management
    Git Integration
    Application Insights Integration
    New-UDDrawer

    3/31

    Long Term

    v9

    3/29

    9/32

    Standard

    v4

    6/23

    6/26

    Legacy

    v5

    8/24

    8/26

    Legacy

    v6

    3/26

    3/29

    Long Term

    v7

    3/27

    9/28

    Standard

    v8

    3/28

    environment
    https://blog.ironmansoftware.com/webapp-benchmark-siege/
    variables page
    New-PSUEndpoint
    Get-PSUEndpoint
    Remove-PSUEndpoint
    Set-UASetting
    DayJS documentation
    DayJS formatting template
    New-UDDateTime
    New-UDList
    New-UDListItem
    New-UDProgress
    New-UDDatePicker
    New-UDCheckbox
    New-UDMenu
    New-UDMenuItem
    New-UDSkeleton
    Animations
    New-UDHidden
    New-UDStack
    New-UDAlert
    Advanced Alerts
    theme-based
    New-UDTypography
    New-UDTimePicker
    New-UDFloatingActionButton
        New-UDLink -Text 'Ironman Software' -Url https://www.ironmansoftware.com -Variant h2 -Underline always
    New-UDLink
    API
    • New-UDHtml

    • New-UDHelmet

    New-UDHtml -Markup "<ul><li>First</li><li>Second</li><li>Third</li></ul>"
    New-UDHelmet -Tag 'script' -Attributes @{
        src = 'https://unpkg.com/[email protected]/dist/mermaid.min.js'
    } 
    API
    • New-UDExpansionPanel

    • New-UDExpansionPanelGroup

    Configuration Files

    Downgrading the configuration files will require removing or altering the .universal repository files to remove or rename new parameters. New cmdlets will be ignored by PowerShell Universal. If a cmdlet was renamed, it may have to be updated as well. You will need to refer to the changelog to see which cmdlets have changed in each version.

    Major versions may include breaking changes. Minor versions may have additional cmdlets or parameters but will not have any breaking changes.

    You can find information about each configuration file in the Repository page.

    It is much easier to restore from a backup of the configuration files before the upgrade rather than manually updating files.

    Database

    Restoring the database to a previous version requires downgrading the schema. This can be accomplished with PSU CLI. Using the schema command, you will be able to select the down-level version.

    Downgrading the database schema can be a destructive operation. You may remove tables and columns that contain data. Always backup a database before performing these operations.

    Below is an example of downgrading the schema of a SQLite database to version 5.3.0. You will need to stop the PowerShell Universal services before doing so.

    You can downgrade a nightly build version of the database to a stable version of the database to allow for an upgrade to the stable build of the target version. You will lose any data found in new columns or tables.

    Application Files

    Downgrading the application files is typically a simple process and depends on how you installed the product. You will need to perform the configuration file and database downgrades before performing the application downgrade.

    MSI

    To downgrade an MSI installation, you will need to first uninstall the current version. PowerShell Universal will not allow you to run a downgrade. After the uninstall is complete, perform an installation of the target version.

    If you have configured a service account, you will need to set the service account again after install. This will require the service account credentials.

    ZIP

    To downgrade a ZIP installation, simply delete the PowerShell Universal application files. Once the directory is clear unzip the target version's ZIP into the installation directory. Ensure that you run Get-ChildItem -Recurse | Unblock-File after doing so.

    IIS

    Similar to the ZIP installation, remove the old version's files and unzip and unblock the target version's files. Ensure that the web site and App Pool are stopped before attempting so.

    always recommend
    To create an event hub, click APIs \ Event Hub and click Create New Event Hub. Event Hubs are named and can choose to enforce authentication and authorization.

    Agent

    You will need to install and configure the PowerShell Universal Agent to use Event Hubs.

    Send Events

    From within the PowerShell Universal server, you can send events from a hub to connected clients using the Send-PSUEvent cmdlet.

    The -Data parameter accepts an object and will be serialized using CLIXML and send to the client. The data will be deserialized before passing to the script block.

    You can also run commands. This does not require defining a script on the event hub client. You can also use the Invoke-PSUCommand alias to mimic native PowerShell behavior.

    Receive Data from Clients

    This feature is only available when sending data to an individual client, rather than all clients connected to a hub.

    Example: Running Scripts on Remote Machines

    This example provides a way to run scripts on remote machines without having to install another instance of PowerShell Universal.

    This example allows for sending scripts to remote machines and executing them with a generic event hub script.

    First, create an event hub in PowerShell Universal. This example does not use authentication.

    Next, install the PowerShell Universal Agent on the remote machine. Create a configuration file in %ProgramData%\PowerShellUniversal\agent.json.

    Next, create a helper script.ps1 to receive the event hub data and process requests from PSU to invoke scripts. It creates a new temporary PS1 file and uses the $EventData passed down from the event hub message with the contents and parameters for the script.

    In PowerShell Universal, add a script that you want to run on the remote machine. In this example, it simply starts a process.

    Finally, add another script that sends the event down to the client. This could be from an API or an App as well. Because the script on the agent is generic, it will just run whatever is passed to it.

    From here you could event use the script to schedule jobs to run on the remote machines using the agent.

    license
    top
    ,
    bottom
    ,
    left
    or
    right
    .

    Custom Content

    Tooltip content can contain any UD element.

    Tooltip Type

    Tooltips can be over various types including: "dark", "success", "warning", "error", "info", "light"

    New-UDTooltip -Content {
        New-UDIcon -Icon 'User'
    } -TooltipContent {
        "User"
    }
    New-UDTooltip -Content {
        New-UDIcon -Icon 'User'
    } -TooltipContent {
        "User"
    } -Place 'bottom'
    OnChange

    An event handler that is called when the radio group is changed. the $Body variable will contain the current value.

    Default Value

    Set the default value of the radio group.

    Custom Formatting

    You can use custom formatting within the radio group. The below example will place the radio buttons next to each other instead of on top of each other.

    API

    • New-UDRadio

    • New-UDRadioGroup

    OnClick Handler

    The backdrop provides an -OnClick handler that you can use to close the backdrop when clicked. You can use Set-UDElement to open and close the backdrop.

    API

    • New-UDBackdrop

    Backdrop component
    Designing Layouts

    You can employ the -Design parameter to configure the layout of your page. This allows dynamic drag and drop of components that you place within the content of the grid layout. As you drag and resize components, the layout will be copied to your clipboard. Note: All components must possess a static -Id

    Using Layouts

    Once you have configured the layout to fit your needs, you can paste the JSON into your script and assign it with the -Layout parameter. Remove the -Design parameter to lock elements in place.

    Allowing Users to Modify Layouts

    You can allow your users to dynamically modify layouts by using the -Draggable, -Resizable and -Persist parameters. The layout changes are stored locally so the next time each user visits a page, it will be loaded with their chosen layout.

    OnChange Event

    Respond to when a switch value is changed. The $EventData variable will include whether or not the switch was checked or unchecked.

    Get-UDElement Support

    You can retrieve the value of the switch within another component by using Get-UDElement. Use the Checked property to determine whether the switch is checked out not.

    API

    • New-UDSwitch

    Configuring Rate Limiting

    To configure rate limiting, visit the APIs / Rate Limiting page. Click the Add button and define a new rate limit rule.

    Rate limiting affects all URLs for the server. If you enforce rate limiting that isn't correctly configured, you can negatively affect the management API.

    Method

    The method is the HTTP method to for this rule. If you use * , this rule affects all HTTP methods. You can also select a single method by picking it from the drop down.

    Endpoint

    The endpoint is the URL that you are rate limiting. You can rate limit all URLs by using a *. You can define specific URLs by defining the relative path: /api/user.

    Limit

    This is the number of requests in the time frame before rate limiting kicks in.

    Period

    This is the period over which the rate limit is counted. For example, if you select a period of 10 minutes and a limit of 100, then up to 100 requests can be made to the method and endpoint you have selected.

    Allow Lists

    To disable rate limiting for particular IP Addresses, clients, and endpoints, add them to the rate limiting allow lists. Find these by clicking the settings button.

    The below example prevents the loopback adapter from being rate limited.

    Example: Limit Custom API

    Limits callers of the /api/users endpoint to 100 requests per minute.

    Example: Limit Management API

    Limits callers of the management API to 100 requests per second.

    API

    • New-PSURateLimit

    • Set-PSUSetting

    license
    Set-PSUSetting -ApiEnvironment '7.1'
    New-PSUEndpoint -Url /environment -Environment Integrated -Endpoint {
        $PSUEnvironment
    }
    New-UDDateTime -InputObject (Get-Date)
    New-UDDateTime -InputObject (Get-Date) -Format 'DD/MM/YYYY'
    New-UDDateTime -InputObject (Get-Date) -Locale 'es'
    New-UDList -Content {
        New-UDListItem -Label 'Inbox' -Icon (New-UDIcon -Icon envelope -Size 3x) -SubTitle 'New Stuff'
        New-UDListItem -Label 'Drafts' -Icon (New-UDIcon -Icon edit -Size 3x) -SubTitle "Stuff I'm working on "
        New-UDListItem -Label 'Trash' -Icon (New-UDIcon -Icon trash -Size 3x) -SubTitle 'Stuff I deleted'
        New-UDListItem -Label 'Spam' -Icon (New-UDIcon -Icon bug -Size 3x) -SubTitle "Stuff I didn't want" -OnClick {
            Show-UDToast -Message 'Clicked'
        }
    }
    New-UDProgress
    New-UDProgress -PercentComplete 75
    New-UDDatePicker -Variant static
    New-UDDatePicker -Locale fr
    New-UDDatePicker -Minimum ((Get-Date).AddDays(-15)) -Maximum ((Get-Date).AddDays(15))
    $Year = (Get-Date).Year
    $MinDate = [DateTime]::new($year, 1, 1)
    $MaxDate = [DateTime]::new($year, 12, 31)
    New-UDDatePicker -Views "day" -MinimumDate $MinDate -MaximumDate $MaxDate
    New-UDRating -Max 10
    New-UDRating -Precision .5
    New-UDRating -Size large
    $Icon = New-UDIcon -Icon angry -Size lg -Regular
    $CheckedIcon = New-UDIcon -Icon angry -Size lg
    New-UDCheckBox -Icon $Icon -CheckedIcon $CheckedIcon -Style @{color = '#2196f3'}
    New-UDCheckBox -OnChange {
        Show-UDToast -Title 'Checkbox' -Message $Body
    }
    New-UDCheckBox -Label 'Demo' -LabelPlacement start
    New-UDCheckBox -Label 'Demo' -LabelPlacement top
    New-UDCheckBox -Label 'Demo' -LabelPlacement bottom
    New-UDCheckBox -Label 'Demo' -LabelPlacement end
    New-UDCheckbox -Id 'MyCheckbox' 
    
    New-UDButton -Text 'Get Value' -OnClick {
        Show-UDToast -Message (Get-UDElement -Id 'MyCheckbox').checked
    }
    New-UDMenu -Content {
       New-UDMenuItem -Text 'Item 1'
       New-UDMenuItem -Text 'Item 1'
       New-UDMenuItem -Text 'Item 1'
    } -Variant outlined
    New-UDMenu -Content {
       New-UDMenuItem -Text 'Item 1' -Value 'item1'
       New-UDMenuItem -Text 'Item 1' -Value 'item2'
       New-UDMenuItem -Text 'Item 1' -Value 'item3'
    }
    New-UDMenu -Text 'Click Me' -OnChange {
        Show-UDToast $EventData
    } -Children {
        New-UDMenuItem -Text 'Test'
        New-UDMenuItem -Text 'Test2'
        New-UDMenuItem -Text 'Test3'
    }
    New-UDSkeleton
    New-UDSkeleton -Animation disabled
    New-UDSkeleton -Animation wave
    New-UDHidden -Up xl -Content {
        New-UDTypography 'xl'
    }
    New-UDHidden -Down xs -Content {
        New-UDTypography 'xs'
    }
    New-UDHidden -Only 'sm' -Content {
        New-UDTypography 'sm'
    }
    New-UDHidden -Only @('sm', 'xl') -Content {
        New-UDTypography 'sm,xl'
    }
    New-UDStack -Content {
       New-UDPaper -Content { "Item 1" } -Elevation 3
       New-UDPaper -Content { "Item 2" } -Elevation 3
       New-UDPaper -Content { "Item 3" } -Elevation 3
    } -Spacing 2 -Direction 'column'
    New-UDAlert -Severity 'error' -Content { New-UDHtml 'This is an error alert — <strong>check it out!</strong>' } -Title "Error"
    New-UDAlert -Severity 'warning' -Content { New-UDHtml 'This is an warning alert — <strong>check it out!</strong>' } -Title "Warning"
    New-UDAlert -Severity 'info' -Content { New-UDHtml 'This is an error info — <strong>check it out!</strong>' } -Title "Info"
    New-UDAlert -Severity 'success' -Content { New-UDHtml 'This is an success alert — <strong>check it out!</strong>' } -Title "Success"
    New-UDTypography -Text 'My Text' -Style @{ color = 'blue' }
    
    New-UDTypography -Text 'Secondar' -Sx @{
        color = 'text.secondary'
    }
    New-UDTimePicker -Locale fr
    New-UDTimePicker -DisableAmPm
    New-UDFloatingActionButton -Icon (New-UDIcon -Icon user) -OnClick {
        Show-UDToast -Message "Hello!"
    }
    New-UDLink -Text 'Ironman Software' -Url https://www.ironmansoftware.com -OpenInNewWindow
    New-UDLink -Text 'Ironman Software' -OnClick {
        Show-UDToast "Hello!"
    }
    New-UDExpansionPanelGroup -Children {
        New-UDExpansionPanel -Title "Hello" -Children {}
    
        New-UDExpansionPanel -Title "Hello" -Id 'expContent' -Children {
            New-UDElement -Tag 'div' -Content { "Hello" }
        }
    }
    .\psu.exe schema --target-version '5.3.0' --connection-string "Data Source=C:\ProgramData\UniversalAutomation\database.db" --database-type "SQLite"
    Send-PSUEvent -Hub 'MyHub' -Data "Hello!"
    Invoke-PSUCommand -Hub "MyHub" -Command "Start-Process" -Parameters @{
        FilePath = "Notepad"
    }
    $Connection = Get-PSUEventHubConnection | Where-Object UserName -eq 'Admin'
    $Result = Send-PSUEvent -Hub 'Hub' -Data 'Say Hello!' -Connectionid $Connection.ConnectionId
    Show-UDToast $Result
    {
        "Connections": [
            {
                "Url": "http://localhost:5000",
                "Hub": "eventHub",
                "ScriptPath": "script.ps1"
            }
        ]
    }
    $TempFile = (New-TemporaryFile).FullName + ".ps1"
    $EventData.Contents | Out-File -FilePath $TempFile
    $Parameters = $EventData.Parameters
    & $TempFile @Parameters
    param($Name)
    
    Start-Process $Name
    param($TargetComputer, $ProcessName)
     
    Send-PSUEvent -Computer $TargetComputer -Data @{
        Contents = Get-Content StartAProcess.ps1 -Raw
        Parameters = @{
            Name = $ProcessName
        }
    }
    New-UDTooltip -Content {
        New-UDIcon -Icon 'User'
    } -TooltipContent {
        New-UDPaper -Children {
            "User"
        }
    }
    New-UDTooltip -Content {
        New-UDIcon -Icon 'User'
    } -TooltipContent {
        "User"
    } -Type 'success'
    New-UDRadioGroup -Label "Day" -Content {
        New-UDRadio -Label Monday -Value 'monday'
        New-UDRadio -Label Tuesday -Value 'tuesday'
        New-UDRadio -Label Wednesday -Value 'wednesday'
        New-UDRadio -Label Thursday -Value 'thursday'
        New-UDRadio -Label Friday  -Value 'friday'
        New-UDRadio -Label Saturday -Value 'saturday'
        New-UDRadio -Label Sunday -Value 'sunday'
    }
    New-UDRadioGroup -Label "Day" -Content {
        New-UDRadio -Label Monday -Value 'monday'
        New-UDRadio -Label Tuesday -Value 'tuesday'
        New-UDRadio -Label Wednesday -Value 'wednesday'
        New-UDRadio -Label Thursday -Value 'thursday'
        New-UDRadio -Label Friday  -Value 'friday'
        New-UDRadio -Label Saturday -Value 'saturday'
        New-UDRadio -Label Sunday -Value 'sunday'
    } -OnChange { Show-UDToast -Message $Body }
        }
    New-UDRadioGroup -Label "Day" -Content {
        New-UDRadio -Label Monday -Value 'monday'
        New-UDRadio -Label Tuesday -Value 'tuesday'
        New-UDRadio -Label Wednesday -Value 'wednesday'
        New-UDRadio -Label Thursday -Value 'thursday'
        New-UDRadio -Label Friday  -Value 'friday'
        New-UDRadio -Label Saturday -Value 'saturday'
        New-UDRadio -Label Sunday -Value 'sunday'
    } -Value 'sunday'
    New-UDRadioGroup -Label "Day" -Content {
        New-UDRow -Columns {
            New-UDColumn -LargeSize 1 -Content {
                New-UDRadio -Label Monday -Value 'monday'        
            }
            New-UDColumn -LargeSize 1 -Content {
                New-UDRadio -Label Sunday -Value 'sunday'
            }
        }
    }
    New-UDBackdrop -Content {
        New-UDTypography -Text "Loading..." -Variant h2
    } -Open
    New-UDBackdrop -Id 'backdrop' -Content {
        New-UDTypography -Text "Loading..." -Variant h2
    } -Open -OnClick {
        Set-UDElement -Id 'backdrop' -Properties @{
            open = $false
        }
    }
    New-UDGridLayout -Content { 1..10 | ForEach-Object { New-UDPaper -Id "Paper$" -Content { New-UDTypography -Text $ } -Elevation 5 } } -Design
    $Layout = '{"lg":[{"w":7,"h":7,"x":5,"y":0,"i":"grid-element-Paper1","moved":false,"static":false},{"w":7,"h":5,"x":5,"y":7,"i":"grid-element-Paper2","moved":false,"static":false},{"w":1,"h":1,"x":0,"y":0,"i":"grid-element-Paper3","moved":false,"static":false},{"w":1,"h":1,"x":0,"y":1,"i":"grid-element-Paper4","moved":false,"static":false},{"w":1,"h":1,"x":0,"y":2,"i":"grid-element-Paper5","moved":false,"static":false},{"w":1,"h":1,"x":0,"y":3,"i":"grid-element-Paper6","moved":false,"static":false},{"w":1,"h":1,"x":0,"y":4,"i":"grid-element-Paper7","moved":false,"static":false},{"w":1,"h":1,"x":0,"y":5,"i":"grid-element-Paper8","moved":false,"static":false},{"w":1,"h":1,"x":0,"y":6,"i":"grid-element-Paper9","moved":false,"static":false},{"w":1,"h":1,"x":0,"y":7,"i":"grid-element-Paper10","moved":false,"static":false}]}' 
    New-UDGridLayout -Content { 1..10 | ForEach-Object { New-UDPaper -Id "Paper$" -Content { New-UDTypography -Text $ } -Elevation 5 } } -Layout $Layout
    New-UDGridLayout -Content { 1..10 | ForEach-Object { New-UDPaper -Id "Paper$" -Content { New-UDTypography -Text $ } -Elevation 5 } } -Draggable -Resizable -Persist
    New-UDSwitch -Checked $true 
    New-UDSwitch -Checked $true -Disabled
    New-UDSwitch -OnChange { Show-UDToast -Message $EventData }
    New-UDSwitch -Id 'switch' 
    New-UDButton -Text 'Click' -OnClick {
        Show-UDToast -Message (Get-UDElement -Id 'switch').checked
    }
    Set-PSUSetting -RateLimitIpAddressAllowList @("127.0.0.1")
    New-PSURateLimit -Endpoint "GET|/api/users" -TimeSpan "00:01:00" -Limit 100
    New-PSURateLimit -Endpoint "*|/api/v1/*" -TimeSpan "00:00:01" -Limit 100
    Chips with Icons

    OnClick

    Shows a toast when the chip is clicked.

    OnDelete

    API

    New-UDChip

    Dynamic List of Options

    When text is typed, it can be filtered with OnLoadOptions. $Body will contain the current text that is typed.

    This example filters the array with Where-Object.

    OnChange

    $Body contains the currently selected item. The OnChange event will fire when the user selects one or more items.

    Icon

    You can place an icon before an autocomplete by using the -Icon parameter.

    OnEnter

    OnEnter is triggered when the user presses the enter key within the autocomplete.

    Options

    You can use New-UDAutoCompleteOption to specify name and values.

    API

    • New-UDAutocomplete

    Dynamic Tree View

    Dynamic tree views allow you to run PowerShell whenever a node is clicked. You can then return a list of nodes that should be rendered underneath the clicked node. You can also take other actions such as opening a modal or showing a toast.

    API

    • New-UDTreeView

    • New-UDTreeNode

    Basic Tree View
    Outlined Button

    Outlined buttons are medium-emphasis buttons. They contain actions that are important, but aren’t the primary action in an app.

    Control Button Size

    You can control the pixel size of a button based on pixel size by using the Style parameter

    Buttons with icons and label

    Sometimes you might want to have icons for certain button to enhance the UX of the application as we recognize logos more easily than plain text. For example, if you have a delete button you can label it with a dustbin icon.

    Buttons with event handlers

    You can specify a script block to execute when the button is clicked

    Loading Button

    Loading buttons will display a loading icon while an event handler is running. This is useful for longer running events.

    Button Group

    A button group produces a button with a drop down menu. This is also referred to a split button.

    Button Group

    Disable Button After Click

    This example uses Set-UDElement to disable the button after performing an action.

    API

    • New-UDButton

    Configure A Terminal

    You can configure a new terminal by navigating to Automation \ Terminals and clicking Create New Terminal. You'll be able to select the environment and credential to run the terminal as.

    List of available terminals

    Use a Terminal

    To use a terminal, click the Open Terminal button for the terminal you wish to launch. Depending on your configuration, this may start a new PowerShell process based on the environment you selected.

    Once the terminal has launched, you'll be able to issue commands.

    Using a terminal

    Stop a Terminal

    To stop a terminal, you can navigate to the terminal instances tab on the Terminals page. Click the trash can to stop the terminal.

    Terminal Instances

    Reconnect to a Terminal

    If you navigate away from PowerShell Universal, the terminal will go idle. You can reconnect to a terminal by clicking the Open Terminal button for the idle terminal instance.

    Reconnecting to a terminal

    Terminals will time out automatically after 5 minutes. You can customize the timeout by setting the -IdleTimeout parameter of New-PSUTerminal.

    History

    Terminal history can be enabled per terminal configuration.

    Enable Command History

    When terminal history is enabled, you will be able to view the history of all commands that were executed within the terminal. Click the View Command History button for the instance in question.

    View Command History

    You will be able to review what the command was that ran, when it was ran, who started the terminal and what the output of the command was.

    license
    Arguments List

    An array of arguments may be passed to the dynamic region.

    utilizing the arguments list

    You can use Sync-UDElement to change the argument values.

    Auto Refresh

    Dynamic regions enable the ability to auto refresh components after a certain amount of time. The entire region's script block will be run when autorefreshing.

    If you have multiple related components that use the same data, consider putting them in the same dynamic region to improve performance.

    Auto refresh dynamic region

    Loading Component

    Sometimes refreshing a dynamic component may take some time. For example, if you are querying another service's REST API or a data. Dynamic regions support configuration of the component that shows when the region is reloading. By default, nothing is shown. This can be any app component.

    Loading component for dynamic region

    API

    New-UDDynamic

    Reload on button click
    Slider with minimum and maximum values

    Disabled Slider

    Slider with custom step size

    Slider with marks

    Range based slider

    OnChange event for slider

    API

    • New-UDSlider

    Test Discovery

    Tests files are located based on file name. Any files found in the respository that end in .Tests.ps1 will be listed in the Test Files tab. You can create new test files on the Automation \ Scripts page.

    Test Files

    Test Execution

    Pester v5 or later needs to be installed to run tests.

    Tests can be run by clicking the Run Test or Run All Tests buttons. Run Test will run the individual Test Files and Run All Tests will run all the Test Files.

    You will have the option to select the environment, credential and computer to run the tests.

    Run Tests

    Test Results

    Test Results are produced after the test run finishes. You will be able to see the overal status of the test run and the result of individual test suites and cases.

    Test Results
    license
    Pester
    Vertical Tabs

    Dynamic Tabs

    Dynamic tabs will refresh their content when they are selected. You will need to include the -RenderOnActive parameter to prevent all the tabs from rendering even if they are not shown.

    Icons

    API

    • New-UDTabs

    • New-UDTab

    Get Started

    Get started with PowerShell Universal

    Install PowerShell Universal

    You'll need to install the PowerShell Universal server. There are a lot of ways to do so, but you can use the command line below to get started quickly:

    You can install PowerShell Universal as a service. Ensure that PowerShell is running as administrator, or the service won't install correctly.

    Install-Module Universal
    Install-PSUServer

    You can install PowerShell Universal using the following shell script:

    Install-Module Universal
    Install-PSUServer

    You can install PowerShell Universal using the Universal PowerShell module:

    Open PowerShell Universal

    By default, PowerShell Universal runs on port 5000 of localhost.

    First Run Wizard

    The first run wizard will step you through the basic settings of PowerShell Universal. This includes the default admin username and password, security settings, telemetry settings and license.

    Admin Account

    The admin account is used to login to PowerShell Universal. It will display a warning if the password does not match the complexity requirements. You can always change it later.

    Security Settings

    Select from the drop down of security settings. They tweak certain features of PowerShell Universal in different levels of security. If you plan on cloning from a git repository, skip this step or set it to default.

    Telemetry

    PowerShell Universal can if you opt-in to do so. If you plan to clone from a git repository, skip this setting.

    License

    Add your license file. This is optional and needs to be an account-based license key.

    Create an API

    APIs allow you to call PowerShell scripts over HTTP. To create an API, click API \ Endpoints and click Create New Endpoint. Specify a URL.

    Next, click details on your new API and enter the following command into the editor:

    Save the script and then click the Execute button to test it out.

    You can also execute the API via Invoke-RestMethod.

    Create a Script

    To create a script, click Automation \ Scripts and then click Create New Script.

    Enter the following script into the editor and save:

    Once the script is saved, click Run.

    Create an App

    To create a new PowerShell-based user interface (app), you can click User Interfaces \ Apps and then Create New App.

    After clicking Ok, click the Details button to edit the PowerShell script. Add the following script to the editor:

    Save the app, click the Restart button and then click the View button. Click the Click Me button.

    Learn more about the various features of PowerShell Universal:

    OpenAPI

    Standardized documentation for your endpoints.

    About

    API documentation can be produced for your endpoints by creating a new OpenAPI definition and assigning endpoints to it. OpenAPI is a standard format and can be consumed by tools, such as the OpenAPI Generator or Swagger Codegen, to create clients. The Swagger dashboard is also integrated into PowerShell Universal to provide interactive documentation.

    Management API Documentation

    You can view the Managment API documentation by visiting the built in Swagger dashboard.

    Create an OpenAPI Document

    To create an OpenAPI definition, click APIs \ Documentation and then Create new Endpoint Documentation. You can set the name, URL, description and authentication details for the documentation.

    Once created, you can assign endpoints to the documentation by editing the endpoint.

    The documentation for your endpoint will appear within the Swagger dashboard. Select the definition with the Select a definition dropdown.

    All your custom endpoints will be listed.

    Help Text

    You can specify help text for your APIs using comment-based help. Including a synopsis, description and parameter descriptions will result in each of those pieces being documented in the OpenAPI documentation and Swagger age.

    For example, with a simple /get/:id endpoint, we could have comment-based help such as this.

    The resulting Swagger page will show each of these descriptions.

    Input and Output Types

    Types can be defined within an endpoint documentation ScriptBlock. Click the Edit Details button on the API documentation record.

    APIs can also be documented using input and output types by creating a PowerShell class and referencing it within your comment-based help. PowerShell Universal takes advantage of the .INPUTS and .OUTPUTS sections to specify accepted formats and define status code return values.

    Within the .INPUTS and .OUTPUTS , you will define a YAML block to provide this information. To create types, use the Endpoint Documentation editor. This file is loaded when reading OpenAPI documents. This information is stored in endpointsDocumentation.ps1.

    Inputs

    Input types are defined in the .INPUTS section. This section is a YAML block that defines if the input is required, provides a description and specifies the content type. This is a content type followed by the PowerShell class you defined in the endpoint documentation.

    Outputs

    Output types are similar to input but are specified on return codes as well as their content type and PowerShell class. The below example returns an ADAccountType class when a HTTP OK (200) is returned from the API. A 400 (Bad Request) does not return data but does provide a description that will be displayed in the API documentation.

    Security

    Authentication and authorization for REST APIs.

    Once enabled, you will be able to enforce authentication and authorization on your endpoints.

    Defining Secure Endpoints

    You can define secure endpoints in the UI by enabling authentication. You will endpoint authentication and authorization under the Security tab of an endpoint's properties.

    You can also define secure endpoints using the .universal/endpoints.ps1 file or the Management API using New-PSUEndpoint.

    When authentication is enabled, it will enforce the use of one of the configured authentication methods. APIs support the following methods.

    • JWT App Tokens

    • Windows Authentication

    • Cookie Authentication

    • Basic Authentication

    Accessing Secure Endpoints

    Once you have defined a secure endpoint, you will need to provide authentication and authorization to access the endpoint.

    Authenticating with tokens

    Note that if you are hosting in IIS and do not have Anonymous Authentication enabled, you will not be able to pass app tokens to the PowerShell Universal server.

    To authenticate with tokens, first, you need generate a new app token for use. You can use the Grant-PSUAppToken cmdlet to do so remotely or you can create an app token in the UI using the Settings Security AppTokens tab.

    Hover over your user name in the top right of the admin console, click Tokens and click Create Application Token.

    Once you have created your app token, you can now use it to authenticate against the secure endpoint. To do so, pass the Authorization header along with the request.

    Custom Authorization Header

    PowerShell Universal provides a custom authorization header to support scenarios with reverse proxies that may require their own Authorization header. If the X-PSU-Authorization header is specified, PSU will ignore the Authorization header and use this header instead.

    Authenticating with Windows Authentication

    To authenticate with , you can use the -UseDefaultCredentials parameter of Invoke-RestMethod and Invoke-WebRequest . This will perform negotiate authentication whether you are running inside IIS or a service.

    Authenticating with Cookies

    To authenticate with cookies, you will first need to call the login API to receive a valid cookie from the system. You can use Invoke-WebRequest to do so. Pass the user name and password as the body. Specify the -SessionVariable parameter to establish a session.

    Once you have successfully authenticated, you can use your $mySession variable to call secure endpoints.

    Enforcing Roles

    In addition to creating endpoints that require authentication, you can also enforce roles by define a role in the New-PSUEndpoint cmdlet or by selecting one in the UI. If a role is selected, it's possess the role.

    Windows and Cookie authentication will assign roles based on the Identity of the user and the role policies as they are applied.

    JWT app tokens will use the role that was defined when they were generated.

    API

    Schedules

    Schedule scripts to run in PowerShell Universal.

    Assign schedules to scripts to define frequency and other parameters for a script, such as run as credentials.

    Schedules are stored in the schedules.ps1 configuration file.

    Scheduling a Job

    To schedule a job, go to the Automation / Schedules page and click the New Schedule button. To schedule a script, go to the script's page and click Schedule.

    You can define schedules based on simple selections like Every Minute or Every Hour, or you can define CRON expressions yourself for more configurable schedules. You can also run One Time schedules that run once at a later date.

    You can also define under which user the scheduled job runs, as well as which PowerShell version it uses.

    Simple Schedules

    Simple schedules are really just helpers for various standard CRON schedules. When you select one, it defines a CRON schedule for your script.

    CRON

    CRON schedules use CRON expressions to define schedules. PowerShell Universal takes advantage of Chronos. For examples of valid expressions, .

    One-Time

    One-time schedules will run once in the future. You can select the time and day of when they will run.

    Continuous

    Continuous schedules run over and over again. You can define a delay between each scheduled job run.

    Parameters

    Schedules support setting parameters for scripts. For example, if you have a script that accepts a parameter, you can choose to pass a value to the parameter during the schedule.

    Within the modal for defining the schedule, you can set the parameter value.

    When editing schedules from PowerShell, you can define the parameters on the New-PSUSchedule cmdlet. This cmdlet accepts a hashtable representing the scripts parameters so that you can pass the values in for your schedule.

    Environments

    When creating a schedule, you can specify the for your job to run. By default, it will use the default environment. You can define an environment in the UI by using the Environment drop down. You can define an environment using the -Environment parameter in New-PSUSchedule.

    Run As

    You can define as which user to run the schedule by using the Run As selector in the UI. The Run As selector contains a list of PSCredential you have defined. You need to define a PSCredential variable before the Run As selector is visible. By default, scheduled jobs run under the credentials of the user that is running PowerShell Universal.

    You can define a Run As user in a script by using the -Credential parameter. The value should be the name of the variable that contains your credential.

    Computer

    You can select the computer or computers on which to run the schedule. By default, schedules run on any available computer. If you select All Computers, the schedule runs on all computers connected to the PSU cluster. If you select a specific computer, the schedule runs on only that computer.

    Conditions

    You can define conditions that determine whether a schedule should be run. This is useful if you are using the same repository scripts for multiple environments. Currently, conditions cannot be defined within the admin console. Conditions are passed to the current script and schedule as parameters. The condition scriptblock runs within the integrated environment.

    The condition needs to return true or false. Below is an example of a condition where the schedule only runs if there is an environment variable named Slot that contains the value production.

    Pausing Schedules

    You can pause a schedule by setting the Paused property. When a schedule is paused, it does not run. This is useful to stop a schedule from running without deleting it.

    Time Out

    You can set a time out for scheduled jobs. The time out is the number of minutes before the scheduled job is canceled.

    Random Delay

    The Random Delay property causes a schedule to start anywhere between 0 and 60 seconds from the scheduled time. This is useful when running many schedules at the same time. For example, if you had 10 schedules that start at midnight, you may want to set a random delay to limit resource contention on the PowerShell Universal service.

    Available in Branch

    In multi-branch environments, it may be necessary to avoid running schedules based on the branch that is loaded in PowerShell Universal. You can use the -AvailableInBranchoption on New-PSUSchedule to avoid having a schedule run when running in a certain branch. This value is also available in the admin console under the schedule settings when git is enabled.

    API

    Icon

    Icon component for Universal Apps

    FontAwesome icons to include in your app. Icon names are slightly different than those shown on the FontAwesome website. For example, if you want to use the network-wired icon, you would use the following string.

    Finding an Icon

    We include FontAwesome v6 with PowerShell Universal. You can use Find-UDIcon to search through the list of included icons.

    The UniversalDashboard.FontAwesomeIcons enum should not be used and is only included for backwards compatibility. Many of the icons are no longer a part of FontAwesome 6.

    Icon

    Create icons by specifying their names. You can use the icon reference below to find icons.

    Size

    Set the size of the icon. Valid values are: xs, sm, lg, 2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x

    Rotation

    Rotate icons. The value represents the degrees of rotation.

    Border

    Add a border to your icon.

    Style

    Apply CSS styles to your icon.

    Visually Search for Icons

    Complete Icon List

    API

    Timeline

    Time line control for PowerShell Universal Apps

    The timeline control can be used to display a sequence of events over time.

    Basic Timeline

    Create a basic timeline with information on both sides of the timeline.

    Alternating Timeline

    Colors

    Icons

    API

    • New-UDTimeline

    • New-UDTimelineItem

    Upload

    Component for uploading files in Universal Apps.

    The UDUpload component is used to upload files to Universal Apps. You can process files the user uploads. You will receive the data for the file, a file name and the type of file if it can be determined by the web browser.

    This component works with UDForm and UDStepper.

    Uploading a File

    Upload ony supports files up to 2 GB in size.

    Uploads a file and shows the contents via a toast.

    The body of the OnUpload script block is a JSON string with the following format.

    The $EventData is an object with the following structure.

    Uploading a File with a Form

    Uploads a file as part of a UDForm.

    The body of the OnSubmit script block is the same one you will see with any form and the file will be contains as one of the fields within the form.

    Example: Uploading a file and save to it the temp directory

    This example allows a user to upload a file. Once the file is uploaded, it will be saved to the temporary directory.

    API

    Editor

    A text editor component for Universal Apps.

    The editor component is based on Editor.js. It's a block editor that accepts text, links, lists, code and images.

    When working with the editor, you can receive data about the current document via the OnChange parameter. By default, data is returned in the Editor.js JSON format.

    Creating an Editor

    To create a basic editor, use the New-UDEditor cmdlet.

    The editor will be available and you can add new blocks by clicking the plus button.

    Working with Data

    If you define a script block for the -OnChange event handler. The $EventData variable will contain the current status of the editor. By default, this returns the Editor.JS .

    You can also use the HTML render plugin by specifying the -Format parameter.

    To specify the default data for the editor, use the -Data parameter. You need to specify the JSON block format.

    Image Support

    In order to support images, you will need to provide a in which to upload the images. Once a published folder is defined, images can be uploaded directly in the editor. They will be placed within the directory and then served through the request path.

    API

    New-UDEditor

    Name
    Type
    Description
    Required

    Transfer List

    A transfer list (or "shuttle") enables the user to move one or more list items between lists.

    Simple Transfer List

    Create a simple transfer list.

    New-UDTransferList -Item {
        New-UDTransferListItem -Name 'test1' -Value 1
        New-UDTransferListItem -Name 'test2' -Value 2
        New-UDTransferListItem -Name 'test3' -Value 3
        New-UDTransferListItem -Name 'test4' -Value 4
        New-UDTransferListItem -Name 'test5' -Value 5
    } 

    Transfer List Value on Change

    Use the OnChange event handler to get the value of the selected items.

    Transfer List in a Form

    Transfer lists can be used within forms and steppers.

    API

    Card

    Card component for Universal Apps

    Cards contain content and actions about a single subject.

    Cards are surfaces that display content and actions on a single topic. They should be easy to scan for relevant and actionable information. Elements, like text and images, should be placed on them in a way that clearly indicates hierarchy.

    Simple Card

    Although cards can support multiple actions, UI controls, and an overflow menu, use restraint and remember that cards are entry points to more complex and detailed information.

    New-UDCard -Title 'Simple Card' -Content {
        "This is some content"
    }

    Advanced Card

    You can use the body, header, footer and expand cmdlets to create advanced cards. The below example creates a card with various features based on a Hyper-V VM.

    API

    Paper

    Paper component for Universal Apps

    In Material Design, the physical properties of paper are translated to the screen.

    The background of an application resembles the flat, opaque texture of a sheet of paper, and an application’s behavior mimics paper’s ability to be re-sized, shuffled, and bound together in multiple sheets.

    Paper

    Paper Content Formatting

    By default, the paper component uses the flex display type for content within the paper. This can cause issues with other types of content that may be stored within the paper. You can override the display type by using the -Style parameter.

    Square Paper

    By default, paper will have rounded edges. You can reduce the rounding by using a square paper.

    Colored Paper

    The -Style parameter can be used to color paper. Any valid CSS can be included in the hashtable for a style.

    The following example creates paper with a red background.

    API

    Migrate and Restore

    Migrate or restore configuration of a PowerShell Universal system.

    It is often desirable to migrate a PowerShell Universal server configuration from one machine to another. This can be due to change of infrastructure or restoring from backup. This can be for operating system upgrades or general data center maintenance.

    This document explains the steps necessary to migrate PowerShell Universal configuration to another machine.

    We recommend stopping the PowerShell Universal service before performing the migration or restore.

    Depending on the type of migration or restoration, you may not need to perform all of these actions.

    Configuration Data

    Licensing

    Licensing options for PowerShell Universal

    PowerShell Universal is licensed per server. We provide licenses for individuals and organizations.

    You can purchase a license on .

    What's a server?

    A server is a single running instance of PowerShell Universal.

    Modal

    Modal component for Universal Apps.

    Modals inform users about a task and can contain critical information, require decisions, or involve multiple tasks.

    Basic

    Select

    Select component for Universal Apps

    Select components are used for collecting user provided information from a list of options.

    Simple Select

    Create a simple select with multiple options.

    System Requirements

    System requirements for PowerShell Universal

    Hardware

    Hardware recommendations are based on use and depend on how many scripts are running on the PowerShell Universal server and how many users are accessing the machine. Unix based machines typically require less hardware requirements than Windows based machines. The below recommendations are based on low use of the system.

    Code Editor

    Code editor component for Universal Apps.

    The code editor component allows you to host the editor within your dashboards.

    Creating a Code Editor

    You can create a new code editor with the New-UDCodeEditor cmdlet. Specifying the -Language parameter will enable syntax highlighting for that language. You will need to specify a height in pixels.

    Grid

    Grid layout component for Universal Apps.

    The responsive layout grid adapts to screen size and orientation, ensuring consistency across layouts.

    The grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs. Material Design’s responsive UI is based on a 12-column grid layout.

    Basic Layout

    Transitions

    Transition component for Universal Apps.

    Transitions allow you to transition components in and out of view within your dashboard using various animations. You can take advantage of interactive cmdlets like Set-UDElement to change the transition state and cause an element to move in.

    In the following example, we have a card that transitions in via a Fade. Clicking the switch the toggle the card in and out.

    The resulting effect looks like this.

    Collapse

    The collapse transition will collapse a section in and out. You can specify a collapse height to only collapse a portion of the section.

     New-UDChip -Label 'Basic'
    New-UDChip -Label 'Basic' -Icon (New-UDIcon -Icon 'user')
    New-UDChip -Label 'OnClick' -OnClick {
        Show-UDToast -Message 'Hello!'
    }
    New-UDChip -Label 'OnDelete' -OnClick {
        Show-UDToast -Message 'Goodbye!'
    }
    New-UDAutocomplete -Options @('Test', 'Test2', 'Test3', 'Test4')
    New-UDAutocomplete -OnLoadOptions { 
        @('Test', 'Test2', 'Test3', 'Test4') | Where-Object { $_ -like "*$Body*" } | ConvertTo-Json
    }
    New-UDAutocomplete -OnLoadOptions { 
        @('Test', 'Test2', 'Test3', 'Test4') | Where-Object { $_ -like "*$Body*" } | ConvertTo-Json
    } -OnChange {
        Show-UDToast $Body 
    }
    New-UDAutocomplete -Options @("Test", "No", "Yes") -Icon (New-UDIcon -Icon 'Users') 
    New-UDAutocomplete -Options @("Test", "No", "Yes") -onEnter {
        Show-UDToast ((Get-UDElement -Id 'ac').value)
    } -Id 'ac'
    New-UDAutocomplete -Options @(
        New-UDAutoCompleteOption -Name 'Adam D' -Value '1'
        New-UDAutoCompleteOption -Name 'Sarah F' -Value '2'
        New-UDAutoCompleteOption -Name 'Tom S' -Value '3'
    )
    New-UDTreeView -Node {
        New-UDTreeNode -Name 'Level 1' -Children {
            New-UDTreeNode -Name 'Level 2 - Item 1' 
            New-UDTreeNode -Name 'Level 2 - Item 2'
            New-UDTreeNode -Name 'Level 2 - Item 3' -Children {
                New-UDTreeNode -Name 'Level 3'
            }
        }
    }
    New-UDDashboard -Title 'File System' -Content {
        Get-PSDrive -PSProvider 'FileSystem' | ForEach-Object {
            New-UDTreeView -Node { New-UDTreeNode -Name $_.Name -Id "$($_.Name):\" } -OnNodeClicked {
                Get-ChildItem $EventData.Id | ForEach-Object {
                    New-UDTreeNode -Name $_.Name -Id $_.FullName -Leaf:$(-not $_.PSIsContainer)
                }
            }
        }
    }
     New-UDButton -Variant 'contained' -Text 'Default'
    New-UDButton -Variant 'outlined' -Text 'Default'
    New-UDButton -Id "Submit" -Text "Submit" -Style @{ Width = "150px"; Height = "100px" }
    New-UDButton -Icon (New-UDIcon -Icon trash) -Text 'Delete'
    New-UDButton -Text 'Message Box' -OnClick {
        Show-UDToast -Message 'Hello, world!'
    }
    New-UDButton -Text 'Message Box' -OnClick {
        Show-UDToast -Message 'Hello, world!'
        Start-Sleep 10
    } -ShowLoading
    New-UDButtonGroup -Children {
        New-UDButtonGroupItem -Text "Button 1" -OnClick {
            Show-UDToast "Button 1"
        }
        New-UDButtonGroupItem -Text "Button 2" -OnClick {
            Show-UDToast "Button 2"
        }
        New-UDButtonGroupItem -Text "Button 3" -OnClick {
            Show-UDToast "Button 3"
        }
    }
    New-UDButton -Id "btn1" -OnClick {
        Show-UDToast "Hello!"
        Set-UDElement -Id 'btn1' -Attributes @{
            disabled = $true
        }
    }
    New-UDApp -Title "Hello, World!" -Content {
        New-UDDynamic -Id 'date' -Content {
            New-UDTypography -Text "$(Get-Date)"
        }
    
        New-UDButton -Text 'Reload Date' -OnClick { Sync-UDElement -Id 'date' }
    }
    New-UDDynamic -Id 'dynamic_01' -Content {
        New-UDTypography -Text "This is an $($ArgumentList[0]) an $($ArgumentList[1]) in a UDDynamic"
    } -ArgumentList @('example of', 'arguments list') 
     New-UDDynamic -id DynamicRegion -ArgumentList 5,'hello' -content {
      foreach($item in $argumentList){
          New-UDTypography -Variant h3 -Text "Item: $item"
      }
     }
    
     New-UDButton -Text "Sync Dynamic" -onclick {
      Sync-UDElement -id DynamicRegion -ArgumentList 10,'World'
     }
        New-UDDynamic -Id 'date' -Content {
            New-UDTypography -Text "$(Get-Date)" -Variant h3
            New-UDTypography -Text "$(Get-Random)" -Variant h3
        } -AutoRefresh -AutoRefreshInterval 1
        New-UDDynamic -Content {
            Start-Sleep -Seconds 3
            New-UDTypography -Text "Done!"
        } -LoadingComponent {
            New-UDProgress -Circular
        }
    New-UDSlider -Value 1
    New-UDSlider -Min 10 -Max 1000
    New-UDSlider -Disabled
    New-UDSlider -Min 10 -Max 1000 -Step 100
    New-UDSlider -Marks
    New-UDSlider -Value @(1, 10)
    New-UDSlider -OnChange {
        Show-UDToast -Message $Body 
        Set-TestData $Body
    }
    New-UDTabs -Tabs {
        New-UDTab -Text 'Item One' -Content { New-UDTypography -Text 'Item One' -Variant 'h2' }
        New-UDTab -Text 'Item Two' -Content { New-UDTypography -Text 'Item Two' -Variant 'h2' }
        New-UDTab -Text 'Item Three' -Content { New-UDTypography -Text 'Item Three' -Variant 'h2' }
    }
    New-UDTabs -Tabs {
        New-UDTab -Text 'Item One' -Content { New-UDTypography -Text 'Item One' -Variant 'h2' }
        New-UDTab -Text 'Item Two' -Content { New-UDTypography -Text 'Item Two' -Variant 'h2' }
        New-UDTab -Text 'Item Three' -Content { New-UDTypography -Text 'Item Three' -Variant 'h2' }
    } -Orientation vertical
    New-UDTabs -Tabs {
        New-UDTab -Text 'Item One' -Content { Get-Date } -Dynamic
        New-UDTab -Text 'Item Two' -Content { Get-Date } -Dynamic
        New-UDTab -Text 'Item Three' -Content { Get-Date } -Dynamic
    } -RenderOnActive
    New-UDTabs -Tabs {
        New-UDTab -Text 'Item One' -Content { New-UDTypography -Text 'Item One' -Variant 'h2' } -Icon (New-UDIcon -Icon Users)
        New-UDTab -Text 'Item Two' -Content { New-UDTypography -Text 'Item Two' -Variant 'h2' } -Icon (New-UDIcon -Icon Desktop)
        New-UDTab -Text 'Item Three' -Content { New-UDTypography -Text 'Item Three' -Variant 'h2' } -Icon (New-UDIcon -Icon Exclamation)
    }
    New-PSUEndpoint -Url '/endpoint' -Method 'GET' -Endpoint {
       "Hello, world!"
    } -Authentication
    New-UDIcon -Icon 'NetworkWired'
    Find-UDIcon User
    New-UDTimeline -Children {
        New-UDTimelineItem -Content {
            'Breakfast'
        } -OppositeContent {
            '7:45 AM'
        } 
        New-UDTimelineItem -Content {
            'Welcome Message'
        } -OppositeContent {
            '9:00 AM'
        }
        New-UDTimelineItem -Content {
            'State of the Shell'
        } -OppositeContent {
            '9:30 AM'
        }
        New-UDTimelineItem -Content {
            'General Session'
        } -OppositeContent {
            '11:00 AM'
        }
    }
    New-UDEditor
    New-UDPaper -Elevation 0 -Content {} 
    New-UDPaper -Elevation 1 -Content {} 
    New-UDPaper -Elevation 3 -Content {}
    Minimum

    This is the base line for a Universal server with very minimal server load. It should be used for trial or development purposes only.

    • 2 CPU

    • 4 GB

    • 250 GB

    • SQLite Database

    Recommended

    This is the base line for a Universal server running several jobs an hour, hosting APIs with fewer than 100 requests per hour and a single App. It will support up to 10 concurrent users.

    • 4 CPU

    • 16 GB

    • 500 GB

    • MS SQL\PostgreSQL Database

    Performance

    This is the base line for a Universal server running dozens of jobs an hour, hosting APIs with greater than 100 requests per hour and up to 5 Apps. It will support up to 50 concurrent users.

    • 16 CPU

    • 32 GB

    • 1 TB

    • MS SQL\PostgreSQL Database

    Distributed

    A distributed system employs multiple instances of PowerShell Universal connected to the same database. It requires either Git or managed deployments to share configuration data. We recommend this for widely used production instances. It provides the best performance, stability and redundancy.

    This configuration can support hundreds of jobs per hour, thousands of API requests and many apps. The number of concurrent users will depend on the number of PSU servers in the cluster.

    • 32 CPU

    • 64 GB

    • 1 TB

    • MS SQL\PostgreSQL Database

    Software

    Windows

    • Optional*: Windows PowerShell v5.1 or later

    • Optional*: PowerShell v7.2 or later

    • .NET Framework v4.8.0 or later (only for Windows PowerShell)

    Linux

    • Optional*: PowerShell v7.2 or later

    • Validated Distributions: Ubuntu 18.04 and 20.04

    Mac OS

    • Optional*: PowerShell v7.2 or later

    *PowerShell Universal packages a version of the PowerShell SDK. If you do not have a version of PowerShell installed, the integrated versions of PowerShell will be used.

    Network

    PowerShell Universal communicates on the port configured during installation and\or configuration.

    Web Server Front End

    • Default Port: 5000

    • Typical Configured Ports: 443, 80

    TCP Backend

    • Dynamically assigned local port on loopback

    Database

    • MS SQL: 1433

    • PostgreSQL: 5432

    Agent

    • Web Server Front End Port, default 5000

    Git

    • Standard HTTP Ports, typically 443

    Online Licensing

    Online licensing requires access to www.ironmansoftware.com on port 443. Offline licenses do not require internet access.

    Proxy

    PowerShell Universal provides proxy configuration settings in the Settings \ General page. These are used for communication with remote git, database or internet services.

    Install-Module Universal
    Install-PSUServer -AddToPath
    Start-PSUServer -Port 5000
    wget https://imsreleases.blob.core.windows.net/universal/production/2.4.0/Universal.linux-arm.2.4.0.zip
    unzip Universal.linux-arm.2.3.2.zip -d ./PSU
    chmod +x ./PSU/Universal.Server
    ./PSU/Universal.Server
    
    send anonymous telemetry data
    APIs
    Automation
    Apps
    Figure shows the final step of the first run with the license key entered
    Endpoint Documentation Dialog
    Edit Endpoint
    Swagger Documentation for APIs
    Endpoint Documentation Editor
    Set-PSUSetting
    Windows Authentication
    New-PSUEndpoint
    Get-PSUEndpoint
    Remove-PSUEndpoint
    New-PSUApiResponse
    click here
    environment
    variables
    New-PSUSchedule
    Get-PSUSchedule
    Remove-PSUSchedule
    Available in Branch
    Click here to view the complete icon list.
    New-UDIcon
    New-UDUpload

    Format

    string

    Whether to return either json or html in the OnChange script block.

    Id

    string

    The ID of this component.

    Data

    Hashtable

    The Editor.JS data for this component

    OnChange

    ScriptBlock

    The script block event handler to call when the editor data changes.

    JSON block format
    published folder
    New-UDTransferList
    New-UDTransferListItem
    New-UDCardHeader
  • New-UDCardMedia

  • New-UDCard
    New-UDCardBody
    New-UDCardExpand
    New-UDCardFooter
    New-UDPaper
    Populating Code

    Use the -Code parameter to specify code that will be populated within the code editor when it loads.

    Retrieving code from another component

    You can retrieve code from another component using the Get-UDElement cmdlet and accessing the code property of the hashtable that is returned.

    Setting code from another component

    You can set code from another component using the Set-UDElement cmdlet. Specify the code value in a hashtable passed to the -Properties parameter.

    Options

    The Monaco editor supports a wide range of options. If you wish to use options that aren't available on the New-UDCodeEditor cmdlet, you can use the -Options parameter and pass a hashtable of options instead.

    For a full list of options, check the IEditorConsturctionOptions interface.

    API

    • New-UDCodeEditor

    Microsoft Monaco
    Spacing

    Adjust the spacing between items in the grid

    Row and Columns

    You can also use the New-UDRow and New-UDColumn functions when working with the grid.

    When working with columns, you will need to specify the medium and large sizes, otherwise they will always be set to 12.

    API

    • New-UDGrid

    • New-UDRow

    • New-UDColumn

    Get-ComputerInfo
    PS C:\Users\adamr> Invoke-RestMethod http://localhost:5000/hello-world
    
    WindowsBuildLabEx                                       : 22000.1.amd64fre.co_release.210604-1628
    WindowsCurrentVersion                                   : 6.3
    WindowsEditionId                                        : Professional
    WindowsInstallationType                                 : Client
    WindowsInstallDateFromRegistry                          : 8/6/2021 4:05:12 PM
    WindowsProductId                                        : 00330-52452-93139-AAOEM
    WindowsProductName                                      : Windows 10 Pro
    WindowsRegisteredOrganization                           :
    Read-Host "What should I say?"
    
    1..100 | ForEach-Object {
        Write-Progress -PercentComplete $_ -Activity "Processing..."
    }
    
    Get-Service
    New-UDApp -Title "Hello, World!" -Content {
        New-UDButton -Text "Click Me" -OnClick {
            Show-UDToast -Message 'Success!!'
        }
    }
    http://localhost:5000/swagger/index.html
    <# 
    .SYNOPSIS
    This is an endpoint
    
    .DESCRIPTION
    This is a description
    
    .PARAMETER ID
    This is an ID.
    
    #>
    param($ID)
        
    $Id
    [Documentation()]
    class MyReturnType {
        [string]$Value
    }
    <#
      .INPUTS
      Required: false
      Description: This is an input value.
      Content:
          application/json: MyReturnType 
    #>
    param()
    <#
    .OUTPUTS
    200:
      Description: This is an output value. 
      Content:
          application/json: ADAccountType
    
    400:
      Description: Invalid input
    #>
    param()
    Invoke-RestMethod http://localhost:5000/auth -Headers @{ Authorization = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiQWRtaW4iLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9oYXNoIjoiMWUyY2IzNzAtMmMyNS00ZDU5LTk4YzgtMzc5MTFjMDAyZmI5Iiwic3ViIjoiUG93ZXJTaGVsbFVuaXZlcnNhbCIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluaXN0cmF0b3IiLCJuYmYiOjE2MDU2NjEyNTUsImV4cCI6MTYzNzM2NzI1OCwiaXNzIjoiSXJvbm1hblNvZnR3YXJlIiwiYXVkIjoiUG" }
    Invoke-RestMethod http://localhost:5000/auth -Headers @{ 
        Authorization = "Bearer msft_xyz_123" 
        'X-PSU-Authorization' = 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiQWRtaW4iLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9oYXNoIjoiMWUyY2IzNzAtMmMyNS00ZDU5LTk4YzgtMzc5MTFjMDAyZmI5Iiwic3ViIjoiUG93ZXJTaGVsbFVuaXZlcnNhbCIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluaXN0cmF0b3IiLCJuYmYiOjE2MDU2NjEyNTUsImV4cCI6MTYzNzM2NzI1OCwiaXNzIjoiSXJvbm1hblNvZnR3YXJlIiwiYXVkIjoiUG'
    }
    Invoke-RestMethod http://localhost:5000/auth -UseDefaultCredentials
    Invoke-WebRequest http://localhost:5000/api/v1/signin -Body (@{ 
        UserName = "Admin"
        Password = "Any"
    } | ConvertTo-Json) -ContentType 'application/json' -SessionVariable mySession -Method POST
     Invoke-WebRequest http://localhost:5000/auth -WebSession $mySession
    param($UserName)
    
    $UserName
    New-PSUSchedule -Script "MyScript.ps1" -Cron '* * * * *' -Parameters @{ UserName = 'adam' }
    New-PSUSchedule -Script "MyScript.ps1" -Cron '* * * * *' -Environment '7.1'
    New-PSUSchedule -Script "MyScript.ps1" -Cron '* * * * *' -Credential 'MyUser'
    New-PSUSchedule -Script "MyScript.ps1" -Cron '* * * * *' -Computer 'PSUNODE1'
    New-PSUSchedule -Script "MyScript.ps1" -Cron '* * * * *' -Condition {
      $ENV:Slot -eq 'production'
    }
    New-UDIcon -Icon 'AddressBook'
        New-UDIcon -Icon 'AddressBook' -Size 'sm'
        New-UDIcon -Icon 'AddressBook' -Size 'lg'
        New-UDIcon -Icon 'AddressBook' -Size '5x'
        New-UDIcon -Icon 'AddressBook' -Size '10x'
    New-UDIcon -Icon 'AddressBook' -Size '5x' -Rotation 90
    New-UDIcon -Icon 'AddressBook' -Size '5x' -Border
    New-UDIcon -Icon 'AddressBook' -Size '5x' -Style @{
        backgroundColor = "red"
    }
    New-UDTextbox -Id 'txtIconSearch' -Label 'Search' 
    New-UDButton -Text 'Search' -OnClick {
        Sync-UDElement -Id 'icons'
    }
    
    New-UDElement -tag 'p' -Content {}
    
    New-UDDynamic -Id 'icons' -Content {
        $IconSearch = (Get-UDElement -Id 'txtIconSearch').value
        if ($null -ne $IconSearch -and $IconSearch -ne '')
        {
            $Icons =$Icons = Find-UDIcon -Name $IconSearch
        }
    
        foreach($icon in $icons) {
            try{
                New-UDChip -Label $icon -Icon (New-UDIcon -Icon $icon)
            }
            catch{
                New-UDChip -Label "$icon Unknown" 
            }
        }
    }
    New-UDTimeline -Children {
        New-UDTimelineItem -Content {
            'Breakfast'
        } -OppositeContent {
            '7:45 AM'
        } 
        New-UDTimelineItem -Content {
            'Welcome Message'
        } -OppositeContent {
            '9:00 AM'
        }
        New-UDTimelineItem -Content {
            'State of the Shell'
        } -OppositeContent {
            '9:30 AM'
        }
        New-UDTimelineItem -Content {
            'General Session'
        } -OppositeContent {
            '11:00 AM'
        }
    } -Position alternate
    New-UDDashboard -Title 'PowerShell Universal' -Content {
            New-UDTimeline -Children {
                New-UDTimelineItem -Content {
                    'Breakfast'
                } -OppositeContent {
                    '7:45 AM'
                }  -Color 'error'
                New-UDTimelineItem -Content {
                    'Welcome Message'
                } -OppositeContent {
                    '9:00 AM'
                } -Color 'info'
                New-UDTimelineItem -Content {
                    'State of the Shell'
                } -OppositeContent {
                    '9:30 AM'
                } -Color 'success'
                New-UDTimelineItem -Content {
                    'General Session'
                } -OppositeContent {
                    '11:00 AM'
                } -Color 'grey'
            } -Position alternate
    }
    New-UDTimeline -Children {
        New-UDTimelineItem -Content {
            'Breakfast'
        } -OppositeContent {
            '7:45 AM'
        }  -Icon (New-UDIcon -Icon Microsoft)
        New-UDTimelineItem -Content {
            'Welcome Message'
        } -OppositeContent {
            '9:00 AM'
        } -Icon (New-UDIcon -Icon Apple)
        New-UDTimelineItem -Content {
            'State of the Shell'
        } -OppositeContent {
            '9:30 AM'
        } -Icon (New-UDIcon -Icon NetworkWired)
        New-UDTimelineItem -Content {
            'General Session'
        } -OppositeContent {
            '11:00 AM'
        } -Icon (New-UDIcon -Icon User)
    } -Position alternate
    New-UDUpload -OnUpload {
        Show-UDToast $Body
    } -Text 'Upload'
    {
      data: "base64 encoded string of data",
      name: "file name of the file uploaded",
      type: "file type as determined by the browser"
    }
    public class Upload
    {
        public string Name { get; set; }
        public string FileName { get; set; }
        public DateTime TimeStamp { get; set; }
        public string ContentType { get; set; }
        public string Type => ContentType;
    }
    New-UDForm -Content {
        New-UDUpload -Id 'myFile' -Text 'Upload File'
    } -OnSubmit {
        Show-UDToast $Body 
    }
    New-UDUpload -Text 'Upload Image' -OnUpload {
        $Data = $Body | ConvertFrom-Json 
        $bytes = [System.Convert]::FromBase64String($Data.Data)
        [System.IO.File]::WriteAllBytes("$env:temp\$($Data.Name)", $bytes)
    }
    New-UDEditor -OnChange {
        Show-UDToast $EventData
    }
    New-UDEditor -OnChange {
        Show-UDToast $EventData
    } -Format 'html'
    New-UDEditor -Data $Data
    New-UDEditor -PublishedFolder 'MyImages'
    New-UDTransferList -Item {
        New-UDTransferListItem -Name 'test1' -Value 1
        New-UDTransferListItem -Name 'test2' -Value 2
        New-UDTransferListItem -Name 'test3' -Value 3
        New-UDTransferListItem -Name 'test4' -Value 4
        New-UDTransferListItem -Name 'test5' -Value 5
    } -OnChange {
        Show-UDToast ($EventData | ConvertTo-Json)
    }
    New-UDForm -Content {
        New-UDTransferList -Item {
            New-UDTransferListItem -Name 'test1' -Value 1
            New-UDTransferListItem -Name 'test2' -Value 2
            New-UDTransferListItem -Name 'test3' -Value 3
            New-UDTransferListItem -Name 'test4' -Value 4
            New-UDTransferListItem -Name 'test5' -Value 5
        }
    } -OnSubmit {
        Show-UDToast ($EventData | ConvertTo-Json)
    }
    $Header = New-UDCardHeader -Avatar (New-UDAvatar -Content { "R" } -Sx @{ backgroundColor = "#f44336" }) -Action (New-UDIconButton -Icon (New-UDIcon -Icon 'EllipsisVertical')) -Title 'Shrimp and Chorizo Paella' -SubHeader 'September 14, 2016';
    $Media = New-UDCardMedia -Image 'https://mui.com/static/images/cards/paella.jpg'
    $Body = New-UDCardBody -Content {
        New-UDTypography -Text ' This impressive paella is a perfect party dish and a fun meal to cook together with your guests. Add 1 cup of frozen peas along with the mussels, if you like.' -Sx @{
            color = 'text.secondary'
        } -Variant body2
    }
    $Footer = New-UDCardFooter -Content {
        New-UDIconButton -Icon (New-UDIcon -Icon 'Heart')
        New-UDIconButton -Icon (New-UDIcon -Icon 'ShareAlt')
    }
    $Expand = New-UDCardExpand -Content {
        $Description = @"
        Heat oil in a (14- to 16-inch) paella pan or a large, deep skillet over
        medium-high heat. Add chicken, shrimp and chorizo, and cook, stirring
        occasionally until lightly browned, 6 to 8 minutes. Transfer shrimp to a
        large plate and set aside, leaving chicken and chorizo in the pan. Add
        pimentón, bay leaves, garlic, tomatoes, onion, salt and pepper, and cook,
        stirring often until thickened and fragrant, about 10 minutes. Add
        saffron broth and remaining 4 1/2 cups chicken broth; bring to a boil.
        New-UDTypography -Text $Description
    }
    New-UDCard -Header $Header -Media $Media -Body $Body -Footer $Footer -Expand $Expand -Sx @{
        maxWidth = 345
        border   = '2px solid #f0f2f5'
    }
    $m=@'
    # Hello
    
    ## world
    - a
    - b
    -c
    '@
    
    New-UDPaper -Elevation 7 -Children {
       New-UDMarkdown -markdown $m
    } -Style @{
       display = 'block'
    }
    New-UDPaper -Square -Content {}
    New-UDPaper  -Content { } -Style @{ 
         backgroundColor = 'red'
    }
    New-UDCodeEditor -Height '500' -Language 'powershell'
    New-UDCodeEditor -Height '500' -Language 'powershell' -Code '#Hello, world!'
    New-UDCodeEditor -Height '500' -Language 'powershell' -Code '#Hello, world!' -Id 'editor'
    
    New-UDButton -Text 'Get Code' -OnClick {
        Show-UDToast -Message (Get-UDElement -id 'editor').Code
    }
    New-UDCodeEditor -Height '500' -Language 'powershell' -Code '#Hello, world!' -Id 'editor'
    
    New-UDButton -Text 'Get Code' -OnClick {
        Set-UDElement -Id 'editor' -Properties @{
            code = "# Hello!"
        }
    }
    New-UDCodeEditor -Language powershell -Height 100 -Options @{ fontSize = 10 }
    New-UDGrid -Container -Content {
        New-UDGrid -Item -ExtraSmallSize 12 -Content {
            New-UDPaper -Content { "xs-12" } -Elevation 2
        }
        New-UDGrid -Item -ExtraSmallSize 6 -Content {
            New-UDPaper -Content { "xs-6" } -Elevation 2
        }
        New-UDGrid -Item -ExtraSmallSize 6 -Content {
            New-UDPaper -Content { "xs-6" } -Elevation 2
        }
        New-UDGrid -Item -ExtraSmallSize 3 -Content {
            New-UDPaper -Content { "xs-3" } -Elevation 2
        }
        New-UDGrid -Item -ExtraSmallSize 3 -Content {
            New-UDPaper -Content { "xs-3" } -Elevation 2
        }
        New-UDGrid -Item -ExtraSmallSize 3 -Content {
            New-UDPaper -Content { "xs-3" } -Elevation 2
        }
        New-UDGrid -Item -ExtraSmallSize 3 -Content {
            New-UDPaper -Content { "xs-3" } -Elevation 2
        }
    }
    New-UDDynamic -Id 'spacingGrid' -Content {
        $Spacing = (Get-UDElement -Id 'spacingSelect').value
    
        New-UDGrid -Spacing $Spacing -Container -Content {
            New-UDGrid -Item -ExtraSmallSize 3 -Content {
                New-UDPaper -Content { "xs-3" } -Elevation 2
            }
            New-UDGrid -Item -ExtraSmallSize 3 -Content {
                New-UDPaper -Content { "xs-3" } -Elevation 2
            }
            New-UDGrid -Item -ExtraSmallSize 3 -Content {
                New-UDPaper -Content { "xs-3" } -Elevation 2
            }
            New-UDGrid -Item -ExtraSmallSize 3 -Content {
                New-UDPaper -Content { "xs-3" } -Elevation 2
            }
        }
    }
    
    New-UDSelect -Id 'spacingSelect' -Label Spacing -Option {
        for($i = 0; $i -lt 10; $i++)
        {
            New-UDSelectOption -Name $i -Value $i
        }
    } -OnChange { Sync-UDElement -Id 'spacingGrid' } -DefaultValue 3
    New-UDRow -Columns {
        New-UDColumn -SmallSize 12 -Content {
            New-UDPaper -Content { "xs-12" } -Elevation 2
        }
        New-UDColumn -SmallSize 12 -Content {
            New-UDPaper -Content { "xs-12" } -Elevation 2
        }
    }
    New-UDRow -Columns {
        New-UDColumn -SmallSize 12 -MediumSize 12 -LargeSize 12 -Content {
            New-UDPaper -Content { "xs-12" } -Elevation 2
        }
        New-UDColumn -SmallSize 12 -MediumSize 12 -LargeSize 12 -Content {
            New-UDPaper -Content { "xs-12" } -Elevation 2
        }
    }
    The configuration data files are stored in
    %ProgramData%\UniversalAutomation\Repository
    by default. These will consist of features such as APIs, Scripts and Apps. The entire directory is necessary for the configuration of the target system to function.

    You can either copy the folder manually or via PowerShell. Ensure that you include all subdirectories.

    Database

    The database migration will depend on the type of database used.

    SQLite

    You will need to copy the SQLite database file to the configured, or default, database location. On a default installation, this will be %ProgramData%\UniversalAutomation\database.db. The target machine account or service account will need read and write access to this database file.

    If you are restoring from backup, you may need to Downgrade the schema if you upgraded the version.

    SQL and PostgreSQL

    Because these databases are stored outside of the PowerShell Universal server, you do not need to perform a migration of the database itself. You will need to ensure that the target server has network access to the SQL host.

    If you are restoring from backup, you may need to Downgrade the schema if you upgraded the version.

    Application Settings

    The PowerShell Universal appsettings.json file is necessary for providing the appropriate server settings to the platform. By default, this is stored in %ProgramData%\PowerShellUniversal\appsettings.json. You will need to copy this to the new server in the same location.

    This file contains configuration settings such as HTTP certificate, authentication, git sync settings, API configuration options and more.

    appsettings.json files do not change between upgrades and you likely not need to perform this action during a restore.

    Secret Vaults

    PowerShell Universal includes 3 built-in secret vaults that may need to be migrated. The database vault is included with the database migration and does not require extra steps. If you are performing a restore, it's unlikely you will need to perform these restore operations unless the secret vaults have become corrupted.

    PSUSecretStore

    The PSUSecretStore vault uses the Microsoft's SecretStore module. This module stores secrets, on disk, using symmetric encryption. A default encryption key is included with PowerShell Universal installations. If the key was updated, the new key will be in the appsettings.json file you migrated in the previous step. You will also need to move the physical secret store to the new server's file system.

    The SecretStore module uses a user-specific storage location to ensure that ACLs are enforced on the files themselves. You will need to ensure that you copy the vault's contents to the account of the user that will be running PowerShell Universal on the new system.

    BuiltInLocalVault

    The BuiltInLocalVault is only available on Windows and uses Credential Manager to store secrets. You will need to recreate these secrets in the Credential Manager store on the new system.

    Within Credential Manager, you will find PowerShell secrets stored with a ps: prefix.

    While it's not possible to extract credentials directly from the Credential Manager UI, you can use the Secret Management modules directly. To retrieve secrets, you can do the following.

    On the new server, you can do the reverse and call Set-Secret. Note that these commands need to run as the service account running PowerShell Universal in order to store them properly in the Credential Manager account for the user.

    Authentication

    Certain authentication types will require configuration outside of PowerShell Universal. Unless you are moving the machine running PowerShell Universal or changing the accessible URLs, you will not need to perform these actions.

    OpenID Connect

    Ensure that the proper sign-on URLs are configured in your Identity provider (e.g. Azure AD or Okta) if the host name of the server is changing. Without properly configured sign-on URLs, users will not be able to sign on the new system.

    Windows

    Windows authentication requires the setup of an SPN for the service account running the PowerShell Universal service. Ensure this SPN is in place before attempting to use Windows authentication with the new system.

    Other Resources and Considerations

    There may be other resources that PowerShell Universal uses on the system that should be taken into account when migrating or restoring servers. Typically, you will not need to worry about these resources during a restore as they should remain the same if the machine has not changed.

    • PowerShell Modules

    • Environment Variables

    • Local Account Privileges

    • File System Permissions

    • Proxy Configuration

    • Certificates

    • Git SSH keys or credentials

    • DNS Settings

    Application Files

    Once all the following steps have been taken, you can now install PowerShell Universal on the new server. If you are downgrading during a restore, please follow the Downgrade documentation.

    MSI \ Kestrel

    The MSI package for PowerShell Universal installs the platform as a Windows service that hosts its own web server called Kestrel. To install the service, download the MSI and run it. We recommend using the exact same version as the source server.

    During the MSI install, leave all settings as default. We recommend leaving the service account blank and unchecking the box that states to start the PowerShell Universal service after the installation is complete.

    After the installation completes, the service will be created but not running. Open Service Control Manager (services.msc) and set the service account for the PowerShell Universal service. Start the service.

    Debugging Issues

    When migrating a PowerShell Universal service, you may run into issues the arise from configuration differences between the two systems. The following are places to look for more information.

    Event Viewer

    if the service starts and stops, there may be an issue with the database access. We recommend looking in the Application log within Event Viewer. PowerShell Universal will report two application Errors that will include .NET in the name. The second of the two errors will provide a human readable exception with more details.

    System Logs

    PowerShell Universal will write system logs to the %ProgramData%\PowerShellUniversal directory. Search for strings starting with [ERR] to gather more information about issues with the installation.

    Notifications

    After migrating the service, check for any error notifications that may indicate misconfiguration of the system.

    What if I have multiple containers?

    The license applies to each container instance and not the container host. For example, if you have 10 container instances running, you will need 10 licenses.

    What if I have multiple sites on a single IIS server?

    Each website running PowerShell Universal will need a license and not a single license for the entire IIS server.

    Install a License

    To install a license, click Settings \ License. Click the Add License button to upload your license file. You can also install licenses using the Set-PSULicense cmdlet. Offline licenses do not require an internet connection but will need to be reinstalled when the subscription expires, in you wish to update the version of PowerShell Universal. Online licenses require an internet connection and access to https://ironmansoftware.com in order to verify subscription status.

    You can use the PSULICENSE environment variable to set a license. The value of this environment variable needs to be the contents of the license file.

    Proxy configuration can be done by clicking Settings \ General and configuring the proxy URI and, optionally, credentials. You can also configure proxy settings with the Set-PSUSetting cmdlet.

    Account-Based Licensing

    When using account-based licensing, you will enter your account's license key. Whenever you activate a PowerShell Universal server, it will assign a license to computer. This license key does not change so there is no need to install a new license when renewing. You can view the assigned computers and account license key in your Ironman Software account.

    The PowerShell Universal server needs to have access to ironmansoftware.com.

    Offline Licenses

    Offline license files are required for environments that do not have internet access. You will need to install a new license file when you plan to upgrade to a version past the expiration date of the license.

    Online Licenses

    Online licenses work the same as offline but check the subscription status on ironmansoftware.com. The license is tied to a specific subscription and may require a change after renewal. We recommend account-based licensing over online licenses.

    Developer Licenses

    When a server license is purchased, you will be able to generate developer licenses for users building solutions for your team. Their intent is to be used by individual developers in their local environments. Do not use developer licenses when hosting a server for remote access for testing or production. Instances of PowerShell Universal running with a Developer License will display a water mark in the admin console and any apps stating they are intended only for development purposes.

    You can generate a developer license on the Settings \ License page by clicking the Generate Developer License button.

    Generate Developer License

    Licensed Features

    The following features of PowerShell Universal require a license.

    • Debugging Tools

    • Enterprise Authentication

      • OpenID Connect

      • SAML2

      • WS-Federation

      • Windows Authentication

      • Custom Authentication Scripts

      • Client Certificate

      • App Tokens

    • Enterprise Authorization

      • Permissions

      • Custom Authorization Scripts

    • Platform

      • Git Support

      • Module Management

      • Non-Database Credential Vaults

    • Settings

      • Branding

      • Tags

    • APIs

      • Event Hubs

      • OpenAPI Documentation

    • Automation

      • Triggers

      • Terminals

      • Tests

    • Apps

      • App Page Editor

      • App Function Editor

    our website
    Full Screen

    Full Width

    Full width modals take up the full width as defined by the -MaxWidth parameter.

    Persistent

    Persistent modals do not close when you click off of them. You will have to close it with Hide-UDModal.

    Hide a Modal

    You can use the Hide-UDModal button to hide a modal that is currently show.

    When opening multiple modals, you can also use the -All parameter of Hide-UDModal to hide all of them instead of one at a time.

    Styling

    You can style modules using the -Style, -HeaderStyle, -ContentStyle and -FooterStyle parameters. Style is applied to the entire modal itself and the individual section styles are only applied to those sections. The value for these parameters are hashtables of CSS values.

    API

    • Show-UDModal

    • Hide-UDModal

    Grouped Select

    Create a select with groups of selections.

    OnChange

    Execute a PowerShell event handler when the value of the select is changed. $EventData[0] for the single item that was selected.

    Multiple Select

    Execute a PowerShell event handler when the more than one value of the select is changed. $EventData is an array of the selected items.

    Get-UDElement

    Retrieve the value of the select from another component.

    API

    • New-UDSelect

    • New-UDSelectOption

    • New-UDSelectGroup


    Collapse Transition

    Fade

    A fade transition fades a component in and out as seen in the previous example. You can configure the timeout value to specify the number of seconds it takes to complete the transition.

    Slide

    The slide transition moves a component into position. You can determine the position of the slide by specifying the -SlideDirection parameter.

    Slide Transition

    Grow

    The grow transition will fade and grow a component into place.

    Grow Transition

    Zoom

    The zoom transition will zoom a component into place.

    Zoom Transition

    API

    • New-UDTransition

    Transition a card

    Textbox

    Textbox component for Universal Apps

    A textbox lets users enter and edit text.

    Textbox

    Textbox Types

    Textboxes can be switched to accept specific types, such as passwords, numbers, or emails.

    Password Type

    A password textbox will mask the input.

    Number Type

    Only accepts numbers. Some browsers will include up and down arrows to increase and decrease the current value.

    Multiline

    You can create a multiline textbox by using the -Multiline parameter. Pressing enter will add a new line. You can define the number of rows and the max number of rows using -Rows and -RowsMax.

    Interaction

    Retrieving a textbox value

    You can use Get-UDElement to get the value of a textbox

    Setting the textbox value

    Icons

    You can set the icon of a textbox by using the -Icon parameter and the New-UDIcon cmdlet.

    Masking

    You can use the -MaskPattern to define a mask for the textbox. The following is an example of a textbox mask.

    The definition can contain

    • 0 - any digit

    • a - any letter

    • * - any char

    If definition character should be treated as fixed it should be escaped by \\ (E.g. \\0).

    The masking for the textbox is controlled by .

    OnEnter

    The -OnEnter event handler is executed when the user presses enter in the text field. It is useful for performing other actions, like clicking a button, on enter.

    OnBlur

    The -OnBlur event handler is executed when the textbox loses focus.

    OnValidate

    Use the -OnValidate event handler to validate input typed in the textbox.

    API

    Error Handling

    Error handling for Universal API.

    By default, endpoints will return a 200 OK message even if there are errors. If an error occurs, you will get a blank response from the endpoint. This document demonstrates different ways to handle errors within APIs.

    Automatically Returning Errors

    To automatically return errors from APIs, you can change the default behavior by setting the -ErrorAction parameter of New-PSUEndpoint to Stop. Any errors will cause an 500 Internal Server Error to be returned with a list of the errors and stack trace.

    Terminating errors will always return a 500 Internal Server Error.

    You will notice different behavior in Windows PowerShell and PowerShell 7 when calling REST APIs that return errors. In Windows PowerShell, you will receive a generic error that doesn't return the error message.

    In PowerShell 7, when an error is returned, you will see the error message returned.

    You can retrieve the error message in Windows PowerShell, by using the following syntax.

    Manually Returning Errors

    To manually return errors, you need to use the New-PSUApiResponse cmdlet. This cmdlet allows you to define the status code and body for the response.

    In this example, we are returning a 404 error code from the endpoint.

    Similar to the automatic error codes, error codes returned manually will as display better in PowerShell 7. Here's an example of calling the endpoint.

    If called from Windows PowerShell, you will receive an error similar to the one returned automatically.

    You can choose to return error codes if certain conditions are met by using your PowerShell script within the endpoint.

    API

    Stop-Service 'PowerShellUniversal'
    Copy-Item $ENV:ProgramData\UniversalAutomation\Repository \\newServer\C$\ProgramData\UniversalAutomation\Repository -Recurse
    Copy-Item $ENV:ProgramData\UniversalAutomation\database.db \\newServer\C$\ProgramData\UniversalAutomation\database.db 
    Copy-Item $ENV:ProgramData\PowerShellUniversal\appsettings.json \\newServer\C$\ProgramData\PowerShellUniversal\appsettings.json 
    Copy-Item $Env:LOCALAPPDATA\Microsoft\PowerShell\secretmanagement\localstore \\newServer\C$\Users\myServiceAccount\AppData\Local\Microsoft\PowerShell\secretmanagement\localstore 
    Install-Module Micorosoft.PowerShell.SecretManagement
    Install-Module SecretManagement.JustinGrote.CredMan
    Register-SecretVault -Name 'BuiltInLocalVault' -ModuleName SecretManagement.JustinGrote.CredMan
    Get-SecretInfo -Vault BuiltInLocalVault
    $Secret = Get-Secret -Name 'TestApiKey' -Vault 'BuiltInLocalVault' -AsPlainText
    New-UDButton -Text 'Basic' -OnClick {
        Show-UDModal -Content {
            New-UDTypography -Text "Hello"
        }
    }
    New-UDButton -Text 'Full Screen' -OnClick {
        Show-UDModal -Content {
            New-UDTypography -Text "Hello"
        } -Footer {
            New-UDButton -Text "Close" -OnClick { Hide-UDModal }
        }  -FullScreen
    }
    New-UDButton -Text 'Full Width' -OnClick {
        Show-UDModal -Content {
            New-UDTypography -Text "Hello"
        } -FullWidth -MaxWidth 'md'
    }
    New-UDButton -Text 'Persistent' -OnClick {
        Show-UDModal -Content {
            New-UDTypography -Text "Hello"
        } -Footer {
            New-UDButton -Text "Close" -OnClick { Hide-UDModal }
        } -Persistent
    }
    New-UDButton -Text 'Basic' -OnClick {
        Show-UDModal -Content {
            New-UDTypography -Text "Hello"
        }
        Start-Sleep 5
        Hide-UDModal
    }
    New-UDButton -Text 'Parent' -OnClick {
        Show-UDModal -Content {
           New-UDButton -Text 'Child' -OnClick {
              Show-UDModal -Content {
                  New-UDButton -Text 'hide current' -OnClick {
                      Hide-UDModal
                  }
                  New-UDButton -Text 'hide all' -OnClick {
                      Hide-UDModal -All
                  }
              } 
           }
        }
    }
    New-UDButton -Text 'Styling' -OnClick {
        Show-UDModal -Content {
            New-UDTypography -Text "Hello"
        } -Style @{
            backgroundColor = "red"
        }
    }
    New-UDSelect -Option {
        New-UDSelectOption -Name 'One' -Value 1
        New-UDSelectOption -Name 'Two' -Value 2
        New-UDSelectOption -Name 'Three' -Value 3
    }
    New-UDSelect -Option {
        New-UDSelectGroup -Name 'Group One' -Option {
            New-UDSelectOption -Name 'One' -Value 1
            New-UDSelectOption -Name 'Two' -Value 2
            New-UDSelectOption -Name 'Three' -Value 3
        }
        New-UDSelectGroup -Name 'Group Two' -Option {
            New-UDSelectOption -Name 'Four' -Value 4
            New-UDSelectOption -Name 'Five' -Value 5
            New-UDSelectOption -Name 'Size' -Value 6
        }
    }
    New-UDSelect -Option {
        New-UDSelectOption -Name 'One' -Value 1
        New-UDSelectOption -Name 'Two' -Value 2
        New-UDSelectOption -Name 'Three' -Value 3
    } -OnChange { Show-UDToast -Message $EventData[0] }
    New-UDSelect -Multiple -Option {
        New-UDSelectOption -Name 'One' -Value 1
        New-UDSelectOption -Name 'Two' -Value 2
        New-UDSelectOption -Name 'Three' -Value 3
    } -OnChange { 
        Show-UDToast -Message (ConvertTo-json -InputObject $EventData) 
    }
      New-UDSelect -Option {
          New-UDSelectOption -Name 'One' -Value 1
          New-UDSelectOption -Name 'Two' -Value 2
          New-UDSelectOption -Name 'Three' -Value 3
      } -Id 'select' -DefaultValue 2
    
      New-UDButton  -Text 'OnBoard' -OnClick {
        $Element = Get-UDElement -Id 'select'
        if ($Element.Value)
        {
          Show-UDToast -Message $Element.Value
        }
        else 
        {
          Show-UDToast -Message $Element.DefaultValue
        }
      }
    New-UDTransition -Id 'test' -Content {
        New-UDCard -Text "Hey"
    } -In -Fade -Timeout 1000
    
    New-UDSwitch -OnChange {
        Set-UDElement -Id 'test' -Properties @{
            in = $EventData -eq 'True'
        }
    } -Checked $true
    New-UDTransition -Id 'test' -Content {
        New-UDCard -Text "Hey"
    } -In -Collapse -CollapseHeight 100 -Timeout 1000
    
    New-UDSwitch -OnChange {
        Set-UDElement -Id 'test' -Properties @{
            in = $EventData -eq 'True'
        }
    } -Checked $true
    New-UDTransition -Id 'test' -Content {
        New-UDCard -Text "Hey"
    } -In -Fade -Timeout 1000
    
    New-UDSwitch -OnChange {
        Set-UDElement -Id 'test' -Properties @{
            in = $EventData -eq 'True'
        }
    } -Checked $true
    New-UDTransition -Id 'test' -Content {
        New-UDCard -Text "Hey"
    } -In -Slide -SlideDirection 'left' -Timeout 1000
    
    New-UDSwitch -OnChange {
        Set-UDElement -Id 'test' -Properties @{
            in = $EventData -eq 'True'
        }
    } -Checked $true
    New-UDTransition -Id 'test' -Content {
        New-UDCard -Text "Hey"
    } -In -Grow -Timeout 1000
    
    New-UDSwitch -OnChange {
        Set-UDElement -Id 'test' -Properties @{
            in = $EventData -eq 'True'
        }
    } -Checked $true
    New-UDTransition -Id 'test' -Content {
        New-UDCard -Text "Hey"
    } -In -Zoom -Timeout 1000
    
    New-UDSwitch -OnChange {
        Set-UDElement -Id 'test' -Properties @{
            in = $EventData -eq 'True'
        }
    } -Checked $true
    New-UDTextbox -Label 'Standard' -Placeholder 'Textbox'
    New-UDTextbox -Label 'Disabled' -Placeholder 'Textbox' -Disabled
    New-UDTextbox -Label 'Textbox' -Value 'With value'
    SQL Support
  • PostgreSQL Support

  • Published Folders

  • Cache Management

  • Computer Groups

  • Translations

  • other chars which are not in custom definitions supposed to be fixed
  • [] - make input optional

  • {} - include fixed part in unmasked value

  • ` - prevent symbols shift back

  • imaskjs
    New-UDTextbox
    Set-PSUSetting
    New-PSUEndpoint
    Get-PSUEndpoint
    Remove-PSUEndpoint
    New-PSUApiResponse
    New-UDTextbox -Label 'Password' -Type password
    New-UDTextbox -Label 'Number' -Type number -Minimum 10 -Maximum 10000 -Value 1234
    New-UDTextbox -Multiline -Rows 4 -RowsMax 10
    New-UDTextbox -Id 'txtExample' 
    New-UDButton -OnClick {
        $Value = (Get-UDElement -Id 'txtExample').value 
        Show-UDToast -Message $Value
    } -Text "Get textbox value"
    New-UDTextbox -Id 'txtExample' -Label 'Label' -Value 'Value'
    
    New-UDButton -OnClick {
    
        Set-UDElement -Id 'txtExample' -Properties @{
            Value = "test123"
        }
    
    } -Text "Get textbox value"
    New-UDTextbox -Id "ServerGroups" -Icon (New-UDIcon -Icon 'server') -Value "This is my server"
    New-UDTextbox -Id 'textbox16' -MaskPattern '+7 (000) 000-00-00'
    New-UDTextbox -OnEnter {
        Invoke-UDEndpoint -Id 'submit' -Session
    }
    
    New-UDButton -Id 'submit' -OnClick {
        Show-UDToast -Message 'From Textbox'
    }
    New-UDTextbox -OnBlur {
        Show-UDToast "Blurred"
    }
    New-UDTextbox -OnValidate {
        if ($EventData.Length -lt 10)
        {
            New-UDValidationResult -ValidationError 'String needs to be longer than 10'
        }
    }
    New-PSUEndpoint -Url "/error" -Endpoint { 
       throw "Uh oh!"
    } -ErrorAction stop
    
    New-PSUEndpoint -Url /error2 -Endpoint {
        Write-Error "Whoa!"
    } -ErrorAction Stop
    PS C:\Users\adamr> invoke-restmethod http://localhost:5000/error2
    invoke-restmethod : The remote server returned an error: (500) Internal Server Error.
    At line:1 char:1
    + invoke-restmethod http://localhost:5000/error2
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], Web
       Exception
        + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
    PS C:\Users\adamr\Desktop> invoke-restmethod http://localhost:5000/error 
    
    Invoke-RestMethod: Uh oh!
    at , : line 2
    at , : line 1
    
    PS C:\Users\adamr\Desktop> invoke-restmethod http://localhost:5000/error2
    
    Invoke-RestMethod: Whoa
    at , : line 2
    at , : line 1
    PS C:\Users\adamr> try { invoke-restmethod http://localhost:5000/error2 } catch { [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream()).ReadToEnd()}
    Whoa!
    at <ScriptBlock>, <No file>: line 2
    at <ScriptBlock>, <No file>: line 1
    New-PSUEndpoint -Url /broken -Endpoint {
        New-PSUApiResponse -StatusCode 404 -Body 'Failed!'
    }
    PS C:\Users\adamr\Desktop> invoke-restmethod http://localhost:5000/broken
    
    Invoke-RestMethod: Failed!
    PS C:\Users\adamr> invoke-restmethod http://localhost:5000/broken
    invoke-restmethod : The remote server returned an error: (404) Not Found.
    At line:1 char:1
    + invoke-restmethod http://localhost:5000/broken
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], Web
       Exception
        + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
    New-PSUEndpoint -Url /user/:name -Endpoint {
        if ($Name -eq 'User')
        {
            @{ UserName = "Adam" }
        }
        else
        {
            New-PSUApiResponse -StatusCode 404 -Body 'Unknown user!'    
        }
    
    }

    Apps

    Apps are the root component for your web page.

    Create An App

    The first step is to create an app in PowerShell Universal. This is the container for all your pages and components for your app website. We recommend running apps in external environments, like PowerShell 7, to ensure they are isolated from the rest of the server. To create an app, click Apps \ Apps and then Create New App.

    You will need to provide a unique name and URL for the app when creating it. All other properties are optional. After creating the app, you can edit the app code. For example, try adding a component to your app.

    Once you've saved your app, you can click the globe icon to view the result. Clicking the button will display a toast message in your browser.

    New-UDApp

    The top-level cmdlet for dashboards is New-UDApp. You need to call it when returning an app. You can use it with or without pages.

    Content

    The content of the app is a series of components to display on the page. It's a script block that will return all the components in the order they will be rendered on the page. You can use the Grid component to layout items and display things like text with typography.

    Header Customization

    You can customize the header of the app using several parameters.

    Position

    Use the -HeaderPosition parameter to adjust the behavior of the header.

    • absolute\fixed - Remains at the top of the page, even when scrolling

    • relative - Remains at the top of the page. Not visible when scrolling.

    Colors

    You can adjust the colors of the header by specifying the -HeaderColor and -HeaderBackgroundColor parameters. These colors will override the theme colors.

    Content

    You can define custom content to include in the header by using the -HeaderContent parameter.

    Disable Theme Toggle

    You can remove the theme toggle with -DisableThemeToggle.

    Logo

    You can change the logo image by providing a URL to the -Logo parameter.

    Navigation

    To change the navigation layout, use the -Navigation and -NavigationLayout parameters.

    Components

    Components are the individual widgets that you can place on you app. There are components for displaying data, taking user input, adding text and images and more. Components can be downloaded as PowerShell modules and added to your app.

    Components are be caused using the standard verb-name syntax for any PowerShell cmdlet.

    Learn more about .

    Pages

    You can specify multiple pages within an app. Each page defines a route. As for v3, all pages are dynamic. PowerShell will execute on each page load to render the new page. Since UD is a single page application, the web browser does not need to refresh the entire web page when navigating between the different app pages.

    Learn more about .

    Module

    Apps will automatically have access to any commands available within the PSModulePath as well as modules you load directly into the app itself. That said, you can also define functions within the app itself. These functions will be included with a module that is stored alongside your app code. Any functions defined within this file will be automatically included with your app.

    Within the PowerShell Universal admin console, define functions, variables and aliases in the Module tab. Any functions defined with be written to a PSM1 file in the same directory as the application code.

    Built-in Variables

    Built-in variables can be found on the .

    Debugging

    You can also use the with apps.

    When building an app, you will likely run into issues with cmdlet calls or syntax. Apps will auto reload as you make changes to the app files. If an app fails to start, you can navigate to the admin page, click Apps and click the Info button next to your app.

    The Log tab will show all the logging coming from the PowerShell execution from within in your app. This should allow you to easily see errors and warnings coming from your app.

    You can use Write-Debug to add additional log messages to your app. To enable debug logging, you will have to set the $DebugPreference variable at the top of your app script.

    Menu

    You can customize the appmenu by using the -Menu parameter.

    Starting and Stopping Apps

    Similar to jobs, apps can run in separate PowerShell processes. You can start and stop an app process by clicking the Start or Stop button from the Apps page.

    Persistent Runspaces

    Persistent runspaces allow you to maintain runspace state within your app event handlers. This is important for users that perform some sort of initialization within their endpoints that they do not want to execute on subsequent calls.

    By default, runspaces will be reset after each execution. This will cause variables, modules and functions defined during the execution of an endpoint.

    To enable persistent runspaces, you will need to configure an for your API. Set the -PersistentRunspace parameter to enable this feature. This is configured in the environments.ps1 script.

    You will need to ensure that the environment is used by the app.

    Automatically Granting App Tokens

    You can automatically grant app tokens to users that visit apps. This is useful if you want to invoke the management API for PowerShell Universal from within an app. Your app will need to have authentication enabled and you will have to use the -GrantAppToken switch parameter on New-PSUDashboard.

    From within your app, you can now invoke the management API without having to worry about app token management. The API will be invoked in the context of the user that is visiting the app.

    Disable Error Toasts

    By default, apps will display a toast message when an error is generated within an endpoint script. To avoid this behavior, you can use the -DisableErrorToast parameter of New-UDApp

    Disable Startup Logging

    When starting an app, information about the variables and modules is displayed within the app log. If you wish to suppress this information, you can use the -DisableStartupLogging parameter.

    Critical Apps

    Critical apps, set based on the -Critical parameter, define apps that will cause the load balancer status endpoint to return a 500 response code if the app is not running. The load balancer status endpoint is /api/v1/status .

    Triggers

    Trigger scripts when events happen with PowerShell Universal.

    Triggers require a license.

    Triggers allow for automation jobs to be started when certain events happen within PowerShell Universal. For example, this allows you to take action when jobs complete, the server starts or dashboards stop. Triggers are useful for assigning global error handling or sending notifications when certain things happen.

    Triggered jobs will not cause additional triggers to start. Triggers are stored in the triggers.ps1.

    Trigger Events

    The following types of events can be assigned a trigger.

    • Job Started

    • Job Completed

    • Job Requesting Feedback

    • Job Failed

    New User Login

    The user login event takes place when a user accesses PowerShell Universal. The script will receive a $Userparameter with user information.

    User Login

    The user login event takes place when a user accesses PowerShell Universal. The script will receive a $data parameter with user information. The data structure is shown below.

    Use of a Revoked App Token

    The app token event takes place when a revoked app token is used. The script will receive a $data parameter that contains the contents of the app token as a string.

    Git Sync

    This trigger occurs when a git sync is run. This trigger will fire for both successful and unsuccessful git syncs.

    You will receive the following object in the $data parameter.

    Computer Offline

    The computer offline trigger will provide the computer object to the $Data parameter.

    Global Triggers

    Global triggers will start the assigned script whenever the event type is invoked.

    For example, the Script.ps1 will be run whenever any job is run.

    Resource Triggers

    Resource triggers will start the assigned script when the event takes place on the selected resource.

    For example, the Script.ps1 will be run whenever the Dashboard is stopped.

    Event Metadata

    Whenever a job is started from a trigger, it will be provided with metadata about object that caused the event to trigger.

    Triggers related to jobs will be provided a $Job parameter.

    Triggers related to dashboards will be provided a $Dashboard parameter.

    Triggers related to the server status will not receive a parameter.

    Conditions

    Using the -Condition parameter of New-PSUTrigger, you can determine whether or not a trigger should be run based on local conditions on the server. Return $true or $false from the condition.

    For example, you can disable a trigger if the Environment environment variable is not set to production.

    API

    Components

    Components are the building blocks of a PowerShell Universal app.

    A Universal app is composed of components. When building an app, you can simply call the PowerShell cmdlets within your app script to create a new component.

    Component Types

    There are over 50 components that you can use in your apps. Some of the commonly used components include:

    • Data Display

    • Data Visualization

    • Feedback

    • Inputs

    • Navigation

    • Layout

    • Utilities

    • Surfaces

    Event Handlers

    Many components provide event handlers for providing interactive functionality. This can be events like clicks, validation, rendering and more. The interactivity of an event handler is implemented using PowerShell script blocks.

    For example, the below example creates a button that can be clicked that will then show a toast.

    Within event handlers, you can run any valid PowerShell script. This means you can interact with cmdlets outside of PSU module such as ActiveDirectory or Microsoft.Graph.

    Event Data

    Each event handler may provide it's own event data. Components like tables, forms and textboxes return corresponding data about the state of the component. This could be the current data for a row in the table, the values of a form, or the text of a textbox.

    You can access the event data using the $EventData variable. This variable will be different based on the type of component you are using. For example, the -OnChange event of New-UDTextbox returns a string in $EventData.

    This will be different for other controls. For example, a form will return an object that contains properties about the state of the form.

    You can also access the raw string provided to the event handler using the $Body variable. This contains a JSON string the defines the $EventData.

    In addition to the $EventData variable, you can also use the $PSUItem variable to get more information in certain controls that allow for multiple data types. You'll find the PSUItem data type below.

    Scoping

    Understanding event handler scoping is important to ensuring that your apps behave as expected. PowerShell Universal makes an effort to effectively provide all in-scope variables to event handlers.

    For example, the below would be natural use of a variable in a standard PowerShell script and also works in event handlers.

    Using $Global: and $Script: scope will not work as expected because every event handler works in its own runspace. PSU manually moves variables around during processing but does not change variables in other scopes.

    You should use to access variables across event handlers.

    Additionally, you should avoid trying to inherit $EventData within nested event handlers. Although some components do not define their own event data, it should be assumed that the event data will be overwritten in the nested component.

    To avoid this issue, you should store data in another variable to be sent into the nested component's event handler scope.

    Map

    Map component for Universal Apps.

    The UDMap component is a robust control that provides a huge set of features. You can select base layers, configure togglable layers, set markers, define vectors and interact with other Universal App components.

    Basic Map

    This basic map defines a simple base layer using the wmflabs.org tile server. You can use your own custom tile server by specifying a URL. The map is position over Hailey, Idaho.

    Layer Control

    You can enable the layer control by using the New-UDMapLayerControl cmdlet. This map defines several layers with components that you can toggle on and off. You can only have one base layer selected as a time. Map overlay layers can toggle on and off.

    Markers

    Markers are used to highlight particular locations.

    Custom Icons

    You can specify custom icons for markers using the -Icon parameter.

    Popups

    You can create a popup when clicking the marker by using the -Popup parameter and the New-UDMapPopup cmdlet.

    Interactive Maps

    Maps provide a series of interactive capabilities for add components to and manipulating the map.

    Element

    Information about UDElements.

    The New-UDElement cmdlet allows you to create custom React elements within your app. Similar to New-UDHtml, you can define HTML elements using New-UDElement. Unlike, New-UDHtml, you can update elements, set auto refresh and take advantage of the React component system.

    Create an Element

    You need to specify the -Tag and -Content when creating an element. The below example creates a div tag.

    You can nest components within each other to create HTML structures. For example, you could create an unordered list with the following example.

    Setting Attributes

    You can select attributes of an element (like HTML attributes) by using the -Attributes parameter. This parameter accepts a hashtable of attribute name and values. The below example creates red text.

    You can wrap any component with New-UDElement and add an event handler.

    Auto Refreshing Elements

    You can define the -AutoRefresh, -RefreshInterval and -Endpoint parameters to create an element the refreshes on a certain interval. The below example creates an element that refreshes every second and displays the current time.

    Setting Element Properties Dynamically

    You can use the Set-UDElement cmdlet to set element properties and content dynamically. The following example sets the content of the element to the current time.

    You can also set attributes by using the -Properties parameter of Set-UDElement. The following example sets the current time and changes the color to red.

    Adding Child Elements

    You can add child elements using Add-UDElement. The following example adds child list items to an unordered list.

    Clearing Child Elements

    You can clear the child elements of an element by using Clear-UDElement. The following example clears all the list items from an unordered list.

    Forcing an Element to Reload

    You can force an element to reload using Sync-UDElement. The following example causes the div to reload with the current date.

    Removing an Element

    You can remove an element by using Remove-UDElement.

    Example: Color Picker

    Create a color picker with an OnChange event handler using New-UDElement.

    API


    Parameters

    Parameters for PowerShell Universal jobs.

    Parameters

    Jobs support automatically generating forms with parameters based on your script's param block. The type of control will change based on the type you define in the block. Parameters that are mandatory will also be required by the UI.

    Stepper

    Stepper component for Universal Apps

    Steppers convey progress through numbered steps. It provides a wizard-like workflow.

    Steppers display progress through a sequence of logical and numbered steps. They may also be used for navigation. Steppers may display a transient feedback message after a step is saved. The stepper supports storing input data in the stepper context. It supports the following controls.

    New-UDApp -Content {
       New-UDButton -Text 'Click Me' -OnClick {
           Show-UDToast "Ouch!"
       }
    }
    New-UDApp -Title 'Dashboard' -Content {
        New-UDTypography -Text 'Hello, world!'
    }
    New-UDMap -Endpoint {
        New-UDMapRasterLayer -TileServer 'https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png' 
    } -Latitude 43.52107 -Longitude -114.31644 -Zoom 13 -Height '100vh'
    components here
    Pages here
    variables page
    Debugging Tools
    environment
    Module Tab
    Dashboard Started
  • Dashboard Stopped

  • Server Started

  • Server Stopping

  • User Login

  • Use of a Revoked App Token

  • API Authentication Failed

  • API Error

  • New User Login

  • Git Sync

  • License Expired

  • License Expiring

  • Computer Offline

  • New-PSUTrigger
    Remove-PSUTrigger
    Set-PSUTrigger
    Get-PSUTrigger

    Switch

    Alerts
    Tables
    Timeline
    Charts
    Maps
    Modal
    Progress
    Button
    Form
    Textbox
    Menu
    Stepper
    Tabs
    Grid
    Stack
    Dynamic
    Element
    HTML
    Card
    Expansion Panel
    custom scopes
    Clear-UDElement
  • Sync-UDElement

  • Select-UDElement

  • Get-UDElement
    Set-UDElement
    Remove-UDElement
    Add-UDElement
    Color Picker
    New-UDApp -Title 'My New Dashboard' -Content {
        New-UDTypography -Text 'Hello!'
    }
    New-UDApp -HeaderPosition fixed -Content {
        New-UDElement -tag div -Attributes @{
            style = @{
                height = '150vh'
            }
        }
    }
    New-UDApp -Content {
    } -HeaderColor 'black' -HeaderBackgroundColor 'white'
    New-UDApp -Content {
    
    } -HeaderContent {
        New-UDButton -Icon (New-UDIcon -Icon Users) -Text 'User'
    }
    New-UDApp -Content {} -DisableThemeToggle
    New-UDApp -Content {} -Logo '/logo.png'
    New-UDApp -Content {
    } -Navigation (
        New-UDList -Children {
            New-UDListItem -Label "Home"
            New-UDListItem -Label "Getting Started" -Children {
                New-UDListItem -Label "Installation" -OnClick {}
                New-UDListItem -Label "Usage" -OnClick {}
                New-UDListItem -Label "FAQs" -OnClick {}
                New-UDListItem -Label "System Requirements" -OnClick {}
                New-UDListItem -Label "Purchasing" -OnClick {}
            }
        }
    ) -NavigationLayout permanent
    New-UDPage -Content {
        New-UDTextbox
    }
    $Pages = @()
    $Pages += New-UDPage -Name 'My Home Page' -Content {}
    $Pages += New-UDPage -Name 'Diagnostics' -Content {}
    New-UDApp -Pages $Pages -Title 'Dashboard'
    $DebugPreference = 'Continue'
    New-UDApp -Title 'App' -Content {
    
    } -Menu {
        New-UDMenuItem -Text 'Profile' -OnClick {
            Show-UDModal -Content {
                New-UDTypography -Text 'Welcome to your profile!'
            }
        }
    }
    New-PSUEnvironment -Name 'Env' -Path 'powershell.exe' -PersistentRunspace
    New-PSUApp -Name 'App' -BaseUrl '/' -Authenticated -GrantAppToken
    New-UDApp -Title "Hello, World!" -Content {
        New-UDButton -Text 'Job' -OnClick {
            Invoke-UAScript -Name 'Test.ps1'
        }
    }
    New-PSUApp -Name 'App' -BaseUrl '/' -Authenticated -DisableErrorToast
    New-UDApp -Title "Hello, World!" -Content {
        New-UDButton -Text 'Job' -OnClick {
            throw "Exception
        }
    } 
    New-PSUApp -Name 'App' -BaseUrl '/' -DisableStartupLogging
    New-PSUApp -Name 'App' -BaseUrl '/' -DisableStartupLogging
    @{
        Name = "username"
        Roles = @()
    }
    @{
        UserName = 'username'
        RemoteIpAddress = ''
        LocalPort = ''
        RemotePort = ''
    }
    public class GitStatus 
    {
        public long Id { get; set; }
        public string CommitId { get; set; }
        public DateTime Timestamp { get; set; }
        public TimeSpan SyncTime { get; set; }
        public int Changes { get; set; }
        public string Location { get; set; }
        public string Remote { get; set; }
        public GitStatusResult Result { get; set; }
        public string ResultMessage { get; set; }
        public string ComputerName { get; set; }
    }
    class Computer
    {
        [long]$Id
        [string]$Name
        [DateTime]$HeartBeat
        [ComputerStatus]$Status
        [bool]$Maintenance
        [bool]$Deleted
        [List<ComputerTag>]$Tags
        [string]$Version
        [DateTime]$FileSyncTimestamp
        [string]$DeploymentVersion
        [string]$DeploymentName
        [string]$GitSettings
        [string]$GitBranch
        [ComputerType]$Type
    }
    
    enum ComputerStatus
    {
        Offline,
        Online,
        Busy,
        Loading,
        StartupError
    }
    
    enum ComputerType
    {
        Server,
        Agent
    }
    New-PSUTrigger -Name 'Trigger' -EventType JobStarted -TriggerScript Script.ps1
    New-PSUTrigger -Name 'Trigger' -EventType DashboardStopped -TriggerScript Script.ps1 -Dashboard 'Dashboard'
    param($Job)
    
    $Job
    param($Dashboard)
    
    $Dashboard
    New-PSUTrigger -Condition {
       $Env:Environment -eq 'production'
    }
    New-UDButton -Text "Click me!" -OnClick {
        Show-UDToast -Message "Clicked!"
    }
    New-UDTextbox -OnChange {
        Show-UDToast -Message $EventData
    }
    New-UDForm -Content {
        New-UDTextbox -Id "UserName"
    } -OnSubmit {
        Show-UDToast -Message $EventData.UserName
    }
    public class PsuItem
    {
        public string Id { get; set; }
        public string Type { get; set; }
        public string EventId { get; set; }
        public string EventName { get; set; }
        public string EventData { get; set; }
        public string Location { get; set; }
    }
    $Variable = 123
    New-UDButton -Text "Click Me" -OnClick {
        Show-UDToast -Message $Variable
    }
    New-UDTable -Columns @(
        New-UDTableColumn -Property 'Name' -OnRender {
            New-UDButton -Text $EventData.Name -OnClick {
                # $EventData has been replaced by the button and no longer has a name property
                Show-UDToast -Message $EventData.Name
            }
        }
    ) -Data $Data
    New-UDTable -Columns @(
        New-UDTableColumn -Property 'Name' -OnRender {
            $Name = $EventData.Name
            New-UDButton -Text $EventData.Name -OnClick {
                Show-UDToast -Message $Name
            }
        }
    ) -Data $Data
    New-UDMap -Endpoint {
        New-UDMapLayerControl -Content {
            New-UDMapBaseLayer -Name 'Black and White' -Content {
                New-UDMapRasterLayer -TileServer 'https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png' 
            } -Checked
            New-UDMapBaseLayer -Name 'Color' -Content {
                New-UDMapRasterLayer -TileServer 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' 
            }
            New-UDMapOverlay -Name 'Marker' -Content {
                New-UDMapMarker -Latitude 51.505 -Longitude -0.09 
            } -Checked
            New-UDMapOverlay -Name 'Marker 2' -Content {
                New-UDMapMarker -Latitude 51.555 -Longitude -0.00 
            } -Checked
        }
    } -Latitude 51.505 -Longitude -0.09 -Zoom 13 -Height '100vh'
    New-UDMap -Endpoint {
        New-UDMapRasterLayer -TileServer 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' 
        New-UDMapMarker -Latitude "51.100" -Longitude "-0.5"
    } -Latitude 51.505 -Longitude -0.09 -Zoom 13 -Height '100vh'
    New-UDMap -Endpoint {
        New-UDMapRasterLayer -TileServer 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' 
        New-UDMapMarker -Latitude "51.100" -Longitude "-0.5"
    } -Latitude 51.505 -Longitude -0.09 -Zoom 13 -Height '100vh' -Icon (New-UDMapIcon -Url = "https://ironmansoftware.com/img/ps-logo.png")
    }
    New-UDMapMarker -Latitude "51.$RandomLat" -Longitude "-0.$Random" -Popup (
        New-UDMapPopup -Content {
            New-UDAlert -Text "Hello"
        } -MinWidth 200
    )
    New-UDButton -Text 'Add Circle' -OnClick {
        Add-UDElement -ParentId 'Feature-Group' -Content {
            New-UDMapVectorLayer -Id 'Vectors' -Circle -Latitude 51.505 -Longitude -0.09 -Radius 500 -Color blue -FillColor blue -FillOpacity .5 
        }
    }
    
    New-UDButton -Text 'Remove Circle' -OnClick {
        Remove-UDElement -Id 'Vectors' 
    }
    
    New-UDButton -Text 'Add Marker' -OnClick {
        Add-UDElement -ParentId 'Feature-Group' -Content {
            New-UDMapMarker -Id 'marker' -Latitude 51.505 -Longitude -0.09 -Popup (
                New-UDMapPopup -Content {
                    New-UDCard -Title "Test"
                } -MaxWidth 600
            ) 
        }
    }
    
    New-UDButton -Text 'Remove Marker' -OnClick {
        Remove-UDElement -Id 'marker' 
    }
    
    New-UDButton -Text 'Add Layer' -OnClick {
        Add-UDElement -ParentId 'layercontrol' -Content {
            New-UDMapOverlay -Id 'MyNewLayer' -Name "MyNewLayer" -Content {
                New-UDMapFeatureGroup -Id 'Feature-Group2' -Content {
                    1..100 | % {
                        New-UDMapVectorLayer -Id 'test' -Circle -Latitude "51.$_" -Longitude -0.09 -Radius 50 -Color red -FillColor blue -FillOpacity .5        
                    }
                }
            } -Checked
    
        }
    }
    
    New-UDButton -Text 'Remove Layer' -OnClick {
        Remove-UDElement -Id 'MyNewLayer' 
    }
    
    New-UDButton -Text 'Move' -OnClick {
        Set-UDElement -Id 'map' -Attributes @{
            latitude = 51.550
            longitude = -0.09
            zoom = 10
        }
    }
    
    New-UDButton -Text "Add marker to cluster" -OnClick {
        Add-UDElement -ParentId 'cluster-layer' -Content {
            $Random = Get-Random -Minimum 0 -Maximum 100
            $RandomLat = $Random + 400
            New-UDMapMarker -Latitude "51.$RandomLat" -Longitude "-0.$Random"
        }
    }
    
    New-UDButton -Text "Add points to heatmap" -OnClick {
        Add-UDElement -ParentId 'heatmap' -Content {
            @(
                @(51.505, -0.09, "625"),
                @(51.505234, -0.0945654, "625"),
                @(51.50645, -0.098768, "625"),
                @(51.5056575, -0.0945654, "625"),
                @(51.505955, -0.095675, "625"),
                @(51.505575, -0.09657, "625"),
                @(51.505345, -0.099876, "625"),
                @(51.505768, -0.0923432, "625"),
                @(51.505567, -0.02349, "625"),
                @(51.50545654, -0.092342, "625"),
                @(51.5045645, -0.09342, "625")
            )
        }
    }
    
    New-UDButton -Text "Clear heatmap" -OnClick {
        Clear-UDElement -Id 'heatmap'
    }
    
    New-UDMap -Id 'map' -Endpoint {
        New-UDMapLayerControl -Id 'layercontrol' -Content {
            New-UDMapBaseLayer -Name "Black and White" -Content {
                New-UDMapRasterLayer -TileServer 'https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png' 
            } 
    
            New-UDMapBaseLayer -Name "Mapnik" -Content {
                New-UDMapRasterLayer -TileServer 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' 
            } 
    
            New-UDMapBaseLayer -Name "Bing" -Content {
                New-UDMapRasterLayer -Bing -ApiKey 'asdf3rwf34afaw-sdfasdfa23feaw-23424dfsdfa' -Type Road
            } -Checked
    
            New-UDMapOverlay -Name "Markers" -Content {
                New-UDMapFeatureGroup -Id 'Feature-Group' -Content {
                    New-UDMapMarker -Id 'marker' -Latitude 51.505 -Longitude -0.09
                } -Popup (
                    New-UDMapPopup -Content {
                        New-UDCard -Title "Test123"
                    } -MaxWidth 600
                )
            } -Checked
    
            New-UDMapOverlay -Name 'Vectors' -Content {
                New-UDMapFeatureGroup -Id 'Vectors' -Content {
    
                }
            } -Checked
    
            New-UDMapOverlay -Name "Heatmap" -Content {
                New-UDMapHeatmapLayer -Id 'heatmap' -Points @() 
            } -Checked 
    
            New-UDMapOverlay -Name "Cluster" -Content {
                New-UDMapMarkerClusterLayer -Id 'cluster-layer' -Markers @(
                    1..100 | ForEach-Object {
                        $Random = Get-Random -Minimum 0 -Maximum 100
                        $RandomLat = $Random + 400
                        New-UDMapMarker -Latitude "51.$RandomLat" -Longitude "-0.$Random"
                    }
                )
            } -Checked
    
        }
    
    } -Latitude 51.505 -Longitude -0.09 -Zoom 13 -Height '100vh' -Animate
    New-UDElement -Tag 'div' -Content { 'Hello' }
    New-UDElement -Tag 'ul' -Content {
        New-UDElement -Tag 'li' -Content { 'First' }
        New-UDElement -Tag 'li' -Content { 'Second' }
        New-UDElement -Tag 'li' -Content { 'Third' }
    }
    New-UDElement -Tag 'div' -Content { 'Hello' } -Attributes @{
        style = @{
            color = 'red'
        }
    }
    New-UDElement -Tag div -Content {
        New-UDIcon -Icon "user"
    } -Attributes @{
        onClick = {
            Show-UDToast "Nice!"
        }
    }
    New-UDElement -Tag 'div' -Endpoint {
        Get-Date
    } -AutoRefresh -RefreshInterval 1
    New-UDElement -Tag 'div' -Id 'myElement' -Content { }
    
    New-UDButton -Text 'Click Me' -OnClick {
        Set-UDElement -Id 'myElement' -Content { Get-Date }
    }
        New-UDElement -Tag 'div' -Id 'myElement' -Content { }
    
        New-UDButton -Text 'Click Me' -OnClick {
            Set-UDElement -Id 'myElement' -Content { Get-Date } -Properties @{ Attributes = @{ style = @{ color = "red" } } }
        }
    New-UDElement -Tag 'ul' -Content {
    
    } -Id 'myList'
    
    New-UDButton -Text 'Click Me' -OnClick {
        Add-UDElement -ParentId 'myList' -Content {
            New-UDElement -Tag 'li' -Content { Get-Date }
        }
    }
    New-UDElement -Tag 'ul' -Content {
        New-UDElement -Tag 'li' -Content { 'First' }
        New-UDElement -Tag 'li' -Content { 'Second' }
        New-UDElement -Tag 'li' -Content { 'Third' }
    }  -Id 'myList'
    
    New-UDButton -Text 'Click Me' -OnClick {
        Clear-UDElement -Id 'myList'
    }
    New-UDElement -Tag 'div' -Endpoint {
        Get-Date
    } -Id 'myDiv'
    
    New-UDButton -Text 'Click Me' -OnClick {
        Sync-UDElement -Id 'myDiv'
    }
    New-UDElement -Tag 'div' -Endpoint {
        Get-Date
    } -Id 'myDiv'
    
    New-UDButton -Text 'Click Me' -OnClick {
        Remove-UDElement -Id 'myDiv'
    }
    function New-UDColorPicker {
        [CmdletBinding()]
        param(
            [Parameter()]
            [string]$Id = [Guid]::NewGuid(),
            [Parameter()]
            [ScriptBlock]$OnChange,
            [Parameter()]
            [string]$Value,
            [Parameter()]
            [Hashtable]$Style
        )
    
        New-UDElement -Id $Id -Tag "input" -Attributes @{
            value = $Value
            type = "color"
            onChange = $OnChange
            style = $Style
        }
    }
    
    New-UDApp -Content { 
        New-UDColorPicker -Id 'colorPicker' -OnChange {
            Show-UDToast $EventData -Position topLeft -Persistent
        }
    }
    Basic Parameters

    Parameters can be simply defined without any type of parameter attribute and they will show up as text boxes in the UI.

    Parameters Types

    Universal supports various types of parameters. You can use String, String[], Int, DateTime, Boolean, Switch and Enum types.

    String

    You can define string parameters by specifying the [String] type of by not specifying a type at all. Strings will generate a textbox.

    String Arrays

    You can specify string arrays by using the [String[]] type specifier. String arrays will generate a multi-tag select box.

    Date and Time

    You can use the [DateTime] type specifier to create a date and time selector.

    Boolean

    You can use a [Bool] type selector to create a switch.

    Integer

    You can define a number selector by using the [Int] type specifier.

    Switch Parameter

    You can define a switch parameter using the [Switch] type specifier to create a switch.

    Enumerations

    You can use System.Enum values to create select boxes. For example, you could use the System.DayOrWeek to create a day of the week selection box.

    PSCredential

    When you specify a PSCredential , the user will be presented with a drop down of credentials available as variables.

    File

    You can allow users to upload files by using the [File] type.

    Files will be available as a PSUFile object in your scripts. This object has a byte[] array that you can use to process the file.

    For example, you can get the string content for the file by converting it using the Encoding classes.

    Display Name

    You can use the DisplayNameAtrribute to set a display name for the script parameter.

    Help Messages

    You can define help messages for your parameters by using the HelpMessage property of the Parameter attribute.

    Required Parameters

    You can use the Parameter attribute to define required parameters.

    Default Value

    You can use both static and default values for parameters. The default value is calculated when the job is about to be run.

    ValidateSet

    The ValidateSet attribute is used to enforce which values can be passed to a parameter. Learn about ValidateSet here. PowerShell Universal will automatically create a drop-down menu with the values provided to the ValidateSet attribute for parameters.

    For example, a script could define a param block as follows.

    The result is shown below.

    ValidateSet Attribute

    Passing Parameters from PowerShell

    You can pass parameters from PowerShell using the Invoke-PSUScript cmdlet. This cmdlet supports dynamic parameters. If you have a param block on your script, these parameters will automatically be added to Invoke-PSUScript.

    For example, I had a script named Script1.ps1 and the contents were are follows.

    I could then invoke that script using this syntax.

    The result would be that Hello was output in the job log and pipeline.

    Parameter Sets

    PowerShell Universal supports parameter sets. When a parameter set is defined, a drop down is provided that allows for switching between the sets.

    Parameter Sets
    Date Picker
  • Radio

  • Select

  • Slider

  • Switch

  • Textbox

  • Time Picker

  • Upload

  • Stepper

    The $Body variable will contain a JSON string that contains the current state of the stepper. You will receive information about the fields that have been defined within the stepper and info about the current step that has been completed. The $Body JSON string will have the following format.

    Validating a Step

    You can validate a step in a stepper by specifying the OnValidateStep parameter. The script block will receive a $Body variable with JSON that provides information about the current state of the stepper. You will need to return a validation result using New-UDValidationResult to specify whether the current step state is valid.

    The JSON payload will have the following format. Note that steps are 0 indexed. If you want to validate the first step, check to make sure the step is 0.

    You will have to convert the JSON string to an object to work with in PowerShell and then return the validation result.

    Skipping Steps

    You can direct the user to a particular step in the OnValidateStep event handler. Use the New-UDValidationResult -ActiveStep parameter to move the user to any step after clicking next. Step indices are 0 based.

    This example moves the user to the last step after completing the first step.

    Disable Previous Button

    You can disable the previous button by using the -DisablePrevious parameter of New-UDValidationResult .

    This example disables the previous step whenever the user moves forward in the stepper.

    Vertical Steppers

    You can create a vertical stepper by setting the -Orientation parameter to vertical.

    API

    • New-UDStepper

    • New-UDStep

    • New-UDValidationResult

    Autocomplete
    Checkbox

    Scripts

    PowerShell scripts to execute within PowerShell Universal.

    You can create PowerShell scripts within PowerShell Universal to execute manually, on a schedule, or when events happen within the platform. They are stored on disk and they persist to a local or remote Git repository.

    The scripts.ps1 configuration file stores the Script properties.

    Add a New Script

    To add a new script, click the New Script button within the Automation / Scripts page. There are various settings you can provide for the script.

    Script Options

    Name

    This is the name of the script as shown in Universal Automation. This is also the name used to persist the script to disk. The name needs to be unique within the current folder.

    Module and Command

    See below.

    Description

    This description of the script shows in various places within the UA UI and is returned by the Universal cmdlets.

    Disable Manual Invocation

    This prevents a script from running manually. This is enforced in the UI as well as the web server and cmdlets.

    Max Job History

    The max job history defines the amount of jobs stored when running this script. It defaults to 100. Jobs are also cleaned up based on the server-wide job retention duration setting from the Settings / General page.

    Error Action

    The error action changes how the script reacts when it has an error. By default, terminating and non-terminating errors are ignored and the script always succeeds. You can change this setting to stop to cause scripts to fail immediately when an error is encountered.

    If you wish to write errors directly to the error pane without causing changes in how the script is handled (for example in an exception handler), use Write-PSUError to output the error record and it appears in the job's error tab.

    Environment

    This allows you to define the required PowerShell environment for the script. By default, it uses the server-wide default PowerShell environment. PowerShell environments are automatically located the first time the Universal Server starts up or read from the environments.ps1 file. You can also add Environment on the Settings / Environments page.

    Timeout

    The number of minutes before the script times out. The default value of 0 means the script will run forever. Once a script reaches its timeout, it is canceled.

    Discard Pipeline

    When checked, this disables pipeline output storage. This greatly reduces jobs' CPU and storage overhead, but the script still writes to the information, warning, and error streams.

    Concurrent Jobs

    Defines the maximum concurrent jobs with which the script can be run. It defaults to 100.

    Running a Script

    You can run a script in the UI from the Automation / Scripts page by clicking Run or by clicking View and then Run. In each case, the Run Dialog appears, allowing you to select various settings for the job.

    Running a Script With Parameters

    Learn more about parameters .

    PowerShell Universal automatically determines the parameters as defined within your scripts. It takes advantage of static code analysis to determine the type, default values and some validation that is then presented within the UI.

    For example, you may have a script with the following parameters:

    The result is a set of input options based on the types of parameters.

    Running a Script as Another User

    The integrated does not support running as alternate credentials.

    You can run scripts as another user by configuring . PowerShell Universal uses the Microsoft Secret Management module to integrate with secret providers. See variables for more information on secrets.

    1. Create a new PSCredential secret variable.

    Click Platform \ Variables and then click Create Secret. Select the PSCredential variable type. Enter the username and password. Ensure that the Disable Run As Support value is unchecked.

    1. Run the Script and select the credential

    Navigate back to Automation \ Scripts and click the Run Script button. Select an environment besides the Integrated environment. By default, this will be either PowerShell 7 or Windows PowerShell 5.1.

    You will now be prompted with the Run As drop down to select the credential. From there, you can select the credential within the run dialog.

    Running a Script on Another Computer

    You can use the Computer dropdown to select other machines on which to run a script. The default value is to run on any available computer.

    Running a Script on All Computers

    You can run a script on all computers by selecting the All Computers option from the Computer dropdown.

    Running a Script from an App with Output

    If you would like to run a script from an app and display the output as it runs, using the following example. It takes advantage of Invoke-PSUScript and Get-PSUJobOutput.

    Load Balancing

    PowerShell Universal uses a least-busy server loading balancing algorithm. If more than one server is a valid target for a job, PowerShell Universal will select the server with the least number of jobs running on that server.

    Remoting

    You can use PowerShell remoting by taking advantage of Invoke-Command . PowerShell Universal does not support the use of Enter-PSSession or Import-PSSession.

    Comment-Based Help

    You can use comment-based help to define the description, a synopsis, parameter-based help, and links for your scripts. These will be displayed within the PowerShell Universal UI.

    The above yields the following user interface. The synopsis displays as the short description, and a longer description displays in the description section. Links appear under the description.

    Modules and Commands

    Commands and cmdlets found in modules can be used as the target for scripts rather than authoring the script directly.

    Let's assume that we have a module called PSUModule that contains the following function.

    It's possible to expose this function as a script by using the following syntax in scripts.ps1.

    The function surfaces just like other scripts within the admin console. Parameters, help text and other PSU features work the same as with scripts.

    Statistics

    Using a script's job history, PowerShell Universal will provide basic statistics about the execution of the script. These include success rate, average execution time, and breaks downs of environment, user and computer execution.

    Start-Job Support

    While it's possible to start jobs using Invoke-PSUScript, it may be desirable to start a job using the PowerShell Start-Job cmdlet. Using Start-Job does not register the job with PowerShell Universal and the execution information will not be present in the jobs table.

    The Integrated, PowerShell 7 and Windows PowerShell 5.1 environments are not compatible with Start-Job because they are custom PowerShell hosts. In order to use Start-Job, you will need to configure a custom PowerShell environment. Click Settings \ Environments. Next, click Create New Environment. Name the environment, select the Custom environment type and specify pwsh.exe as the executable path.

    You will now be able to use this environment to run the Start-Job cmdlet.

    API

    param($Test)
    
    $Test
    param(
        [String]$Textbox,
        $Textbox2
    )
    param([String[]]$Array)
    param([DateTime]$DateTime)
    param([Bool]$Switch)
    param([Int]$Number)
    param([Switch]$Switch)
    param([System.DayOfWeek]$DayOfWeek)
    param(
        [PSCredential]$Credential
    )
    param(
        [File]$File
    )
    [Text.Encoding]::UTF8.GetString($File.Content)
    param(
        [ComponentModel.DisplayName("My Script")]
        $MyScript
    )
    param(
        [Parameter(HelpMessage = "Class you want to enroll in")]
        [string]$Class
    )
    param(
        [Parameter(Mandatory)]
        $RequiredParameter
    )
    
    $RequiredParameter
    param(
        $Parameter = "Hello, World",
        [DateTime]$ExecutionTime = Get-Date
    )
    
    $Parameter
    $ExecutionTime
    param(
        [ValidateSet("Steve", "Mary")]
        [string]$Name
    )
    param($MyParameter)
    
    $MyParameter
    Invoke-PSUScript -Name 'Script.ps1' -MyParameter "Hello"
    param(
        [Parameter(ParameterSetName = 'Set1')]
        $Parameter1,
        [Parameter(ParameterSetName = 'Set2')]
        $Parameter2
    )
    New-UDStepper -Steps {
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 1" }
            New-UDTextbox -Id 'txtStep1' -Value $EventData.Context.txtStep1
        } -Label "Step 1"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 2" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep2' -Value $EventData.Context.txtStep2
        } -Label "Step 2"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 3" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep3' -Value $EventData.Context.txtStep3
        } -Label "Step 3"
    } -OnFinish {
        New-UDTypography -Text 'Nice! You did it!' -Variant h3
        New-UDElement -Tag 'div' -Id 'result' -Content {$Body}
    }
    {
        context: {
            txtStep1: "value1",
            txtStep2: "value2",
            txtStep3: "value3"
        },
        currentStep: 0
    }
    {
        context: {
            field1: "value1" 
        },
        currentStep: 0
    }
    New-UDStepper -Steps {
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 1" }
            New-UDTextbox -Id 'txtStep1' -Value $EventData.Context.txtStep1
        } -Label "Step 1"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 2" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep2' -Value $EventData.Context.txtStep2
        } -Label "Step 2"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 3" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep3' -Value $EventData.Context.txtStep3
        } -Label "Step 3"
    } -OnFinish {
        New-UDTypography -Text 'Nice! You did it!' -Variant h3
        New-UDElement -Tag 'div' -Id 'result' -Content {$Body}
    } -OnValidateStep {
        $Context = $EventData
        if ($Context.CurrentStep -eq 0 -and $Context.Context.txtStep1 -eq 'bad')
        {
            New-UDValidationResult 
        }
        else
        {
            New-UDValidationResult -Valid 
        }
    }
    New-UDStepper -Steps {
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 1" }
            New-UDTextbox -Id 'txtStep1' -Value $EventData.Context.txtStep1
        } -Label "Step 1"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 2" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep2' -Value $EventData.Context.txtStep2
        } -Label "Step 2"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 3" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep3' -Value $EventData.Context.txtStep3
        } -Label "Step 3"
    } -OnFinish {
        New-UDTypography -Text 'Nice! You did it!' -Variant h3
        New-UDElement -Tag 'div' -Id 'result' -Content {$Body}
    } -OnValidateStep {
        $Context = $EventData
        if ($Context.CurrentStep -eq 0 -and $Context.Context.txtStep1 -eq 'bad')
        {
            New-UDValidationResult 
        }
        else
        {
            New-UDValidationResult -Valid -ActiveStep 2
        }
    }
    New-UDStepper -Steps {
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 1" }
            New-UDTextbox -Id 'txtStep1' -Value $EventData.Context.txtStep1
        } -Label "Step 1"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 2" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep2' -Value $EventData.Context.txtStep2
        } -Label "Step 2"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 3" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep3' -Value $EventData.Context.txtStep3
        } -Label "Step 3"
    } -OnFinish {
        New-UDTypography -Text 'Nice! You did it!' -Variant h3
        New-UDElement -Tag 'div' -Id 'result' -Content {$Body}
    } -OnValidateStep {
        New-UDValidationResult -Valid -DisablePrevious
    }
    New-UDStepper -Steps {
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 1" }
            New-UDTextbox -Id 'txtStep1' -Value $EventData.Context.txtStep1
        } -Label "Step 1"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 2" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep2' -Value $EventData.Context.txtStep2
        } -Label "Step 2"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 3" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep3' -Value $EventData.Context.txtStep3
        } -Label "Step 3"
    } -OnFinish {
        New-UDTypography -Text 'Nice! You did it!' -Variant h3
        New-UDElement -Tag 'div' -Id 'result' -Content {$Body}
    } -Orientation 'vertical'
    Modules and Commands
    here
    environment
    secret variables
    New-PSUScript
    Remove-PSUScript
    Set-PSUScript
    Get-PSUScript
    New Script Dialog
    Run Script Dialog
    Script Parameters Dialog
    Create Secret Variable
    Run as a User
    Additional Script Information
    Script Stats
    New-PSUScript -Name Script.ps1 -Path Script.Ps1 -ConcurrentJobs 1
    param(
        $Test,
        [DateTime]$Time, 
        [int]$Number,
        [PSCredential]$Credential,
        [System.ConsoleColor]$Color
    )
    New-UDButton -OnClick {
        $Job = Invoke-PSUScript -Name Script.ps1
        while($Job.Status -eq 'Running' -or $Job.Status -eq 'Queued')
        {
            [array]$Output = Get-PSUJobOutput -Job $Job
            $Job = Get-PSUJob -Id $Job.Id
            $Session:Code = $Output | ForEach-Object { 
                "$_`r`n"
            } | Join-String 
            Sync-UDElement -Id 'code'
            Start-Sleep -Seconds 1
        }
    } -Text 'Run Script'
    
    New-UDDynamic -Id 'code' -Content {
        if ($Session:Code)
        {
            New-UDSyntaxHighlighter -Code $Session:Code -Language batch
        }
    }
    <#
    .SYNOPSIS 
    
    This is a script for pinging other computers. 
    
    .DESCRIPTION
    
    This script can ping other computers. 
    
    .PARAMETER HostName
    
    The host name or address to ping. 
    
    .LINK
    https://www.ironmansoftware.com
    #>
    param($HostName)
    
    Test-NetConnection $HostName
    function Show-HelloWorld {
        param($Name)
        "Hello, $Name!"
    }
    New-PSUScript -Module 'PSUModule' -Command 'Show-HelloWorld'

    Installation

    Installation instructions for PowerShell Universal.

    MSI Install (Windows)

    The MSI install creates a PowerShell Universal service. By default, PowerShell Universal listens on port 5000. You can navigate to http://localhost:5000

    MSI downloads are available on our download page.

    System installs run as a Windows service. User installs run when the user logs in to the machine. The user install runs in the user's context.

    MSI Parameters

    The following table contains the parameters you can specify if running msiexec against our MSI install for automation purposes:

    Parameter
    Description
    Default Value

    Example

    The example below shows how to run msiexec.exe to install PowerShell Universal and provide parameters to the installer:

    ZIP Install

    You can also download the ZIP from our if you would like to xcopy deploy the files on Windows or Linux.

    Windows

    You can start Universal by unzipping the contents, unblocking the files and then executing Universal.Server.exe.

    Linux

    You can use the following command line on Linux to install and start PowerShell Universal:

    Linux Service

    You can use systemd to start PowerShell Universal as a service. The below script is an example of downloading a version of PowerShell Universal and installing it as a service:

    PowerShell Module

    You can use the PowerShell Universal PowerShell module to install the Universal server. To install the module, use Install-Module.

    To install the Universal server, you can use Install-PSUServer.

    Running this command on Windows creates and starts a Windows service on your machine. Running this command on Linux creates and starts a systemd service on your machine. Running this command on Mac OS downloads and extracts the PowerShell Universal server.

    Docker

    See the .

    IIS Install

    Please visit the for information on how to configure PowerShell Universal as an IIS website.

    Antivirus Configuration

    PowerShell Universal takes full advantage of PowerShell and the PowerShell SDK. It includes PowerShell scripts directly in the product. Consider configuring antivirus to allow execution of PowerShell scripts in PowerShell Universal.

    Directories

    The following directories contain examples from a standard Windows system of scripts and executable files that you may need to exclude from antivirus checks. Changing paths within appsettings.json or within the installer requires changing which directories are excluded.

    Path
    Description

    Executables

    It may be necessary to exclude certain executables that run PowerShell scripts. The below is a list of executables that run PowerShell from PowerShell Universal.

    Name
    Description

    Default Admin Name and Password

    You can use the $ENV:PSUDefaultAdminName and $ENV:PSUDefaultAdminPassword environment variables to change this behavior. These values are only used if no administrator account already exists. This is useful for cloud-based installations.

    Agent

    The PowerShell Universal Agent executes Event Hub actions. Install it depending on your environment:

    Windows (MSI)

    The PowerShell Universal Agent MSI is on our download page. After installing the MSI, a PowerShell Universal Agent service runs on your machine. to connect to PowerShell Universal.

    ZIP

    ZIP files for each platform we support are on our downloads page. Each ZIP contains a PSUAgent.exe or PSUAgent file that can start an agent. Run the process as a service for it to start whenever the machine reboots.

    Docker

    The ironmansoftware/universal-agent:latest container image provides the PowerShell Universal Agent as a Linux docker container.

    Next Steps

    At this point, Universal is up and running. Visit http://localhost:5000 or your default port to navigate to the admin console. Log in with the default admin name and password or create a default admin account.

    Upgrade

    This document covers upgrading the PowerShell Universal application.

    Overview

    This document will cover the upgrade process for production PowerShell Universal instances. We will cover the following topics.

    1. Data Backup

    2. Upgrade Process

    3. Upgrade Validation

    The Universal application binaries can generally be upgraded without having to change the configuration or database manually, but we do recommend backups of production data.

    Recommendations

    For production environments, we recommend deploying PowerShell Universal to a staging or development environment prior to major upgrades. This allows for testing before end users are affected. You can use to test changes in PowerShell Universal instances without purchasing another license.

    1. Data Backup

    PowerShell Universal uses a script-based configuration system alongside a database used for retention of entities such as app tokens, job history and identities. If possible, you will want to backup these items before running an upgrade for easy rollback in case an issue is encountered during validation.

    Database

    Backing up the database ensures that all apptokens, job history, identities and database secrets are retained in the case of an upgrade failure. SQL databases also may adjust the schema of the database and may require a rollback of not only the data, but also the schema of the tables in the database.

    SQLite

    By default, PowerShell Universal uses a single file database called SQLite. Unless configured otherwise, the database is stored in %ProgramData%\UniversalAutomation. You should have a database.db and possibility a database-log.db. Both of these files should be backed up. The service must be stopped in order to back up the files.

    SQL

    When using SQL for persistence, backup the entire database (including schema). There isn't necessarily a need to stop the PowerShell Universal service when backing up the database, but it may continue to write to the database (for example when running scheduled jobs) after the backup has been completed.

    Configuration Scripts

    Scripts make up the main configuration data to backup when upgrading a production PowerShell Universal instance. For production, we recommend using a version control system. You can also take advantage of the built-in git integration. If you are using a two-way sync for PowerShell Universal git integration, consider tagging your git branch prior to the upgrade to allow for easy rollback to unexpected changes within the git repository.

    2. Upgrade Progress

    Below are sections for each type of system upgrade and the steps that you should take based on how you originally installed PSU.

    MSI

    When installing via the MSI, you will want to follow the same backup procedures above.

    appsettings.json

    You will want to back up the appsettings.json file stored in %ProgramData%\PowerShellUniversal. This file contains information such as port, data storage location and other server settings. Typically, the MSI will not make changes to this file once created. It will use the settings found for the upgraded version. That said, if necessary, the MSI will make changes to the appsettings file. These changes are considered breaking and will be listed in the changelog for the release.

    Service Account

    When running an MSI upgrade, the PSU service is not uninstalled, and thus, the service account will still be set once the service starts up.

    If you perform an uninstall and then an install using the MSI, then the service account will be removed.

    Upgrade Process

    Once all the configuration files and the database are backed up, you can run the new MSI installer.

    For major upgrades (e.g. v4.3.4 to v5.0.4 etc), you will need to uninstall the previous version prior to running the new version.

    The installer may prompt for a restart of the machine if files are locked. The PSU MSI will uninstall all the files in the installation directory and install entirely new files.

    Once the MSI has completed, you can navigate to your PowerShell Universal admin console to perform installation validation.

    IIS

    Below you will find information about upgrading an IIS install.

    web.config

    In addition to the files listed to backup above, you will also want to consider backing up your web.config file. If you have made no changes to this file, you do not need to back it up.

    The web.config file that is included in the application installation directory will be overwritten during upgrades. If you have moved your web.config file to an alternate location, it will not be overwritten. When creating an IIS website, you can simply include the web.config file in the web app's directory and have the .

    Upgrade Process

    When upgrading with IIS, you will need to first stop your application pool to ensure that the binaries used by IIS are no longer in use and then replace the binaries with the new ones. To ensure that the upgrade works as expected, it's recommended to delete all the application files and then unzip the new ones into the same directory to avoid assembly conflicts.

    As with any installation from a ZIP file, make sure that you run Get-ChildItem -Recurse | Unblock-File from an elevated command prompt across the PowerShell Universal files to ensure they can be executed properly.

    Once you have copied the new files and unblocked them, start the app pool, navigate to the PowerShell Universal Admin Console and perform installation validation.

    Universal Module

    The Universal module can be used to upgrade installations of PowerShell Universal previously installed by the module.

    Do not use the Universal module to upgrade instances installed via MSI.

    Follow the backup procedures above and then perform the upgrade.

    First, upgrade the local PowerShell Universal module and verify the expected version is installed.

    Next, run Update-PSUServer to download and unzip the new PSU instance.

    After the upgrade is complete, navigate to the PowerShell Universal Admin Console and begin upgrade validation.

    ZIP

    Perform the necessary backup procedures and download the latest ZIP of PowerShell Universal.

    Stop the PowerShell Universal service. Delete the existing PowerShell Universal application files. Extract the ZIP files to the same directory. Finally, run Unblock-File against the directory to ensure that PSU can execute properly. Always run this command as administrator.

    After the upgrade is complete, navigate to the PowerShell Universal Admin Console and begin upgrade validation.

    Database

    By default, the PSU service will migrate to the latest database version during the startup process.

    The database can also be upgraded before upgrading the application. This is recommended for larger installations that may require some time for the schema update to take place. In some environments, allowing the service to upgrade the database can result in a timeout, like with Service Control Manager in Windows.

    If you are using SQL, you can find SQL files generated and placed in the SQL folder within the PSU installation media. Run these scripts against your database before upgrading.

    All types of databases support the psu command line tool for upgrades.

    3. Upgrade Validation

    After running an upgrade, you should perform basic validation against your PSU server to ensure that it is fully functional.

    Notifications

    Verify that there are no errors within the notification drop down. They may be a sign of issues during the upgrade.

    Modules

    Upgrades to PowerShell Universal may change assembly versions of DLLs shipped with the platform. This can cause other modules to fail to load. While this may not be obvious at first, you may consider taking an inventory of modules used in your platform to ensure that the versions are consistent before and after the upgrade to limit changes.

    If you have installed a version of the Universal module outside of PowerShell Universal (for example, with Install-Module), you must make sure to update the module or it can conflict with the new one installed with PowerShell Universal.

    Apps

    The most common upgrade issues come due to changes in the Universal App framework. Apps can be complex and bug fixes or features can sometimes cause for certain user's app while fixing issues pertaining to another user's app. Please read the changelog before upgrading to understand the impact of changes made to the app framework and consider testing the app with development data before upgrading in production.

    Nightly Builds

    When using nightly builds, you cannot upgrade from one nightly version to another. You can upgrade from a generally available version to a nightly version. In order to test a new nightly build, you will need to uninstall the current nightly build, rollback the database schema and then install the new version. You can roll back the database schema with psu.exe .

    Common Upgrade Issues

    IIS App Pool Does Not Start After Upgrade

    The most common upgrade issue is that Unblock-File is not called properly on the extracted files when performing an upgrade of a IIS ZIP install. Also make sure to run the Unblock-File command recursively and from within an administrative session.

    Another command issue is extracting the files over the top of the existing files. This can cause assembly conflicts and puts the application in an unknown state. Follow the IIS upgrade documentation and delete the files before extracting them.

    Command Not Found Errors

    When new functionality is added to PowerShell Universal it is typically done using new cmdlets. If older versions of the PowerShell Universal module are installed on the system, it can cause conflicts with the one shipped within the installation media. Ensure that you have removed older versions of the Universal module if you encounter these errors.

    Table\Column Not Found Error when using SQL Persistence

    This can happen if SQL schema upgrades are not being run during upgrades. If you set the RunMigrations setting to false in appsettings.json, you must run the migrations manually or the PowerShell Universal service will not function properly.

    Breaking App Component Change

    These changes can be visual or functional. Please ensure that you review the changelog for items that may be related to the change you are seeing. Consider posting the forums or opening a GitHub issue to see if the issue is as designed and if there is a viable workaround.

    We make the best possible effort to support everyone's' apps without breaking changes. That said, every configuration is pretty unique so we are more than happy to address issues you may encounter. Please, just let us know.

    License Issues after Upgrade

    The licensing model of PowerShell Universal provides licensed users the ability to upgrade to whatever is the newest version as long as they have an active perpetual or subscription license. If you attempt to upgrade a server that is no longer within the license window, the server will not function as expected. You will need to downgrade back to the previous version to restore functionality.

    Additionally, you may encounter issues due to the PSU service restart. When the service starts, it verifies license subscription status. If it fails to do so, it may not be licensed properly and cause other issues. The root cause is typically networking issues while attempting to access the IronmanSoftware.com website for activation. Offline license keys do not contact the IMS website for activation and will not encounter this issue.

    Mixed Versions

    Mixing versions of PowerShell Universal servers with the same database may cause issues as schema changes between the database or protocol changes in internal APIs may be mismatched. We recommend staging your upgrades in a way that you will eventually be running all PowerShell Universal servers as the same version.

    There is a known compatibility issue between PSU 5.5.4 and earlier and PSU 5.6.0 and later. We do not recommend mixing these versions. Scheduling jobs may fail on existing 5.5.4 servers when combined with 5.6.0 servers.

    5.0 Breaking Changes

    Removal of Pages

    The drag and drop page designer has been removed in favor of and .

    Removal of App Pages Designer

    The drag and drop page designer for apps has been removed. Apps created with the designer will still function.

    Removal of Access Controls

    Access Controls have been removed in favor of . You can also use the to assign resources, like scripts, to users without the need for complicated permissions.

    Cmdlet Communication Channel and Authorization Changes

    Prior to v5, cmdlets would send data over HTTP or by using an internal gRPC channel. Now, all cmdlets use an externally facing gRPC Channel that is protected by authentication and authorization. It no longer uses standard REST API HTTP calls.

    This can be a problem for PowerShell Universal instances behind and requires that the proper header values are sent.

    Please review the documentation for more information.

    Common cmdlet errors you may encounter during upgrade

    HTTP Status Code 403

    The cmdlet you are calling does not have access to the PowerShell Universal APIs. You will need to specify an -AppToken parameter on the cmdlets in order to use them.

    You can also enable the to allow internally called cmdlets from PowerShell Universal without the need for authorization.

    URI Not Defined

    The cmdlets are unable to determine how to call the PowerShell Universal APIs. You will need to either specify a -ComputerName parameter or setup the API URL in appsettings.json.

    SSL Certificate Error

    If you are using a self-signed certificate, you will need to specify the -TrustCertificate parameter of the cmdlets.

    PowerShell 7 Environment No longer Uses Pwsh.exe

    The default PowerShell 7 environment uses a .NET version of Universal.Agent.exe executable running PowerShell 7.5. This allows for the greatest compatibility with PowerShell Universal libraries and other modules.

    It's still possible to use the pwsh.exe process in custom environment configurations.

    IIS Hosting Package

    If you are hosting in IIS, ensure that you install the .

    Integrated Environment PowerShell Version

    The integrated environment now uses PowerShell 7.5.

    SQLite by Default

    SQLite is the default persistence method. You will need to perform a manual conversion from LiteDB before installing version 5.

    LiteDB Support Removed

    LiteDB has been removed as a supported database engine. Included with the PowerShell Universal installation files, you will find psu.exe. It can be used to convert a LiteDB database into a SQLite database. Use the following command line.

    The tool will create a database.bak file before performing the conversion. Progress will be reported in the console.

    After converting the database, you will need to update the appsettings.json file to use the new SQLite plugin and update the connection string to a SQLite format. Below is a snippet you can use to apply to your configuration file.

    Converting a Database for a MSI Upgrade

    In order for the PowerShell Universal installer to run successfully, you will need to update the database before running the MSI installer. Below are the steps to take to do so.

    1. Download the ZIP package for Windows and extract to a local directory.

    2. Stop the PowerShell Universal service

    3. Run the psudb.exe command from the ZIP directory, as stated above, to convert the database file in %ProgramData%\UniversalAutomation

    4. Update the %ProgramData%\PowerShellUniversal\appsettings.json file to use the SQLite plugin rather than the LiteDB plugin

    Desktop Mode Removed

    Desktop mode has been removed. Resources such as hot keys, file associations and shortcuts are no longer supported. The MSI now supports User scope installs that will run as the current user and start upon login.

    Install-PSUServer on Windows Installs from the MSI

    In previous versions of PowerShell Universal, this command would install to a directory and create the service manually. This command now installs from MSI. If you previously installed with this module, you would need to remove the existing install with a previous version of the module and then install with the new version of the module.

    Open a new command prompt and run the following.

    Git Database Storage Removed

    PowerShell Universal no longer supports storing the git repository directly in the database. We recommend using a remote git provider like GitHub, GitLab, or Gitea. PowerShell Universal v5 does support local git repositories without the need to sync to a remote. This allows for storing file history directly on the PowerShell Universal server.

    Removed Heatmap and Marker Cluster from New-UDMap

    Maps no longer support heatmaps or marker clusters.

    Jobs

    Jobs are the history of scripts that have been run.

    Jobs are the result of running a script. Jobs are retained based on the script and server level settings.

    Viewing Jobs

    Jobs can be viewed by clicking the Automation / Jobs page. Click the View button to navigate to the job. Jobs in progress can also been cancelled.

    Job List

    View Job Output

    Standard PowerShell streams such as information, host, error, warning and verbose are shown within the output pane.

    View Job Pipeline Output

    Storing large amounts of pipeline output can negatively affect performance. You can discard pipeline output by setting the Discard Pipeline setting on scripts.

    Pipeline output for jobs is also stored within PowerShell Universal. Any object that is written to the pipeline is stored as CliXml and available for view within the Pipeline Output tab.

    You can expand the tree view to see the objects and properties from the pipeline.

    Viewing Errors

    Any errors written to the error stream will be available on the Error tab within the job page.

    Status

    Jobs will return various statuses depending on configuration and the result of the execution. Settings that can affect job status include:

    • ErrorActionPreference

    • WarningActionPreference

    The following table describes how PowerShell Universal treats statuses.

    Status
    Description
    Suppress

    Feedback

    Some jobs will require feedback. Any script that contains a Read-Host call will wait until there is user interaction with that job. The job will be in a Waiting for Feedback state, and you can respond to that feedback by click the Response to Feedback button on the job page.

    To accept a SecureString with a password input field, you can use the -AsSecureString parameter of Read-Host.

    Invoking Jobs from PowerShell

    You can use Invoke-PSUScript to invoke jobs from the command line. You will need a valid to do so. Parameters are defined using dynamic parameters on the Invoke-PSUScript cmdlet.

    Call Scripts from Scripts

    You can also call UA scripts from UA scripts. When running a job in UA, you don't need to define an app token or the computer name manually. These will be defined for you. You can just call Invoke-PSUScript within your script to start another script. Both jobs will be shown in the UI. If you want to wait for the script to finish, use Wait-PSUJob.

    Waiting for a Script to Finished

    You can use the Wait-PSUJob cmdlet to wait for a job to finish. Pipe the return value of Invoke-PSUScript to Wait-UAJob to wait for the job to complete. Wait-PSUJob will wait indefinitely unless the -Timeout parameter is specified.

    Return Pipeline Data

    You can use the Get-PSUJobPipelineOutput cmdlet to return the pipeline output that was produced by a job. This pipeline output will be deserialized objects that were written to the pipeline during the job. You can access this data from where you have access to the PowerShell Universal Management API.

    Returning the last job's output

    It may be required to return the output from a script's last job run. In order to do this, you will need to use a combination of cmdlets to retrieve the script, the last job's ID and then return the pipeline or host output.

    Returns the last job's output as an object

    By default, Get-PSUJobOutput will return the output as a string. To return the output as an object with information about the output, use -AsObject.

    Invoke a Script and Wait for Output

    You can use the -Wait parameter of Invoke-PSUScript to achieve this.

    Additionally, the following example invokes a script, stores the job object in a $job variable, waits for the job to complete and then returns the pipeline and host output.

    Integrated Mode

    The integrated mode allows calling these cmdlets from within PowerShell Universal without an App Token or Computer Name. It uses the internal RPC channel to communicate.

    You can set the -Integrated parameter to switch to integrated mode. This parameter does not work outside of PowerShell Universal.

    The following cmdlets support integrated mode.

    • Get-PSUScript

    • Invoke-PSUScript

    • Get-PSUJob

    • Get-PSUJobOutput

    Invoking Jobs with REST

    You can call jobs over REST using the management API for PowerShell Universal. You will need a valid app token to invoke jobs.

    Call Scripts with REST

    To call a script, you call an HTTP POST to the script endpoint with the ID of the script you wish to execute.

    Providing Parameters

    You can provide parameters to the job via a query string. Parameters will be provided to your script as strings.

    Setting the Environment

    You can set the environment by pass in the environment property to the job context. The property must be the name of an environment defined within your PSU instance.

    Setting the Run As account

    You can set the run as account by passing in the name of a PSCredential variable to the Credential property.

    Invoke Jobs from Apps

    You can use the same cmdlets that you use in other PowerShell scripts to run scripts in apps. You may want a more interactive experience when doing so. Below are some examples of how to achieve that using the app framework.

    Displaying Output

    You can use the Invoke-PSUScript and the Get-PSUJobOutput cmdlets to create a UI element that updates as the script runs. Assume you have a script like the one below. It simply writes to the Information stream every 100 milliseconds 100 times.

    Within your app, you can start the script based on some user interaction and then update an element as the script is running. The below example creates a button and a pre tag element to serve as the destination for the script's output. When the user clicks the button, it will start the job and wait for it to finish. Within the loop, it retrieves the script's output and then updates the pre element with the output content.

    Finally, it retrieves an updated status of the job and waits 100 milliseconds before running again.

    Displaying Progress

    The app framework's PowerShell host will automatically interface with features of PowerShell, like Write-Progress.

    If you use the -Wait parameter of Invoke-PSUScript , it will automatically display the script's progress in a dialog in your app. Assume we have a script defined as below. This script writes progress every 100 milliseconds.

    Within our app, we can simply call the script with the -Wait parameter. When the user clicks the button, the progress will be displayed in the app.

    Requesting Input

    The app framework's PowerShell host will automatically interface with features of PowerShell, like Read-Host.

    If you use the -Wait parameter of Invoke-PSUScript , it will automatically prompt the user for input when encountering the Read-Host command. Assume we have a script that requests user input using Read-Host.

    In your app, simply call the script with the -Wait parameter.

    The user will then be prompted as the script runs.

    Variables Defined in Jobs

    Variables defined in jobs can be found on the .

    Job Run ID

    The default behavior for PowerShell Universal is to track jobs based on an autoincrementing int64-based ID. Every time a new job is run, the job is one higher in ID than the last. Because of this behavior, it is easy to guess other job IDs and can potentially lead to a security risk.

    In order to avoid this issue, you can enable the JobRunID experimental feature. Although internally the system still creates jobs with ascending numeric IDs, you cannot access jobs based on those IDs. Instead, a new field called RunID is used. RunID utilizes a GUID rather than an ID for look ups. This greatly reduces the ability for an attacker to guess a job ID.

    You will need to enable this feature to use it.

    API

    Form

    Form component for Universal Apps

    Forms provide a way to collect data from users.

    Forms can include any type of control you want. This allows you to customize the look and feel and use any input controls.

    Data entered via the input controls will be sent back to the the OnSubmit script block when the form is submitted. Within the OnSubmit event handler, you will access to the $EventData variable that will contain properties for each of the fields in the form.

    For example, if you have two fields, you will have two properties on $EventData.

    Supported Controls

    The following input controls automatically integrate with a form. The values that are set within these controls will be sent during validation and in the OnSubmit event handler.

    Simple Form

    Simple forms can use inputs like text boxes and checkboxes.

    Formatting a Form

    Since forms can use any component, you can use standard formatting components within the form.

    Returning Components

    When a form is submitted, you can optionally return another component to replace the form on the page. You can return any Universal Dashboard component. All you need to do is ensure that the component is written to the pipeline within the OnSubmit event handler.

    Validating a Form

    Form validation can be accomplished by using the OnValidate script block parameter.

    Canceling a Form

    You can define an -OnCancel event handler to invoke when the cancel button is pressed. This can be used to take actions like close a modal.

    Displaying output without Replacing the form

    Although you can return components directly from a form, you may want to retain the form so users can input data again. To do so, you can use Set-UDElement and a placeholder element that you can set the content to.

    In this example, we have an empty form that, when submitted, will update the results element with a UDCard.

    Schema Forms

    Instead of defining all the layout and logic for forms using cmdlets, you can also define a form based on a hashtable of schema. This version of forms is based on .

    Fields

    You define fields that accept string, number, integer, enum and boolean types. This changes the type of input shown.

    Required Properties

    You can use the required property to set a list of required properties.

    Note that the properties need to be lower case! For example, you need to ensure the keys in your properties hashtable are lower case and the list of required properties are also lower case.

    Ordering

    You can use the schemaUI property to modify the ordering of the fields.

    Arrays

    You can create forms that accept 0 to many objects. The user will be able to add and remove objects to the form.

    Script Forms

    You can automatically generate forms based on scripts in your PowerShell Universal environment. Script forms will generate input components based on the param block. Script forms automatically support progress and feedback.

    Script forms also support displaying the output as text or a table.

    API

    Docker

    This page provides installation and configuration information for Docker.

    Docker

    Confirming Docker is installed correctly

    Pages

    Information about Universal App pages.

    An app can consist of one or more pages. A page can have a particular name and URL. You can define a URL that accepts one or more variables in the URL to define a dynamic page.

    Creating a new page

    Within the app editor, click the Create App Page button.

    Once the page has been created, it will be listed in the pages tab.

    To update the content of a page, click the Edit Code button.

    AppBar

    AppBar component for Universal Apps

    The App Bar displays information and actions relating to the current screen.

    The top App Bar provides content and actions related to the current screen. It's used for branding, screen titles, navigation, and actions.

    AppBar with Custom Drawer

    New-UDForm -Content {
        New-UDTextbox -Id 'txtTextField'
        New-UDCheckbox -Id 'chkCheckbox'
    } -OnSubmit {
        Show-UDToast -Message $EventData.txtTextField
        Show-UDToast -Message $EventData.chkCheckbox
    }

    DATABASETYPE

    SQL, SQLite, or PostgreSQL

    SQLite

    STARTSERVICE

    Whether to start the service after install (0 or 1)

    1

    SERVICEACCOUNT

    The service account to set for the Windows service. Use the format of domain\username.

    None

    SERVICEACCOUNTPASSWORD

    The service account password to set for the Windows Service. The password will be masked with ***'s in the installer log.

    None

    TELEMETRY

    Anonymous telemetry collection

    0

    ADDPSMODULEPATH

    Adds the PowerShell Universal module directory to the PSModulePath environment variable.

    1

    STARTSERVICE

    Whether to start the service after install.

    1

    INSTALLTYPE

    Whether to perform a server or user install.

    Server

    INSTALLFOLDER

    The installation folder for PowerShell Universal

    %ProgramFiles(x86)%\Universal

    TCPPORT

    The TCP port the HTTP server will be listening on.

    5000

    REPOFOLDER

    The repository folder to save the configuration files to.

    %ProgramData%\UniversalAutomation\Repository

    CONNECTIONSTRING

    The SQL, SQLite, or PostgreSQL connection string.

    %ProgramData%\PowerShellUniversal

    Contains log files and appsettings.json

    %ProgramData%\UniversalAutomation

    Contains PowerShell scripts and artifacts. Contains the single file database when not using SQL integration.

    %ProgramFiles(x86)\Universal

    Contains PowerShell Universal application executables, libraries and modules.

    Universal.Server.exe

    The PowerShell Universal core service.

    PowerShellUniversal.Host.exe

    The PowerShell Universal host environment executable.

    pwsh.exe

    PowerShell 7.x

    PowerShell.exe

    PowerShell 5.x

    Downloads page
    Docker page
    IIS hosting documentation
    Configure it

    Data Source=%ProgramData%\UniversalAutomation\database.db

    Run the PowerShell Universal v5 installer to upgrade the application files.

    Development Licenses
    binaries stored in a different location
    Portal Pages
    Widgets
    Permissions
    Portal
    reverse proxies
    Module
    permissive API security model
    .NET 9.0 hosting bundle

    Running

    The script is currently running.

    N\A

    Queued

    The script is currently queued to run.

    N\A

    Get-PSUJobPipelineOutput
  • Get-PSUJobFeedback

  • Set-PSUJobFeedback

  • Wait-PSUJob

  • Get-PSUJobPipelineOutput
  • Wait-PSUJob

  • Error

    A script had a non-terminating error.

    Set ErrorActionPreference to SilentlyContinue

    Warning

    A script had a warning.

    Set WarningActionPreference to SilentlyContinue

    Failed

    A script had a terminating error.

    Handle the terminating error or catch it.

    Waiting on Feedback

    A script is waiting on feedback, such as Read-Host.

    Avoid user callbacks such as read-host.

    App Token
    variables page
    Invoke-PSUScript
    Get-PSUJob
    Get-PSUJobFeedback
    Get-PSUJobOutput
    Job Stream Output
    Job Pipeline Output
    Job Error Output
    Job Waiting on Feedback
    NOTE:
    Apple M1 devices: At the time of writing there are some issues on Apple M1 devices and, some ARM64/ARMv8 devices. Please review
    before proceeding.

    Docker

    Run the following command to confirm Docker is installed:

    Example Output:

    Docker Compose

    Docker Compose v1 uses the command docker-compose. As of June 2023, support ends for Docker Compose v1.

    Docker Compose v2 uses the command docker compose.

    If you are using Docker Compose v1 please adjust the commands accordingly. More information on Docker Compose can be found here.

    Run one of the following commands to confirm that Docker Compose is installed:

    Docker Compose v1:

    Docker Compose v2:

    Example Output:

    A Docker Hello-World

    To ensure that Docker has the ability to pull and run container images run the following command:

    Example Output:

    Installation

    Using the pre-built Container

    In order to run PowerShell Universal, use the provided container image. The docker image is available on Docker Hub.

    The prebuilt version supports both free & paid features of PowerShell Universal.

    Start the container by pulling the image and then running a container with the default port bound.

    Running a basic image

    Present an image to a different port

    If port 5000 is unavailable on your host, switch to another port.

    e.g. Present on port 80

    Mount a volume

    The docker run command allows you to mount a volume for persistent storage. Mount the volume to the /root folder.

    Mount a volume on container in Windows

    The following command mounts the folder C:\docker\volumes\PSU to /root on your container:

    Mount a volume on Container on Mac and Linux

    The following command mounts the folder /docker/volumes/PSU to /root on your container:

    Stopping a Container

    The following command removes a stopped container named PSU:

    Removing a Container

    The following command stops a container named PSU:

    The --force flag can remove a running container:

    Docker Compose

    Docker Compose allows you to use a yaml text file to standardize your build and script the deployment (or build) or multiple containers.

    The default name for any compose file is docker-compose.yml. It is recommended you use this as your compose filename.

    Creating a Compose Script for Windows

    The following compose file runs a Powershell Universal container in Windows:

    Creating a Compose Script for Mac / Linux

    The following compose file runs a Powershell Universal container on Mac and Linux:

    Starting Containers using Compose Scripts

    Using a Terminal shell or PowerShell for Windows. Use the cd command to change the working directory with your docker-compose.yml script.

    Run the following command:

    Example Output:

    Stopping Containers using Compose Scripts

    Using a Terminal shell, or PowerShell for Windows. cd to the directory with your docker-compose.yml script.

    Run the following command

    Example Output:

    Using Environment Variables and SQL Persistence

    You can add Environment variables into your Compose Scripts. Below is an example of:

    • Setting a node name

    • Adding SQL persistence

    • Adding a SQL Connection String

    Using Environment Variables and PostgreSQL Persistence

    You can add Environment variables into your Compose Scripts. Below is an example of:

    • Setting a node name

    • Adding PostgreSQL persistence

    • Adding a PostgreSQL Connection String

    Building a Custom Container

    If you wish to build more features, modify, or hardcode Environment Variables into your container, then create a Dockerfile

    NOTE: Dockerfiles' are case-sensitive and must start with a capital 'D'.

    To create a Docker image that can persist the Universal data, create a dockerfile like the one below.

    This Dockerfile exposes port 5000, creates a /data volume, sets configuration environment variables to store the Universal repository and database in the volume and then sets the Universal.Server as the entry point to the container.

    Writing a Dockerfile script for Linux

    Building a container

    From the path that hosts your Dockerfile, run the following command:

    Windows

    Run a build with the build command:

    Start the docker container with the run command and make sure to specify the volume to mount:

    SQL

    To use SQL persistence, define the plugin and connection string as follows:

    PostgreSQL

    To use PostgreSQL persistence, define the plugin and connection string as follows:

    Time Zones

    To properly support time zones on Linux when scheduling jobs, include the tzdata package in your dockerfile along with an environment variable that specifies the server time zone.

    Tags

    We publish the following tags to Docker Hub:

    • latest - Current version using Ubuntu LTS

    • 5.x-preview-modules - Nightly build of version 5 using Ubuntu LTS and select AZ modules

    • 5.x-preview-<OS>-<PS> - Nightly build of version 5 with the specified OS and PS version

    • 4.x-preview-<OS>-<PS> - Nightly build of version 4 with the specified OS and PS version

    • 5.x-<OS>-<PS> - Production version 5 with the specified OS and PS version

    • 5.x-modules - Current production version on Ubuntu LTS with select AZ modules installed

    • 4.x-<OS>-<PS> - Current production version 4 with the specified OS and PS versions

    Included Modules

    The module container images include the following modules:

    • Az.Accounts

    • Az.Compute

    • Az.KeyVault

    • Az.Resources

    • Invoke-SqlCmd2

    Summary

    This basic "How to Get Started" enables you to start running or building PSU Containers. This references section links all sources for commands:

    References

    Running Containers

    https://docs.docker.com/engine/reference/commandline/run/

    https://docs.docker.com/engine/reference/commandline/stop/

    https://docs.docker.com/engine/reference/commandline/rm/

    https://docs.docker.com/compose/

    Building Containers

    https://docs.docker.com/engine/reference/commandline/build/

    this forum thread
    Footer

    To create an app bar that is pinned to the bottom of the page, you can use the -Footer parameter.

    Relative Footer

    A relative footer always stays at the bottom of the document. If the contents of the page do not take up 100% of the screen height, the footer will be positioned at the bottom of the view. If the content is greater than 100% of the screen height, the footer will only be visible when scrolled to th bottom of the correct.

    Fixed AppBar

    A fixed AppBar will show even when the screen is scrolled. It will remain stuck to the top. This example creates an AppBar that is fixed with a div that is 10000 pixels high.

    API

    • New-UDAppBar

     Start-Process msiexec.exe -ArgumentList "/I C:\Users\adamr\Downloads\PowerShellUniversal.5.5.2.msi /q /norestart /L*V `"C:\users\adamr\desktop\msi.log.txt`" STARTSERVICE=0 SERVICEACCOUNT=contoso\service_account SERVICEACCOUNTPASSWORD=ThisPasswordWillBeReplacedWithAsterisksInTheMSILogs" -Wait -NoNewWindow
    Expand-Archive -Path .\Universal.zip -DestinationPath .\Universal
    Get-ChildItem .\Universal -Recurse | Unblock-File
    Start-Process .\Universal\Universal.Server.exe
     wget https://imsreleases.blob.core.windows.net/universal/production/5.5.2/Universal.linux-x64.5.2.1.zip
     sudo apt install unzip 
     unzip Universal.linux-x64.5.5.2.zip -d PSU
     chmod +x ./PSU/Universal.Server
     ./PSU/Universal.Server
    # ----
    # This script will install PowerShell Universal on Linux as a service
    # This has been tested on Ubuntu 20.04 (ARM64) on a Raspberry Pi 4
    # ----
    # Dependencies:
    # wget
    # unzip
    #
    # Make sure they are installed
    # ----
    
    # These are used to derive the download URL
    PSU_VERSION="5.5.2" # Change this to the current version
    PSU_ARCH="arm64" # Change this to your desired architecture
    PSU_FILE="Universal.linux-${PSU_ARCH}.${PSU_VERSION}.zip"
    PSU_URL="https://imsreleases.blob.core.windows.net/universal/production/${PSU_VERSION}/${PSU_FILE}"
    
    # These are used for installing PowerShell Universal
    # If you'd like to use a different path, change this
    PSU_PATH="/opt/psuniversal"
    PSU_EXEC="${PSU_PATH}/Universal.Server"
    
    # These are for installing it as a service
    PSU_SERVICE="psuniversal"
    PSU_USER="psuniversal"
    
    # ----
    # BEGIN
    # ----
    
    echo "Creating $PSU_PATH and granting access to $USER"
    sudo mkdir $PSU_PATH
    sudo setfacl -m "u:${USER}:rwx" $PSU_PATH
    
    echo "Creating user $PSU_USER and making it the owner of $PSU_PATH"
    sudo useradd $PSU_USER -m
    sudo chown $PSU_USER -R $PSU_PATH
    
    echo "Downloading PowerShell Universal $PSU_VERSION ($PSU_ARCH)"
    wget -q $PSU_URL -O $PSU_FILE
    
    echo "Extracting $PSU_FILE to $PSU_PATH"
    unzip -o -qq $PSU_FILE -d $PSU_PATH
    
    echo "Make $PSU_EXEC executable"
    sudo chmod +x $PSU_EXEC
    
    echo "Creating service configuration"
    cat <<EOF > ~/$PSU_SERVICE.service
    [Unit]
    Description=PowerShell Universal
    [Service]
    ExecStart=$PSU_EXEC
    SyslogIdentifier=psuniversal
    User=$PSU_USER
    Restart=always
    RestartSec=5
    [Install]
    WantedBy=multi-user.target
    EOF
    
    echo "Creating and starting service"
    sudo cp -f ~/$PSU_SERVICE.service /etc/systemd/system
    sudo systemctl daemon-reload
    sudo systemctl enable $PSU_SERVICE
    sudo systemctl start $PSU_SERVICE
    sudo systemctl status $PSU_SERVICE --no-pager
    
    # If you don't use UFW, you can comment this out
    echo "Allow port 5000/tcp"
    sudo ufw allow 5000/tcp
    
    # ----
    # END
    # ----
    Install-Module Universal
    Install-PSUServer -LatestVersion
    docker pull ironmansoftware/universal-agent:latest
    Update-Module Universal
    Import-Module Universal -PassThru
    Update-PSUServer
    Get-ChildItem -Recurse | Unblock-File
    psu db schema latest --connection-string "Data Source=C:\ProgramData\UniversalAutomation\database.db"
    .\psu.exe db schema --schema-version 5.4.0
    {
       "API" : {
          "URL": "http://localhost:5000"
       }
    }
    .\psu.exe db convert --Path "$ENV:ProgramData\UniversalAutomation\database.db"
    {
        "Plugins": [
            "SQLite"
        ],
        "Data": {
            "ConnectionString": "Data Source=C:\ProgramData\UniversalAutomation\database.db"
        }
    }
    Install-Module Universal -RequiredVersion 4.4.0
    Remove-PSUServer
    Uninstall-Module Universal
    Install-Module Universal
    Install-PSUServer
    Invoke-PSUScript -Script 'Script1.ps1' -RequiredParameter 'Hello'
    Invoke-PSUScript -Script 'Script1.ps1' -RequiredParameter 'Hello' | Wait-PSUJob
    Get-PSUJobPipelineOutput -JobId 10
    $Job = Get-PSUScript -Name 'Script.ps1' | Get-PSUJob -OrderDirection Descending -First 1
    Get-PSUJobPipelineOutput -Job $Job
    Get-PSUJobOutput -Job $Job
    $Job = Get-PSUScript -Name 'Script.ps1' | Get-PSUJob -OrderDirection Descending -First 1
    Get-PSUJobOutput -Job $Job -AsObject
    $Output = Invoke-PSUScript -Script 'Script1.ps1' -RequiredParameter 'Hello' -Wait
    Invoke-PSUScript -Script 'Script1.ps1' -RequiredParameter 'Hello' | Tee-Object -Variable job | Wait-PSUJob
    
    $Output = Get-PSUJobPipelineOutput -Job $Job
    Get-PSUJobOutput -Job $Job
    Invoke-PSUScript -Script 'Script.ps1' -Integrated
    Invoke-RestMethod http://localhost:5000/api/v1/script/7 -Method POST -Body "" -Headers @{ Authorization = "Bearer appToken" } -ContentType 'application/json'
    $Parameters = @{
        Uri = "http://localhost:5000/api/v1/script/path/PNP.ps1?Server=tester&Domain=test" 
        Method = "POST"
        Headers = @{Authorization = "Bearer $Apptoken"}
        ContentType = 'application/json'
        Body = '{}'
    }
    
    Invoke-RestMethod @Parameters
    $JobContext = @{
        Environment = "PowerShell 7"
    } | ConvertTo-Json
    
    Invoke-RestMethod http://localhost:5000/api/v1/script/7 -Method POST -Body $JobContext -Headers @{ Authorization = "Bearer appToken" } -ContentType 'application/json'
    $JobContext = @{
        Credential = "MyUser"
    } | ConvertTo-Json
    
    Invoke-RestMethod http://localhost:5000/api/v1/script/7 -Method POST -Body $JobContext -Headers @{ Authorization = "Bearer appToken" } -ContentType 'application/json'
    1..100 | % {
        Write-Output "Hello $_"
        Start-Sleep -Milliseconds 100
    }
    New-UDApp -Content {
        New-UDButton -Text "Run Script" -OnClick {
            $Job = Invoke-PSUScript -Name "AppExample.ps1" -Integrated
            while($Job.Status -eq 'Queued' -or $Job.Status -eq 'Running')
            {
                $Output = Get-PSUJobOutput -Job $Job
                Set-UDElement -Id 'output' -Content {
                    $Output | ForEach-Object {
                        $_ +  [Environment]::NewLine
                    }
                }
                $Job = Get-PSUJob -Id $Job.Id
                Start-Sleep -Milliseconds 100
            }
        } -ShowLoading
    
        New-UDElement -Tag 'pre' -Id 'output'
    }
    1..100 | % {
        Write-Progress -Activity "Working..." -PercentComplete $_
        Start-Sleep -Milliseconds 100
    }
    New-UDApp -Content {
        New-UDButton -Text "Run Script" -OnClick {
            Invoke-PSUScript -Name "AppExample.ps1" -Integrated -Wait
        } -ShowLoading
    }
    Read-Host -Prompt "What should I say?"
    New-UDApp -Content {
        New-UDButton -Text "Run Script" -OnClick {
            Invoke-PSUScript -Name "AppExample.ps1" -Integrated -Wait
        } -ShowLoading
    }
    Set-PSUSetting -JobRunId
    docker version
    Client: Docker Engine - Community
     Version:           23.0.1
     API version:       1.42
     Go version:        go1.19.5
     Git commit:        a5ee5b1
     Built:             Thu Feb  9 19:47:01 2023
     OS/Arch:           linux/amd64
     Context:           default
    
    Server: Docker Engine - Community
     Engine:
      Version:          23.0.1
      API version:      1.42 (minimum version 1.12)
      Go version:       go1.19.5
      Git commit:       bc3805a
      Built:            Thu Feb  9 19:47:01 2023
      OS/Arch:          linux/amd64
      Experimental:     false
     containerd:
      Version:          1.6.18
      GitCommit:        2456e983eb9e37e47538f59ea18f2043c9a73640
     runc:
      Version:          1.1.4
      GitCommit:        v1.1.4-0-g5fd4c4d
     docker-init:
      Version:          0.19.0
      GitCommit:        de40ad0
    
    docker-compose version
    docker compose version
    Docker Compose version v2.16.0
    docker run hello-world
    Unable to find image 'hello-world:latest' locally
    latest: Pulling from library/hello-world
    2db29710123e: Pull complete 
    Digest: sha256:ffb13da98453e0f04d33a6eee5bb8e46ee50d08ebe17735fc0779d0349e889e9
    Status: Downloaded newer image for hello-world:latest
    
    Hello from Docker!
    This message shows that your installation appears to be working correctly.
    
    To generate this message, Docker took the following steps:
     1. The Docker client contacted the Docker daemon.
     2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
        (amd64)
     3. The Docker daemon created a new container from that image which runs the
        executable that produces the output you are currently reading.
     4. The Docker daemon streamed that output to the Docker client, which sent it
        to your terminal.
    
    To try something more ambitious, you can run an Ubuntu container with:
     $ docker run -it ubuntu bash
    
    Share images, automate workflows, and more with a free Docker ID:
     https://hub.docker.com/
    
    For more examples and ideas, visit:
     https://docs.docker.com/get-started/
    docker pull ironmansoftware/universal
    docker run --name 'PSU' -it -p 5000:5000 ironmansoftware/universal
    docker pull ironmansoftware/universal
    docker run --name 'PSU' -it -p 80:5000 ironmansoftware/universal
    docker pull ironmansoftware/universal
    docker run --name 'PSU' -it -p 5000:5000 -v C:\docker\volumes\PSU:/root ironmansoftware/universal 
    docker pull ironmansoftware/universal
    docker run --name 'PSU' -it -p 5000:5000 -v /docker/volumes/PSU:/root ironmansoftware/universal 
    docker stop PSU
    docker rm PSU
    docker rm --force PSU
    version: "5.2.1"
    services:
      PSU:
        container_name: PSU
        image: ironmansoftware/universal:latest
        ports:
          - 5000:5000
        restart: unless-stopped
        environment:
          - TZ=Europe/London
        volumes:
          - C:\docker\volumes\PSU:/root
    version: "5.2.1"
    services:
      PSU:
        container_name: PSU
        image: ironmansoftware/universal:latest
        ports:
          - 5000:5000
        restart: unless-stopped
        environment:
          - TZ=Europe/London
        volumes:
          - /docker/volumes/PSU:/root
    docker compose up -d
    Creating network "PSU_default" with the default driver
    Pulling PSU (ironmansoftware/universal:latest)...
    latest: Pulling from ironmansoftware/universal
    7608715873ec: Pull complete
    4e66273c6cfb: Pull complete
    2649c52300c2: Pull complete
    a20175666bc7: Pull complete
    65ce93bc0653: Pull complete
    Digest: sha256:d7ff98e6197d21070aac325c2efbefa393a4952d2e8ba6b1327dc97824ec4d55
    Status: Downloaded newer image for ironmansoftware/universal:latest
    Creating PSU ... done
    docker compose down
    [+] Running 2/2
     â ¿ Container PSU         Removed                                           0.5s
     â ¿ Network PSU_default   Removed                                           0.4s
    version: "5.2.1"
    services:
      PSU:
        container_name: PSU
        image: ironmansoftware/universal:latest
        ports:
          - 5000:5000
        restart: unless-stopped
        environment:
          - TZ=Europe/London
          - Plugins__0=SQL
          - Data__ConnectionString=Data Source=sql1.domain.com;Initial Catalog=PSUTicketBridge;User Id=psu_ticketbridge_dbo;Password=Password123;TrustServerCertificate=True;Trusted_Connection=True;integrated security=false;
          - NodeName=mynodename
        volumes:
          - /docker/volumes/PSU:/root
    version: "5.2.1"
    services:
      PSU:
        container_name: PSU
        image: ironmansoftware/universal:latest
        ports:
          - 5000:5000
        restart: unless-stopped
        environment:
          - TZ=Europe/London
          - Plugins__0=PostgreSQL
          - Data__ConnectionString=Host=PGhostname; Database=PGdatabase; User Id=PGusername; Password=PGpassword!;Port=5432
          - NodeName=mynodename
        volumes:
          - /docker/volumes/PSU:/root
    FROM ironmansoftware/universal:latest
    LABEL description="Universal - The ultimate platform for building web-based IT Tools" 
    
    EXPOSE 5000
    VOLUME ["/home/data"]
    ENV Data__RepositoryPath /home/data/Repository
    ENV Data__ConnectionString Data Source=/home/data/database.db
    ENV UniversalDashboard__AssetsFolder /home/data/UniversalDashboard 
    ENV Logging__Path /home/data/logs/log.txt
    ENTRYPOINT ["./Universal/Universal.Server"]
    docker build . --tag=universal-persistent
    FROM ironmansoftware/universal:5.0.0-windowsservercore-1809
    LABEL description="Universal - The ultimate platform for building web-based IT Tools" 
    
    EXPOSE 5000
    VOLUME ["C:/data"]
    ENV Data__RepositoryPath C:/data/Repository
    ENV Data__ConnectionString Data Source=C:/data/database.db
    ENV UniversalDashboard__AssetsFolder C:/data/UniversalDashboard 
    ENV Logging__Path C:/data/logs/log.txt
    ENTRYPOINT ["C:/ProgramData/Universal/Universal.Server.exe"]
    docker build . --tag=universal-persistent
    docker run -it --name powershelluniversal --mount source=psudata,target=/home/data --rm -d  -p 5000:5000/tcp universal-persistent:latest
    ENV Data__ConnectionString=Data Source=ServerName; Initial Catalog=DatabaseName; Integrated Security=SSPI;
    ENV Plugins:0=SQL
    ENV Data__ConnectionString=Host=PGhostname; Database=PGdatabase; User Id=PGusername; Password=PGpassword!;Port=5432
    ENV Plugins:0=PostgreSQL
    ENV TZ Europe/Amsterdam
    RUN apt-get install -y tzdata
    $Drawer = New-UDDrawer -Children {
        New-UDList -Children {
            New-UDListItem -Label "Home"
            New-UDListItem -Label "Getting Started" -Children {
                New-UDListItem -Label "Installation" -OnClick {}
                New-UDListItem -Label "Usage" -OnClick {}
                New-UDListItem -Label "FAQs" -OnClick {}
                New-UDListItem -Label "System Requirements" -OnClick {}
                New-UDListItem -Label "Purchasing" -OnClick {}
            }
        }
    }
    
    New-UDAppBar -Position relative -Children { New-UDElement -Tag 'div' -Content { "Title" } } -Drawer $Drawer
    New-UDAppBar -Children { "Hello" } -Footer
    New-UDApp -Title 'PowerShell Universal' -Pages @(
        New-UDPage -Title home -Name home -Blank -HideNavigation -Content {
            New-UDHelmet -Tag 'style' -Content '
                #Footer {
                    position: relative;
                }
                #Footer + div {
                    display: none
                }
                #content {
                    min-height: calc(100vh - 128px);
                }
            '
            New-UDAppBar -Position sticky -ClassName header -DisableThemeToggle -Children {
                New-UDParagraph -Text "Header"
            }
            New-UDElement -Tag 'div' -Content {
                1..100 | % {
                    New-UDTypography -Text 'Hello' -Variant h1
                }
            } -Id 'content'
            New-UDAppBar -Id Footer -Footer -Children {
                New-UDParagraph -Text "Footer"
            }
        }
    )
    New-UDAppBar -Position fixed -Children { New-UDElement -Tag 'div' -Content { "Title" } }
    
    New-UDElement -Tag 'div' -Content {
    
    } -Attributes @{
        style = @{
            height = "10000px"
        }
    }
    Select
  • Slider

  • Switch

  • Textbox

  • Time Picker

  • Transfer List

  • Upload

  • Autocomplete
    Checkbox
    Date Picker
    Radio
    react-jsonschema-form
    New-UDForm
    New-UDFormValidationResult
    Edit App Page

    As an example, you could add a button to your page.

    Pages are automatically added to apps with the -AutoInclude parameter. Simply call New-UDApp in the root of your app's PS1 file.

    Basic Page

    A basic page can be defined using the New-UDPage cmdlet. You could navigate to this page by visiting the /appURL of your dashboard.

    App with Multiple Pages

    Apps can have multiple pages, and those pages can be defined by passing an array of UDPages to New-UDApp

    You may want to organize your app into multiple PS1 files. You can do this using pages.

    Page with a Custom URL

    A page can have a custom URL by using the -Url parameter. You could navigate to this page by visiting the /db URL of your app.

    Page with Variables in URL

    You can define a page with variables in the URL to create pages that adapt based on that URL.

    Query string parameters

    Query string parameters are passed to pages and other endpoints as a hashtable variable called $Query.

    For example, if you visited a page with the following query string parameter: http://localhost:5000/dashboard/Page1?test=123

    You would have access to this value using the following syntax:

    Role-Based Access

    This feature requires a license.

    You can prevent users from accessing pages based on their role by using the -Role parameter of pages. You can configure roles and role policies on the Security page.

    Header

    The following options are available for customizing the header.

    Position

    Use the -HeaderPosition parameter to adjust the behavior of the header.

    • absolute\fixed - Remains at the top of the page, even when scrolling

    • relative - Remains at the top of the page. Not visible when scrolling.

    Colors

    You can adjust the colors of the header by specifying the -HeaderColor and -HeaderBackgroundColor parameters. These colors will override the theme colors.

    Navigation

    You can customize the navigation of a page using the -Navigation and -NavigationLayout parameters. Navigation is defined using the List component. Navigation layouts are either permanent or temporary.

    Custom Navigation

    Custom navigation can be defined with a list. List items can include children to create drop down sections in the navigation.

    Custom navigation

    Dynamic Navigation

    Dynamic navigation can be used to execute scripts during page load to determine which navigation components to show based on variables like the user, IP address or roles.

    You can generate dynamic navigation by using the -LoadNavigation parameter. The value of the parameter should be a script block to execute when loading the navigation.

    Role-Based Access

    You can use dynamic navigation to create a navigation menu that takes advantage of roles. Use Protect-UDSection to limit who has access to particular menu items. Ensure that you also include the same role on the page.

    Layouts

    The permanent layout creates a static navigation drawer on the left hand side of the page. It cannot be hidden by the user.

    Permanent navigation drawer

    The temporary layout creates a navigation drawer that can be opened using a hamburger menu found in the top left corner. This is the default setting.

    Temporary navigation drawer

    Horizontal Navigation

    Horizontal Navigation

    You can use New-UDAppBar with a blank page to create horizontal navigation.

    Logo

    You can display a logo in the navigation bar by using the -Logo parameter.

    First, setup a published folder to host your logo.

    Published assets folder

    Now, when creating your page, you can specify the path to the logo.

    The logo will display in the top left corner.

    Logo

    To customize the style of your logo, you can use a cascading style sheet and target the ud-logo element ID.

    Header Content

    You can define custom content to include in the header by using the -HeaderContent parameter.

    Button in Header

    Dynamic Page Title

    Page titles are static by default, but you can override this behavior by using -LoadTitle. It will be called when the page is loaded. This is useful when defining pages in multilingual dashboards.

    Static Pages

    Static pages allow for better performance by not executing PowerShell to load the content of the page. This can be useful when displaying data that does not require dynamic PowerShell execution. The page content is constructed when the dashboard is started.

    Static pages do not have access to user specific data. This includes variables such as:

    • $Headers

    • $User

    • $Roles

    You can still include dynamic regions within pages. These dynamic regions will have access to user data. Reloading the below example will update the date and time listed in the page.

    API

    New-UDPage

    Create App Page Button
    Pages Tab

    Endpoints

    Endpoint configuration for Universal APIs.

    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.

    To invoke the above method, you can use Invoke-RestMethod.

    When defining endpoints in the management API, you can skip the New-PSUEndpoint call, as the admin console defines it.

    API Properties

    The only contents that you need to provide in the editor are the script you wish to call.

    API Content

    Avoid using endpoint URLs that match internal PowerShell Universal Management API URLs, as this causes unexpected behavior. You can reference the for the to verify that none of the URLs match.

    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.

    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.

    To call this API and specify the ID, do the following:

    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.

    The resulting Invoke-RestMethod call must then include the query string parameter.

    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.

    Security Considerations

    When accepting input via Query String parameters you may be vulnerable to . 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.

    In order to avoid this particular issue, you can use a param block.

    Headers

    Request headers are available in APIs using the $Headers variable. The variable is a hashtable. To access a header, use the following syntax:

    Cookies

    Request cookies are available in APIs using the $Cookies variable. The variable is a hashtable. To access a cookie, use the following syntax:

    Send back request cookies with the New-PSUApiResponse cmdlet. Use the -Cookies parameter with a supplied hashtable.

    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.

    To call the above endpoint, specify the body of Invoke-RestMethod.

    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.

    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.

    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.

    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.

    Form Data

    You can pass data to an endpoint as form data. Form data will pass into your endpoint as parameters.

    You can then use a hashtable with Invoke-RestMethod to pass form data.

    JSON Data

    You can pass JSON data to an endpoint and it will automatically bind to a param block.

    You can then send JSON data to the endpoint.

    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.

    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.

    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.

    That said, you cannot specify additional parameters to the endpoint. Doing the following will cause an error.

    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.

    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.

    The multipart/form-datacontent type is not supported for uploading files to APIs.

    You can also save the file into a directory.

    Downloading Files

    You can send files down using the New-PSUApiResponse cmdlet.

    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.

    You can also return custom body data with the -Body parameter of New-PSUApiResponse.

    Invoking the REST method returns the custom error code.

    You can control the content type of the returned data with the -ContentType parameter.

    You can control the response headers with a hashtable of values that you pass to the -Headersparameter.

    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 for your API. Set the -PersistentRunspace parameter to enable this feature. This is configured in the environments.ps1 script.

    You can then assign the API environment in the settings.ps1 script.

    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:

    C# APIs

    C# APIs are enabled as a .

    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.

    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.

    Some other useful services include:

    • IDatabase

    • IApiService

    • IConfigurationService

    • IJobService

    You can choose to return an ApiResponse from your endpoint.

    Once you have defined your C# endpoint file, you can add it by editing endpoints.ps1.

    The PowerShell Universal service automatically compiles and runs C# endpoints.

    API

    New-UDForm -Content {
        New-UDTextbox -Id 'txtTextfield'
        New-UDCheckbox -Id 'chkCheckbox'
    } -OnSubmit {
        Show-UDToast -Message $EventData.txtTextfield
        Show-UDToast -Message $EventData.chkCheckbox
    }
    New-UDForm -Content {
    
        New-UDRow -Columns {
            New-UDColumn -SmallSize 6 -LargeSize 6 -Content {
                New-UDTextbox -Id 'txtFirstName' -Label 'First Name' 
            }
            New-UDColumn -SmallSize 6 -LargeSize 6 -Content {
                New-UDTextbox -Id 'txtLastName' -Label 'Last Name'
            }
        }
    
        New-UDTextbox -Id 'txtAddress' -Label 'Address'
    
        New-UDRow -Columns {
            New-UDColumn -SmallSize 6 -LargeSize 6  -Content {
                New-UDTextbox -Id 'txtState' -Label 'State'
            }
            New-UDColumn -SmallSize 6 -LargeSize 6  -Content {
                New-UDTextbox -Id 'txtZipCode' -Label 'ZIP Code'
            }
        }
    
    } -OnSubmit {
        Show-UDToast -Message $EventData.txtFirstName
        Show-UDToast -Message $EventData.txtLastName
    }
    New-UDForm -Content {
        New-UDTextbox -Id 'txtTextfield'
    } -OnSubmit {
        New-UDTypography -Text $EventData.txtTextfield
    }
    New-UDForm -Content {
        New-UDTextbox -Id 'txtValidateForm'
    } -OnValidate {
        $FormContent = $EventData
    
        if ($FormContent.txtValidateForm -eq $null -or $FormContent.txtValidateForm -eq '') {
            New-UDFormValidationResult -ValidationError "txtValidateForm is required"
        } else {
            New-UDFormValidationResult -Valid
        }
    } -OnSubmit {
        Show-UDToast -Message $Body
    }
    New-UDButton -Text 'On Form' -OnClick {
        Show-UDModal -Content {
            New-UDForm -Content {
                New-UDTextbox -Label 'Hello'
            } -OnSubmit {
                Show-UDToast -Message 'Submitted!'
                Hide-UDModal
            } -OnCancel {
                Hide-UDModal
            }
        }
    }
    New-UDForm -Content {
    
    } -OnSubmit {
       Set-UDElement -Id 'results' -Content {
          New-UDCard -Content { "Hello " + (Get-Date) }
       }
    }
    
    New-UDElement -Id 'results' -Tag 'div'
    New-UDForm -Schema @{
       title = "Test Form"
       type = "object"
       properties = @{
           name = @{
               type = "string"
           }
           age = @{
               type = "number"
           }
       }
    } -OnSubmit {
       # $EventData.name
       # $EventData.age
    }
    New-UDForm -Schema @{
       title = "Test Form"
       type = "object"
       properties = @{
           name = @{
               type = "string"
           }
           age = @{
               type = "number"
           }
       }
       required = @('name')
    } -OnSubmit {
       # $EventData.name
       # $EventData.age
    }
    New-UDForm -Schema @{
            title = "Test"
            type = "object"
            properties = @{
                hostname = @{
                    title = "Hostname"
                    type = "string"
                    }
                ipaddress= @{
                    title = "IP Address"
                    type = "string"
                    format = "ipv4"
                    }
                description = @{
                    title = "Server Description"
                    type = "string"
                    }
                servertype = @{
                    title = "Server Type"
                    type = "string"                            
                    enum = "App","DB"
                    }
                environment = @{
                    title = "Environment"
                    type = "string"
                    enum = "Prod", "Dev" , "QA"
                    }
                }
    		required = @('hostname','ipaddress','description','servertype','environment')                    
    	} -uiSchema @{
    		"ui:order" = @('environment','hostname','ipaddress','description')
    	} -OnSubmit {
    		Show-UDModal -Content {                        
    			New-UDTypography -Text $EventData
    		} -Footer {
    			New-UDButton -Text "Close" -OnClick {Hide-UDModal}
    		} -Persistent
    	}
    New-UDForm -Schema @{
       title = "Test Form"
       type = "array"
       items = @{
          type = "object" 
           properties = @{
               name = @{
                   type = "string"
               }
               age = @{
                   type = "number"
               }
           }
       }
    } -OnSubmit {
       # $EventData[0].name
       # $EventData[0].age
    }
    New-UDForm -Script "Script.ps1" -OutputType 'text'
    New-UDPage -Name 'Users' -Url '/users' -Content {
        New-UDButton -Text 'What page is this?' -OnClick {
            Show-UDToast $UDPage
        }
    } -AutoInclude
    New-UDApp 
    $Pages = @()
    $Pages += New-UDPage -Name 'App' -Content {
        New-UDTypography -Text 'App'
    }
    
    New-UDApp -Title 'Pages' -Pages $Pages
    $Pages = @()
    $Pages += New-UDPage -Name 'App One' -Content {
        New-UDTypography -Text 'App Two'
    }
    
    $Pages += New-UDPage -Name 'App Two' -Content {
        New-UDTypography -Text 'App Two'
    }
    
    New-UDApp -Title 'Pages' -Pages $Pages
    $UDScriptRoot = $PSScriptRoot
    $Pages = @()
    $Pages += New-UDPage -Name 'App One' -Content {
        . "$UDScriptRoot\db1.ps1"
    }
    
    $Pages += New-UDPage -Name 'App Two' -Content {
        . "$UDScriptRoot\db2.ps1"
    }
    
    New-UDApp -Title 'Pages' -Pages $Pages
    $Pages = @()
    $Pages += New-UDPage -Name 'App' -Url '/db' -Content {
        New-UDTypography -Text 'App'
    }
    
    New-UDApp -Title 'Pages' -Pages $Pages
    $Pages = @()
    $Pages += New-UDPage -Name 'Dashboard' -Url '/db/:user' -Content {
        New-UDTypography -Text 'Dashboard for user: $User'
    }
    
    New-UDApp -Title 'Pages' -Pages $Pages
    $Query.test
    $Query['test']
    $Pages = @()
    $Pages += New-UDPage -Name 'Administrators' -Content {
        New-UDTypography -Text 'Dashboard for user: $User'
    } -Role 'Administrator'
    
    $Pages += New-UDPage -Name 'Operators' -Content {
        New-UDTypography -Text 'Dashboard for user: $User'
    } -Role 'Operator'
    
    New-UDApp -Title 'Pages' -Pages $Pages
    New-UDPage -HeaderPosition fixed -Content {
        New-UDElement -tag div -Attributes @{
            style = @{
                height = '150vh'
            }
        }
    }
    New-UDPage -Name 'Home' -Content {
    } -HeaderColor 'black' -HeaderBackgroundColor 'white'
    $Navigation = @(
        New-UDListItem -Label "Home"
        New-UDListItem -Label "Getting Started" -Children {
            New-UDListItem -Label "Installation" -Href '/Installation' 
            New-UDListItem -Label "Usage" -Href '/Usage' 
            New-UDListItem -Label "FAQs" -Href '/faqs' 
            New-UDListItem -Label "System Requirements" -Href'/requirements' 
            New-UDListItem -Label "Purchasing" -Href '/purchasing' 
        }
    )
    
    $Pages = @()
    $Pages += New-UDPage -Name 'Installation' -Content {
     New-UDTypography -Text "Installation"
    }
    
    $Pages += New-UDPage -Name 'Usage' -Content {
        New-UDTypography -Text "Usage"
    } 
    
    New-UDApp -Title "Hello, World!" -Pages $Pages -NavigationLayout permanent -Navigation $Navigation
    $Navigation = {
        New-UDListItem -Label "Home - $(Get-Date)"
        New-UDListItem -Label "Getting Started" -Children {
            New-UDListItem -Label "Installation" -Href '/installation' 
            New-UDListItem -Label "Usage" -Href '/usage' 
            New-UDListItem -Label "FAQs" -Href '/faqs' 
            New-UDListItem -Label "System Requirements" -Href'/requirements' 
            New-UDListItem -Label "Purchasing" -Href '/purchasing' 
        }
    }
    
    $Pages = @()
    $Pages += New-UDPage -Name 'Test' -Content {
     New-UDTypography -Text "Hello"
    } -NavigationLayout permanent -LoadNavigation $Navigation
    
    $Pages += New-UDPage -Name 'Test2' -Content {
        New-UDTypography -Text "Hello"
    } -NavigationLayout permanent -LoadNavigation $Navigation
    
    
    New-UDApp -Title "Hello, World!" -Pages $Pages
    $Navigation = {
        New-UDListItem -Label "Home" -Href '/Home' 
        Protect-UDSection -Role "Administrator" -Content {
            New-UDListItem -Label "Admins" -Href '/Admins' 
        }
    }
    
    $Pages = @()
    $Pages += New-UDPage -Name 'Home' -Content {
     New-UDTypography -Text "Hello"
    } -NavigationLayout permanent -LoadNavigation $Navigation
    
    $Pages += New-UDPage -Name 'Admins' -Content {
        New-UDTypography -Text "Hello"
    } -NavigationLayout permanent -LoadNavigation $Navigation -Roles "Administrator"
    
    New-UDApp -Title "Hello, World!" -Pages $Pages
    $Pages = @()
    $Pages += New-UDPage -Name 'Test' -Content {
     New-UDTypography -Text "Hello"
    } -NavigationLayout permanent
    
    $Pages += New-UDPage -Name 'Test2' -Content {
        New-UDTypography -Text "Hello"
    } -NavigationLayout permanent
    
    
    New-UDApp -Title "Hello, World!" -Pages $Pages
    $Pages = @()
    $Pages += New-UDPage -Name 'Test' -Content {
     New-UDTypography -Text "Hello"
    } -NavigationLayout temporary
    
    $Pages += New-UDPage -Name 'Test2' -Content {
        New-UDTypography -Text "Hello"
    } -NavigationLayout temporary
    
    
    New-UDApp -Title "Hello, World!" -Pages $Pages
    New-UDApp -Title 'PowerShell Universal' -Pages @(
        New-UDPage -Name 'Page' -Content {
            New-UDAppBar -Children {
                New-UDTypography -Text "Title" -Variant h4 -Style @{
                    marginRight = "50px"
                }
                New-UDMenu -Variant text -Text "Settings" -Children {
                    New-UDMenuItem -Text 'Item 1' -OnClick { Invoke-UDRedirect "/item1" }
                    New-UDMenuItem -Text 'Item 2' -OnClick { Invoke-UDRedirect "/item1" }
                    New-UDMenuItem -Text 'Item 3' -OnClick { Invoke-UDRedirect "/item1" }
                }
                New-UDMenu -Variant text -Text "Options" -Children {
                    New-UDMenuItem -Text 'Item 1' -OnClick { Invoke-UDRedirect "/item1" }
                    New-UDMenuItem -Text 'Item 2' -OnClick { Invoke-UDRedirect "/item1" }
                    New-UDMenuItem -Text 'Item 3' -OnClick { Invoke-UDRedirect "/item1" }
                }
                New-UDMenu -Variant text -Text "Tools" -Children {
                    New-UDMenuItem -Text 'Item 1' -OnClick { Invoke-UDRedirect "/item1" }
                    New-UDMenuItem -Text 'Item 2' -OnClick { Invoke-UDRedirect "/item1" }
                    New-UDMenuItem -Text 'Item 3' -OnClick { Invoke-UDRedirect "/item1" }
                }
            } -DisableThemeToggle
        } -Blank
    ) 
    New-UDPage -Name 'Home' -Logo '/assets/favicon.png' -Content {
    }
    $Page = New-UDPage -Name 'Home' -Content {
    
    } -HeaderContent {
        New-UDButton -Icon (New-UDIcon -Icon Users) -Text 'User'
    }
    
    New-UDApp -Title "Dashboard" -Pages $Page
    New-UDPage -Name "Home" -LoadTitle { "Current Time" + (Get-Date) } -Content { } 
    New-UDPage -Name 'Static Page' -Content {
        New-UDTypography (Get-Date)
    } -Static
    New-UDPage -Name 'Static Page' -Content {
       New-UDDynamic -Content {
           New-UDTypography (Get-Date)
       }
    } -Static
    New-PSUEndpoint -Url '/endpoint' -Method 'GET' -Endpoint {
       "Hello, world!"
    }
    Invoke-RestMethod http://localhost:5000/endpoint
    Set-PSUSetting
    OpenAPI documentation
    Management API
    CWE-914: Improper Control of Dynamically-Identified Variables
    environment
    plugin
    New-PSUEndpoint
    Get-PSUEndpoint
    Remove-PSUEndpoint
    New-PSUApiResponse
    Endpoint Live Log
    Endpoint Test Tab
    Test History
    New-PSUEndpoint -Url '/user' -Method @('GET', 'POST') -Endpoint {
        if ($Method -eq 'GET')
        {
           Get-User
        }
        else {
           New-User
        }
    }
    New-PSUEndpoint -Url '/user/:id' -Method 'GET' -Endpoint {
       Get-User -Id $Id
    }
    Invoke-RestMethod http://localhost:5000/user/123
    New-PSUEndpoint -Url '/user' -Method 'GET' -Endpoint {
       Get-User -Id $Id
    }
    Invoke-RestMethod http://localhost:5000/user?Id=123
    Invoke-RestMethod "http://localhost:5000/user?Id=123&name=tim"
    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"
    	}
    }
    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['Content-Type']
    $Cookies['Request-Cookie']
    New-PSUApiResponse -StatusCode 200 -Cookies @{
        ResponseCookie = '123'
    }
    New-PSUEndpoint -Url '/user' -Method Post -Endpoint {
        $User = ConvertFrom-Json $Body 
        New-User $User
    }
    Invoke-RestMethod http://localhost:5000/user -Method Post -Body "{'username': 'adam'}"
    Invoke-RestMethod -Uri 'http://localhost:5000/test-api?Page=1' -Headers @{'X-Custom-Header' = 'Value';} -Method 'POST'
    New-PSUEndpoint -Url '/user' -Method Post -Endpoint {
        param([Parameter(Mandatory)]$userName, $FirstName, $LastName)
         
        New-User $UserName -FirstName $FirstName -LastName $LastName
    }
    Invoke-RestMethod http://localhost:5000/user -Method Post -Body @{ 
        UserName = "adriscoll"
        FirstName = "Adam"
        LastName = "Driscoll"
    }
    New-PSUEndpoint -Url '/user' -Method Post -Endpoint {
        param([Parameter(Mandatory)]$userName, $FirstName, $LastName)
         
        New-User $UserName -FirstName $FirstName -LastName $LastName
    }
    Invoke-RestMethod http://localhost:5000/user -Method Post -Body (@{ 
        UserName = "adriscoll"
        FirstName = "Adam"
        LastName = "Driscoll"
    } | ConvertTo-Json) -ContentType 'application/json'
    New-PSUEndpoint -Url '/user/:name' -Endpoint {
        param([Parameter(Mandatory)$Name, $Role = "Default")
    }
    New-PSUEndpoint -Url '/user/:name' -Endpoint {
        param($Role = "Default")
        
        $Name -eq 'Adam'
    }
    New-PSUEndpoint -Url '/user' -Endpoint {
        param([Parameter(Mandatory)$Name)
    }
    Invoke-RestMethod http://localhost:5000/user -Method Post -Body (@{ 
        Name = "adriscoll"
        DisplayName = 'Adam'
    } | ConvertTo-Json) -ContentType 'application/json'
    New-PSUEndpoint -Url '/user' -Endpoint {
        param($Name)
    }
    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,…
    New-PSUEndpoint -Url '/file' -Method Post -Endpoint {
        [IO.File]::WriteAllBytes("tempfile.dat", $Data)
    }
    New-PSUEndpoint -Url '/image' -Endpoint {
        $ImageData = [IO.File]::ReadAllBytes("image.jpeg")
        New-PSUApiResponse -ContentType 'image/jpg' -Data $ImageData
    }
    New-PSUEndpoint -Url '/file' -Method Get -Endpoint {
        New-PSUApiResponse -StatusCode 410
    }
    New-PSUEndpoint -Url '/file' -Method Get -Endpoint {
        New-PSUApiResponse -Body "Not what you're looking for." -StatusCode 404
    }
    PS C:\Users\adamr\Desktop> invoke-restmethod http://localhost:8080/file
    
    Invoke-RestMethod: Not what you're looking for.
    New-PSUEndpoint -Url '/file' -Method Get -Endpoint {
        New-PSUApiResponse -Body "<xml><node>1</node><node2>2</node2></xml>" -ContentType 'text/xml'
    }
    New-PSUApiResponse -StatusCode 200 -Headers @{
        "Referrer-Policy" = "no-referrer"
    }
    New-PSUEnvironment -Name 'Env' -Path 'powershell.exe' -PersistentRunspace
    Set-PSUSetting -ApiEnvironment 'Env'
    New-PSUEndpoint -Url "/path" -Path "endpoint-path.ps1"
    public class ApiRequest
    {
        public long Id;
        public ICollection<KeyValue> Variables;
        public IEnumerable<ApiFile> Files { get; set; };
        public string Url;
        public ICollection<KeyValue> Headers;
        public byte[] Data;
        public int ErrorAction;
        public ICollection<KeyValue> Parameters;
        public string Method;
        public ICollection<KeyValue> Cookies;
        public string ClaimsPrincipal;
        public string ContentType;
    }
    var dm = ServiceProvider.GetService(typeof(IDashboardManager));
    var dashboard = dm.GetDashboard(1);
    dm.Restart(dashboard);
    return new ApiResponse {
        StatusCode = 404
    };
    New-PSUEndpoint -Url /csharp -Path endpoint.cs -Environment 'C#'

    Data Grid

    Data grid component for Universal Apps.

    The UDDataGrid component is an advanced version of the table that is useful for displaying large amounts of data. It supports many of the same features as the table but also provides complex filtering, row virtualization, multi-column sort and more.

    Simple Data Grid

    Data grids load their data via the -LoadRows event handler. You will need to return a hashtable that contains the row data and the total number of rows.

    Columns are defined using hashtables.

    Columns

    Columns are customizable using New-UDDataGridColumn. More information on this cmdlet can be found .

    Rendering Custom Columns

    You can render custom components in columns by specifying render within the column hashtable. You can access the current row's data by using the $EventData or $Row variable

    In this example, the number is shown in the name column with a New-UDTypography component.

    Flexible Width Columns

    Column fluidity or responsiveness can be achieved by setting the flex property of a column.

    The flex property accepts a value between 0 and ∞. It works by dividing the remaining space in the grid among all flex columns in proportion to their flex value.

    For example, consider a grid with a total width of 500px that has three columns: the first with width: 200; the second with flex: 1; and the third with flex: 0.5. The first column will be 200px wide, leaving 300px remaining. The column with flex: 1 is twice the size of flex: 0.5, which means that final sizes will be: 200px, 200px, 100px.

    To set a minimum and maximum width for a flex column set the minWidth and the maxWidth property on the column.

    Auto Sizing Columns

    If you'd like to let the data grid auto size the column widths, you can use the -AutoSizeColumns parameter of New-UDDataGrid. The data grid will evaluate the size of the data and determine the best size for the columns after the data is loaded. This may cause some UI rearrangement after the data loads.

    LoadRows

    The -LoadRows parameter is used to return data for the data grid. Table state will be provided to the event handler as $EventData. You will find the following properties within the $EventData object.

    Property
    Description
    Type

    Paging

    To implement paging, you can access the page and pageSize properties of the $EventData variable. If you are using a remote data source, you will want to implement custom paging logic. Below is an example of using the paging properties to page through the rows locally. Depending on your data source (e.g. SQL), you may page through data differently.

    Out-UDDataGridData automatically implements paging, and you do not need to do the above if you have all your data in memory. The above is just used for demonstration purposes.

    Filtering

    The data grid supports filtering data based on each column. Multiple filters can be defined to allow the user to narrow down the displayed data set. By default, the Out-UDDataGridData cmdlet implements filtering for local data. When using a remote data source, like SQL, it is suggested to implement custom filtering to improve user experience and performance.

    Filter Data Structure

    The filter object is included in the $EventData for the -LoadRows event handler when a filter is defined. The object has a structure as follows.

    Items

    The items property contains a collection of fields, operators and values. You can use these to filter your data.

    Property
    Description
    Type

    LogicOperator

    The logic operator field is used to specify the link between the filters. This can be and or or.

    QuickFilterValues

    Contains a collection of quick filter values that you can chose how to apply to your data.

    QuickFilterLogicOperator

    Contains the logic operator for the quick filter values specified by the user. This can be and or or.

    Custom Filter

    The Out-UDDataGridData cmdlet provides an implmentation of filtering for static data. If you use this cmdlet, you do not need to implement filtering manually. If you have a remote data source, you will want to provide a custom implementation for filtering. Below is an example of using the filter structure in $EventData to eliminate rows based on the filters provided by the user.

    Custom Quick Filter

    The quick filter is a similar to a simple search box. You can enable quick filtering with the -ShowQuickFilter parameter on New-UDDataGrid. A search box will appear in the top right of the data grid. When the user enters a value in the data grid, the quick filter information will be provided.

    Below is an example of how to use quick filters. Out-UDDataGridData implements quick filtering and is not required when using local data. The below is done for demonstration only.

    Sorting

    The $EventData object will contain a Sort property when the user sorts the data grid. It contains properties for each column that is sorted. The properties will start as 0 and increment as more columns are sorted.

    For example, you can access the first sorted column as follows.

    You will also receive the sort direction for each column.

    Property
    Description
    Type

    Detailed Content

    You can use the -LoadDetailContent event handler to display additional information about the row you are expanding. Information about the current row is available in $EventData.row.

    Example: Nested Data Grids

    You can use the -LoadDetailContent parameter to look up nested data about an object. In this example, we load a data grid of virtual machines and display the name, operating system, memory and CPU cores. Expanding the detail content provides a data grid of the network cards available on the virtual machine. We are using dummy data in this example but you could use any cmdlet available to PowerShell Universal.

    Editing

    Tables provide editor support by specifying the -OnEdit event handler. The new row data will be provided as $EventData. You can chose to return updated row information (for example, adjusting something the user has entered) and return it from the event handler. If you do not return anything, the row will reflect what the user entered.

    The $EventData has the following format.

    Ensure that you provide the editable property to each column you wish for the user to edit.

    Selection

    You can enable row selection using the -CheckboxSelection parameter to display checkboxes for the rows to select. Row selection requires a deterministic ID for the data rows provided. In the below example, you will see each row has a specific ID specified.

    You can access selected data with the -OnSelectionChange event handler or by retrieving the row IDs via Get-UDElement.

    Custom Export

    To override the default export functionality, use the -OnExport event handler. $EventData will be the same context object used for -LoadRows. You should use Out-UDDataGridExport to return the data from -OnExport.

    Multiple Export Types

    When using a custom export, you can use the -ExportOptionsparameter to define multiple export types. When the user selects the export type, you can check the Type property of $EventDatato determine which type of export to produce.

    Example: Static Data

    In this example, we generate an array of 10,000 records. We will create a new function, Out-UDDataGridData to manage the paging, sorting and filtering. This function is already included in the .

    Example: SQL Data

    In this example, we'll query the PowerShell Universal database with dbatools.

    API

    Filter

    A filter object that you can use to construct filters against your data.

    Hashtable

    Page

    The current page. Starts at 0.

    Integer

    PageSize

    The number of records in a page.

    Integer

    Sort

    The sort options for the table

    Hashtable

    Field

    The name of the field to filter

    String

    Operator

    The type of operator to use when filtering the data.

    String

    Value

    The value used to filter

    Object

    Field

    The field to sort.

    String

    Sort

    The direction to sort the field.

    asc, desc

    here
    Universal module
    New-UDDataGrid
    New-UDDataGridColumn
    Out-UDDataGridData
    Out-UDDataGridExport
    New-UDDataGrid -LoadRows {
        $Data = @(
            @{ Name = 'Adam'; Number = Get-Random}
            @{ Name = 'Tom'; Number = Get-Random}
            @{ Name = 'Sarah'; Number = Get-Random}
        )
        $Data | Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
    } -Columns @(
        New-UDDataGridColumn -Field name
        New-UDDataGridColumn -Field number
    ) -AutoHeight $true
    New-UDDataGrid -LoadRows {  
        $Rows = 1..100 | % {
            @{ Name = 'Adam'; Number = Get-Random}
        }
        $Rows| Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
    } -Columns @(
        New-UDDataGridColumn -Field name -Render {
             New-UDTypography $EventData.number 
        }
        New-UDDataGridColumn -Field number
    ) -AutoHeight $true
    New-UDDataGrid -LoadRows {  
        $Rows = 1..100 | % {
            @{ Name = 'Adam'; Number = "This column is a very long string. This column is a very long string. This column is a very long string. This column is a very long string. This column is a very long string. This column is a very long string."}
        }        
        $Rows| Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
    } -Columns @(
        New-UDDataGridColumn -Field name -Render {
             New-UDTypography $EventData.number 
        }
        New-UDDataGridColumn -Field number -Flex 1.0
    ) -AutoHeight $true
    New-UDDataGrid -LoadRows {  
        $Rows = 1..100 | % {
            @{ Name = 'Adam'; Number = "This column is a very long string. This column is a very long string. This column is a very long string. This column is a very long string. This column is a very long string. This column is a very long string."}
        }        
        $Rows| Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
    } -Columns @(
        New-UDDataGridColumn -Field name -Render {
             New-UDTypography $EventData.number 
        }
        New-UDDataGridColumn -Field number
    ) -AutoHeight $true -AutoSizeColumns
    New-UDDataGrid -LoadRows {  
        $Rows = 1..100 | % {
            @{ Name = 'Adam'; Number = Get-Random}
        } 
        
        $Rows = $Rows | Select-Object -First $EventData.pageSize -Skip ($EventData.Page * $EventData.PageSize)
        
        $Rows| Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
    } -Columns @(
        New-UDDataGridColumn -Field name
        New-UDDataGridColumn -Field number
    ) -AutoHeight $true -Pagination
    @{
        items = @(
            @{ 
                field = "Name"
                operator = "contains"
                value = "test"
            }
        )
        logicOperator = "and"
        quickFilterValues = @("test")
        quickFilterLogicOperator = "and"
    }
    New-UDDataGrid -LoadRows {  
        $Rows = 1..100 | % {
            @{ Name = 'Adam'; Number = Get-Random}
        }
    
        foreach($filter in $eventData.Filter.items)
        {
            if ($filter.operator -eq 'equals')
            {
                $Rows = $Rows | Where-Object $filter.field -eq $filter.value
            }
            elseif ($filter.operator -eq 'contains')
            {
                $Rows = $Rows | Where-Object $filter.field -match $filter.value
            }
        }
    
        $Rows| Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
    } -Columns @(
        New-UDDataGridColumn -Field name
        New-UDDataGridColumn -Field number
    ) -AutoHeight $true
    New-UDDataGrid -LoadRows {  
        $Rows = 1..100 | % {
            @{ Name = 'Adam'; Number = Get-Random}
        }
    
        foreach($filter in $eventData.QuickFilterValues)
        {
            $Rows = $Rows | Where-Object $filter.field -match $filter
        }
    
        $Rows| Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
    } -Columns @(
        New-UDDataGridColumn -Field name
        New-UDDataGridColumn -Field number
    ) -AutoHeight $true
    $EventData.Sort.'0'.field
    New-UDDataGrid -LoadRows {
        $Data = @(
            @{ Name = 'Adam'; Number = Get-Random }
            @{ Name = 'Tom'; Number = Get-Random }
            @{ Name = 'Sarah'; Number = Get-Random }
        )
        $Data| Out-UDDataGridData -Context $EventData -TotalRows $Data.Length
    } -Columns @(
        New-UDDataGridColumn -Field name
        New-UDDataGridColumn -Field number
    ) -AutoHeight $true -LoadDetailContent {
        Show-UDToast $Body
        New-UDAlert -Text $EventData.row.Name
    }
    function Get-VirtualMachine {
        1..10 | ForEach-Object {
            [PSCustomObject]@{
                Name = "VM-$_"
                OperatingSystem = @("Windows Server 2019", "Windows Server 2022", "Ubuntu 20.02") | Get-Random
                Memory = @(64, 128, 512, 1024) | Get-Random
                Cores = @(8, 16, 32) | Get-Random
            }
        }
    }
    
    function Get-NetworkCard {
        param($VirtualMachine)
    
        1..4 | ForEach-Object {
            [PSCustomObject]@{
                Name = "NIC-$_"
                Speed = @(64, 128, 512, 1024) | Get-Random
            }
        }
    }
    
    New-UDApp -Content { 
        New-UDDataGrid -LoadRows {
            $VMs = Get-VirtualMachine
            $VMs| Out-UDDataGridData -Context $EventData -TotalRows $VMs.Length
        } -Columns @(
            New-UDDataGridColumn -Field Name
            New-UDDataGridColumn -Field OperatingSystem
            New-UDDataGridColumn -Field Memory -Render {
                New-UDTypography -Text "$($EventData.Memory) GB\s"
            }
            New-UDDataGridColumn -Field Cores
        ) -AutoHeight $true -LoadDetailContent {
            $VirutalMachine = $EventData.row
            New-UDDataGrid -LoadRows {
                $NICs = Get-NetworkCard -VirtualMachine $VirutalMachine
                $NICs | Out-UDDataGridData -Context $EventData -TotalRows $NICs.Length
            } -Columns @(
                New-UDDataGridColumn -Field Name
                New-UDDataGridColumn -Field Speed -Render {
                    New-UDTypography -Text "$($EventData.Speed) GB\s"
                }
            ) -AutoHeight $true
        }
    }
    @{
        newRow = @{}
        oldRow = @{}
    }
    $Cache:Data = @(
        @{ Name = 'Adam'; number = Get-Random }
        @{ Name = 'Tom'; number = Get-Random }
        @{ Name = 'Sarah'; number = Get-Random }
    )
    
    New-UDDataGrid -LoadRows {
        $Cache:Data| Out-UDDataGridData -Context $EventData -TotalRows $Cache:Data.Length
    } -Columns @(
        New-UDDataGridColumn -Field name -Render {
            New-UDButton -Text $EventData.number
        }
        New-UDDataGridColumn -Field number -Editable
    ) -AutoHeight $true -OnEdit {
        $Cache:Data | Where-Object { $_.Name -eq $EventData.NewRow.Name } | ForEach-Object {
            $_.Number = $EventData.NewRow.Number
        }
    }
    New-UDApp -Content { 
        $Rows = 1..100 | % {
            @{ Id = $_; Name = 'Adam'; Number = Get-Random}
        } 
        New-UDDataGrid -id DataGrid -LoadRows {  
        $Rows| Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
    } -Columns @(
        New-UDDataGridColumn -Field name
        New-UDDataGridColumn -Field number
    ) -AutoHeight $true -Pagination -CheckboxSelection -CheckboxSelectionVisibleOnly -DisableRowSelectionOnClick
    New-UDButton -Text 'Get Selected Rows' -OnClick {
       $Value = Get-UDElement -ID 'DataGrid'
       Show-UDToast $Value.selection
    }
    $Data = @(
        @{ name = 'Adam'; Number = Get-Random}
        @{ name = 'Tom'; Number = Get-Random}
        @{ name = 'Sarah'; Number = Get-Random}
    )
    
    New-UDDataGrid -LoadRows {
        @{
            rows = $Data 
            rowCount = $Data.Length
        }
    } -Columns @(
        New-UDDataGridColumn -Field name
        New-UDDataGridColumn -Field number
    ) -OnExport {
        $ExportContent = $Data | ConvertTo-Csv -NoTypeInformation | Out-String
        Out-UDDataGridExport -Data $ExportContent -FileName 'export.csv' 
    }
    $Data = @(
        @{ name = 'Adam'; Number = Get-Random}
        @{ name = 'Tom'; Number = Get-Random}
        @{ name = 'Sarah'; Number = Get-Random}
    )
    
    New-UDDataGrid -LoadRows {
        @{
            rows = $Data 
            rowCount = $Data.Length
        }
    } -Columns @(
        New-UDDataGridColumn -Field name
        New-UDDataGridColumn -Field number
    ) -OnExport {
        if ($EventData.Type -eq 'CSV')
        {
            $ExportContent = $Data | ConvertTo-Csv -NoTypeInformation | Out-String
            Out-UDDataGridExport -Data $ExportContent -FileName 'export.csv' 
        }
    } -ExportOptions @("CSV", "PDF")
    New-UDApp -Title 'PowerShell Universal' -Content {
         $Data =  1..10000 | % {
            @{ Name = 'Adam'; Number = Get-Random }
        } 
        New-UDDataGrid -LoadRows {  
          $Data | Out-UDDataGridData -Context $EventData
        } -Columns @(
            New-UDDataGridColumn -Field name
            New-UDDataGridColumn -Field number -Render {
                        New-UDButton -Icon (New-UDIcon -Icon User) -OnClick { Show-UDToast $EventData.Name } 
            }
        ) -AutoHeight $true -Pagination -HeaderFilters
    }     
    function Out-UDSQLDataGrid {
        param(
            $Context,
            [Parameter(Mandatory)]
            [string]$Table,
            [Parameter(Mandatory)]
            [string]$SqlInstance,
            [Parameter(Mandatory)]
            [string]$Database,
            [Parameter(Mandatory)]
            [pscredential]$SqlCredential,
            [Int32]$RowsPerPage
        )
    
        End {
            $simpleFilter = @()
    
            if ($null -ne $Context.Filter.Items -and $Context.Filter.Items.Count -gt 0) {
                $logicOperator = $Context.Filter.logicOperator #The link operator is 'AND' or 'OR'. It will always be one or the other for all properties
    
                foreach ($item in $Context.Filter.Items) {         
                    $simpleFilter += [PSCustomObject]@{
                        Property = $item.Field
                        Value    = $item.Value
                        Operator = $item.Operator
                    }
                }
            }
    
            if ($null -ne $simpleFilter -and $simpleFilter.Count -gt 0) {
                $count = 1
                foreach ($filter in $simpleFilter) {
                    if ($count -gt 1) {               
                        $SqlFilter += " $($logicOperator) "
                    }
                    else {
                        $SqlFilter += " WHERE "
                    }
    
                    switch ($filter.Operator) {
                        "contains" { $SqlFilter += " $($filter.Property) LIKE '%$($filter.Value)%' " }
                        "equals" { $SqlFilter += " $($filter.Property) = '$($filter.Value)' " }
                        "startsWith" { $SqlFilter += " $($filter.Property) LIKE '$($filter.Value)%' " }
                        "endsWith" { $SqlFilter += " $($filter.Property) LIKE '%$($filter.Value)' " }
                        "isAnyOf" {
                            $count = 1
                            foreach ($val in $filter.Value) {
                                if ($count -gt 1) {
                                    $list += ", '$val'"
                                }
                                else {
                                    $list += "'$val'"
                                }  
                                $count += 1
                            }
                            $SqlFilter += " $($filter.Property) IN ($($list)) "
                        }
                        "isempty" { $SqlFilter += " TRIM ($($filter.Property)) IS NULL " }
                        "isnotempty" { $SqlFilter += " TRIM ($($filter.Property)) IS NOT NULL " }
                        "notequals" { $SqlFilter += " $($filter.Property) != '$($filter.Value)' " }
                        "notcontains" { $SqlFilter += " $($filter.Property) NOT LIKE '%$($filter.Value)%' " }
                    }
                    $count += 1
                }
            }
            else {
                $SqlFilter = $null
            }
    
            if ($null -eq $SqlFilter) {
                $totalCount = (Invoke-DbaQuery -SqlInstance $SqlInstance -Database $Database -SqlCredential $SqlCredential -Query "SELECT COUNT(*) As Count FROM $Table").Count
            }
            else {
                $totalCount = (Invoke-DbaQuery -SqlInstance $SqlInstance -Database $Database -SqlCredential $SqlCredential -Query "SELECT COUNT(*) As Count FROM $Table $SqlFilter").Count
                $sort = $Context.Sort.'0'
            }
    
            if ($sort) {
                $sqlSort = "ORDER BY $($sort.field) $($sort.Sort) "
            }
            else {
                $sqlSort = "ORDER BY (SELECT NULL)"
            }
    
            if ($null -ne $SqlFilter) {
                $sqlPage = "OFFSET $($Context.Page * $Context.PageSize) ROWS FETCH NEXT $($Context.PageSize) ROWS ONLY;"
                $Query = "SELECT * FROM $Table $sqlFilter $sqlSort $sqlPage"
            }
            else {
                $sqlPage = "OFFSET $($RowsPerPage) ROWS FETCH NEXT $($RowsPerPage) ROWS ONLY;"
                $Query = "SELECT * FROM $Table $sqlSort $sqlPage"
            }
    
            $Rows = Invoke-DbaQuery -SqlInstance $SqlInstance -Database $Database -SqlCredential $SqlCredential -Query $Query -As PSObject
    
            @{
                rows     = [Array]$Rows
                rowCount = $TotalCount
            }
        }   
    }
    
    New-UDDashboard -Title 'PowerShell Universal' -Content {
        New-UDDataGrid -LoadRows {  
          Out-UDSqlDataGrid -Context $EventData -SqlInstance "(localdb)\MSSQLLocalDb" -Database "PSU" -Table "Job"
        } -Columns @(
            New-UDDataGridColumn -Field id
            New-UDDataGridColumn -Field startTime
            New-UDDataGridColumn -Field status -Render {
              if ($EventData.Status -eq 2) {
                    New-UDAlert -Severity 'Success' -Text 'Success'
                }
    
                if ($EventData.Status -eq 3) {
                    New-UDAlert -Severity 'Error' -Text 'Failed'
                }
            }
        ) -AutoHeight $true -Pagination
    }

    Table

    Table component for Universal Apps

    Tables display sets of data. They can be fully customized.

    Tables display information in a way that’s easy to scan, so that users can look for patterns and insights. They can be embedded in primary content, such as cards.

    Simple Table

    A simple example with no frills. Table columns are defined from the data.

    Table with Custom Columns

    Define custom columns for your table.

    Table with Custom Column Rendering

    Define column rendering. Sorting and exporting still work for the table.

    Table Column Width

    Column width can be defined using the -Width parameter. You can also decide to truncate columns that extend past that width.

    Filters

    You can configure custom filters per column. The table supports text, select, fuzzy , slider, range, date , number, and autocomplete filters.

    Static Options for Select Filters

    When using server-side processing, the available filters may not display the full range of options since the select dropdown only has access to the current page of results. To avoid this, you can use the -Options parameter on New-UDTableColumn.

    Search

    To enable search, use the -ShowSearch parameter on New-UDTable.

    When using custom columns, you will need to add the -IncludeInSearch parameter to the columns you'd like to include in the search.

    Table with server-side processing

    Process data on the server so you can perform paging, filtering, sorting and searching in systems like SQL. To implement a server-side table, you will use the -LoadData parameter. This parameter accepts a ScriptBlock. The $EventData variable includes information about the state of the table. You can use cmdlets to process the data based on this information.

    $EventData Structure

    The $EventData object contains the following properties.

    Property Name
    Type
    Description

    Example

    Retrieving Displayed Data

    You may want to allow the user to take action on the current set of displayed data. To do so, use Get-UDElement in the input object you want to retrieve the data from and get the table by Id. Once you have the element, you can use the Data property of the element to get an array of currently displayed rows.

    Paging

    By default, paging is disable and tables will grow based on how many rows of data you provide. You can enable paging by using the -ShowPagination cmdlet (alias -Paging). You can configure the page size using the -PageSize cmdlet.

    Disable Page Size All

    By default, the page size selector provides an option to show all rows. If you want to prevent users from doing this, use the -DisablePageSizeAll cmdlet.

    Pagination Location

    You can change the location of the pagination control by using the -PaginationLocation parameter. It accepts top, bottom and both.

    Page Sizes

    The page size, by default, is set to 5. Users can adjust the number of rows per page by using the Rows per page drop down. You can adjust the default page size by using the -PageSize parameter. To adjust the values available within the Rows per page drop down, you can use an array of integers pass to the -PageSizeOptions parameter.

    Sorting

    To enable sorting for a table, use the -ShowSort parameter. When you enable sorting, you will be able to click the table headers to sort the table by clicking the headers. By default, multi-sort is enabled. To multi-hold shift and click a column header.

    You can control which columns can be sorted by using New-UDTableColumn and -ShowSort parameter.

    Disable Sort Remove

    By default, the sorting of a table has 3 states. Unsorted, ascending and descending. If you would like to disable the unsorted state, use the -DisableSortRemove parameter of New-UDTable.

    Selection

    Static Table Data

    Tables support selection of rows. You can create an event handler for the OnRowSelected parameter to receive when a new row is selected or unselected or you can use Get-UDElement to retrieve the current set of selected rows.

    The following example creates a table with row selection enabled. A toast is show when clicking the row or when clicking the GET Rows button.

    The $EventData variable for the -OnRowSelected event will include all the columns as properties and a selected property as to whether the row was selected or unselected.

    For example, the service table data would look like this.

    Dynamic (Server-Side) Tables

    When using selection and -LoadData, the -OnRowSelected $EventData will be the IDs of the rows and not the entire row data. It will still indicate where the row has been selected or de-selected.

    Collapsible Rows

    You can include additional information within the table by using the -OnRowExpand parameter of New-UDTable. It accepts a ScriptBlock that you can use to return additional components.

    Exporting

    Tables support exporting the data within the table. You can export as CSV, XLSX, JSON or PDF. You can define which columns to include in an export and choose to export just the current page or all the data within the table.

    Hidden Columns

    Hidden columns allow you to include data that is not displayed in the table but is included in the exported data.

    The following hides the StartType column from the user but includes it in the export.

    Server-Side Exporting

    You can control the export functionality with a PowerShell script block. This is useful when exporting from server-side sources like SQL server tables.

    In this example, I have a SQL table that contains podcasts. When exporting, you will receive information about the current state of the table to allow you to customize what data is exported.

    Customizing Export Options

    You can decide which export options to present to your users using the -ExportOption cmdlet. The following example would only show the CSV export option.

    Customizing Labels

    You can use the -TextOption parameter along with the New-UDTableTextOption cmdlet to set text fields within the table.

    Refresh with a button

    Data Parameter

    You can externally refresh a table by putting the table within a dynamic region and using Sync-UDElement.

    This example creates a button to refresh the table.

    LoadData Parameter

    If you use the -LoadData parameter, you can sync the table directly. This has the benefit of maintaining the table state, such as the page and filtering, after the refresh.

    Show Refresh Button

    You can use the -ShowRefresh parameter to provide a refresh button for server-side tables.

    Alternating Row Colors

    You can use a theme to create a table with alternating row colors.

    Custom Row Styles

    Use the -OnRowStyleparameter to style the rows based on the row content. Return a hashtable with CSS styles for the row.

    API

    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    )
    
    New-UDTable -Data $TableData

    PageSize

    int

    The selected page size.

    Properties

    string[]

    An array of properties being shown in the table.

    Search

    string

    A search string provided by the user.

    TotalCount

    int

    The total number of records before filtering or paging.

    Filters

    Hashtable[] @{ id = 'fieldName'

    value = 'filterValue' }

    A list of filter values. Each hashtable has an Id and a Value property.

    OrderBy

    Hashtable @{ field = "fieldName" }

    Property name to sort by.

    OrderDirection

    string

    asc or desc depending on the sort order.

    Page

    int

    New-UDTable
    New-UDTableColumn
    Out-UDTableColumn
    New-UDTableTextOption
    Pagination Location
    Row selection
    Table Custom Row Style

    The current page (starting with 0).

    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    $Columns = @(
        New-UDTableColumn -Property Dessert -Title "A Dessert"
        New-UDTableColumn -Property Calories -Title Calories 
        New-UDTableColumn -Property Fat -Title Fat 
        New-UDTableColumn -Property Carbs -Title Carbs 
        New-UDTableColumn -Property Protein -Title Protein 
    )
    
    New-UDTable -Id 'customColumnsTable' -Data $TableData -Columns $Columns
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 1; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 200; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    $Columns = @(
        New-UDTableColumn -Property Dessert -Title Dessert -Render { 
            New-UDButton -Id "btn$($EventData.Dessert)" -Text "Click for Dessert!" -OnClick { Show-UDToast -Message $EventData.Dessert } 
        }
        New-UDTableColumn -Property Calories -Title Calories 
        New-UDTableColumn -Property Fat -Title Fat 
        New-UDTableColumn -Property Carbs -Title Carbs 
        New-UDTableColumn -Property Protein -Title Protein 
    )
    
    New-UDTable -Data $TableData -Columns $Columns -Sort -Export
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    $Columns = @(
        New-UDTableColumn -Property Dessert -Title Dessert -Render { 
            New-UDButton -Id "btn$($EventData.Dessert)" -Text "Click for Dessert!" -OnClick { Show-UDToast -Message $EventData.Dessert } 
        }
        New-UDTableColumn -Property Calories -Title Calories -Width 5 -Truncate
        New-UDTableColumn -Property Fat -Title Fat 
        New-UDTableColumn -Property Carbs -Title Carbs 
        New-UDTableColumn -Property Protein -Title Protein 
    )
    
    New-UDTable -Data $TableData -Columns $Columns -Sort
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    $Columns = @(
        New-UDTableColumn -Property Dessert -Title "A Dessert" -Filter -FilterType AutoComplete
        New-UDTableColumn -Property Calories -Title Calories -Filter -FilterType Range
        New-UDTableColumn -Property Fat -Title Fat -Filter -FilterType Range
        New-UDTableColumn -Property Carbs -Title Carbs -Filter -FilterType Range
        New-UDTableColumn -Property Protein -Title Protein -Filter -FilterType Range
    )
    
    New-UDTable -Id 'customColumnsTable' -Data $TableData -Columns $Columns -ShowFilter
    New-UDTableColumn -Property Dessert -Title 'Dessert' -Filter -FilterType 'Select' -Options @('Frozen yoghurt', 'Eclair', 'Cupcake')
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    )
    
    New-UDTable -Data $TableData -ShowSearch
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    $Columns = @(
        New-UDTableColumn -Property Dessert -Title "A Dessert" -IncludeInSearch
        New-UDTableColumn -Property Calories -Title Calories 
        New-UDTableColumn -Property Fat -Title Fat 
        New-UDTableColumn -Property Carbs -Title Carbs 
        New-UDTableColumn -Property Protein -Title Protein 
    )
    
    New-UDTable -Id 'customColumnsTable' -Data $TableData -Columns $Columns -ShowSearch
    $Columns = @(
        New-UDTableColumn -Property Name -Title "Name" -ShowFilter
        New-UDTableColumn -Property Value -Title "Value" -ShowFilter
    )
    
    $TableData = 1..1000 | ForEach-Object {
      [PSCustomObject]@{
          Name = "Record-$_"
          Value = $_ 
      }
    }
    
    New-UDTable -Columns $Columns -LoadData {
        foreach($Filter in $EventData.Filters)
        {
            $TableData = $TableData | Where-Object -Property $Filter.Id -Match -Value $Filter.Value
        }
        
        if ($EventData.Search)
        {
            $TableData = $TableData | Where-Object { $_.Name -match $EventData.Search -or $_.Value -match $EventData.Search }
        }
    
        $TotalCount = $TableData.Count 
    
        if (-not [string]::IsNullOrEmpty($EventData.OrderBy.Field))
        {
            $Descending = $EventData.OrderDirection -ne 'asc'
            $TableData = $TableData | Sort-Object -Property ($EventData.orderBy.Field) -Descending:$Descending
        }
        
        $TableData = $TableData | Select-Object -First $EventData.PageSize -Skip ($EventData.Page * $EventData.PageSize)
    
        $TableData | Out-UDTableData -Page $EventData.Page -TotalCount $TotalCount -Properties $EventData.Properties 
    } -ShowFilter -ShowSort -ShowPagination
    $Columns = @(
        New-UDTableColumn -Property Name -Title "Name" -ShowFilter
        New-UDTableColumn -Property Value -Title "Value" -ShowFilter
    )
    
    $TableData = 1..1000 | ForEach-Object {
      @{
          Name = "Record-$_"
          Value = $_ 
      }
    }
    
    New-UDButton -Text 'Get Filtered Data' -OnClick {
        $Element = Get-UDElement -Id 'filteredTable'
        Show-UDModal -Content {
            New-UDElement -Tag 'pre' -Content {
               $Element | ConvertTo-Json
            }
        }
    }
    
    New-UDTable -Id 'filteredTable' -Columns $Columns -LoadData {
        foreach($Filter in $EventData.Filters)
        {
            $TableData = $TableData | Where-Object -Property $Filter.Id -Match -Value $Filter.Value
        }
    
        $TotalCount = $TableData.Count 
    
        if (-not [string]::IsNullOrEmpty($EventData.OrderBy))
        {
            $Descending = $EventData.OrderDirection -ne 'asc'
            $TableData = $TableData | Sort-Object -Property $EventData.orderBy -Descending:$Descending
        }
        
        $TableData = $TableData | Select-Object -First $EventData.PageSize -Skip ($EventData.Page * $EventData.PageSize)
    
        $TableData | Out-UDTableData -Page $EventData.Page -TotalCount $TotalCount -Properties $EventData.Properties 
    } -ShowFilter -ShowSort -ShowPagination
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    New-UDTable -Data $TableData -Paging -PageSize 2
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    New-UDTable -Data $TableData -Paging -PageSize 2 -PageSizeOptions @(2, 4, 6)
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    New-UDTable -Data $TableData -ShowSort
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    $Columns = @(
        New-UDTableColumn -Property Dessert -Title "A Dessert" -ShowSort
        New-UDTableColumn -Property Calories -Title Calories 
        New-UDTableColumn -Property Fat -Title Fat 
        New-UDTableColumn -Property Carbs -Title Carbs -ShowSort
        New-UDTableColumn -Property Protein -Title Protein -ShowSort
    )
    
    New-UDTable -Id 'customColumnsTable' -Data $TableData -Columns $Columns
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    New-UDTable -Data $TableData -ShowSort -DisableSortRemove
    $TableData = try { get-service -ea Stop | select Name,@{n = "Status";e={ $_.Status.ToString()}},@{n = "StartupType";e={ $_.StartupType.ToString()}},@{n = "StartType";e={ $_.StartType.ToString()}} } catch {}
    $Columns = @(
        New-UDTableColumn -Property Name -Title "Service Name" -ShowSort -IncludeInExport -IncludeInSearch -ShowFilter -FilterType text
        New-UDTableColumn -Property Status -Title Status -ShowSort -DefaultSortColumn -IncludeInExport -IncludeInSearch -ShowFilter -FilterType select 
        New-UDTableColumn -Property StartupType -Title StartupType -IncludeInExport -ShowFilter -FilterType select
        New-UDTableColumn -Property StartType -Title StartType -IncludeInExport -ShowFilter -FilterType select 
    )
    New-UDTable -Id 'service_table' -Data $TableData -Columns $Columns -Title 'Services' -ShowSearch -ShowPagination -ShowSelection -Dense -OnRowSelection {
        $Item = $EventData
        Show-UDToast -Message "$($Item | out-string)"
    }
    New-UDButton -Text "GET Rows" -OnClick {
        $value = Get-UDElement -Id "service_table"
        Show-UDToast -Message "$( $value.selectedRows | Out-String )"
    }
    @{
       Id = 0
       Name = 'AESMService',
       Status = 'Running'
       StartupType = 'AutomaticDelayedStart'
       StartType = 'Automation'
       selected = $true
    }
    New-UDTable -Data (Get-Service) -OnRowExpand {
        New-UDAlert -Text $EventData.DisplayName
    } -Columns @(
        New-UDTableColumn -Title 'Name' -Property 'Name'
        New-UDTableColumn -Title 'Status' -Property 'Status'
    )
    $TableData = try { get-service -ea Stop | select Name,@{n = "Status";e={ $_.Status.ToString()}},@{n = "StartupType";e={ $_.StartupType.ToString()}},@{n = "StartType";e={ $_.StartType.ToString()}} } catch {}
    $Columns = @(
        New-UDTableColumn -Property Name -Title "Service Name" -IncludeInExport
        New-UDTableColumn -Property Status -Title Status 
        New-UDTableColumn -Property StartupType
        New-UDTableColumn -Property StartType -IncludeInExport
    )
    New-UDTable -Id 'service_table' -Data $TableData -Columns $Columns -Title 'Services' -ShowSearch -ShowPagination -Dense -Export
    $TableData = try { get-service -ea Stop | select Name,@{n = "Status";e={ $_.Status.ToString()}},@{n = "StartupType";e={ $_.StartupType.ToString()}},@{n = "StartType";e={ $_.StartType.ToString()}} } catch {}
    $Columns = @(
        New-UDTableColumn -Property Name -Title "Service Name" -IncludeInExport
        New-UDTableColumn -Property Status -Title Status 
        New-UDTableColumn -Property StartupType
        New-UDTableColumn -Property StartType -IncludeInExport -Hidden
    )
    New-UDTable -Id 'service_table' -Data $TableData -Columns $Columns -Title 'Services' -ShowSearch -ShowPagination -Dense -Export
    $Columns = @(
        New-UDTableColumn -Property Name -Title "Name" -ShowFilter -IncludeInExport
        New-UDTableColumn -Property Value -Title "Value" -ShowFilter -IncludeInExport
    )
    
    $TableData = 1..1000 | ForEach-Object {
      [PSCustomObject]@{
          Name = "Record-$_"
          Value = $_ 
      }
    }
    
    New-UDTable -Columns $Columns -LoadData {
        foreach($Filter in $EventData.Filters)
        {
            $TableData = $TableData | Where-Object -Property $Filter.Id -Match -Value $Filter.Value
        }
    
        $TotalCount = $TableData.Count 
    
        if (-not [string]::IsNullOrEmpty($EventData.OrderBy.Field))
        {
            $Descending = $EventData.OrderDirection -ne 'asc'
            $TableData = $TableData | Sort-Object -Property ($EventData.orderBy.Field) -Descending:$Descending
        }
        
        $TableData = $TableData | Select-Object -First $EventData.PageSize -Skip ($EventData.Page * $EventData.PageSize)
    
        $TableData | Out-UDTableData -Page $EventData.Page -TotalCount $TotalCount -Properties $EventData.Properties 
    } -ShowFilter -ShowSort -ShowPagination  -Export -OnExport {
       $Query = $Body | ConvertFrom-Json
    
            <# Query will contain
                filters: []
                orderBy: undefined
                orderDirection: ""
                page: 0
                pageSize: 5
                properties: (5) ["dessert", "calories", "fat", "carbs", "protein"]
                search: ""
                totalCount: 0
                allRows: true
            #>
    
        $TableData | ConvertTo-Json
    }
    $TableData = try { get-service -ea Stop | select Name,@{n = "Status";e={ $_.Status.ToString()}},@{n = "StartupType";e={ $_.StartupType.ToString()}},@{n = "StartType";e={ $_.StartType.ToString()}} } catch {}
    $Columns = @(
        New-UDTableColumn -Property Name -Title "Service Name" -IncludeInExport
        New-UDTableColumn -Property Status -Title Status 
        New-UDTableColumn -Property StartupType
        New-UDTableColumn -Property StartType -IncludeInExport
    )
    New-UDTable -Id 'service_table' -Data $TableData -Columns $Columns -Title 'Services' -ShowSearch -ShowPagination -Dense -Export -ExportOption "csv"
    $TableData = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) 
    
    $Option = New-UDTableTextOption -Search "Search all these records"
    
    New-UDTable -Data $TableData -TextOption $Option -ShowSearch
    New-UDDynamic -Id 'table' -Content {
        $TableData = @(
            @{ Random = Get-Random }
            @{ Random = Get-Random }
            @{ Random = Get-Random }
            @{ Random = Get-Random }
            @{ Random = Get-Random }
        )
        
        # Store in the page so we can get the current ID. 
        # Using the same ID fails to update when the dynamic reloads.
        $Page:Table = New-UDTable -Data $TableData -Paging -ShowSelection
        $Page:Table
    } 
    
    New-UDButton -Text 'Refresh Table' -OnClick {
        Sync-UDElement -Id 'table'
    }
    
    New-UDButton -Text 'Get Data' -OnClick {
        Show-UDToast (Get-UDElement -Id $Page:Table.Id | ConvertTo-Json)
    }
    New-UDButton -Text 'Table1' -OnClick { Sync-UDElement -Id 'Table1' }
    
    $Columns = @(
        New-UDTableColumn -Property Name -Title "Name" -ShowFilter -Render { $EventData.Name }
        New-UDTableColumn -Property Value -Title "Value" -ShowFilter
    )
    
    New-UDTable -Columns $Columns -LoadData {
        $TableData = 1..1000 | ForEach-Object {
            @{
                Name = "Record-$_"
                Value = $_ 
            }
        }
        
        foreach($Filter in $EventData.Filters)
        {
            $TableData = $TableData | Where-Object -Property $Filter.Id -Match -Value $Filter.Value
        }
    
        $TotalCount = $TableData.Count 
    
        if (-not [string]::IsNullOrEmpty($EventData.OrderBy))
        {
            $Descending = $EventData.OrderDirection -ne 'asc'
            $TableData = $TableData | Sort-Object -Property $EventData.orderBy -Descending:$Descending
        }
        
        $TableData = $TableData | Select-Object -First $EventData.PageSize -Skip ($EventData.Page * $EventData.PageSize)
    
        $TableData | Out-UDTableData -Page $EventData.Page -TotalCount $TotalCount -Properties $EventData.Properties 
    } -ShowFilter -ShowSort -ShowPagination  -Id 'Table1'
    $Columns = @(
        New-UDTableColumn -Property Dessert -Title "A Dessert"
        New-UDTableColumn -Property Calories -Title Calories 
        New-UDTableColumn -Property Fat -Title Fat 
        New-UDTableColumn -Property Carbs -Title Carbs 
        New-UDTableColumn -Property Protein -Title Protein 
    )
    
    New-UDTable -ShowRefresh -Columns $Columns -LoadData {
        $Query = $Body | ConvertFrom-Json
    
        <# Query will contain
            filters: []
            orderBy: undefined
            orderDirection: ""
            page: 0
            pageSize: 5
            properties: (5) ["dessert", "calories", "fat", "carbs", "protein"]
            search: ""
            totalCount: 0
        #>
    
        @(
            @{Dessert = 'Frozen yoghurt'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
            @{Dessert = 'Ice cream sandwich'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
            @{Dessert = 'Eclair'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
            @{Dessert = 'Cupcake'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
            @{Dessert = 'Gingerbread'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
        ) | Out-UDTableData -Page 0 -TotalCount 5 -Properties $Query.Properties 
    }
    $Theme = @{
        overrides = @{
            MuiTableRow = @{
                root = @{
                    '&:nth-of-type(odd)' = @{
                        backgroundColor = "rgba(0,0,0,0.04)"
                    }
                }
                head = @{
                    backgroundColor = "rgb(255,255,255) !important"
                }
            }
        }
    }
    
    New-UDDashboard -Content {
    $TableData = 1..10 | % { [PSCustomObject]@{ Item = $_}}
      New-UDTable -ShowPagination -PageSize 10 -PageSizeOptions @(10, 10) -DisablePageSizeAll -Columns @(
            New-UDTableColumn -Property 'Item' -Title 'Item' -Width 180 -Truncate
        ) -Data $TableData -Dense -ShowSearch
    } -Theme $Theme
    $Data = @(
         @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 1; Protein = 4.0 }
         @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 150.0; Carbs = 34; Protein = 4.0 }
         @{Dessert = 'Eclair'; Calories = 159; Fat = 100.0; Carbs = 73; Protein = 4.0 }
         @{Dessert = 'Cupcake'; Calories = 159; Fat = 30.0; Carbs = 25; Protein = 4.0 }
         @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 99; Protein = 4.0 }
     )
     $Columns = @(
         New-UDTableColumn -Property Dessert -Title "Dessert" 
         New-UDTableColumn -Property Calories -Title "Calories" 
         New-UDTableColumn -Property Fat -Title "Fat" 
         New-UDTableColumn -Property Carbs -Title "Carbs"  -DefaultSortColumn
         New-UDTableColumn -Property Protein -Title "Protein" 
     )
     New-UDTable -Data $Data -Id 'table14' -Columns $Columns -OnRowStyle {
         if ($EventData.Fat -lt 10) { $Color = 'green' }
         elseif ($EventData.Fat -ge 10 -and $EventData.Fat -lt 50) { $Color = 'Yellow' }
         else { $Color = 'Red' }
         @{ backgroundColor = $Color }    
     }

    Charts

    Charting components for Universal Apps.

    Universal Apps provides several built-in charting solutions to help visualize your data retrieved from PowerShell.

    ChartJS

    Universal Apps integrates with ChartJS.

    Creating a Chart

    To create a chart, use New-UDChartJS and New-UDChartJSData. The below chart shows the top ten CPU using processes.

    Types

    Bar

    Stacked Bar

    Horizontal Bar

    Bubble

    A bubble chart consists of x and y coordinates and an r value for the radius of the circles.

    Line

    Doughnut

    Pie

    Radar

    Colors

    Colors can be defined using the various color parameters of New-UDChartJS.

    Data Sets

    By default, you do not need to define data sets manually. A single data set is created automatically when you use the -DataProperty and -LabelProperty parameters. If you want to define multiple data sets for a single chart, you can use the -Dataset property in conjunction with New-UDChartJSDataset.

    Click Events

    You can take action when a user clicks the chart. This example shows a toast with the contents of the $Body variable. The $Body variable contains a JSON string with information about the elements that were clicked.

    Auto refreshing charts

    You can use New-UDDynamic to create charts that refresh on an interval.

    Monitors

    Monitors are a special kind of chart that tracks data over time. Monitors are good for displaying data such as server performance stats that change frequently. You return a single value from a monitor and it is graphed automatically over time.

    Options

    The New-UDChartJS cmdlet supports accepting advanced ChartJS options. You can use the -Options parameter to pass in a hashtable.

    This example hides the legend.

    Title

    You can include a title with the title option.

    Nivo Charts

    Universal Dashboard integrates with . Below you will find examples and documentation for using these charts.

    Creating a Chart

    All the Nivo charts can be created with New-UDNivoChart. You will specify a switch parameter for the different types of charts. Each chart type will take a well defined data format via the -Data parameter.

    Patterns

    Nivo provides the ability to specify patterns to display over data sets. You can configure these patterns with New-UDNivoPattern and New-UDNivoFill .

    Responsive Widths

    Nivo charts provide responsive widths so they will resize automatically when placed on a page or the browser is resized. A height is required when using responsive widths.

    Auto Refreshing Charts

    Like many components in Universal Dashboard v3, Nivo charts do not define auto-refresh properties themselves. Instead, you can take advantage of New-UDDynamic to refresh the chart on an interval.

    OnClick

    Nivo charts support OnClick event handlers. You will be provided with information about the data set that was clicked as JSON.

    Types of Charts

    Bar

    Bubble

    Calendar

    Heatmap

    Line

    Network

    Assuming the below JSON data, you can use the following app code.

    Stream

    Treemap

    Color Based on Data

    You can use the following format to use colors based on your data.

    Nivo Charts
    Network Chart
    Color based on data
     $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
     New-UDChartJS -Type 'bar' -Data $Data -DataProperty CPU -LabelProperty ProcessName
     $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
     New-UDChartJS -Type 'bar' -Data $Data -DataProperty CPU -LabelProperty ProcessName
        $GraphPrep = @(
            @{ RAM = "Server1"; AvailableRam = 128; UsedRAM = 10 }
            @{ RAM = "Server2"; AvailableRam = 64; UsedRAM = 63 }
            @{ RAM = "Server3"; AvailableRam = 48; UsedRAM = 40 }
            @{ RAM = "Server4"; AvailableRam = 64;; UsedRAM = 26 }
            @{ RAM = "Server5"; AvailableRam = 128; UsedRAM = 120 }
        )
    
        $AvailableRamDataSet = New-UDChartJSDataset -DataProperty AvailableRAM -Label 'Available' -BackgroundColor blue
        $UsedRamDataset = New-UDChartJSDataset -DataProperty UsedRAM -Label 'Used' -BackgroundColor red
        $Options = @{
            Type          = 'bar'
            Data          = $GraphPrep
            Dataset       = @($AvailableRamDataSet, $UsedRamDataset)
            LabelProperty = "RAM"
            Options = @{
                scales = @{
                    xAxes = 
                    @{
                        stacked = $true
                    }            
                yAxes = 
                    @{
                        stacked = $true
                    }            
                }
            }
        } 
    
        New-UDChartJS @Options
        $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
        New-UDChartJS -Type 'bar' -Data $Data -DataProperty CPU -LabelProperty ProcessName -Options @{
            indexAxis = "y"
            plugins = @{
                legend = @{
                    position = "right"
                }
            }
        }p
    $Data = @(
        @{ x = 1; y = 10; r = 15 }
        @{ x = 12; y = 25; r = 35 }
        @{ x = 8; y = 10; r = 95 }
        @{ x = 6; y = 95; r = 25 }
    )
    New-UDChartJS -Type 'bubble' -Data $Data 
     $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
     New-UDChartJS -Type 'line' -Data $Data -DataProperty CPU -LabelProperty ProcessName
     $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
     New-UDChartJS -Type 'doughnut' -Data $Data -DataProperty CPU -LabelProperty ProcessName
     $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
     New-UDChartJS -Type 'pie' -Data $Data -DataProperty CPU -LabelProperty ProcessName
     $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
     New-UDChartJS -Type 'radar' -Data $Data -DataProperty CPU -LabelProperty ProcessName
     $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
    
     $Options = @{
       Type = 'bar'
       Data = $Data
       BackgroundColor = 'Red'
       BorderColor = '#c61d4a'
       HoverBackgroundColor = 'Blue'
       HoverBorderColor = '#451dc6'
       DataProperty = 'CPU'
       LabelProperty = 'ProcessName'
     }
    
     New-UDChartJS @Options
    $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
    
     $CPUDataset = New-UDChartJSDataset -DataProperty CPU -Label CPU -BackgroundColor '#126f8c'
     $MemoryDataset = New-UDChartJSDataset -DataProperty HandleCount -Label 'Handle Count' -BackgroundColor '#8da322'
    
     $Options = @{
       Type = 'bar'
       Data = $Data
       Dataset = @($CPUDataset, $MemoryDataset)
       LabelProperty = "ProcessName"
     }
    
     New-UDChartJS @Options
     $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
    
      $Options = @{
       Type = 'bar'
       Data = $Data
       DataProperty = 'CPU'
       LabelProperty = "ProcessName"
       OnClick = { 
          Show-UDToast -Message $Body
       }
     }
    
    
     New-UDChartJS @Options
    New-UDDynamic -Content {
        $Data = 1..10 | % { 
            [PSCustomObject]@{ Name = $_; value = get-random }
        }
        New-UDChartJS -Type 'bar' -Data $Data -DataProperty Value -Id 'test' -LabelProperty Name -BackgroundColor Blue
    } -AutoRefresh -AutoRefreshInterval 1
    New-UDChartJSMonitor -LoadData {
        Get-Random -Max 100 | Out-UDChartJSMonitorData
    } -Labels "Random" -ChartBackgroundColor "#297741" -RefreshInterval 1
     $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
     New-UDChartJS -Type 'bar' -Data $Data -DataProperty CPU -LabelProperty ProcessName -Options @{  
     legend = @{  
         display = $false  
     }  
    }
    $Data = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 
    New-UDChartJS -Type 'bar' -Data $Data -DataProperty CPU -LabelProperty ProcessName -Options @{
        plugins = @{
            legend = @{
                title = @{
                    display = $true
                    text    = 'Bar Chart'
                }
            }
        }
    }
    $Data = 1..10 | ForEach-Object { 
        $item = Get-Random -Max 1000 
        [PSCustomObject]@{
            Name = "Test$item"
            Value = $item
        }
    }
    New-UDNivoChart -Id 'autoRefreshingNivoBar' -Bar -Keys "value" -IndexBy 'name' -Data $Data -Height 500 -Width 1000
    $Data = @(
        @{
            country = 'USA'
            burgers = (Get-Random -Minimum 10 -Maximum 100)
            fries = (Get-Random -Minimum 10 -Maximum 100)
            sandwich = (Get-Random -Minimum 10 -Maximum 100)
        }
        @{
            country = 'Germany'
            burgers = (Get-Random -Minimum 10 -Maximum 100)
            fries = (Get-Random -Minimum 10 -Maximum 100)
            sandwich = (Get-Random -Minimum 10 -Maximum 100)
        }
        @{
            country = 'Japan'
            burgers = (Get-Random -Minimum 10 -Maximum 100)
            fries = (Get-Random -Minimum 10 -Maximum 100)
            sandwich = (Get-Random -Minimum 10 -Maximum 100)
        }
    )
    
    $Pattern = New-UDNivoPattern -Dots -Id 'dots' -Background "inherit" -Color "#38bcb2" -Size 4 -Padding 1 -Stagger
    $Fill = New-UDNivoFill -ElementId "fries" -PatternId 'dots'
    
    New-UDNivoChart -Definitions $Pattern -Fill $Fill -Bar -Data $Data -Height 400 -Width 900 -Keys @('burgers', 'fries', 'sandwich')  -IndexBy 'country'
    New-UDDynamic -Content {
        $Data = 1..10 | ForEach-Object { 
            $item = Get-Random -Max 1000 
            [PSCustomObject]@{
                Name = "Test$item"
                Value = $item
            }
        }
        New-UDNivoChart -Id 'autoRefreshingNivoBar' -Bar -Keys "Value" -IndexBy 'name' -Data $Data -Height 500 -Width 1000
    } -AutoRefresh
    $Data = @(
        @{
            country = 'USA'
            burgers = (Get-Random -Minimum 10 -Maximum 100)
            fries = (Get-Random -Minimum 10 -Maximum 100)
            sandwich = (Get-Random -Minimum 10 -Maximum 100)
        }
        @{
            country = 'Germany'
            burgers = (Get-Random -Minimum 10 -Maximum 100)
            fries = (Get-Random -Minimum 10 -Maximum 100)
            sandwich = (Get-Random -Minimum 10 -Maximum 100)
        }
        @{
            country = 'Japan'
            burgers = (Get-Random -Minimum 10 -Maximum 100)
            fries = (Get-Random -Minimum 10 -Maximum 100)
            sandwich = (Get-Random -Minimum 10 -Maximum 100)
        }
    )
    New-UDNivoChart -Bar -Data $Data -Height 400 -Width 900 -Keys @('burgers', 'fries', 'sandwich')  -IndexBy 'country' -OnClick {
        Show-UDToast -Message $EventData -Position topLeft
    }
    New-Example -Title 'Bar' -Description '' -Example {
        $Data = 1..10 | ForEach-Object { 
            $item = Get-Random -Max 1000 
            [PSCustomObject]@{
                Name = "Test$item"
                Value = $item
            }
        }
        New-UDNivoChart -Bar -Keys "Value" -IndexBy 'name' -Data $Data -Height 500 -Width 1000
    }
    $TreeData = @{
        Name     = "root"
        children = @(
            @{
                Name  = "first"
                children = @(
                    @{
                        Name = "first-first"
                        Count = 7
                    }
                    @{
                        Name = "first-second"
                        Count = 8
                    }
                )
            },
            @{
                Name  = "second"
                Count = 21
            }
        )
    }
    
    New-UDNivoChart -Bubble -Data $TreeData -Value "count" -Identity "name" -Height 500 -Width 800
    $Data = @()
    for($i = 365; $i -gt 0; $i--) {
        $Data += @{
            day = (Get-Date).AddDays($i * -1).ToString("yyyy-MM-dd")
            value = Get-Random
        }
    }
    
    $From = (Get-Date).AddDays(-365)
    $To = Get-Date
    
    New-UDNivoChart -Calendar -Data $Data -From $From -To $To -Height 500 -Width 1000 -MarginTop 50 -MarginRight 130 -MarginBottom 50 -MarginLeft 60
    $Data = @(
        @{
            state = "idaho"
            cats = 72307
            dogs = 23429
            moose = 23423
            bears = 784
        }
        @{
            state = "wisconsin"
            cats = 2343342
            dogs = 3453623
            moose = 1
            bears = 23423
        }
        @{
            state = "montana"
            cats = 9234
            dogs = 3973457
            moose = 23472
            bears = 347303
        }
        @{
            state = "colorado"
            cats = 345973789
            dogs = 0237234
            moose = 2302
            bears = 2349772
        }
    )
    New-UDNivoChart -Heatmap -Data $Data -IndexBy 'state' -keys @('cats', 'dogs', 'moose', 'bears')  -Height 500 -Width 1000 -MarginTop 50 -MarginRight 130 -MarginBottom 50 -MarginLeft 60
    [array]$Data = [PSCustomObject]@{
        id = "DataSet"
        data = (1..20 | ForEach-Object {
            $item = Get-Random -Max 500 
            [PSCustomObject]@{
                x = "Test$item"
                y = $item
            }
        })
    }
    New-UDNivoChart -Line -Data $Data -Height 500 -Width 1000 -LineWidth 1
    {
      "nodes": [
        {
          "id": "Node 1",
          "height": 1,
          "size": 24,
          "color": "rgb(97, 205, 187)"
        },
        {
          "id": "Node 2",
          "height": 1,
          "size": 24,
          "color": "rgb(97, 205, 187)"
        },
        {
          "id": "Node 3",
          "height": 1,
          "size": 24,
          "color": "rgb(97, 205, 187)"
        },
        {
          "id": "Node 4",
          "height": 1,
          "size": 24,
          "color": "rgb(97, 205, 187)"
        },
        {
          "id": "Node 5",
          "height": 1,
          "size": 24,
          "color": "rgb(97, 205, 187)"
        },
        {
          "id": "Node 6",
          "height": 1,
          "size": 24,
          "color": "rgb(97, 205, 187)"
        },
        {
          "id": "Node 7",
          "height": 1,
          "size": 24,
          "color": "rgb(97, 205, 187)"
        },
        {
          "id": "Node 8",
          "height": 1,
          "size": 24,
          "color": "rgb(97, 205, 187)"
        },
        {
          "id": "Node 0",
          "height": 2,
          "size": 32,
          "color": "rgb(244, 117, 96)"
        },
        {
          "id": "Node 1.0",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 1.1",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 1.2",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 1.3",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 1.4",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 1.5",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 1.6",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 2.0",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 2.1",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 2.2",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 2.3",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 2.4",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 2.5",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 2.6",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 2.7",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 2.8",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 3.0",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 3.1",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 3.2",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 3.3",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 3.4",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 3.5",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 3.6",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 3.7",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 3.8",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 4.0",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 4.1",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 4.2",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 4.3",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 4.4",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 4.5",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 4.6",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 4.7",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 4.8",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 5.0",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 5.1",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 5.2",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 6.0",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 6.1",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 6.2",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 6.3",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 6.4",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 7.0",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 7.1",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 7.2",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 7.3",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 7.4",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 7.5",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 8.0",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 8.1",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 8.2",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 8.3",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 8.4",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 8.5",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 8.6",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 8.7",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        },
        {
          "id": "Node 8.8",
          "height": 0,
          "size": 12,
          "color": "rgb(232, 193, 160)"
        }
      ],
      "links": [
        {
          "source": "Node 0",
          "target": "Node 1",
          "distance": 80
        },
        {
          "source": "Node 1",
          "target": "Node 1",
          "distance": 80
        },
        {
          "source": "Node 1",
          "target": "Node 1.0",
          "distance": 50
        },
        {
          "source": "Node 1",
          "target": "Node 1.1",
          "distance": 50
        },
        {
          "source": "Node 1",
          "target": "Node 1.2",
          "distance": 50
        },
        {
          "source": "Node 1",
          "target": "Node 1.3",
          "distance": 50
        },
        {
          "source": "Node 1",
          "target": "Node 1.4",
          "distance": 50
        },
        {
          "source": "Node 1",
          "target": "Node 1.5",
          "distance": 50
        },
        {
          "source": "Node 1",
          "target": "Node 1.6",
          "distance": 50
        },
        {
          "source": "Node 0",
          "target": "Node 2",
          "distance": 80
        },
        {
          "source": "Node 2",
          "target": "Node 2.0",
          "distance": 50
        },
        {
          "source": "Node 2",
          "target": "Node 2.1",
          "distance": 50
        },
        {
          "source": "Node 2",
          "target": "Node 2.2",
          "distance": 50
        },
        {
          "source": "Node 2",
          "target": "Node 2.3",
          "distance": 50
        },
        {
          "source": "Node 2",
          "target": "Node 2.4",
          "distance": 50
        },
        {
          "source": "Node 2",
          "target": "Node 2.5",
          "distance": 50
        },
        {
          "source": "Node 2",
          "target": "Node 2.6",
          "distance": 50
        },
        {
          "source": "Node 2",
          "target": "Node 2.7",
          "distance": 50
        },
        {
          "source": "Node 2",
          "target": "Node 2.8",
          "distance": 50
        },
        {
          "source": "Node 0",
          "target": "Node 3",
          "distance": 80
        },
        {
          "source": "Node 3",
          "target": "Node 3.0",
          "distance": 50
        },
        {
          "source": "Node 3",
          "target": "Node 3.1",
          "distance": 50
        },
        {
          "source": "Node 3",
          "target": "Node 3.2",
          "distance": 50
        },
        {
          "source": "Node 3",
          "target": "Node 3.3",
          "distance": 50
        },
        {
          "source": "Node 3",
          "target": "Node 3.4",
          "distance": 50
        },
        {
          "source": "Node 3",
          "target": "Node 3.5",
          "distance": 50
        },
        {
          "source": "Node 3",
          "target": "Node 3.6",
          "distance": 50
        },
        {
          "source": "Node 3",
          "target": "Node 3.7",
          "distance": 50
        },
        {
          "source": "Node 3",
          "target": "Node 3.8",
          "distance": 50
        },
        {
          "source": "Node 0",
          "target": "Node 4",
          "distance": 80
        },
        {
          "source": "Node 4",
          "target": "Node 8",
          "distance": 80
        },
        {
          "source": "Node 4",
          "target": "Node 4.0",
          "distance": 50
        },
        {
          "source": "Node 4",
          "target": "Node 4.1",
          "distance": 50
        },
        {
          "source": "Node 4",
          "target": "Node 4.2",
          "distance": 50
        },
        {
          "source": "Node 4",
          "target": "Node 4.3",
          "distance": 50
        },
        {
          "source": "Node 4",
          "target": "Node 4.4",
          "distance": 50
        },
        {
          "source": "Node 4",
          "target": "Node 4.5",
          "distance": 50
        },
        {
          "source": "Node 4",
          "target": "Node 4.6",
          "distance": 50
        },
        {
          "source": "Node 4",
          "target": "Node 4.7",
          "distance": 50
        },
        {
          "source": "Node 4",
          "target": "Node 4.8",
          "distance": 50
        },
        {
          "source": "Node 0",
          "target": "Node 5",
          "distance": 80
        },
        {
          "source": "Node 5",
          "target": "Node 5.0",
          "distance": 50
        },
        {
          "source": "Node 5",
          "target": "Node 5.1",
          "distance": 50
        },
        {
          "source": "Node 5",
          "target": "Node 5.2",
          "distance": 50
        },
        {
          "source": "Node 0",
          "target": "Node 6",
          "distance": 80
        },
        {
          "source": "Node 6",
          "target": "Node 6.0",
          "distance": 50
        },
        {
          "source": "Node 6",
          "target": "Node 6.1",
          "distance": 50
        },
        {
          "source": "Node 6",
          "target": "Node 6.2",
          "distance": 50
        },
        {
          "source": "Node 6",
          "target": "Node 6.3",
          "distance": 50
        },
        {
          "source": "Node 6",
          "target": "Node 6.4",
          "distance": 50
        },
        {
          "source": "Node 0",
          "target": "Node 7",
          "distance": 80
        },
        {
          "source": "Node 7",
          "target": "Node 7.0",
          "distance": 50
        },
        {
          "source": "Node 7",
          "target": "Node 7.1",
          "distance": 50
        },
        {
          "source": "Node 7",
          "target": "Node 7.2",
          "distance": 50
        },
        {
          "source": "Node 7",
          "target": "Node 7.3",
          "distance": 50
        },
        {
          "source": "Node 7",
          "target": "Node 7.4",
          "distance": 50
        },
        {
          "source": "Node 7",
          "target": "Node 7.5",
          "distance": 50
        },
        {
          "source": "Node 0",
          "target": "Node 8",
          "distance": 80
        },
        {
          "source": "Node 8",
          "target": "Node 8.0",
          "distance": 50
        },
        {
          "source": "Node 8",
          "target": "Node 8.1",
          "distance": 50
        },
        {
          "source": "Node 8",
          "target": "Node 8.2",
          "distance": 50
        },
        {
          "source": "Node 8",
          "target": "Node 8.3",
          "distance": 50
        },
        {
          "source": "Node 8",
          "target": "Node 8.4",
          "distance": 50
        },
        {
          "source": "Node 8",
          "target": "Node 8.5",
          "distance": 50
        },
        {
          "source": "Node 8",
          "target": "Node 8.6",
          "distance": 50
        },
        {
          "source": "Node 8",
          "target": "Node 8.7",
          "distance": 50
        },
        {
          "source": "Node 8",
          "target": "Node 8.8",
          "distance": 50
        }
      ]
    }
    New-UDNivoChart -Network -Data (Get-Content "$Repository\network.json" | ConvertFrom-Json) 
    $Data = 1..10 | ForEach-Object { 
        @{
            "Adam" = Get-Random 
            "Alon" = Get-Random 
            "Lee" = Get-Random 
            "Frank" = Get-Random 
            "Bill" = Get-Random 
        }
    }
    
    New-UDNivoChart -Stream -Data $Data -Height 500 -Width 1000 -Keys @("adam", "alon", "lee", "frank", "bill")
    $TreeData = @{
        Name     = "root"
        children = @(
            @{
                Name  = "first"
                children = @(
                    @{
                        Name = "first-first"
                        Count = 7
                    }
                    @{
                        Name = "first-second"
                        Count = 8
                    }
                )
            },
            @{
                Name  = "second"
                Count = 21
            }
        )
    }
    
    New-UDNivoChart -Treemap -Data $TreeData -Value "count" -Identity "name" -Height 500 -Width 800
    $Data =
    $([PSCustomObject]@{
            value = 30
            color = '#BF5290'
        }
        [PSCustomObject]@{
            value = 100
            color = '#52BE80'
    
        }
    )
    
    New-UDNivoChart -Pie -Data ($Data | Where-Object { $_.Value -ne 0 }) -InnerRadius 0.7 -CornerRadius 5 -PadAngle 1 -Colors @{datum = 'data.color' }` -MarginLeft "150" -MarginTop 1 -Height 370 -Responsive