TerraSnow Enterprise

Enables the deployment of AWS resources from ServiceNow via Terraform Enterprise


Overview

TerraSnow Enterprise is a collection of scripts that enable the deployment of Terraform resources from a ServiceNow instance via Terraform Enterprise. It was designed to simplify cloud resource consumption at the user level and to operate within a multi-tenant AWS environment.

This project contains a terraform template to deploy a Ngnix reverse proxied, Flask based endpoint that handles Gitlab Tag and Push events by creating a ServiceNow Terraform Module Catalog Item.

Contents

Installation

The following instructions outline how to install TerraSnow Enterprise using the included terraform deployable host that is maintained in this project’s repo under /scripting_host.

Assumptions

  • A working familiarity with terraform module development and terraform based resource deployment within AWS
  • A passing understanding of ServiceNow workflows

Requirements

  • Admin access to the target AWS account
  • Latest version of terraform installed locally
  • Pre-configured Gitlab instance
  • Pre-configured ServiceNow Instance
  • A MidServer deployed in the target AWS account that has been associated with the target ServiceNow instance
  • Web console access to Terraform Enterprise

NOTE: All of these services can be used with TerraSnow Enterprise in their SaaS form(s) with the exception of the ServiceNow MidServer.

Setup

Start by first cloning the TerraSnow Enterprise repo.

NOTE: Configuration management aims to be as consolidated as possible through the use of a config file. However, there are some caveats so please read through this documentation carefully.

Configuration File

Terraform Enterprise and ServiceNow environment specific details are stored within a configuration file. These settings are pulled from this configuration automatically and as needed.

This file must be stored in an S3 bucket that is read-accessible by the TerraSnow instance. Additionally it is recommended that this file be stored in an encrypted S3 bucket due to its sensitive nature.

The expected file structure is as follows:

File Name: config.ini

Contents:

[SERVICENOW]
INSTANCE_NAME=
SN_API_USER_NAME=
SN_API_USER_PWD=
TF_CATALOG=
CATEGORY=
TFE_WORKFLOW=

[TERRAFORM_ENTERPRISE]
INSTANCE_NAME=
ATLAS_TOKEN=
ServiceNow

Overview of the config.ini settings for ServiceNow specific information

Value Description
INSTANCE_NAME url of the target ServiceNow instance ex: https://mysninstance.com
SN_API_USER_NAME user name of the user performing API actions against ServiceNow
SN_API_USER_PWD password of the user performing API actions against ServiceNow
TF_CATALOG sys_id of the target Catalog
CATEGORY sys_id of the target Category
TFE_WORKFLOW sys_id of the TF catalog item order workflow
Terraform enterprise

Overview of the config.ini settings for Terraform Enterprise specific information

Value Description
INSTANCE_NAME url of the target TFE instance ex: https://app.terraform.io
ATLAS_TOKEN User API access token to create and populate TFE workspaces
ServiceNow

NOTE: These procedures outline installing this project without an associated ServiceNow application and will be built within the global scope. However, all ServiceNow scripts/workflows have been written to work within a scoped application.

MID Server

Deploy a mid server into the target AWS environment. This mid server will be making the api calls against TerraSnow in order to deploy resources against terraform enterprise.

API User
  1. Create an account on the ServiceNow instance that has the following roles:
role_name requirement
admin place_holder
api_analytics_read place_holder
catalog_editor place_holder
catalog place_holder
catalog_admin place_holder
credential_admin place_holder
rest_api_explorer place_holder
user_criteria_admin place_holder
web_service_admin place_holder
  1. Keep the username and password of this user on hand for addition into the Terrasnow configuration file
Terraform Catalog
  1. Create a new catalog within ServiceNow by using the instructions in the following link or with the ServiceNow shortcut sc_catalog.list
  2. Create a category for the terraform resources catalog
  3. Copy the sys_id of the catalog (Retrievable from the sys_id option of the right click context menu in the catalog list view) and update the value of TF_CATALOG in config.ini
  4. Copy the sys_id of the terraform resources catalog category (retrievable from the sys_id option of the right click context menu in the catalog Categories tab) and update the value of CATEGORY in config.ini
Order Workflow
  1. Create a new workflow in ServiceNow using the following instructions here
  2. Create 4 Run Script activities (Core > Utilities > Run Script), link them, and populate them with the following scripts via copy paste:

NOTE: It is useful when troubleshooting to capture Run Script activity content via log message activities during activity transitions.

Order Script Name Requires variable replacements
1 CreateWebhookRequestBodies.js NO
2 SendTFEWorkflowCreationRequest.js YES
3 SendTFEVariablesRequest.js YES
4 SendUploadConfigRequest.js YES

For the three workflow activities listed here that require variable replacement ensure YOUR_WEBHOOK_URL and YOUR_MIDSEVER_DNS_NAME are replaced with environment specific details.

  1. Once the workflow has been created copy its sys_id and update the value of self.workflow in config.ini
Terraform Enterprise

NOTE: Testing and development was done against Terraform Enterprise using a single Organization.

User API Token
  1. Generate an API token for a Terraform Enterprise user: TFE console > User Settings > Tokens
  2. Update the value of ATLAS_TOKEN in config.ini
TerraSnow Instance

This instance will perform all the ‘heavy lifting’ when it comes to building the catalog item(s) within ServiceNow as well as the Workspace creation within Terraform Enterprise when the catalog item is ordered.

Deployment

NOTE: Successful deployment requires that the environment specific configuration file has been populated with the correct information and uploaded to S3.

  1. Navigate to the scripting_host folder and create a terraform.tfvars file specific to the target AWS env
  2. Configure the local env to target the correct AWS account either via the AWS cli or by modifying the provider block in main.tf
  3. Run terraform apply
  4. Proceed to the usage section for catalog item creation and gitlab repo configuration.
API Endpoints

On successful deployment the instance is configured with the following endpoints:

Endpoint Description
/ Sends 200 regardless of content, used for testing
/aws-assume-role-webhook Listens for AWS assume role data, creates the required TFE credential env vars
/gitlab-webhook Listens for tag update events sent from gitlab and creates the associated SN catalog item
/tfe-run-webhook Listens for workflow run events, uploads the source terraform module to the target workspace to trigger a TFE workflow event
/variables-webhook Listens for ServiceNow variables creation requests, sends associated API call to SN to create the variable
/workflow-webhook Listens for TFE workspace creation events, creates an empty workspace

Configuration

Usage

Requirements

The Installation procedures have been completed successfully.

Note:

  • Module repositories must meet the same requirements as those outlined for addition to the Terraform Module Private Registry
  • This project was successfully tested against the watchmaker lx-instance module.
  • The source terraform module has a separate main.tf and variables.tf file. Variables not defined in variables.tf will not be included in the resulting ServiceNow catalog item.

ServiceNow TF catalog item creation

Note: This procedure requires that the TerraSnow installation steps have been completed successfully

  1. Create a Gitlab repo with the terraform-<PROVIDER>-<MODULE_NAME> name format
  2. Add the TerraSnow instance public key to the repo (available at the https://YOUR_TERRASNOW_INSTANCE/pub-key/key.txt) and grant the TerraSnow instance read access to the repo.
  3. Add a version tag to the project before commit that follows the PEP 440 standard (ex: 1.0.2)
  4. Add the TerraSnow instance url as a webhook under Repo > Settings > Integrations
    • Select Tag push events and Enable SSL verification
    • Paste in the TerraSnow instance gitlab webhook url: http://YOUR_TERRASNOW_INSTANCE/gitlab-webhook
    • Click the Add Webhook button to complete
  5. Kick off the ServiceNow catalog item build process by either manually triggering the webhook or incrementing the project’s version tag: git tag -a v0.0.1 -m 'test' && git push origin --tags
  6. The Terraform resource catalog item should now be available for order via the target ServiceNow instance.

Note: There is an issue with the ServiceNow catalog item OnLoad client scripts associating with their target variables (see the comments in the embedded client scripts for more details). Unfortunately, this is a manual step for now.

Deploying TF resources from ServiceNow

Navigate to the ServiceNow Catalog that was created in the installation steps and complete the catalog item request. On order, the workflow should be triggered and a workspace will be created on the target Terraform Enterprise instance.

Current workspace naming convention is the ServiceNow REQ sys_id but this may be updated in a future release

Scripting Host

Documentation that outlines the configuration of the terraform deployable scripting host.

Module Input Variables

Variable: subnet_id Description: The target subnet id for the TerraSnow instance

Variable: env_type Description: Suffix added to the instance name (dev, test, prod, etc.)

Variable: alias_name Description: Value used in building the instance name and the instance domain name.

Variable: target_r53_zone Description: Target route 53 zone in which to build the resulting domain name entry.

Variable: pub_access_sg Description: The security group within the target AWS account that allows public access.

Variable: priv_access_vpc_id Description: ID of the VPC that provides private access within the target AWS account.

Variable: priv_alb_subnets Description: List of subnets that are backed by the private ALB.

Variable: subnet_id Description: The id of the security group in which to place the instance

Variable: sg_allow_inbound_from Description: Source security group to allow inbound traffic into the instance’s private security group.

Variable: instance_type Description: AWS instance type (t2.micro, t2.medium, etc.)

Variable: key_name Description: SSH public key used to login to the TerraSnow instance.

Variable: instance_role Description: Role to associate with the TerraForm Scripting host instance. Requires read access to the S3 bucket where the TerraSnow configuration file is stored.

Variable: private_gitlab_server Description: “hostname of the gitlab server. ex: gitlab.mydomain.net. Passed as a variable into the TerraSnow host initialization script. Used to add the gitlab host as a trusted ssh endpoint and enable use of git clone via SSH.

Outputs

Variable: _private_ip Value: IPv4 IP address Description: The private IP address of the TerraSnow instance

Variable: aws_assume_role_webhook Value: https://INSTANCE_FQDN/aws-assume-role-webhook Description: The AWS assume role API endpoint of the TerraSnow instance

Variable: gitlab_webhook Value: https://INSTANCE_FQDN/gitlab-webhook Description: The gitlab webhook endpoint of the TerraSnow instance

Variable: pub_deployment_key Value: https://INSTANCE_FQDN/pub-key/key.txt Description: The web accessible path to the public key of the TerraSnow instance. This key is added to the target gitlab repo as a deploy key with read access to enable the TerraSnow instance to successfully git clone.

Variable: tfe_workflow_webhook Value: https://INSTANCE_FQDN/workflow-webhook Description: The Terraform Enterprise workspace API endpoint of the TerraSnow instance.

Variable: sn_variables_webhook Value: http://INSTANCE_FQDN/variables-webook Description: The webhook that triggers the ServiceNow catalog item variables.

Overview

The included terraform module will deploy the following resources.

Terraform Enterprise Scripting Host

Description: An EC2 instance of the size of your choosing (via the instance_type variable).

Requirements:

  • An IAM role that at a minimum has read access to the S3 bucket where the TerraSnow configuration file is stored.
  • An AWS environment that has a security group that provides public access. Port 443 is required as all communications done with the TerraSnow api endpoints are over https via the TerraSnow alb.
Application Load balancer

Description: Created via the included alb module. An ALB that proxies http connections from the TerraSnow instance to https. Backed by an AWS issued https certificate.

Requirements: A separate public access security group within the target AWS account.

TerraSnow Initialization Script

Description: A bash script that will install and configure the flask application on an EC2 instance.

Requirements: The EC2 instance on which this script is run will require internet access.

API Reference

TerraSnow API endpoints

Endpoint Description
/ Sends 200 regardless of content, used for testing
/aws-assume-role-webhook Listens for AWS assume role data, creates the required TFE credential env vars
/gitlab-webhook Listens for tag update events sent from gitlab and creates the associated SN catalog item
/tfe-run-webhook Listens for workflow run events, uploads the source terraform module to the target workspace to trigger a TFE workflow event
/variables-webhook Listens for ServiceNow variables creation requests, sends associated API call to SN to create the variable
/workflow-webhook Listens for TFE workspace creation events, creates an empty workspace
Assume Role

Listens for AWS assume role data, and creates the following TFE workspace environment variables:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY (created with is_senative=True)
  • AWS_DEFAULT_REGION
  • AWS_SESSION_TOKEN

Request Syntax

{
  "data": [
    {
     "region": "us-east-1",
     "org_name": "MyTFEorg",
     "workspace_name": "ws-123456ASDFhjklmn",
     "role": "arn:aws:iam::0123456789123:role/target_role",
     "duration": "900"
    }
   ]
}

Parameters

  • region (string)[REQUIRED] – Target region for resource creation.
  • org_name (string)[REQUIRED] – Name of the target TFE region
  • workspace_name (string)[REQUIRED] – Id of the target TFE workspace
  • role (string)[REQUIRED] – The target AWS role to assume. This role requires the necessary permissions to deploy the source terraform template in the target account.
  • duration (string)[REQUIRED] – Maps to the DurationSections option in boto3’s assume_role and is subject to the same limitations. Set to 15 minutes by default.

Returns

The response contains the TFE api responses for each environment variable that is created within the target TFE workspace.

{
  "access_key_id": "TFE VARIABLE CREATION RESPONSE",
  "secret_access_key": "TFE VARIABLE CREATION RESPONSE",
  "region": "TFE VARIABLE CREATION RESPONSE",
  "aws_session_token": "TFE VARIABLE CREATION RESPONSE"
}
Gitlab

Designed to be triggered on Gitlab tag update events. This endpoint triggers a query against the target ServiceNow instance for a catalog item of the source terraform module. If a ServiceNow catalog item is found and its version is less than the current repo’s version tag a new ServiceNow catalog item will be created and the previous version’s catalog item will be disabled, otherwise no actions are taken.

Request Syntax

Expects the standard gitlab tag update request body

Returns

{
  "Status": "200"
}
TFE run

This endpoint will query the target workspace for the configuration upload url, git clone the target repo from Gitlab, and upload the resulting zip of your repo to the workspace. Currently workspace creation sets Auto Apply to true so any change in the configuration will trigger a Plan and Apply events.

Request Syntax

{
   "data" : [
    {
    "project_name": "terraform-aws-lx-instance",
    "repo_url": "git@your_gitlab_instance:gitlab.user/terraform-aws-lx-instance.git",
    "module_version": "vx.y.z",
    "workspace_id": "ws-123456ASDFhjklmn",
    "region": "us-east-1"
    }
   ]
  }

Parameters

  • project_name (string)[REQUIRED] – Name of your terraform module project.
  • repo_url (string)[REQUIRED] – SSH URI to the target gitlab repo containing your terraform module
  • module_version (string)[REQUIRED] – specific version tag of your repo that you want to associate the workspace with.
  • workspace_id (string)[REQUIRED] – target TFE workspace id
  • region (string)[REQUIRED] – target AWS region in which your terraform resources will be deployed.

Returns

If successful:

{
  "Status": "SUCCESS"
}

In the event of an error TerraSnow will return the response given by the TFE instance against it’s call to PUT https://archivist.terraform.io/v1/object/<UNIQUE OBJECT ID>

Workflow

Listens for TFE workspace events, creates an empty TFE workspace and backs it with your source repo and version tag

Request Syntax

{
  "data" :
     [
       {
         "region": "us-east-1",
         "org_name": "your_tfe_org",
         "workspace_name": "your_tfe_workspace_name",
         "repo_id": "gitlab.user/tf_project",
         "repo_version": "x.y.z",
         "action": "CREATE"
      }
    ]
}

Parameters

  • region (string)[REQUIRED] – target AWS region in which the terraform resources will be deployed
  • org_name (string)[REQUIRED] – the target TFE organization name
  • workspace_name (string)[REQUIRED] – the target TFE workspace name
  • repo_id (string)[REQUIRED] – the id of the source terraform module’s repo
  • repo_version (string)[REQUIRED] – the target version tag of the terraform module’s repo
  • action (string)[REQUIRED] – the desired action on the target workspace, accepts CREATE or DELETE

Returns

TerraSnow simply passes back the response to the workspace creation api endpoint from the TFE instance.

From the official TFE workspace api documentation:

{
  "data": {
    "id": "ws-SihZTyXKfNXUWuUa",
    "type": "workspaces",
    "attributes": {
      "name": "workspace-2",
      "environment": "default",
      "auto-apply": false,
      "locked": false,
      "created-at": "2017-11-02T23:55:16.142Z",
      "working-directory": null,
      "terraform-version": "0.10.8",
      "can-queue-destroy-plan": true,
      "vcs-repo": {
        "identifier": "skierkowski/terraform-test-proj",
        "branch": "",
        "oauth-token-id": "ot-hmAyP66qk2AMVdbJ",
        "ingress-submodules": false
      },
      "permissions": {
        "can-update": true,
        "can-destroy": false,
        "can-queue-destroy": false,
        "can-queue-run": false,
        "can-update-variable": false,
        "can-lock": false,
        "can-read-settings": true
      }
    },
    "relationships": {
      "organization": {
        "data": {
          "id": "my-organization",
          "type": "organizations"
        }
      },
      "ssh-key": {
        "data": null
      },
      "latest-run": {
        "data": null
      }
    },
    "links": {
      "self": "/api/v2/organizations/my-organization/workspaces/workspace-2"
    }
  }
}

ServiceNow Catalog Item

The details below include descriptions of the variables, client scripts, and script includes utilized in each ServiceNow terraform resource catalog item. Unless otherwise stated variables and client scripts are created automatically.

Variables

ServiceNow catalog item variables are automatically populated with the default values of their terraform module counterparts. Variables that are defined in the terraform module without a default value are created as required ServiceNow catalog item variables.

The provided terraform module’s variable description is populated in both the ServiceNow variable question text and tool tip.

tfv

Type: String

Description: Denotes the prefix given to the variables included in the terraform module’s variables.tf file.

adv_toggle

Type: CheckBox

Description: Advanced mode toggle that is used to show/hide catalog item variables that are not marked as required.

Roles

Type: Select Box

Description: Used in conjunction with the populateAWSRoleInfoOnLoad.js client script. Contains the AWS account information for the user’s select role.

gen_OS_Type

Type: String

Description: not currently in use

gen_aws_role

Type: String

Description: Holds the ARN of the role selected from the Roles dropdown. Auto filled via the enableAfterPopulateRolesOnChange.js OnChange event

gen_AwsAccountInfo

Type: Multi Line Text

Description: Used to hold a JSON object of AWS account info. Details on how this information is populated are not currently documented. More information to follow in a later release.

gen_module_version

Type: String

Description: The version of the terraform module as provided in the Gitlab repo tag event.

gen_region

Type: String

Description: The target region in which AWS resources will be provisioned. Populated via the enableAfterPopulateRolesOnChange.js OnChange event.

gen_org_name

Type: String

Description: The name of the Terraform Enterprise Organization. Currently populated from the TerraSnow configuration file.

gen_repo_url

Type: String

Description: SSH URI to the gitlab repo

Client Scripts

This project contains several ServiceNow client scripts contained within the /sn_javascript directory that support ease of use when ordering a terraform resource catalog item.

createDiaplyToggleOnChange.js

Type: OnChange

Associated Variable: adv_toggle

Description: Used to show or hide ‘advanced’/default terraform module options (those variables included in the the terraform module that were provided with default values.)

hideGenericVariablesOnLoad.js

Type: OnLoad

Description: Hides variables prefixed with gen_ on the catalog item load event.

populateAWSRoleInfoOnLoad.js

Type: OnLoad

Description: invokes the populateAWSRoleInfoScriptInclude to populate the roles variable dropdown. This variable’s selection value is then passed to TerraSnow via the /aws-assume-role-webhook endpoint.

enableAfterPopulateRolesOnChange.js

Type: OnChange

Associated Variable: Roles

Description: popluates the gen_aws_role, gen_region variables on selection of the AWS role provided in the Roles variable dropdown

Script Includes

populateAWSRoleInfoScriptInclude.js

Description: Queries a custom table for the ServiceNow user’s associated Active directory group, their default AWS region, and the AWS account ARN that has been associated with that Active Directory group. Returns a JSON object containing this information.

Project Flow Diagrams

Terraform Module Creation Workflow

alt text

ServiceNow Catalog Item Order

alt text

ServiceNow Catalog Item Creation - Detailed

alt text

Supported Versions of ServiceNow

  • Jakarta (tested working)