Skip to content

Commit

Permalink
Add Elastio terraform deployment automation example
Browse files Browse the repository at this point in the history
  • Loading branch information
Veetaha committed Aug 7, 2024
1 parent 7502ba0 commit 9698d1a
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 0 deletions.
62 changes: 62 additions & 0 deletions elastio-terraform-deployment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Elastio terraform deployment

This project presents an example of how you may automate the deployment of the Elastio stack in your AWS account.

## Obtain a personal access token (PAT)

First of all, you'll need a secret PAT token to authenticate your Elastio installation with the Elastio Portal. You can generate one by following the steps below.

1. Open the [Elastio Portal](https://login.elastio.com/) in your web browser.
2. Click on the profile button at the top right corner of the page.
3. Go to the `API access` tab.
4. Click on `Add New Access Token`.
5. Enter the name for the token, for example `Elastio deployment`.
6. Select the scope `Sources: Write` for the token.
7. Click on `Generate Token`.
8. Copy the generated token.
9. *Optional step.* Save the token in a secure place like 1Password or any other secret management system of your choice. This way you won't lose it.

## Add Elastio to your Terraform

There is a terraform module under the `module` directory. It deploys all the necessary resources for Elastio to operate. It includes the following:

- AWS Cloudformation stack named `elastio-account-level-stack`, which is deployed once per AWS account and contains the required IAM resources
- Elastio Cloud Connector stack deployed by Elastio Portal via a REST API call. It contains Lambda functions, DynamoDB databases, S3 buckets, AWS Batch compute environments and other non-IAM resources.
- *Optional.* A NAT provisioning stack that deploys NAT gateways in the private subnets where Elastio scan job workers run. This is necessary only if you deploy Elastio into private subnets that don't have outbound Internet access already. Alternatively you can deploy your own NAT gateway if you want to.

Add this terraform module to your terraform project add specify the necessary input variables. Here you'll need to pass the PAT token you [generated earlier](#obtain-a-personal-access-token-pat), specify your Elastio tenant name and the list of regions with VPC/subnet configurations where you want to deploy Elastio.

Here is an example usage of the module

```tf
provider "aws" {}
provider "http" {}
module "elastio" {
source = "../module"
elastio_pat = "{pat_token_from_elastio_portal}"
elastio_tenant = "mycompany.app.elastio.com"
elastio_cloud_connectors = [
{
region = "us-east-2"
vpc_id = "vpc-0001"
subnet_ids = [
"subnet-0001",
"subnet-0002"
]
},
{
region = "us-east-1"
vpc_id = "vpc-0002"
subnet_ids = [
"subnet-0003",
"subnet-0004",
]
}
]
# This input is optional. Here you can specify the version of the NAT provisioning stack.
# If you don't need it, just omit this input variable.
elastio_nat_provision_stack = "v4"
}
```
95 changes: 95 additions & 0 deletions elastio-terraform-deployment/module/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
locals {
elastio_endpoint = "https://${var.elastio_tenant}/public-api/v1"
headers = {
Authorization = "Bearer ${var.elastio_pat}"
}
}

data "http" "cloudformation_template" {
url = "${local.elastio_endpoint}/cloudformation-template"
request_headers = local.headers

retry {
attempts = 10
max_delay_ms = 10000
}

lifecycle {
postcondition {
condition = self.status_code >= 200 && self.status_code < 300
error_message = "Status code invalid"
}
}
}

resource "aws_cloudformation_stack" "elastio_account_level_stack" {
name = "elastio-account-level-stack"
template_url = data.http.cloudformation_template.response_body
tags = {
"elastio:resource" = "true"
}
capabilities = ["CAPABILITY_NAMED_IAM"]
}

resource "aws_cloudformation_stack" "elastio_nat_provision_stack" {
count = var.elastio_nat_provision_stack == null ? 0 : 1

name = "elastio-nat-provision-lambda"
template_url = join(
"/",
[
"https://elastio-prod-artifacts-us-east-2.s3.us-east-2.amazonaws.com",
"contrib/elastio-nat-provision-lambda/${var.elastio_nat_provision_stack}",
"cloudformation-lambda.yaml"
]
)
tags = {
"elastio:resource" = "true"
}
capabilities = ["CAPABILITY_IAM"]
}

data "aws_caller_identity" "current" {}

locals {
elastio_cloud_connector_deploy_requests = [
for connector in var.elastio_cloud_connectors : merge(
connector,
{ account_id = data.aws_caller_identity.current.account_id },
)
]
}

resource "terraform_data" "elastio_cloud_connector" {
for_each = {
for request in local.elastio_cloud_connector_deploy_requests :
request.region => request
}

input = each.value
triggers_replace = each.value

provisioner "local-exec" {
command = <<CMD
curl "$elastio_endpoint/deploy-cloud-connector" \
--location \
--fail-with-body \
--show-error \
--retry-all-errors \
--retry 5 \
-X POST \
-H "Authorization: Bearer $elastio_pat" \
-H "Content-Type: application/json; charset=utf-8" \
-d "$request_body"
CMD

environment = {
elastio_endpoint = local.elastio_endpoint
request_body = jsonencode(self.input)

// Using nonsensitive() to workaround the problem that the script's
// output is entirely suppressed: https://github.com/hashicorp/terraform/issues/27154
elastio_pat = nonsensitive(var.elastio_pat)
}
}
}
12 changes: 12 additions & 0 deletions elastio-terraform-deployment/module/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
terraform {
required_providers {
http = {
source = "hashicorp/http"
version = "3.4.4"
}
aws = {
source = "hashicorp/aws"
version = "5.61.0"
}
}
}
43 changes: 43 additions & 0 deletions elastio-terraform-deployment/module/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
variable "elastio_pat" {
description = "Personal Access Token generated by the Elastio Portal"
sensitive = true
type = string
nullable = false
}

variable "elastio_tenant" {
description = "Name of your Elastio tenant. For example `mycompany.app.elastio.com`"
type = string
nullable = false
}

variable "elastio_cloud_connectors" {
description = "List Cloud Connectors to deploy"
type = list(object({
region = string
vpc_id = string
subnet_ids = list(string)
}))
nullable = false
default = []
}

variable "elastio_nat_provision_stack" {
description = <<DESCR
Specifies the version of Elastio NAT provision stack to deploy (e.g. `v4`).
This is a Cloudformation stack that provisions automatically provisions
NAT Gateways in your VPC when Elastio worker instances run to provide them
with the outbound Internet access when Elastio is deployed in private subnets.
If you don't need this stack (e.g. you already have NAT gateways in your VPC
or you deploy into public subnets) you can omit this parameter. The default
value of `null` means there won't be any NAT provision stack deployed.
The source code of this stack can be found here:
https://github.com/elastio/contrib/tree/master/elastio-nat-provision-lambda
DESCR
type = string
nullable = true
default = null
}

0 comments on commit 9698d1a

Please sign in to comment.