Skip to main content

Scripting

Frame Guest Agent (FGA) gives customers the ability to tailor the behavior of their workload VMs (e.g., Sandbox, shadow, production, utility server, etc.) to execute scripts at different event-points of a VM and/or user session. These events are defined as “lifecycle hooks.”

Once a Frame account is created, the Frame administrator can place custom scripts (PowerShell for Windows, Bash scripts for Linux) in the Sandbox and then publish the Sandbox to make them available to the workload VMs.

Custom Script Location

note

In the context of scripts, spaces are not valid characters. For example script1.ps1 is a valid script name, while script 1.ps1 is not valid.

FGA scripts must be placed into a specific directory. The FGA Users Scripts directory for Windows VMs is:

C:\ProgramData\Nutanix\Frame\FGA\Scripts\User

Example valid script paths for Windows:

  • C:\ProgramData\Nutanix\Frame\FGA\Scripts\User\powershell\my_custom_script.ps1
  • C:\ProgramData\Nutanix\Frame\FGA\Scripts\User\my_custom_script.ps1
  • C:\ProgramData\Nutanix\Frame\FGA\Scripts\User\a\b\c\d\my_custom_script.ps1

For Linux VMs, the FGA Users Scripts directory is:

/opt/nutanix/frame/fga/scripts/user

Example valid script paths for Linux:

  • /opt/nutanix/frame/fga/scripts/user/powershell/my_custom_script.sh
  • /opt/nutanix/frame/fga/scripts/user/my_custom_script.sh
  • /opt/nutanix/frame/fga/scripts/user/a/b/c/d/my_custom_script.sh

Script Invocation

Invoking or executing custom scripts requires a definition.yml file (case sensitive) to be present in the FGA User Scripts directory. For example, this path for Windows would be C:\ProgramData\Nutanix\Frame\FGA\Scripts\User\definition.yml. FGA reads this file at each lifecycle hook and will invoke scripts as defined in this file.

Definition.yml

The definition.yml file describes detailed script context and execution information:

  • Script groups -- a convenient way to group multiple scripts based on their context.

  • Lifecycle hooks or run-policies -- phases in Frame's lifecycle of VMs and Sessions.

  • Script paths and filenames -- only use relative paths (from the FGA Users Script directory) or filenames. Look out for typos!

  • Pool-types -- specifies which type of Frame VMs you'd like the scripts to execute on (Sandbox, Production, etc.)

  • Error policies -- specifies the behavior FGA should follow if an error is encountered: continue or abort.

Below is an example of a definition.yml file.

definition.yml
    groups:
-
desc: Scripts on first boot
name: first-boot
timeout: 30
run-policy: first-boot
pool-type:
- sandbox
- production
scripts:
-
desc: My script A
path: A.ps1
error-policy: continue
timeout: 10
-
desc: My script B
path: B.ps1
error-policy: continue
timeout: 10
-
desc: Scripts on each boot
name: every-boot
timeout: 30
run-policy: every-boot
pool-type:
- sandbox
- production
scripts:
-
desc: My script C
path: C.ps1
error-policy: continue
timeout: 10
-
desc: My script D
path: D.ps1
error-policy: continue
timeout: 10
-
desc: Scripts before session is started
name: pre-session
timeout: 30
run-policy: pre-session
pool-type:
- sandbox
- production
scripts:
-
desc: My script before sessions is started
path: before-session.ps1
error-policy: continue
timeout: 20
-
desc: Scripts after session is closed
name: post-session
timeout: 30
run-policy: post-session
pool-type:
- sandbox
- production
scripts:
-
desc: My script after sessions is closed
path: after-session.ps1
error-policy: continue
timeout: 20

Script Execution Order

Order in the definition.yml file is important. When executing the scripts, FGA iterates through the definition.yml file and executes scripts in order from top to bottom. This also applies to Script Groups. If you provide two script groups of the same type (pre-session, post-session, etc), the group that is higher in the YAML file will get executed before any groups that are lower in the file.

In the following definition.yml example, Group 3 will get executed before Group 1 (since Group 3 is higher in the file). Also, before-session-3.ps1 will get executed prior to before-session-2.ps1 for the same reason:

definition.yml
    groups:
-
desc: Group 3 - Scripts before session is started
name: Group 3 Pre-session
timeout: 30
run-policy: pre-session
pool-type:
- production
scripts:
-
desc: The 3 pre-session script
path: before-session-3.ps1
error-policy: continue
timeout: 20
-
desc: The 2 pre-session script
path: before-session-2.ps1
error-policy: continue
timeout: 20
-
desc: Group 1 - Scripts after session is closed
name: Group 1 pre-session
timeout: 30
run-policy: pre-session
pool-type:
- production
scripts:
-
desc: My script after sessions is closed
path: after-session.ps1
error-policy: continue
timeout: 20
Important

Each script will need to be fully completed before the next script is executed (Sequential execution vs. parallel execution)

Frame Lifecycle Hooks

The following table outlines various Frame Lifecycle Hooks, used as values for run-policy in a definition.yml script group. Each lifecycle hook is optional.

VM Lifecycle Hooks

Hook Name
DescriptionApplicable pool groupsUser Context
pre-generalizationExecuted before Domain-Join generalization process (which includes sysprep) is started.shadow, persistent_desktop_shadowFrame user
post-generalizationExecuted after Domain-Join generalization process (which includes sysprep) is finished.shadow, persistent_desktop_shadowFrame user
first-bootExecuted only on the very first boot of the virtual machine, after it is created. This hook is available for all instance types, allowing scripts to make stateful changes right after instances are provisioned/created. For domain joined instances and enterprise profiles, the local user frameuser needs to have elevated privileges.shadow, production, persistent_desktop_shadow, persistent_desktop_productionFrame user
every-bootExecuted upon every system/OS boot. This hook can be used to update the image before non-persistence is enabled (on shadow/production instances).shadow, production, sandbox, utilityFrame user

Session Lifecycle Hooks

Hook Name
DescriptionApplicable pool groupsUser Context
pre-sessionExecuted right before a user session is started.

User Context info:

For example, if the workload VM is joined to the domain and the user authenticates to the Windows domain, then the scripts would run in the domain user context. Pre-session scripts are executed after the Windows login screen and before the onboarded application or desktop is displayed. Pre-session scripts never run in SYSTEM context.
sandbox, production, persistent_desktop_production, utilityCurrently logged in user
on-idleExecuted when session goes to idle state (workload stops streaming).sandbox, production, persistent_desktop_production, utilityCurrently logged in user
on-activeExecuted when session goes from idle to active state (workload resumes streaming).sandbox, production, persistent_desktop_production, utilityCurrently logged in user
post-sessionExecuted immediately after a session is closed. This occurs after the session has stopped streaming and before both the Windows logoff process and the profile disk unmount process.sandbox, production, persistent_desktop_production, utilityCurrently logged in user

Script User Context (Windows)

It is important for Frame administrators to know which Windows user is running which script. Refer to the table above to see each lifecycle hook and their associated execution context. Frame's default Frame user is a member of the local Windows administrator group.

To determine the user context, you can execute whoami within your script and it will print out the current script context.

Script Group Properties

desc

A detailed description of the group.

name

A descriptive name of the group.

timeout

Integer value in seconds. Defines the maximum amount of time for all scripts executing within a group. If the execution time of the scripts exceeds this timeout value, FGA will kill the currently running script and skip any remaining the scripts in the group.

run-policy

Defines the Frame Lifecycle Hook that will execute the scripts.

pool-type

Defines target pool types on which the scripts in the group are going to be applied. If none of the pool types is set, FGA will execute the script on all pool types.

Pool types and descriptions

  • sandbox - Scripts are going to be applied only on sandbox VMs
  • production - Scripts are going to be applied only on production VMs
  • shadow - Scripts are going to be applied only on shadow VMs that are part of the publishing process
  • utility - Scripts are going to be applied only on utility VMs
  • persistent_desktop_production - Scripts are going to be applied on persistent desktops
  • persistent_desktop_shadow - Scripts are going to be applied on freshly provisioned persistent desktops that are not yet assigned
note

Newly published VMs that have been placed in the production pool will still be marked as shadow until they are rebooted.

scripts

Specifies the list of scripts that needs to be executed.

Did you know?

A “pool” in Frame's context is a grouping of VMs/instances associated with a Frame account.

Script Item Properties

desc

Description of the script.

path

Script file location, relative to base user scripts directory.

error-policy

Defines FGA strategies when script fails.

Follow these three simple steps to get started creating scripts and customizing the VMs on first boot (or every boot) and your Frame sessions.

Error-policy values

  • continue - FGA to proceed on even if the script fails.
  • abort - FGA to abort the task if script fails; use with caution.
    • For Session Lifecycle hooks, this will immediately end the session for the end-user.
    • For VM Lifecycle hooks, this can prevent publishes from completing, or spinning up new instances when the pool's capacity is expanded

timeout

Integer value in seconds. Defines the maximum amount of time for the script to run. If the execution time of the script exceeds the timeout value, FGA will kill the running script. Be sure that your script's combined timeouts are less than or equal to the group's timeout duration.

Getting Started

Follow these three simple steps to get started creating scripts and customizing the VMs on first boot (or every boot) and your Frame sessions.

1. Create a Simple Script

Create a new PowerShell file (hello-world.ps1) in the FGA Scripts User directory.

# Writing a simple text file with warm greeting.
$timestamp = Get-Date -Format "dddd MM/dd/yyyy HH:mm K"
"$timestamp - Hello, World!" | Out-File "C:\ProgramData\Nutanix\Frame\Logs\sandbox_greeting.txt" -Append

2. Your definition.yml

Next, you need to communicate to FGA about this script via a definition.yml file to specify which Frame Session Lifecycle hook will trigger this script, timeout settings, the pool type(s), etc.

definition.yml
groups:
-
desc: Simple script example
name: A very simple pre-session script.
timeout: 10
run-policy: pre-session
pool-type:
- sandbox
scripts:
-
desc: Frame Hello World Example script!
path: hello-world.ps1
error-policy: continue
timeout: 10

The definition.yml file specifies that we have one script group that only executes during the pre-session Lifecycle Hook. We also specify that this group should only execute on the sandbox instance pool. Next, we specify a single script called hello-world.ps1. This script can run for a maximum of 10 seconds before reaching either timeout value, causing FGA to quit waiting and continue on.

3. Testing and Debugging

Finally, double-check to make sure the files are saved, named correctly, and are located in the correct locations. Next, quit your sandbox session. Now we're ready to test!

Testing our script is simple. When starting a new Sandbox Session, you should see a file populated at C:/sandbox_greeting.txt, showing our execution timestamp and our greeting.

Logging and Troubleshooting

Logging can be incredibly useful for debugging. However, if you're using non-persistent desktops and trying to debug scripts in production, any logging you do on the VM is wiped out after the session.

Logging tips for various environments

  1. Sandbox and Persistent Desktops: Create any text file somewhere on C: with the output of your logs.
  2. Non-persistent Shadow and Production VMs: External logging service, either on the public internet (like Loggly, Splunk, etc.) or perhaps a service running on an accessible Utility Server.

Troubleshooting Tips

  • Be mindful of about group and script timeouts, how they're different, and that your values don't overlap.

  • Always take backups before scripting.

  • If your scripts reference any file paths, be sure to use absolute filepaths, even for files residing in the FGA User Scripts folder.

  • If possible, test by executing the Powershell code in the Sandbox using PowerShell ISE.

  • If your scripts contain any sensitive information or credentials, be sure to write a cleanup pre-session script that can execute after your credentials are used and delete the sensitive script/files before users are connected to a Session.

  • Use identifying elements from Frame environment variables to help understand who and when (if needed).

  • You can set and get environment variables or registry entries to help communicate between scripts and lifecycle hooks.

  • Avoid using error-policy: abort for sandbox scripts, as failing scripts can make the Sandbox inaccessible, requiring a restoration from a backup or termination (and recreation) of the Sandbox.

  • If your scripts need to run with Elevated Privilege in Windows 10, make sure that the below registry key value is set to 0. Otherwise, your scripts will fail due to Microsoft Windows User Account Controls (UAC) preventing the script from executing in an elevated context.

    • Key: SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
    • Value: "EnableLUA"
    • Type: REG_DWORD

Environment Variables

FGA populates environment variables via registry entries to HKCU/Environment. These values are dynamically set for each Frame session and are useful when used with logging.

Env VariableDescription
FRAME_VENDOR_IDA unique identifier tied to the Frame Account the VM is associated with.
FRAME_VENDOR_NAMEThe name of the instance's Account.
FRAME_USER_EMAILThe current session user's email address.
FRAME_SESSION_IDThe unique Session ID associated with the current session. Useful for debugging and support purposes.
FRAME_SESSION_INFOProvides additional session information represented within a JSON object: colorspace (YUV420 or YUV444), protocol (FRP7 or FRP8), and connectionType (tcp or udp)
FRAME_SESSION_LABELCustom value set in Session Settings through the Advanced Server Argument -frame-session-label
FRAME_SESSION_TOKENUnique token specific to the session. This token can be used to query user assertion/claims data, or base64 decoded to access various user and authentication-related data.
FRAME_VENDOR_EMAILContrary to the name, this value is the unique GUID associated to the Frame account the VM belongs to.
FRAME_MAX_SESSION_DURATIONThe maximum amount of time (in seconds) the user can be in the session. This value is set in the Account, Launchpad, Sandbox, and Utility Server session settings.
FRAME_SESSION_START_TIMEThe date/time when the session started.
FRAME_POOL_GROUP_TYPEType of pool group the session is currently connected to (sandbox, production, test, utility).
FRAME_POOL_NAMEName of instance pool the session is currently connected to as defined within Dashboard > Capacity
FRAME_INSTANCE_TYPEThe instance type name of the current instance.
FRAME_IMAGE_FAMILYImage Family Name that the instance is based off of.
FRAME_DATACENTERName of the datacenter where the instance is located.
FRAME_CLOUD_PROVIDERName of the infrastructure provider (ahv, azure, gcp, amazon).

Frame Script Helper Tool

Frame Script Helper is a tool designed for the creation and maintenance of the definition.yml file. Frame Script Helper can be found in C:\ProgamData\Nutanix\Frame\Tools. Key tool features include the import, validation, editing, and export of definition.yml files.

note

As this tool was written for Windows, it only supports .ps1 scripts.

Import and Validation of the Definition.yml File

Importing the definition.yml file can be accomplished in two ways:

  • Automatically: When launched the tool will search for a definition.yml file in C:\ProgramData\Nutanix\Frame\FGA\Scripts\User
  • Manually: By using the import section from toolbar menu: Configuration>Import
caution

The file will not be imported until any errors are resolved.

If the tool cannot find a definition.yml file at startup, it will display a prompt; you can still continue using the tool. All changes will be saved afterwards into a new definition.yml file.

Edit the Definition.yml File

All available Frame Lifecycle hooks (see above for hook definitions) are displayed to the left of the tool window.

To begin, select one of the hooks. By default, "first-fga-boot" is selected. Next to the name of the hook are two numbers. The first number represents the number of pool-type groups, the second represents the total number of scripts assigned to that hook.

To attach your scripts to the selected hook, drag scripts files from File Explorer into the drop area at the top of the screen as shown below:

Frame Script Helper Tool

note

Another way to import the scripts is to directly drag their file onto the existing pool-type group.

Next, a prompt will ask you to specify the pool-type groups for your scripts. Check the box next to each desired pool and click “Next” when all desired options are checked.

Frame Script Helper Tool - Pool Types

Changing Script Order

To change the order of the scripts, simply drag a script to desired position in the pool-type group.

note

Scripts can only be moved within the current group, not to another.

Rearranging the Pool-Type Groups

Order of the pool-types can be also changed. Each of the pool-type group "box" can be dragged over the other group.

Export the Definition.yml File

After making your changes, you can save your file by clicking “File” and then “Save”, or even export it to a new file from Configuration > Export.

Advanced Scripting

Update Script (Windows only)

If found, FGA will execute an “update” script right after a VM boots, but before FGA User scripts are executed. This allows Frame administrators to create custom update scripts to automated or dynamic maintenance operations on their custom scripts and files before user scripts defined in definition.yml are executed. The update script must be in the following path:

C:\ProgramData\Nutanix\Frame\FGA\Scripts\User\update.ps1

Updating the user scripts might include completely new packages of scripts or just a new definition.yml file. The following are best practices when it comes to updating your custom scripts using the Update script feature.

  1. Make sure your updated scripts and/or definition.yml are packaged in a bundle.
  2. Make sure your workload VMs are able to access and download that bundle.
  3. Create an update script that downloads the package and checks if there is a need for the update to be applied.
    • If not, the "update" script should just exit.
    • If yes, the "update" script should perform the update (it can delete existing definition.yml and all script files and then recreate definition.yml file and/or scripts).
  4. Place the update script content inside the update.ps1 (for Windows) and update.sh (for Linux).

When the VM gets into boot phase, FGA is going to search for the "update" script. If it does not find the script, FGA will read the definition.yml file and run the scripts as they are defined in the definition.yml file. If the FGA finds the "update" script, FGA will execute the script first. Once the script is executed, FGA will proceed to execute the custom scripts following the definition.yml file.

If the update script on Windows OS takes more than 10 minutes to execute, FGA will timeout and continue with its startup workflow.

Example Scripts

Below are some example use cases where a custom script and associated definition.yml file can customize the end user experience.

Mount a Network Volume

Scenario: A Frame administrator would like to mount an existing read-only file share once a user starts a Frame session in either the Sandbox (Frame administrator) or a production workload VM (user).

The following simple PowerShell script "mount-read-only-volume.ps1" is placed in the scripts folder to be executed:

echo off
net use * /delete /Y
net use K: \\10.0.0.5\Fileshare /user:username password

The corresponding definition.yml file:

definition.yml
groups:
-
desc: Scripts before session is started in either Sandbox or production workload VMs
name: pre-session
timeout: 30
run-policy: pre-session
pool-type:
- sandbox
- production
scripts:
-
desc: Pre-session script to mount a read-only volume
path: mount-read-only-volume.ps1
error-policy: continue
timeout: 20

Exclusion/Inclusion Rules for Enterprise Profiles

Scenario: A Frame administrator wishes to exclude specific folders from persisting and/or include specific folders to persist in users' enterprise profiles. The following PowerShell script “exclude-include-folders.ps1” is placed in the scripts folder to be executed:

# Exclude four folders in the %USERPROFILE%
Add-ProfileDiskExclusion -SourcePath $env:USERPROFILE\Downloads -TargetPath C:\_Profile
Add-ProfileDiskExclusion -SourcePath $env:USERPROFILE\Music -TargetPath C:\_Profile
Add-ProfileDiskExclusion -SourcePath $env:LOCALAPPDATA\Autodesk -TargetPath C:\_Profile
Add-ProfileDiskExclusion -SourcePath $env:LOCALAPPDATA\Spotify -TargetPath C:\_Profile

# Include two separate folders C:\MyData and C:\MoreData to the user's enterprise profile.
Add-ProfileDiskInclusion -SourcePath C:\MyData
Add-ProfileDiskInclusion -SourcePath C:\MoreData

The corresponding definition.yml file specifies that this PowerShell script should only be executed in the context of production workload VMs where the user's enterprise profile volume is used (in a non-persistent Frame account):

definition.yml
groups:
-
desc: Scripts before session is started in production workload VMs
name: pre-session
timeout: 30
run-policy: pre-session
pool-type:
- production
scripts:
-
desc: Pre-session script to include/exclude folders from the user's enterprise profile
path: exclude-include-folders.ps1
error-policy: continue
timeout: 20

Autohide the Windows Task Bar in App Mode 2.0

Scenario: A Frame administrator wants to have the Windows Task Bar automatically hidden when using an Application Launchpad with App Mode 2.0. The Windows registry key that controls this behavior is at HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects3. The following PowerShell script is placed in the scripts folder to be executed:

$p='HKCU:SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StuckRects3';
$v=(Get-ItemProperty -Path $p).Settings;
$v[8]=1;
Set-ItemProperty -Path $p -Name Settings -Value $v;
Stop-Process -f -ProcessName explorer

The corresponding definition.yml file specifies that this PowerShell script should only be executed in the context of production workload VMs:

definition.yml
groups:
-
desc: Scripts before session is started in production workload VMs
name: pre-session
timeout: 30
run-policy: pre-session
pool-type:
- production
scripts:
-
desc: Pre-session script to autohide the Windows Task Bar
path: autohide-taskbar.ps1
error-policy: continue
timeout: 20

If you wish to show the Windows Task Bar, then the PowerShell script can be used:

$p='HKCU:SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StuckRects3';
$v=(Get-ItemProperty -Path $p).Settings;
$v[8]=2;
Set-ItemProperty -Path $p -Name Settings -Value $v;
Stop-Process -f -ProcessName explorer

Add Applications dynamically to the Frame Taskbar in App Mode 2.0

Scenario: A Frame administrator needs to dynamically add an application shortcut/launch link to the Frame Taskbar in App Mode 2.0. App Mode 2.0 allows you to dynamically modify the Frame Taskbar to any configuration you need (in real time).

The following PowerShell script should be placed in the scripts folder to be executed. This is designed as a pre-session script that modifies the Frame Taskbar configuration before the session starts. The script will override any of the default Frame Taskbar configuration and show Notepad only (update-taskbar.ps1):

note

Notice that any URI path requires two backslashes in the path (Within the JSON file only)

$jsonFilePath = 'C:\ProgramData\Nutanix\Frame\Config\server_launchpad_override.json'

$jsonOverrideContent = @"
{
"name": "root",
"order": 0,
"applications": [
{
"name": "Frame",
"path": "C:\\Windows\\System32\\notepad.exe",
"icon_url": "",
"order": 0,
"arguments": ""
}
],
"folders": []
}
"@

Set-Content -Path $jsonFilePath -Value $jsonOverrideContent

The corresponding definition.yml file specifies that this PowerShell script should only be executed in the context of production workload VMs:

definition.yml
groups:
-
desc: Scripts before session is started in production workload VMs
name: pre-session
timeout: 30
run-policy: pre-session
pool-type:
- production
scripts:
-
desc: Pre-session script to add Notepad into the Frame Taskbar icon/shortcut list
path: update-taskbar.ps1
error-policy: continue
timeout: 20

Playing in the Sandbox

In this example, we'll create a few more scripts, each set to execute at common session lifecycle hooks.

caution

Before continuing with any changes to your sandbox, please make a backup first! Sandbox scripts, if improperly configured, can cause undesired behaviors and in some cases can cause the sandbox to become unresponsive, requiring restoration from a backup or a fresh sandbox.

To get started, grab your shovel and connect to your Sandbox. Then, open up your favorite text editor and create the following five files in the FGA User Scripts Directory.

Pre-session script: pre-session.ps1

To test this, the scripts will create or append to a single log file created at .\logs\sandbox_bucket.txt with a timestamp and message from the script.

pre-session.ps1
$RootPath = "C:\ProgramData\Nutanix\Frame\FGA\Scripts\User"
If (!(test-path "$RootPath\logs")){ New-Item -Path "$RootPath\logs" -Type Directory }
$timestamp = Get-Date -Format "dddd MM/dd/yyyy HH:mm K"
"$timestamp - Hello from pre-session.ps1" | Out-File "$RootPath\logs\sandbox_bucket.txt" -Append

Session Disconnect script: on-idle.ps1

on-idle.ps1
$RootPath = "C:\ProgramData\Nutanix\Frame\FGA\Scripts\User"
If (!(test-path "$RootPath\logs")){ New-Item -Path "$RootPath\logs" -Type Directory }
$timestamp = Get-Date -Format "dddd MM/dd/yyyy HH:mm K"
"$timestamp - Hello from on-idle.ps1" | Out-File "$RootPath\logs\sandbox_bucket.txt" -Append

Session Resume script: on-active.ps1

on-active.ps1
$RootPath = "C:\ProgramData\Nutanix\Frame\FGA\Scripts\User"
If (!(test-path "$RootPath\logs")){ New-Item -Path "$RootPath\logs" -Type Directory }
$timestamp = Get-Date -Format "dddd MM/dd/yyyy HH:mm K"
"$timestamp - Hello from on-active.ps1" | Out-File "$RootPath\logs\sandbox_bucket.txt" -Append

Post-session script: post-session.ps1

post-session.ps1
$RootPath = "C:\ProgramData\Nutanix\Frame\FGA\Scripts\User"
If (!(test-path "$RootPath\logs")){ New-Item -Path "$RootPath\logs" -Type Directory }
$timestamp = Get-Date -Format "dddd MM/dd/yyyy HH:mm K"
"$timestamp - Hello from post-session.ps1" | Out-File "$RootPath\logs\sandbox_bucket.txt" -Append

Let's walk through each line in these files.

  • Line 1: This line declares the root path as a variable variable. We could also use Set-Location.
  • Line 2: Creates the log folder inside of the root path if it doesn't exist.
  • Line 3: Gets a timestamp in a readable format (based on the Sandbox's local time).
  • Line 4: Writes our timestamp and hello message to the specified "sandbox_bucket" txt file.

Defining our scripts: definition.yml

We'll create four different script groups (one for each lifecycle hook). These scripts will only execute on the Sandbox; each will have a total of 5 seconds to execute (timeout); and if these scripts fail for any reason, FGA will continue with the next lifecycle procedures.

definition.yml
groups:
-
desc: Pre-session script
name: Pre-session script group
timeout: 5
run-policy: pre-session
pool-type:
- sandbox
scripts:
-
desc: Example sandbox pre-session script.
path: pre-session.ps1
error-policy: continue
timeout: 5
-
desc: On-idle disconnect script
name: On-idle script group
timeout: 5
run-policy: on-idle
pool-type:
- sandbox
scripts:
-
desc: Example sandbox on-idle script.
path: on-idle.ps1
error-policy: continue
timeout: 5
-
desc: On-active resume script
name: On-active script group
timeout: 5
run-policy: on-active
pool-type:
- sandbox
scripts:
-
desc: Example sandbox on-active script.
path: on-active.ps1
error-policy: continue
timeout: 5
-
desc: Post-session script
name: Post-session script group
timeout: 5
run-policy: post-session
pool-type:
- sandbox
scripts:
-
desc: Example sandbox post-session script.
path: post-session.ps1
error-policy: continue
timeout: 5
note

Notice that we're only setting - sandbox for each pool-type. Publishing this Sandbox configured like this would ensure that these scripts will not execute on any other VM on this account (Shadow, non-persistent Production, Persistent VM instances).

Testing our Sandbox scripts

You made it this far, you have your scripts and definition.yml file in place on the Sandbox, great! Let's test. Before we do, changes to the definition.yml are active in real-time. The Frame Guest Agent (FGA) does a fresh read of the definition.yml at each Session lifecycle hook. This means that if you are in your Sandbox session right now, we can test the disconnect and resume scripts by disconnecting from your Sandbox using the Gear menu at the bottom left corner of the Frame Session. Next, resume the session -- we should see two new log entries in our /logs/sandbox_bucket.txt files.

Let's test all four lifecycle hooks.

  1. This time, quit the session via the Gear menu. Wait for the session to fully close.
  2. Once you can, start a new session.
  3. Disconnect from the Gear Menu. This will fire our disconnect or "on-idle" script.
  4. Resume the Session. This will trigger our resume or "on-active" script.
  5. Close the session.
  6. Finally, start one more session to verify our sandbox_bucket.txt file contains messages and timestamps in order.

That's it! If you run into any problems, here are a few basic troubleshooting steps before contacting Support:

  1. Please ensure that your files are in the correct paths.
  2. Please ensure that the file names match in the directory as well as in the definition.yml file.
  3. Test your PowerShell scripts manually from the Sandbox.
Cleanup

Once you're done with testing these scripts and scenarios, all you need to do is remove the scripts from the definition.yml, delete the files, or customize them to your liking!

Onboarding Applications via CLI

Onboarding Windows applications to a Sandbox can be automated. If the applications are present on the Sandbox, we have a command-line utility that can be used to onboard a single application, multiple applications, or multiple apps defined in a file.

Introducing ShellHandler! Find and run ShellHandler.exe located in C:\Program Files\Nutanix\Frame\Server\. Using the command-line arguments listed below, you can onboard applications in a number of ways:

ShellHandler Arguments

Command-line Argument
Description
onboardInstructs the ShellHandler that we're onboarding an application or more. onboard must be the first argument and proceeded next by one of the three following onboard options.

Example:
ShellHandler.exe onboard -i C:\MyApp.exe -s
--item or -iOnboard a single application item by filepath.

Example:
ShellHandler.exe onboard --item C:\MyApp.exe -s
--list or -lOnboard multiple applications via a list of application filepaths.

Example:
ShellHandler.exe onboard --list "C:\MyApp1.exe, C:\MyApp2.exe, C:\MyApp3.exe" -s
--file or -fOnboard multiple applications via a file containing a list of application filepaths.
C:\ExampleAppList.txt:
C:\MyApp1.exe
C:\MyApp2.exe
C:\Program Files\TestApp\HelloWorld.exe

Example:
ShellHandler.exe onboard --file C:\ExampleAppList.txt -s
--silent or -sIf present, application(s) filepaths will be onboarded without any user prompt.

Example:
ShellHandler.exe onboard -i C:\MyApp.exe --silent