AWS Cloud Operations Blog

Deploying packages sequentially using AWS Systems Manager

AWS Systems Manager helps to control the sequence of package deployment in managed instances. Managed instances can be Amazon Elastic Compute Cloud (Amazon EC2) instances, virtual machines (VM) including VMs in other cloud environments, and on-premises servers. Customers are trying to automate the process of managing their state of hybrid infrastructure. They need to run specific packages for specific managed instances in a sequential manner so that packages don’t interfere with each other’s order of installation. Currently, Systems Manager Distributor packages can be used to publish the software packages in the managed instances. However, the execution of the packages may happen concurrently for managed instances when the installation of the packages is set up for a one-time install.

In this blog post, we describe two different approaches to sequentially installing specific packages for specified managed instances. The first approach explains how to use Systems Manager Distributor to sequentially install specific packages for specified managed instances. The second approach explains how to use a Systems Manager Automation document in combination with Distributor to perform sequential installations.

Option 1: Using the Systems Manager Distributor

Systems Manager Distributor helps to simplify the distribution, installation, and updates of the packages to managed instances. The package document includes an attached ZIP file of software for the target operating system, and a manifest file to describe the contents of the package. As an example, we show you the sequential installation of three software packages (Example1.msi, Example2.msi, and Example3.msi) using Systems Manager Distributor. We use a state file to track the status of each of the installations. A package is created including one .zip file per operating system. For this post, we create a package for the Windows operating system.

Step 1 – Create the first package using Systems Manager Distributor

In this step, we take an example for installing the Example1.msi in our managed instances. Steps to install Example1.msi:

  1. Create a zip file name Package1.zip containing:
    • An install and an uninstall PowerShell script
    • The software binaries
  2. The following diagram illustrates the flow of business logic in the install script to check software dependencies:

Business logic flow for the install script

Figure 1: Business logic flow for the install script

    • The install script checks whether a state file exists. If a state file does not exist, then create the state file. If the state file already exists, then check if it has any dependencies on a previous package.
    • Then the install script checks whether there is a dependency on a previous package.
    • If the previous software installation is complete, then set the status as “InProgress” and start the execution of the software.
    • If the previous software installation is not complete, then wait for the configured wait time and check once again until the number of counts you have set.
    • If there is no dependency on a previous package, then set the status as “InProgress” and start the execution of the software.
    • Check whether the installation of the current package software was completed. If so, set the status as “Completed”. If the installation of the current package software failed, then set the status as “Failed”.

Here is a reference install document for first package installation in Windows instances:

# User Configuration. Please feel free to make changes in the variables here.
$Statefile = "C:\state.txt"
$thisPkg = "Example1.msi"
$prevPkg = "None"
$WaitCount = 2
$SleepDuration = 5

# <--- SCRIPT START --->

$checkCondition = $true
$msiargumentlist = "/i $thisPkg /q"

# Check if StateFile exists or not
if (Test-Path $Statefile) {
    Write-Host "Statefile present in location. Checking if previous software was installed.."
    while ($checkCondition) {
    $StateFileObj = Get-Content $Statefile | Out-String | ConvertFrom-Json
    if ($prevPkg -eq "None") {
        $checkCondition = $false
        break
    }
    $WaitCount = $WaitCount - 1
    if ($WaitCount -eq 0) {
            # Quit Script entirely if waiting for Count*SleepDuration milliseconds doesn't resolve the 'InProgress' status
            Write-Host "Previous Software Install Stuck... Quitting"
            $StateFileObj| Add-Member -Type NoteProperty -Name $thisPkg -Value 'Failed'
            $StateFileObj | ConvertTo-Json | Out-File $Statefile
            exit 1
        }
    Write-Host "Attempt $WaitCount/3."
    if ($StateFileObj.$prevPkg -contains "Completed") {
        Write-Host "Previous Package Completed."
        $checkCondition = $false
        }
    Start-Sleep $SleepDuration
    }
    # Set InProgress in StateFile and start install
    $StateFileObj| Add-Member -Type NoteProperty -Name $thisPkg -Value 'InProgress'
    $StateFileObj | ConvertTo-Json | Out-File $Statefile
    $return = Start-Process msiexec -ArgumentList $msiArgumentList -Wait -passthru

} else {
    # State File not present - Create a new file and set 'InProgress' status
    $StateFileObj = @{}
    $StateFileObj[$thisPkg] = "InProgress"
    $StateFileObj | ConvertTo-Json | Out-File $Statefile
    $return = Start-Process msiexec -ArgumentList $msiArgumentList -Wait -passthru
}

$StateFileObj = Get-Content $Statefile | Out-String | ConvertFrom-Json

If ("0" -contains $return.ExitCode) {
    Write-Host "$thisPkg install Successful!"
    $StateFileObj.$thisPkg = "Completed"
    $StateFileObj | ConvertTo-Json | Out-File $Statefile
} else {
    Write-Host "$thisPkg install Failed!"
    $StateFileObj.$thisPkg = "Failed"
    $StateFileObj | ConvertTo-Json | Out-File $Statefile
    exit $return.ExitCode
}
  1. Create a JSON package manifest file after preparing the zip file. Here is a reference JSON package manifest example file:
{
    "schemaVersion": "2.0",
    "version": "1.0.2",
    "packages": {
        "windows": {
            "_any": {
                "x86_64": {
                    "file": "Example1.zip"
                }
            }
        }
    },
    "files": {
        "Example1.zip": {
            "checksums": {
                "sha256": "A6D34DF065F613B42736DD957AC54244E1E85615DD1410318F85072A962F3EAB"
            }
        }
    }
}
  1. Create a bucket or choose an existing bucket. Upload the zip file to an Amazon Simple Storage Service (Amazon S3) bucket as shown in the following image. For more information, please see Uploading an Object to a bucket.
  1. Create the package in AWS Management Console.
    1. Open the Systems Manager console.
    2. Choose Distributor in the navigation pane.
    3. On the Distributor page, click on Create package.

Distrbutor- Create Package

Figure 2: Systems Manager Distributor

    1. Select Advanced under the Create Package page

Distributor Package Creation

Figure 3: Distributor package creation

    1. Enter the Name of the package – DistributorPKG1.

Distributor Package Details

Figure 4: Distributor package details

    1. Enter the Amazon S3 bucket URL where the zip file was uploaded.

Bucket Location

Figure 5: Amazon S3 bucket location

    1. Enter the manifest details that you created:

Manifest

Figure 6: Manifest file creation

    1. (Optional step) Modify the package permissions. By default, the packages are set to private. This means that only those with access to the package creator’s AWS account can view package information, and update or delete the package.Edit the package’s permissions in the Systems Manager console.
    2. Install the packages by using the Systems Manager console.

Step 2 – Create the second package using Systems Manager Distributor

In this step, we install Example2.msi in our managed instances. As part of this example, we are targeting Windows instances. We follow the same steps as mentioned in creation of first package. Modify the install script to add the dependency of DistributorPKG1 installation to start the installation of DistributorPKG2 as shown in the following snapshot.

$thisPkg = "Example2.msi"
$prevPkg = "Example1.msi"

Step 3 – Create the third package using Systems Manager Distributor

In this step, we install Example3.msi in our managed instances as an example, targeting Windows instances. We follow the same steps as mentioned in creation of first package. Modify the install script to add the dependency of Package2 installation for starting the installation of DistributorPKG3.

$thisPkg = "Example3.msi"
$prevPkg = "Example2.msi"

A Systems Manager Command document is used to run commands. State Manager uses Command documents to apply a configuration to one or more targets at any point during the lifecycle of an instance. Once we create the Distributor packages using the process explained earlier, we can use a Systems Manager Composite Document to install the packages. This Composite document can then be configured as a State Manager association to bootstrap managed instances. In this example, we showed the creation of the Distributor packages as DistributorPKG1, DistributorPKG2, and DistributorPKG3.

A reference Command document example file:

---
schemaVersion: "2.2"
description: "Command Document Example JSON Template"
mainSteps:
- action: "aws:runDocument"
  name: "Install_Software1"
  inputs:
    documentType: "SSMDocument"
    documentPath: "AWS-ConfigureAWSPackage"
    documentParameters:
      action: "Install"
      name: "DistributorPKG1"

- action: "aws:runDocument"
  name: "Install_Software2"
  inputs:
    documentType: "SSMDocument"
    documentPath: "AWS-ConfigureAWSPackage"
    documentParameters:
      action: "Install"
      name: "DistributorPKG2"
      
- action: "aws:runDocument"
  name: "Install_Software3"
  inputs:
    documentType: "SSMDocument"
    documentPath: "AWS-ConfigureAWSPackage"
    documentParameters:
      action: "Install"
      name: "DistributorPKG3"

Option 2 – Using the Distributor packages in Systems Manager Automation document

Systems Manager Automation document helps us to define the actions to be performed on the managed instances as part of the Automation workflow. Automation documents are written using JavaScript Object Notation (JSON) or YAML, and include steps and parameters that you define as needed.

Automation documents help in performing common maintenance and deployment tasks. State Manager uses Automation documents to apply a configuration to one or more targets at any point during the lifecycle of an instance. While a Command document installs all the software mentioned in the document, an Automation document provides the flexibility: you can create Automation workflows that perform conditional branching. This makes it possible to create Automation workflows that evaluate different choices or dynamically respond to changes when a step is completed.

As an example, we show you the option of installing the sample packages in our managed instances using Automation document.  Rather than relying on scripting logic within our PowerShell script, we can rely on logic of the Automation workflow using aws:branch. This validates which software must be installed before proceeding to the next step.

Here are the steps to create a custom Automation document to install Systems Manager Distributor packages:

  1. Create the Automation document in the Systems Manager console:
    1. Open the Systems Manager console.
    2. Choose Documents in the navigation pane.
    3. Choose Owned by me tab and click on Create Automation
    4. Choose Editor mode and paste in your Automation document.
    5. Choose Create Automation to save the document.

Here is a reference Automation example document:

description: This is an example Automation Document to install Distributor Packages sequentially. Please make the required edits to the Package Names and installation order.
schemaVersion: '0.3'
parameters:
  Software1:
    type: Boolean
    description: (Required) Do you want to Install Software 1?
  Software2:
    type: Boolean
    description: (Required) Do you want to Install Software 2?
  Software3:
    type: Boolean
    description: (Required) Do you want to Install Software 3?
  InstanceID:
    type: String
    allowedPattern: '^[i|mi]-[a-z0-9]{8,17}$'
    description: (Required) ID of your EC2 instance where you want to install the software.
mainSteps:
  - name: checkSoft1
    action: 'aws:branch'
    inputs:
      Choices:
        - BooleanEquals: true
          NextStep: installSoftware1
          Variable: '{{Software1}}'
        - BooleanEquals: false
          NextStep: checkSoft2
          Variable: '{{Software1}}'
    description: This step checks if Software 1 is supposed to be installed or not
  - name: installSoftware1
    action: 'aws:runCommand'
    inputs:
      DocumentName: AWS-ConfigureAWSPackage
      InstanceIds:
        - '{{InstanceID}}'
      Parameters:
        action: Install
        name: Example1
    description: Install Software 1 on Instance
  - name: checkSoft2
    action: 'aws:branch'
    inputs:
      Choices:
        - BooleanEquals: true
          NextStep: installSoftware2
          Variable: '{{Software2}}'
        - BooleanEquals: false
          NextStep: checkSoft3
          Variable: '{{Software2}}'
    description: This step checks if Software 2 is supposed to be installed or not
  - name: installSoftware2
    action: 'aws:runCommand'
    inputs:
      DocumentName: AWS-ConfigureAWSPackage
      InstanceIds:
        - '{{InstanceID}}'
      Parameters:
        action: Install
        name: Example2
    description: Install Software 2 on Instance
  - name: checkSoft3
    action: 'aws:branch'
    inputs:
      Choices:
        - BooleanEquals: true
          NextStep: installSoftware3
          Variable: '{{Software3}}'
    description: This step checks if Software 3 is supposed to be installed or not
  - name: installSoftware3
    action: 'aws:runCommand'
    inputs:
      DocumentName: AWS-ConfigureAWSPackage
      InstanceIds:
        - '{{InstanceID}}'
      Parameters:
        action: Install
        name: Example3
    description: Install Software 3 on Instance

The Automation document works by using ‘aws:branch’ statements to install only the required software. Concurrency control and sequential execution are achieved by the Automation workflow. The workflow does not allow the automation to proceed if a critical step (such as installation of a specific software) fails. Customers can also look to enhance the functioning of this document by including dependency detection logic, approval steps, and rollback mechanism.

Once the Automation document is created, you can run it. Enter the instance details and other details based on the parameter configuration settings in your Automation document.

Automation Document Configuration

Figure 7: Automation document configuration

Conclusion

In this blog post, we discussed two different approaches to sequentially installing specific packages for specified managed instances. The first approach explained how to use Systems Manager Distributor to sequentially install specific packages for specified managed instances. The second  explained how to use Automation document in combination with Distributor to perform sequential installations.

About the Authors

Suman Banerjee is a Global Enterprise Solution Architect and and a Builder at heart. He has spent 20+ years helping enterprises to architect and build solutions to achieve their business goals. Architecting solutions for customers is what keeps him motivated. When he is not helping customer, he enjoys playing with his 2 kids Swapnil and Ayushmaan.

Suman’s LinkedIn Profile

 

Aman Gupta is a Cloud Support Engineer for AWS Premium Support. He has been working with AWS technology for more than 4 years. Aman works with AWS customers to design, implement and support complex cloud infrastructures. Outside of work, he enjoys spending time with his dog, family and binge watching The Office or Parks and Recreation.

Aman’s LinkedIn Profile