Desktop and Application Streaming

Creating an image programmatically with AppStream 2.0 Image Assistant CLI operations

September 2022: This blog has been updated to use a PSRemoting configuration file.

Amazon AppStream 2.0 now lets you programmatically create an image using the Image Assistant command line interface (CLI) operations which are made available via an executable on the image builder. The CLI enables you to specify which applications your users can launch, what files should be optimized to accelerate application launch, and other image details.

The CLI allows you to create the image, without having to connect to the image builder. With the CLI, you can integrate AppStream 2.0 with your continuous integration/continuous deployment (CI/CD) process to automatically create an image with each build of your software.

For more about the Image Assistant CLI operations, see Create Your AppStream 2.0 Image Programmatically by Using the Image Assistant CLI Operations in the AppStream 2.0 documentation.

Walkthrough
At a high level, this post covers the following steps:

  1. Preparing the image builder for accepting remote commands.
  2. Configuring a Microsoft Windows EC2 instance as an administrative server that is used to remotely control the image builder.
  3. Programmatically creating an image that has Notepad++ installed.
  4. Validating that the image worked successfully.

Prerequisites
To follow along, you need the following resources:

  1. An AppStream 2.0 image builder in the running state.
  2. A Microsoft Windows EC2 instance in the same subnet as the image builder with an IAM role attached that has permission to the DescribeImageBuilders API.
  3. An S3 bucket containing the Notepad++ installer renamed to Notepad++.exe.

Step 1: Prepare the image builder for accepting remote commands
To get started, there are a few steps that must be performed on the image builder that you are targeting. The first is to create a local administrator to be used for remotely connecting and running the commands. If your image builder is joined to an Active Directory domain, you can instead use a domain account that has been provided administrator rights on the image builder using Group Policy or Active Directory group membership.

Create and install PSRemoting configuration file on the image builder.

New-PSSessionConfigurationFile -Path C:\runas.pssc -RunAsVirtualAccount
Register-PSSessionConfiguration -Path C:\runas.pssc -Name runas
Remove-Item -Path C:\runas.pssc

To create a local administrator account:

  1. Connect to the image builder instance.
  2. Choose Start, type compmgmt.msc, and then press Enter.
  3. Expand Local Users and Groups.
  4. On the Users folder, open the context (right-click) menu and choose New User.
  5. Fill out the requested username and password information, than press OK to create the user. For this example, I created ImageAdmin as the username.
  6. Now open the Groups folder and open Administrators.
  7. In the Administrators Properties window, choose Add and type in the name of the user that you just created. Choose OK (twice).
  8. Repeat steps 6 and 7 to add the user to Remote Management Users.

Next, verify whether PowerShell remoting is enabled. For this post, you have the EC2 Instance and the image builder you are targeting on the same subnets, so no change is necessary. For more information about PowerShell remoting, see About Remote Requirements.

Step 2: Configure a Microsoft Windows EC2 administrative server to remotely control the image builder
The EC2 administrative server must be configured with the information to remotely install applications, specify them for the image using the Image Assistant APIs. To configure this, you need an application that can be installed silently or without needing any interaction. For this example, I am using Notepad++.
Next, open up File Explorer and browse to the C drive. In the root of the C drive, create the following folder structure:

C:\Source\Software\Notepad++

In the Notepad++ folder, create a file called manifest.txt and place the following lines in it. This file is used to optimize the application launch files to accelerate launch time. This is an example for how the file should be formatted, and not a complete list for Notepad++. For more details about optimizing the launch of your application, see Optimizing the Launch Performance of Your Applications with the Image Assistant CLI Operations section in the AppStream 2.0 documentation.

C:\Program Files (x86)\Notepad++\notepad++.exe
C:\Windows\SYSTEM32\ntdll.dll
C:\Windows\SYSTEM32\wow64.dll
C:\Windows\system32\wow64cpu.dll
C:\Windows\system32\wow64win.dll

Next, create a CSV file in C:\Source named ApplicationList.csv. This file specifies the application install details to be executed on the image builder and the application metadata for the Image Assistant APIs.
The CSV fields match the fields that you see on the Image Assistant graphical user interface and the Image Assistant APIs. The CSV should have a first row with column names, and subsequent rows with the application metadata. For this example, the CSV contains:

Name,DisplayName,LaunchPath,WorkingDir,IconPath,LaunchParameters,ManifestPath,InstallerPath
Notepad++,Notepad++,'C:\Program Files (x86)\Notepad++\Notepad++.exe',,,,C:\Source\Software\Notepad++\manifest.txt,C:\Source\Software\Notepad++\Notepad++.exe

Finally, in C:\Source, create a new Powershell file called Create-Image.ps1 and paste the following code in it. This script has two mandatory parameters:

  1. SourceFile—This should point to the CSV file created previously.
  2. ComputerName—The name of the image builder that you are targeting.

A third optional parameter, S3Bucket, can be provided. If it is specified, the script downloads the software out of the provided S3 bucket onto the image builder. It then silently installs the software, and adds it to the Image Assistant, based on the parameters provided in the .csv file.
When the script has processed each of the rows of the CSV file, it then initiates image creation on the remote image builder, creating an image with the following name format: <Image Builder Name>-MMDDYYYY-mm
Here’s the script:

# AppStream 2.0 Remote Programmatic Image Creation Script

param(
    [Parameter(Mandatory = $true,
        HelpMessage = "Full path to CSV file EG: c:\source\ApplicationList.csv")]
    [string]$SourceFile,

    [Parameter(Mandatory = $true,
        ValueFromPipeline = $True,
        HelpMessage = "Image Builder Name")]
    [string[]]$ComputerName,
    
    [Parameter(HelpMessage = "S3 Bucket that Installers are stored in. This is case sensitive.")]
    [string]$s3bucket
)

$CSV = Import-Csv -Delimiter "," -Path $SourceFile
$exePath = "C:\Program Files\Amazon\Photon\ConsoleImageBuilder"
$credential = Get-Credential

foreach ($Computer in $ComputerName) {
    $IBQuery = Get-APSImageBuilderList -name $Computer -region us-west-2
    $IBIP = $IBQuery.NetworkAccessConfiguration.EniPrivateIpAddress
    $session = New-PSSession -ComputerName $IBIP -ConfigurationName 'runas' -Credential $credential

    foreach ($App in $CSV) {
        #AppStream's Image Assistant Required Parameters
        $AppName = $App.Name
        $Params = " --name " + $AppName + " --absolute-app-path " + $App.LaunchPath     
        #AppStream's Image Assistant Optional Parameters
        if ($App.DisplayName) { $Params += " --display-name " + $App.DisplayName }
        if ($App.WorkingDir) { $Params += " --working-directory " + $App.WorkingDir }
        if ($App.IconPath) { $Params += " --absolute-icon-path " + $App.IconPath }      
        if ($App.LaunchParameters) { $Params += " --launch-parameters " + $App.LaunchParameters }     
        if ($App.ManifestPath) { $Params += " --absolute-manifest-path " + $App.ManifestPath }

        #Download and Install applicatoin to Image Builder
        if ($App.InstallerPath) {
            $InstallerPath = $App.InstallerPath
            $exeName = Split-Path -Path $InstallerPath -Leaf
            $exeFolder = Split-Path -Path $InstallerPath
            if ($s3bucket) {
                $s3presigned = "Get-S3PresignedURL -BucketName " + $s3bucket + " -Key " + $exeName + " -Expires (Get-Date).AddDays(30)"
                $s3url = Invoke-Expression $s3presigned
                Invoke-Command -Session $session -ScriptBlock { 
                    New-Item -ItemType "directory" -Path $using:exeFolder -Force | Out-Null 
                    Write-Host "Downloading $using:exeName to $using:exeFolder"
                    Invoke-WebRequest -Uri $using:s3url -OutFile $using:InstallerPath
                    Write-Host "Installing $using:AppName..."
                    Start-Process -FilePath $using:InstallerPath -ArgumentList '/S' -Wait
                }
            }
        }
        #Use AppStream's Image Assistant API to add applications
        Invoke-Command -Session $session -ScriptBlock {
            Set-Location $using:exePath
            $AddAppCMD = '.\image-assistant.exe add-application ' + $using:Params 
            $AddApp = Invoke-Expression  $AddAppCMD | ConvertFrom-Json
            if ($AddApp.status -eq 0) {
                Write-Host "Added $using:AppName"
            } else {
                Write-Host "ERROR adding $using:AppName" 
                Write-Host $AddApp.message
            }
        }
    }
    #Use AppStream's Image Assistant API to create image
    Invoke-Command -Session $session -ScriptBlock {
        Set-Location $using:exePath
        $IBName = (get-item env:AppStream_Resource_Name).Value
        $ImageName = "$IBName-" + "$(Get-Date -Format "MMddyyyy-mm")"
        $CreateCMD = '.\image-assistant.exe create-image --name ' + $ImageName 
        $Create = Invoke-Expression $CreateCMD | ConvertFrom-Json
        if ($Create.status -eq 0) {
            Write-Host "Successfully started creating image $ImageName"
        } else {
            Write-Host "ERROR creating Image $ImageName"
            Write-Host "$Create.message"
        }
    }
    Remove-PSSession $session
}

Step 3: Programmatically create an image that has Notepad++
Now that you have everything prepared, you are ready to run the script. On the EC2 administrative server, launch PowerShell and change your working directory to C:\Source, then type the following:

.\Create-Image.ps1 -SourceFile C:\Source\ApplicationList.csv -ComputerName <Image Builder name> -s3bucket <S3 Bucket Name>

After you run the PowerShell script, a credentials window pops up. Enter the username and password for the account that has administrator rights on the image builder.

First, the script copies Notepad++ to the target image builder, then it silently installs. Next Notepad++ is added to Image Assistant remotely via the Image Assistant CLI operations, then starts the image creation using the name. A successful execution of the script will have the following output.

Step 4: Validate that the image worked successfully
To confirm, launch the AppStream 2.0 console, navigate to the AWS Region in which your image builder is running, then choose Images. You should find the image in a Pending status and the image builder in the Snapshotting status. After the image reaches the available state, you can apply it to a fleet to start launching applications.

Conclusion
The new Image Assistant CLI operations for AppStream 2.0 grant you more control than ever over automating application and security updates. This post reviewed one way in which the Image Assistant CLI operations can be used to update your AppStream 2.0 Image Builders from one central location.