Update EC2 instance

This Workflow allows you to streamline modifications to an existing EC2 instance.

How to use a Workflow in Cortex to update an EC2 instance with Terraform

Prerequisite

Before getting started, you should have already followed the guide to Provision an EC2 instance with Terraform.

Step 1: Start creating the Workflow

Follow the steps in the documentation to create a Workflow and configure its basic settings.

In the Workflow's basic settings, set its scope to apply to the entity type AWS::EC2::Instance and the group terraform. The prerequisite Workflow Provision an EC2 instance with Terraform automatically adds the group to the entities it creates.

Step 2: Add blocks to the Workflow

The instructions on this page describe how to create this Workflow in the Cortex UI, but it is also possible to copy the Workflow YAML and add it to your workspace via the Cortex CLI. This allows you to quickly set up the example configuration then iterate on it for your own use case. Expand the tile below to learn more.

Workflow YAML instructions

To upload the Workflow example YAML into your workspace:

  1. Save the Workflow example YAML file below:

name: "[Terraform Cloud] - Update EC2 instance"
tag: update-ec2-instance
description: null
isDraft: true
filter:
  entityFilter:
    typeFilter:
      types:
      - AWS::EC2::Instance
    entityGroupFilter: null
  ownershipScope: ALL
  type: ENTITY
runResponseTemplate: null
failedRunResponseTemplate: null
actions:
- name: Get Descriptor
  slug: get-descriptor
  schema:
    expression: .context.entity.descriptor
    type: JQ
  outgoingActions:
  - get-terraform-file
  isRootAction: true
- name: Getting Terraform File
  slug: get-terraform-file
  schema:
    inputs:
      ref: main
      path: "{{actions.get-descriptor.outputs.result.info.x-cortex-git.github.basepath}}/main.tf"
      repo: "{{actions.get-descriptor.outputs.result.info.x-cortex-git.github.repository}}"
    integrationAlias: cortex
    actionIdentifier: github.getFile
    type: ADVANCED_HTTP_REQUEST
  outgoingActions:
  - decoding-file-content
  isRootAction: false
- name: Decoding File Content
  slug: decoding-file-content
  schema:
    expression: .actions."get-terraform-file".outputs.response.content | gsub("\n";"")
      | @base64d
    type: JQ
  outgoingActions:
  - getting-current-properties
  isRootAction: false
- name: Getting Current Properties
  slug: getting-current-properties
  schema:
    script: |
      var fileContent = actions['decoding-file-content'].outputs.result

      var fileObject = hcl.parseToObject(fileContent)

      return fileObject
    type: JAVASCRIPT
  outgoingActions:
  - get-current-region
  isRootAction: false
- name: Get Current Region
  slug: get-current-region
  schema:
    expression: ".actions.\"getting-current-properties\".outputs.result[0].provider.aws[0].region"
    type: JQ
  outgoingActions:
  - get-current-instance-type
  isRootAction: false
- name: Get Current Instance Type
  slug: get-current-instance-type
  schema:
    expression: ".actions.\"getting-current-properties\".outputs.result[0].resource.aws_instance.ubuntu[0].instance_type"
    type: JQ
  outgoingActions:
  - get-current-instance-name
  isRootAction: false
- name: Get Current Instance Name
  slug: get-current-instance-name
  schema:
    expression: ".actions.\"getting-current-properties\".outputs.result[0].resource.aws_instance.ubuntu[0].tags.Name"
    type: JQ
  outgoingActions:
  - setting-github-fields
  isRootAction: false
- name: Setting GitHub Fields
  slug: setting-github-fields
  schema:
    expression: |-
      {
      "pr_name" : "PR for " + .actions."get-current-instance-name".outputs.result ,
      "branch_name" : "branch-for-" + .actions."get-current-instance-name".outputs.result ,
      "repo_name" : "cremerica/sample-iac-repo",
      "commit_msg": "Commit automated by Cortex"
      }
    type: JQ
  outgoingActions:
  - user-input
  isRootAction: false
- name: User input
  slug: user-input
  schema:
    inputs:
    - name: Current Instance Type
      description: null
      key: current-instance-type
      required: false
      defaultValue: null
      placeholder: null
      validationRegex: null
      type: INPUT_FIELD
    - name: Instance-Type
      description: null
      key: instance-type
      required: false
      options:
      - t2.micro
      - t2.medium
      - t2.large
      defaultValue: null
      placeholder: ""
      allowAdditionalOptions: false
      type: SELECT_FIELD
    - name: Current Region
      description: null
      key: current-region
      required: false
      defaultValue: null
      placeholder: null
      validationRegex: null
      type: INPUT_FIELD
    - name: New Region
      description: null
      key: new-region
      required: false
      options:
      - us-west-2
      - us-west-1
      - us-east-1
      - us-east-2
      defaultValue: null
      placeholder: null
      allowAdditionalOptions: false
      type: SELECT_FIELD
    inputOverrides:
    - inputKey: current-instance-type
      outputVariable: actions.get-current-instance-type.outputs.result
      editable: false
      type: VALUE
    - inputKey: current-region
      outputVariable: actions.get-current-region.outputs.result
      editable: false
      type: VALUE
    type: USER_INPUT
  outgoingActions:
  - get-new-instance-type
  isRootAction: false
- name: Get New instance Type
  slug: get-new-instance-type
  schema:
    expression: if .actions."user-input".outputs."instance-type" == null then .actions."user-input".outputs."current-instance-type"
      else .actions."user-input".outputs."instance-type" end
    type: JQ
  outgoingActions:
  - get-new-region
  isRootAction: false
- name: Get New Region
  slug: get-new-region
  schema:
    expression: if .actions."user-input".outputs."new-region" == null then .actions."get-current-region".outputs.result
      else  .actions."user-input".outputs."new-region" end
    type: JQ
  outgoingActions:
  - scaffolder
  isRootAction: false
- name: 'Modify '
  slug: scaffolder
  schema:
    scaffolderTemplateId: st2babdd4cc74929f2
    createNewRepository: false
    createService: false
    inputOverrides:
    - inputKey: instance_type
      outputVariable: actions.get-new-instance-type.outputs.result
      editable: true
      type: VALUE
    - inputKey: resource_name
      outputVariable: actions.get-current-instance-name.outputs.result
      editable: true
      type: VALUE
    - inputKey: project_name
      outputVariable: actions.get-current-instance-name.outputs.result
      editable: true
      type: VALUE
    - inputKey: publisherRepoFullName
      outputVariable: actions.setting-github-fields.outputs.result.repo_name
      editable: false
      type: VALUE
    - inputKey: publisherBranch
      outputVariable: actions.setting-github-fields.outputs.result.branch_name
      editable: true
      type: VALUE
    - inputKey: publisherPullRequestTitle
      outputVariable: actions.setting-github-fields.outputs.result.pr_name
      editable: false
      type: VALUE
    - inputKey: publisherPullRequestBody
      outputVariable: actions.setting-github-fields.outputs.result.pr_name
      editable: true
      type: VALUE
    - inputKey: publisherCommitMessage
      outputVariable: actions.setting-github-fields.outputs.result.commit_msg
      editable: false
      type: VALUE
    - inputKey: region
      outputVariable: actions.get-new-region.outputs.result
      editable: true
      type: VALUE
    type: SCAFFOLDER
  outgoingActions: []
  isRootAction: false
runRestrictionPolicies: []
iconTag: null
variables: []
  1. Use the Cortex CLI to run this command, using the path to your Workflow YAML file: cortex workflows create -f <path-to-your-workflow.yaml>

Expand the tiles below to learn about each block in this Workflow and how to configure them in the Cortex UI:

Data transformation

Get the entity descriptor with a data transformation block.

  1. Click + in the center of the page. In the block library modal, choose Data transformation.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Get Descriptor and the slug get-descriptor.

  3. Add a jq expression to get the entity descriptor:

.context.entity.descriptor
  1. At the bottom of the side panel, click Save.

GitHub get file

This block gets the Terraform file you want to update.

  1. Click + in the center of the page. In the block library modal, choose GitHub > Get file.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Getting Terraform file and the slug get-terraform-file.

  3. For the Required parameters, configure the following:

    • Repository: {{actions.get-descriptor.outputs.result.info.x-cortex-git.github.repository}}

    • Path: {{actions.get-descriptor.outputs.result.info.x-cortex-git.github.basepath}}/main.tf

    • Ref: main

  4. Save the block.

Data transformation

Take the output from the previous block and decode its file contents.

  1. Click + in the center of the page. In the block library modal, choose Data transformation.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Decoding file content and the slug decoding-file-content.

  3. Add a jq expression:

.actions."get-terraform-file".outputs.response.content | gsub("\n";"") | @base64d
  1. At the bottom of the side panel, click Save.

JavaScript

Run a script to get the current properties from the file you got in previous steps of the Workflow.

  1. Click + in the center of the page. In the block library modal, choose GitHub > Get file.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Getting current properties and the slug getting-current-properties.

  3. Add a JavaScript expression:

/*#CORTEX_IGNORE*/import {_, YAML, hcl, context, actions, variables, fetch} from './utils.js'; async () => {
var fileContent = actions['decoding-file-content'].outputs.result

var fileObject = hcl.parseToObject(fileContent)

return fileObject

/*#CORTEX_IGNORE*/}
  1. Save the block.

Data transformations

Add four Data transformation blocks to reformat the output from previous blocks:

Get current region

  1. Click + in the center of the page. In the block library modal, choose Data transformation.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Get current region and the slug get-current-region.

  3. Add a jq expression:

.actions."getting-current-properties".outputs.result[0].provider.aws[0].region
  1. At the bottom of the side panel, click Save.

Get current instance type

  1. Click + in the center of the page. In the block library modal, choose Data transformation.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Get current instance type and the slug get-current-instance-type.

  3. Add a jq expression:

.actions."getting-current-properties".outputs.result[0].resource.aws_instance.ubuntu[0].instance_type
  1. At the bottom of the side panel, click Save.

Get current instance name

  1. Click + in the center of the page. In the block library modal, choose Data transformation.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Get current instance name and the slug get-current-instance-name.

  3. Add a jq expression:

.actions."getting-current-properties".outputs.result[0].resource.aws_instance.ubuntu[0].tags.Name
  1. At the bottom of the side panel, click Save.

Set GitHub fields

  1. Click + in the center of the page. In the block library modal, choose Data transformation.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Setting GitHub fields and the slug setting-github-fields.

  3. Add a jq expression:

{
"pr_name" : "PR for " + .actions."get-current-instance-name".outputs.result ,
"branch_name" : "branch-for-" + .actions."get-current-instance-name".outputs.result ,
"repo_name" : "cremerica/sample-iac-repo",
"commit_msg": "Commit automated by Cortex"
}
  1. At the bottom of the side panel, click Save.

User input

In this example, we add a User Input block to gather resource details from the output of previous blocks and prompt the user to select the instance type and new region.

  1. Click + in the center of the page. In the block library modal, choose User input.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name User input and the slug user-input.

  3. Click +Add user input. Add the following:

    • Name: Current instance type

    • Key: current-instance-type

    • Type: Text

    • Path to override value: actions.get-current-instance-type.outputs.result

    • Click Add input.

  4. Click +Add user input. Add the following:

    • Name: Instance type

    • Key: instance-type

    • Type: Select

    • Data source: Manual

    • Placeholder: In this example, the resource size options are t2.micro, t2.medium, and t2.large.

    • Click Add input.

  5. Click +Add user input. Add the following:

    • Name: Current region

    • Key: get-current-region

    • Type: Text

    • Path to override value: actions.get-current-region.outputs.result

    • Click Add input.

  6. Click +Add user input. Add the following:

    • Name: New region

    • Key: new-region

    • Type: Select

    • Placeholder: In this example, the options are us-west-2, us-west-1, us-east-1, and us-east-2.

    • Click Add input.

  7. Save the block.

Data transformations

Use two data transformations to format the new instance type and region that were entered during the previous step:

Get new instance type

  1. Click + in the center of the page. In the block library modal, choose Data transformation.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Get new instance type and the slug get-new-instance-type.

  3. Add a jq expression:

if .actions."user-input".outputs."instance-type" == null then .actions."user-input".outputs."current-instance-type" else .actions."user-input".outputs."instance-type" end
  1. At the bottom of the side panel, click Save.

Get new region

  1. Click + in the center of the page. In the block library modal, choose Data transformation.

  2. In the block configuration side panel, enter a name and unique slug for this block.

    1. In this example, we use the name Get new region and the slug get-new-region.

  3. Add a jq expression:

if .actions."user-input".outputs."new-region" == null then .actions."get-current-region".outputs.result else  .actions."user-input".outputs."new-region" end
  1. At the bottom of the side panel, click Save.

Scaffolder

Run a Scaffolder to make modifications. In this block, you will configure override variables to pre-fill parts of the Scaffolder template based on the output of previous steps.

  1. Click + in the center of the page. In the block library modal, select the Scaffolder block.

  2. In the side panel, enter a name and unique slug for the block.

    1. In this example, we use the name Modify and the slug scaffolder.

  3. Under Scaffolder template, select your template. In this example, we use the New EC2 Instance template.

  4. Select the option to Open a pull request.

  5. Add override variables. The Variable dropdown pulls in all the variables that are able to be overridden in the Git provider and in the Scaffolder template. In this example, we apply the following overrides:

    • instance_type: actions.get-new-instance-type.outputs.result

      • This overrides it with the output of the "Get new instance type" block earlier in the Workflow.

    • resource_name: actions.get-current-instance-name.outputs.result

      • This overrides it with the output of the "Get current instance name" block earlier in the Workflow.

    • project_name: actions.get-current-instance-name.outputs.result

      • This overrides it with the output of the "Get current instance name" block earlier in the Workflow.

    • publisherRepoFullName: actions.setting-github-fields.outputs.result.repo_name

      • This overrides it with the output of the "Setting GitHub fields" block earlier in the Workflow.

    • publisherBranch: actions.setting-github-fields.outputs.result.branch_name

      • This overrides it with the branch_name output of the "Setting GitHub fields" block earlier in the Workflow.

    • publisherPullRequestTitle: actions.setting-github-fields.outputs.result.pr_name

      • This overrides it with the pr_name output of the "Setting GitHub fields" block earlier in the Workflow.

    • publisherPullRequestBody: actions.setting-github-fields.outputs.result.pr_name

      • This overrides it with the pr_name output of the "Setting GitHub fields" block earlier in the Workflow.

    • publisherCommitMessage: actions.setting-github-fields.outputs.result.commit_msg

      • This overrides it with the commit_msg output of the "Setting GitHub fields" block earlier in the Workflow.

    • region: actions.get-new-region.outputs.result

      • This overrides it with the output of the resource region chosen during the "Get new region" block earlier in the Workflow.

  6. Set a timeout of 5 minutes.

  7. At the bottom of the side panel, click Save.

Step 3: Run the Workflow

When you run the Workflow, the following events happen:

  • The Workflow obtains the entity descriptor and gets the Terraform file.

  • The Data transformation steps decode the file contents, reshape the data, then use that reformatted data to set fields in GitHub.

  • The User input step collects the current instance type, new instance type, and current region.

  • The data is transformed, and the reformatted data is used to apply modifications to the EC2 instance.

Last updated

Was this helpful?