THEY-Consulting / they-terraform

Helper modules for an easier terraform life
3 stars 0 forks source link

They Terraform

Collection of modules to provide an easy way to create and deploy common infrastructure components.

Table of Contents

Use in your own project

Prerequisites

Depending on the modules that you want to use, you need to have installed and configured the following command line tools:

Usage

Include the modules that you want to use in your terraform files:

module "lambda_with_build" {
  source = "github.com/THEY-Consulting/they-terraform//aws/lambda"

  description = "Test typescript lambda with build step"
  name        = "they-test-build"
  runtime     = "nodejs20.x"
  source_dir  = "packages/lambda-typescript"
}

and run terraform init.

For more examples see the examples directory.

If you want to use a specific version of the module, you can specify the version, commit or branch name (urlencoded) in the source url:

module "lambda_with_build" {
  source = "github.com/THEY-Consulting/they-terraform//aws/lambda?ref=v0.1.0"
}

module "lambda_with_build" {
  source = "github.com/THEY-Consulting/they-terraform//aws/lambda?ref=ee423515"
}

See the official terraform documentation for more details on using a specific version.

Specific providers can be set for modules by using the providers argument:

provider "aws" {
  region = "eu-west-1"
  alias  = "specific"
}

module "lambda_with_specific_provider" {
  source = "github.com/THEY-Consulting/they-terraform//aws/lambda"

  providers = {
    aws = aws.specific
  }
}

Modules

AWS

The location of all resources is always determined by the region of your aws provider.

RDS postgres database

module "rds_postgres_database" {
  source = "github.com/THEY-Consulting/they-terraform//aws/database/rds"

  db_identifier  = "dev-they-terraform-products" # Unique name used to identify your database in the aws console
  engine         = "postgres"
  engine_version = "15.5"

  user_name      = "psql"
  password       = sensitive("Passw0rd123")

  allocated_storage     = 5
  max_allocated_storage = 30

  instance_class        = "db.t4g.micro"
  multi_az              = false
  storage_type          = "gp2"

  backup_retention_period = 14
  backup_window           = "03:00-04:00"

  publicly_accessible = true
  apply_immediately   = true

  tags = {
    Project   = "they-terraform-examples"
    CreatedBy = "terraform"
  }
}
Inputs
Variable Type Description Required Default
db_identifier string Unique name used to identify your database in the aws console yes
engine string Engine of the database no "postgres"
engine_version string Database's engine version no "15.5"
user_name string Main username for the database no "psql"
password string Password of the main username for the database yes
allocated_storage number Allocated storage for the DB in GBs no 5
max_allocated_storage number Upper limit to which the RDS can automatically scale the storage of the db instance no 30
instance_class string Instance class of database no "db.t4g.micro"
multi_az bool Specifies whether the RDS is multi-AZ no false
storage_type string Database's storage type no "gp2"
backup_retention_period number The number of days to retain backups for no 14
backup_window string Daily time range for when backup creation is run no 03:00-04:00
publicly_accessible bool Enable/Disable depending on whether db needs to be publicly accessible no true
apply_immediately bool Specifies whether db modifications are applied immediately or during the next maintenance window no true
tags map(string) Map of tags to assign to the RDS instance and related resources no {}
vpc_cidr_block string CIDR block for the VPC no "10.0.0.0/24"
skip_final_snapshot bool Creates final DB snapshot when deleting the database. If true, no snapshot is created no false
Outputs
Output Type Description
db_connection_string string Connection String that can be used to connect to created/updated db instance
hostname string Hostname of the RDS instance
port string Port on which database is listening on
engine object Database engine
db_username string Main username for the database

Lambda

module "lambda" {
  source = "github.com/THEY-Consulting/they-terraform//aws/lambda"

  name          = "they-test"
  description   = "Test lambda without build step"
  source_dir    = "packages/lambda-simple"
  handler       = "index.handler"
  runtime       = "nodejs20.x"
  architectures = ["arm64"]
  publish       = true
  memory_size   = 128
  timeout       = 3
  layers        = ["arn:aws:lambda:us-east-1:123456789012:layer:they-test-layer:1"]

  build = {
    enabled   = true
    command   = "yarn run build"
    build_dir = "dist"
  }

  is_bundle = false

  archive = {
    output_path = "dist/lambda.zip"
    excludes    = ["test"]
  }

  cloudwatch = {
    retention_in_days = 30
  }

  cron_trigger = {
    name        = "trigger-they-test-cron-lambda"
    description = "Test cron trigger"
    schedule    = "cron(0 9 ? * MON-FRI *)"
    input = jsonencode({
      "key1" : "value1",
      "key2" : "value2"
    })
  }

  bucket_trigger = {
    name          = "trigger-they-test-bucket-lambda"
    bucket        = "they-dev"
    events        = ["s3:ObjectCreated:*"]
    filter_prefix = "they-test-lambda/"
    filter_suffix = ".txt"
  }

  role_arn = "arn:aws:iam::123456789012:role/lambda-role"
  iam_policy = [{ // only used if role_arn is not set
    name = "custom-policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [{
        Effect   = "Allow"
        Action   = ["some:Action"]
        Resource = "some:resource:arn"
      }]
    })
  }]

  environment = {
    ENV_VAR_1 = "value1"
    ENV_VAR_2 = "value2"
  }

  vpc_config = {
    subnet_ids         = ["subnet-12345678"]
    security_group_ids = ["sg-12345678"]
  }

  tags = {
    createdBy = "terraform"
    environment = "dev"
  }
}
Inputs
Variable Type Description Required Default
name string Name of the lambda function yes
description string Description of the lambda function yes
source_dir string Directory containing the lambda function yes
handler string Function entrypoint no "index.handler"
runtime string The runtime that the function is executed with, e.g. 'nodejs20.x'. yes
architectures list(string) The instruction set architecture that the function supports no ["arm64"]
publish bool Whether to publish creation/change as new Lambda Function Version no true
memory_size number Amount of memory in MB your Lambda Function can use at runtime no 128
timeout number Amount of time your Lambda Function has to run in seconds no 3
layers list(string) List of Lambda Layer Version ARNs (maximum of 5) to attach to your Lambda Function no []
build object Build configurations no see sub fields
build.enabled bool Enable/Disable running build command no true
build.command string Build command to use no "yarn run build"
build.build_dir string Directory where the compiled lambda files are generated, relative to the lambda source directory no "dist"
is_bundle bool Only files inside the 'dist' folder will be included in the zip archive. no false
archive object Configure archive file generation no see sub fields
archive.output_path string Directory where the zipped file is generated, relative to the terraform file no "dist/{name}/lambda.zip"
archive.excludes list(string) List of strings with files that are excluded from the zip file. Only applied when is_bundle is false. no []
cloudwatch object CloudWatch configuration no see sub fields
cloudwatch.retention_in_days number Retention for the CloudWatch log group no 30
cron_trigger object Configuration to trigger the lambda through a cron schedule no null
cron_trigger.name string Name of the trigger, must be unique for each lambda no null
cron_trigger.description string Description of the trigger no null
cron_trigger.schedule string Schedule expression for the trigger (yes)
cron_trigger.input string Valid JSON test passed to the trigger target no null
bucket_trigger object Configuration to trigger the lambda through bucket events no null
bucket_trigger.name string Name of the trigger (yes)
bucket_trigger.bucket string Name of the bucket (yes)
bucket_trigger.events list(string) List of events that trigger the lambda (yes)
bucket_trigger.filter_prefix string Trigger lambda only for files starting with this prefix no null
bucket_trigger.filter_suffix string Trigger lambda only for files starting with this suffix no null
role_arn string ARN of the role used for executing the lambda function, if no role is given a role with cloudwatch access is created automatically no null
iam_policy list(object) IAM policies to attach to the lambda role, only works if no custom role_arn is set no []
iam_policy.*.name string Name of the policy (yes)
iam_policy.*.policy string JSON encoded policy string (yes)
environment map(string) Map of environment variables that are accessible from the function code during execution no null
vpc_config object For network connectivity to AWS resources in a VPC no null
vpc_config.security_group_ids list(string) List of security groups to connect the lambda with (yes)
vpc_config.subnet_ids list(string) List of subnets to attach to the lambda (yes)
tags map(string) Map of tags to assign to the Lambda Function and related resources no {}
Outputs
Output Type Description
arn string The Amazon Resource Name (ARN) identifying your Lambda Function
function_name string The name of the Lambda Function
invoke_arn string The ARN to be used for invoking Lambda Function from API Gateway
build object Build output
archive_file_path string Path to the generated archive file

API Gateway (REST)

data "aws_s3_object" "truststore" {
  bucket = "they-test-api-gateway-with-domain-assets"
  key    = "certificates/truststore.pem"
}
module "api_gateway" {
  source = "github.com/THEY-Consulting/they-terraform//aws/lambda/gateway"

  name = "they-test-api-gateway"
  description = "Test API Gateway"
  stage_name = "dev"
  logging_level = "INFO"
  metrics_enabled = true

  endpoints = [
    {
      path          = "hello-world"
      method        = "GET"
      function_arn  = "some:lambda:arn"
      function_name = "some_lambda_function_name"
    },
  ]

  api_key = {
    name = "they-test-api-key"
    value = "secret-test-api-gateway-key"
    description = "Test API Gateway Key"
    enabled = true
    usage_plan_name = "they-test-api-gateway-usage-plan"
    usage_plan_description = "Test API Gateway Usage Plan"
  }

  authorizer = {
    function_name         = "authorizer_lambda_function_name"
    invoke_arn            = "authorizer:lambda:arn"
    identity_source       = "method.request.header.Authorization"
    result_ttl_in_seconds = 0
    type                  = "REQUEST"
  }

  domain = {
    certificate_arn       = "some:certificate:arn"
    s3_truststore_uri     = "s3://they-test-api-gateway-with-domain-assets/certificates/truststore.pem"
    s3_truststore_version = data.aws_s3_object.truststore.version_id
    zone_name             = "they-code.de."
    domain                = "they-test-lambda.they-code.de"
  }

  redeployment_trigger = "v1.0.0"

  tags = {
    createdBy = "terraform"
    environment = "dev"
  }
}
Inputs
Variable Type Description Required Default
name string Name of the api gateway yes
description string Description of the api gateway no ""
stage_name string Stage to use for the api gateway no "dev"
endpoints list(object) The endpoints to create for the api gateway yes
endpoints.*.path string Path segment where the lambda function is reachable yes
endpoints.*.method string HTTP Method (GET, POST, PUT, DELETE, HEAD, OPTIONS, ANY) yes
endpoints.*.function_name string Name of the lambda function yes
endpoints.*.function_arn string ARN of the lambda function yes
endpoints.*.authorization string Type of authorization used for the method (NONE, CUSTOM, AWS_IAM, COGNITO_USER_POOLS) no "None" or "CUSTOM" if authorizer is set
endpoints.*.authorizer_id string Authorizer id to be used when the authorization is CUSTOM or COGNITO_USER_POOLS no null or authorizer id if authorizer is set
endpoints.*.api_key_required bool Specify if the method requires an API key no true if api_key is set, otherwise false
logging_level string Set the logging level for the api gateway no "INFO"
metrics_enabled bool Enables metrics for the api gateway no true
api_key object Api key configuration to use for the api gateway no null
api_key.name string Specify if the method requires an API key no "${var.name}-api-key"
api_key.value string API key (yes)
api_key.description string Description of the API key no null
api_key.enabled bool Enable/Disable the API key no true
api_key.usage_plan_name string Name of the internally created usage plan no "${var.name}-usage-plan"
api_key.usage_plan_description string Description of the internally created usage plan no null
authorizer object Authorizer configuration no null
authorizer.function_name string Name of the authorizer lambda function (yes)
authorizer.invoke_arn string Invoke ARN of the authorizer lambda function (yes)
authorizer.identity_source string Source of the identity in an incoming request no method.request.header.Authorization (terraform default)
authorizer.type string Type of the authorizer (TOKEN, REQUEST, COGNITO_USER_POOLS) no TOKEN (terraform default)
authorizer.result_ttl_in_seconds number TTL of cached authorizer results in seconds no 300 (terraform default)
authorizer.identity_validation_expression string The incoming token from the client is matched against this expression, and will proceed if the token matches no null
domain object Domain configuration no null
domain.certificate_arn string ARN of the certificate that is used (required if s3_truststore_uri is not set) no
domain.domain string Domain (yes)
domain.s3_truststore_uri string URI to truststore.pem used for verification of client certs (required if certificate_arn is not set) no
domain.s3_truststore_version string version of truststore.pem used for verification of client certs (required if multiple versions of a trustore.pem exist) no
domain.zone_name string Domain zone name (yes)
redeployment_trigger string A unique string to force a redeploy of the api gateway. If not set manually, the module will use the configurations for endpoints, api_key, and authorizer config to decide if a redeployment is necessary. (yes)
tags map(string) Map of tags to assign to the Lambda Function and related resources no {}
Outputs
Output Type Description
invoke_url string The invoke URL of the api gateway
endpoint_urls list(string) List of all endpoint URLs

S3 Bucket

module "s3_bucket" {
  source = "github.com/THEY-Consulting/they-terraform//aws/s3-bucket"

  name       = "my-bucket"
  versioning = true

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "AWSLogDeliveryAclCheck",
        Effect = "Allow",
        Principal = {
          Service = "delivery.logs.amazonaws.com"
        },
        Action   = "s3:GetBucketAcl",
        Resource = "arn:aws:s3:::my-bucket",
        Condition = {
          StringEquals = {
            "aws:SourceAccount" = ["arn:aws:iam::0123456789:*"]
          },
          ArnLike = {
            "aws:SourceArn" = ["arn:aws:logs::0123456789:*"]
          }
        }
      }
    ]
  })

  prevent_destroy = true
}
Inputs
Variable Type Description Required Default
name string Name of the bucket yes
versioning bool Enable versioning of s3 bucket yes
policy string Policy of s3 bucket no null
prevent_destroy bool Prevent destroy of s3 bucket. To bypass this protection even if this is enabled, remove the module from your code and run terraform apply. no true
Outputs
Output Type Description
id string ID of the s3 bucket
arn string ARN of the s3 bucket
versioning string ID of the s3 bucket versioning

Auto Scaling group

data "aws_availability_zones" "azs" {
  state = "available"
}

data "aws_acm_certificate" "certificate" {
  domain   = "they-code.de"
  statuses = ["ISSUED"]
}

module "auto-scaling-group" {
  source = "github.com/THEY-Consulting/they-terraform//aws/auto-scaling-group"

  name        = "they-terraform-test-asg"
  ami_id = "ami-0ba27d9989b7d8c5d" # AMI valid for eu-central-1 (Amazon Linux 2023 arm64).
  instance_type = "t4g.nano"
  desired_capacity = 2
  min_size = 1
  max_size = 3
  key_name = "they-test"
  user_data_file_name = "user_data.sh" # or
  use_data = base64encode(templatefile("cloud_init.yaml", {
    environment = var.environment
  }))
  availability_zones = data.aws_availability_zones.azs.names[*] # Use AZs of region defined by provider.
  vpc_cidr_block = "10.0.0.0/16"
  public_subnets = false
  certificate_arn = data.aws_acm_certificate.certificate.arn
  tags = {
    createdBy = "terraform"
    environment = "dev"
  }
  health_check_path = "/health"
  policies = [{
    name = "ecr_pull"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Action = [
            "s3:GetObject",
            "s3:ListBucket"
          ],
          Effect   = "Allow"
          Resource = "${var.bucket_arn}/*"
        }
      ]
    })
  }]
  permissions_boundary_arn = "arn:aws:iam::123456789012:policy/they-test-boundary"
  allow_all_outbound = false
  multi_az_nat = true
}
Inputs
Variable Type Description Required Default
name string Name of the Auto Scaling group (ASG) yes
ami_id string ID of AMI used in EC2 instances of ASG yes
instance_type string Instance type used to deploy instances in ASG yes
desired_capacity number The number of EC2 instances that will be running in the ASG no 1
min_size number The minimum number of EC2 instances in the ASG no 1
max_size number The maximum number of EC2 instances in the ASG no 1
key_name string Name of key pair used for the instances no null
user_data_file_name string The name of the local file in the working directory with the user data used in the instances of the ASG no null
user_data string User data to provide when launching instances of ASG. Use this to provide plain text instead of user_data_file_name. no null
availability_zones list(string) List of availability zones (AZs) names. A subnet is created for every AZ and the ASG instances are deployed across the different AZs yes
vpc_cidr_block string The CIDR block of private IP addresses of the VPC. The subnets will be located within this CIDR block. no "10.0.0.0/16"
public_subnets bool Specify true to indicate that instances launched into the subnets should be assigned a public IP address no false
certificate_arn string ARN of certificate used to setup HTTPs in Application Load Balancer no null
tags map(string) Additional tags for the components of this module no {}
health_check_path string Destination for the health check request no "/"
policies list(object) List of policies to attach to the ASG instances via IAM Instance Profile no []
policies.*.name string Name of the inline policy yes
policies.*.policy string Policy document as a JSON formatted string yes
permissions_boundary_arn string ARN of the permissions boundary to attach to the IAM Instance Profile no null
allow_all_outbound bool Allow all outbound traffic from instances no false
health_check_type string Controls how the health check for the EC2 instances under the ASG is done no "ELB"
multi_az_nat bool Specify true to deploy a NAT Gateway in each availability zone (AZ) of the deployment. Otherwise, only a single NAT Gateway will be deployed. no false
loadbalancer_disabled bool Specify true to use the ASG without an ELB. By default, an ELB will be used. no false
Outputs
Output Type Description
alb_dns string DNS of the Application Load Balancer of the ASG
alb_zone_id string Zone ID of the Application Load Balancer of the ASG
nat_gateway_ips list(string) Public IPs of the NAT gateways

Azure OpenID role

module "azure_openid" {
  source = "github.com/THEY-Consulting/they-terraform//aws/openid/azure"

  name = "they-test"

  azure_resource_group_name = "they-dev"
  azure_location            = "Germany West Central"
  azure_identity_name = "existing-identity-name"

  policies = [
    {
      name = "they-test-policy"
      policy = jsonencode({
        Version : "2012-10-17",
        Statement : [
          {
            Effect : "Allow",
            Action : [
              "dynamodb:Query",
            ],
            Resource : [
              "arn:aws:dynamodb:::table/they-test-table",
            ]
          }
        ]
      })
    },
  ],
  inline = true
  boundary_policy_arn = "arn:aws:iam::123456789012:policy/they-test-boundary"
  INSECURE_allowAccountToAssumeRole = false # Do not enable this in production!
}
Inputs
Variable Type Description Required Default
name string Name of the role yes
azure_resource_group_name string The Azure resource group yes
azure_location string The Azure region yes
azure_identity_name string Name of an existing azure identity, if not provided, a new one will be created no null
policies list(object) List of additional inline policies to attach to the app no []
policies.*.name string Name of the inline policy yes
policies.*.policy string Policy document as a JSON formatted string yes
inline bool If true, the policies will be created as inline policies. If false, they will be created as managed policies. Changing this will not necessarily remove the old policies correctly, check in the AWS console! no true
boundary_policy_arn string ARN of a boundary policy to attach to the app no null
INSECURE_allowAccountToAssumeRole bool Set to true if you want to allow the account to assume the role. This is insecure and should only be used for testing. Do not enable this in production! no false
Outputs
Output Type Description
role_name string The name of the role
role_arn string The ARN of the role
identity_name string Name of the azure identity
identity_client_id string Client Id of the azure identity

GitHub OpenID role

module "github_action_role" {
  source = "github.com/THEY-Consulting/they-terraform//aws/openid/github"

  name = "they-test"
  repo = "THEY-Consulting/they-terraform"
  policies = [
    {
      name = "they-test-policy"
      policy = jsonencode({
        Version : "2012-10-17",
        Statement : [
          {
            Effect : "Allow",
            Action : [
              "s3:ListBucket",
            ],
            Resource : [
              "arn:aws:s3:::they-test-bucket",
            ]
          }
        ]
      })
    },
  ],
  inline = true
  boundary_policy_arn = "arn:aws:iam::123456789012:policy/they-test-boundary"
  s3StateBackend = true
  INSECURE_allowAccountToAssumeRole = false # Do not enable this in production!
}
Inputs
Variable Type Description Required Default
name string Name of the role yes
repo string Repository that is authorized to assume this role yes
policies list(object) List of additional inline policies to attach to the app no []
policies.*.name string Name of the inline policy yes
policies.*.policy string Policy document as a JSON formatted string yes
inline bool If true, the policies will be created as inline policies. If false, they will be created as managed policies. Changing this will not necessarily remove the old policies correctly, check in the AWS console! no true
boundary_policy_arn string ARN of a boundary policy to attach to the app no null
s3StateBackend bool Set to true if a s3 state backend was setup with the setup-tfstate module (or uses the same naming scheme for the s3 bucket and dynamoDB table). This will set the required s3 and dynamoDB permissions. no true
INSECURE_allowAccountToAssumeRole bool Set to true if you want to allow the account to assume the role. This is insecure and should only be used for testing. Do not enable this in production! no false
Outputs
Output Type Description
role_name string The name of the role
role_arn string The ARN of the role

setup-tfstate

module "setup_tfstate" {
  source = "github.com/THEY-Consulting/they-terraform//aws/setup-tfstate"

  name = "they-test"
}
Inputs
Variable Type Description Required Default
name string Name of the app, used for the s3 bucket and dynamoDB table name yes
Outputs
Output Type Description
s3_bucket_arn string The ARN of the s3 bucket
s3_bucket_name string The name of the s3 bucket

Azure

Function app

module "function_app" {
  source = "github.com/THEY-Consulting/they-terraform//azure/function-app"

  name                = "they-test"
  source_dir          = "packages/function-app"
  location            = "Germany West Central"
  resource_group_name = "they-dev"

  storage_account = {
    preexisting_name = "theydev"
    is_hns_enabled   = true
    tier             = "Standard"
    replication_type = "RAGRS"
    min_tls_version  = "TLS1_2"
  }

  service_plan = {
    name     = "they-test"
    sku_name = "Y1"
  }

  insights = {
    enabled           = true
    sku               = "PerGB2018"
    retention_in_days = 30
  }

  runtime = {
    name = "node"
    version = "~18"
  }

  environment = {
    ENV_VAR_1 = "value1"
    ENV_VAR_2 = "value2"
  }

  build = {
    enabled   = true
    command   = "yarn run build"
    build_dir = "dist"
  }

  is_bundle = false

  archive = {
    output_path = "dist/function-app.zip"
    excludes    = ["test"]
  }

  storage_trigger = {
    function_name                = "they-test"
    events                       = ["Microsoft.Storage.BlobCreated"]
    trigger_storage_account_name = "theydevtrigger"
    trigger_resource_group_name  = "they-dev"
    subject_filter = {
      subject_begins_with = "trigger/"
      subject_ends_with   = ".zip"
    }
    retry_policy = {
      event_time_to_live    = 360
      max_delivery_attempts = 1
    }
  }

  identity = {
    name = "they-test-identity"
  }
  assign_system_identity = true

  tags = {
    createdBy   = "Terraform"
    environment = "dev"
  }
}
Inputs
Variable Type Description Required Default
name string Name of the function app yes
source_dir string Directory containing the function code yes
location string The Azure region where the resources should be created yes
resource_group_name string The name of the resource group in which to create the function app yes
storage_account object The storage account no see sub fields
storage_account.preexisting_name string Name of an existing storage account, if this is null a new storage account will be created no null
storage_account.is_hns_enabled bool Makes the storage account a "data lake storage" if enabled. no false
storage_account.tier string Tier of the newly created storage account, ignored if storage_account.preexisting_name is set no "Standard"
storage_account.replication_type string Replication type of the newly created storage account, ignored if storage_account.preexisting_name is set no "RAGRS"
storage_account.min_tls_version string Min TLS version of the newly created storage account, ignored if storage_account.preexisting_name is set no "TLS1_2"
service_plan object The service plan no see sub fields
service_plan.name string Name of an existing service plan, if this is null a new service plan will be created no null
service_plan.sku_name string SKU name of the service plan, ignored if service_plan.name is set no "Y1"
insights object Application insights no see sub fields
insights.enabled bool Enable/Disable application insights no true
insights.sku string SKU for application insights no "PerGB2018"
insights.retention_in_days number Retention for application insights in days no 30
runtime object The runtime environment no see sub fields
runtime.name string The runtime environment name, valid values are dotnet, java, node, and powershell. Linux also supports python no "node"
runtime.version string The runtime environment version. Depends on the runtime. no "~18"
runtime.os string The os where the function app runs, valid values are windows and linux no "windows"
environment map(string) Map of environment variables that are accessible from the function code during execution no {}
build object Build configuration no see sub fields
build.enabled bool Enable/Disable running build command no true
build.command string Build command to use no "yarn run build"
build.build_dir string Directory where the compiled lambda files are generated, relative to the lambda source directory no "dist"
is_bundle bool If true, node_modules and .yarn directories will be excluded from the archive. no false
archive object Archive configuration no see sub fields
archive.output_path string Directory where the zipped file is generated, relative to the terraform file no "dist/{name}/azure-function-app.zip"
archive.excludes list(string) List of strings with files that are excluded from the zip file no []
storage_trigger object Trigger the azure function through storage event grid subscription no null
storage_trigger.function_name string Name of the function that should be triggered (yes)
storage_trigger.events list(string) List of event names that should trigger the function (yes)
storage_trigger.subject_filter object filter events for the event subscription no null
storage_trigger.subject_filter.subject_begins_with string A string to filter events for an event subscription based on a resource path prefix no null
storage_trigger.subject_filter.subject_ends_with string A string to filter events for an event subscription based on a resource path suffix no null
storage_trigger.retry_policy object Retry policy no see sub fields
storage_trigger.retry_policy.event_time_to_live number Specifies the time to live (in minutes) for events no 360
storage_trigger.retry_policy.max_delivery_attempts number Specifies the maximum number of delivery retry attempts for events no 1
identity object Identity to use no null
identity.name string Name of the identity (yes)
assign_system_identity bool If true, a system identity will be assigned to the function app. no false
tags map(string) Map of tags to assign to the function app and related resources no {}
Outputs
Output Type Description
id string The ID of the Function App
build string Build output
archive_file_path string Path to the generated archive file
endpoint_url string Endpoint URL
identities list(object) Identities if some were assigned

MSSQL Database

module "mssql_database" {
  source = "github.com/THEY-Consulting/they-terraform//azure/database/mssql"

  name                = "they-test-database"
  location            = "Germany West Central"
  resource_group_name = "they-dev"

  server = {
    preexisting_name             = "some-existing-server"
    version                      = "12.0"
    administrator_login          = "AdminUser"
    administrator_login_password = "P@ssw0rd123!"
    allow_azure_resources        = true
    allow_all                    = true
    firewall_rules = [
      {
        name             = "AllowAll"
        start_ip_address = "0.0.0.0"
        end_ip_address   = "255.255.255.255"
      }
    ]
  }

  users = [
    {
      username = "they-test-user"
      password = sensitive("P@ssw0rd123!")
      roles    = ["db_owner"]
    },
    {
      username = "they-test-user-read"
      password = sensitive("P@ssw0rd123!")
      roles    = ["db_datareader"]
    }
  ]

  collation                   = "SQL_Latin1_General_CP1_CI_AS"
  sku_name                    = "GP_S_Gen5_1"
  max_size_gb                 = 16
  min_capacity                = 0.5
  storage_account_type        = "Local"
  auto_pause_delay_in_minutes = 60

  tags = {
    createdBy   = "Terraform"
    environment = "dev"
  }
}
Inputs
Variable Type Description Required Default
name string Name of the database yes
location string The Azure region where the resources should be created yes
resource_group_name string The name of the resource group in which to create the resources yes
server object The database server yes
server.preexisting_name string Name of an existing database server, if this is null a new database server will be created no null
server.version string Version of the MSSQL database, ignored if server.preexisting_name is set no 12.0
server.administrator_login string Name of the administrator login, ignored if server.preexisting_name is set no "AdminUser"
server.administrator_login_password string Password of the administrator login, ignored if server.preexisting_name is set, required otherwise yes*
server.allow_azure_resources bool Adds a database server firewall rule to grant database access to azure resources, ignored if server.preexisting_name is set no true
server.allow_all bool Adds a database server firewall rule to grant database access to everyone, ignored if server.preexisting_name is set no false
server.firewall_rules list(object) Adds server firewall rules, ignored if server.preexisting_name is set no []
server.firewall_rules.name string Name of the firewall rule yes
server.firewall_rules.start_ip_address string Start ip address of the firewall rule yes
server.firewall_rules.end_ip_address string End ip address of the firewall rule yes
users list(object) List of users (with logins) to create in the database no []
users.username string Name for the user and login yes
users.password string Password for the user login yes
users.roles list(string) List of roles to attach to the user yes
collation string The collation of the database no "SQL_Latin1_General_CP1_CI_AS"
sku_name string The sku for the database. For vCores, this also sets the maximum capacity no "GP_S_Gen5_1"
max_size_gb number The maximum size of the database in gigabytes no 16
min_capacity number The minimum vCore of the database. The maximum is set by the sku tier. Only relevant when using a serverless vCore based database. Set this to 0 otherwise. no 0.5
storage_account_type string The storage account type used to store backups for this database. Possible values are Geo, Local and Zone no "Local"
auto_pause_delay_in_minutes number Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled. Only relevant when using a serverless vCore based database. Set this to 0 otherwise. no 60
tags map(string) Map of tags to assign to the resources no {}
Outputs
Output Type Description
database_name string Name of the database
server_administrator_login string Administrator login name
server_domain_name string Domain name of the server
ODBC_connection_string string OBDC Connection string with a placeholder for the password

VM

module "vm" {
  source = "github.com/THEY-Consulting/they-terraform//azure/vm"

  name                = "they-test-vm"
  resource_group_name = "they-dev"

  vm_hostname       = "vm"
  vm_os             = "linux"
  vm_size           = "Standard_B2s"
  vm_username       = "they"
  vm_password       = "P@ssw0rd123!"
  vm_public_ssh_key = file("key.pub")
  custom_data = base64encode(templatefile("setup_instance.yml", {
    hello = "world"
  }))
  vm_image = {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts-gen2"
    version   = "latest"
  }

  network = {
    preexisting_name = "they-dev-vnet"
    address_space    = ["10.0.0.0/16"]
  }
  subnet_address_prefix = "10.0.0.0/24"
  routes = [{
    name           = "all_traffic"
    address_prefix = "0.0.0.0/0"
    next_hop_type  = "Internet"
  }]
  public_ip = true

  allow_ssh = true
  allow_rdp = true
  security_rules = [{
    name                   = "mock-server"
    priority               = 200
    destination_port_range = "80"
  }]

  tags = {
    Project   = "they-terraform-examples"
    CreatedBy = "terraform"
  }
}
Inputs
Variable Type Description Required Default
name string Name of the vm and related resources yes
resource_group_name string The name of the resource group in which to create the resources yes
vm_hostname string Hostname of the vm no var.name
vm_os string The OS to use for the VM. Valid values are 'linux' or 'windows' no "linux"
vm_size string The size of the VM to create no "Standard_B2s"
vm_username string The username for the VM admin user no "they"
vm_password string The password of the VM admin user yes
vm_public_ssh_key string Public SSH key to use for the VM, required for linux VMs yes*
custom_data string The custom data to setup the VM no null
vm_image object The image to use for the VM no see sub fields
vm_image.publisher string Publisher of the VM image no "Canonical"
vm_image.offer string Offer of the VM image no "0001-com-ubuntu-server-jammy"
vm_image.sku string SKU of the VM image no "22_04-lts-gen2"
vm_image.version string Version of the VM image no "latest"
network object The network config to use for the VM no see sb fields
network.preexisting_name string Name of an existing network that should be used, if this is null a new network will be created no null
network.address_space list(string) List of address spaces for the network, ignored if preexisting_name is not null no ["10.0.0.0/16"]
subnet_address_prefix string The address prefix to use for the subnet no "10.0.0.0/24"
routes list(object) The routes to use for the VM no [{ name = "all_traffic", address_prefix = "0.0.0.0/0", next_hop_type = "Internet" }]
routes.name string Name of the route yes
routes.address_prefix string Address prefix of the route yes
routes.next_hop_type string Next hop type of the route yes
public_ip bool Enable a static public IP for the VM no false
allow_ssh bool Allow SSH access to the VM no false
allow_rdp bool Allow RDP access to the VM no false
security_rules list(object) The security rules to use for the VM no []
security_rules.name string Name of the security rule yes
security_rules.description string Description of the security rule no ""
security_rules.direction string Direction of the security rule no "Inbound"
security_rules.access string Access of the security rule no "Allow"
security_rules.priority number Priority of the security rule yes
security_rules.protocol string Protocol of the security rule no "Tcp"
security_rules.source_port_range string Source port range of the security rule no "*"
security_rules.source_address_prefix string Source address prefix of the security rule no "*"
security_rules.destination_port_range string Destination port range of the security rule yes
tags map(string) Map of tags to assign to the resources no {}
Outputs
Output Type Description
public_ip string Public ip if enabled
network_name string Name of the network
subnet_id string Id of the subnet
network_security_group_id string Id of the network security group
vm_username string Admin username

Container Instances

module "container-instances" {
  source = "github.com/THEY-Consulting/they-terraform//azure/container-instances"

  name                = "they-test-container-instances"
  resource_group_name = "they-terraform-test"
  create_new_resource_group = true
  location            = "Germany West Central"
  enable_log_analytics = true
  registry_credential = {
    server   = "test.azurecr.io"
    username = "User"
    password = "PassworD"
  }
  dns_a_record_name = "dns_name"
  dns_resource_group = "they-dev"
  dns_record_ttl = 400
  dns_zone_name = "dns-zone.com"
  exposed_port = [{
      port     = 3000
      protocol = "TCP"
    },{
      port     = 80
      protocol = "TCP"
    }
    ]
  tags = {
    environment = "test"
  }
  containers = [
  {
    name   = "frontend-test"
    image  = "test.azurecr.io/frontend-test:latest"
    cpu    = "2"
    memory = "4"
    environment_variables = {
      ENV1_API_URL= "https://localhost:80/api"
      ENV2   = "demo"
    }
    ports  = {
      port     = 3000
      protocol = "TCP"
    }

  },
  {
    name   = "backend-test"
    image  = "test.azurecr.io/backend-test:latest"
    cpu    = "1"
    memory = "2"
    environment_variables = {
      ENV1 = "test"
    }
    ports  = {
      port     = 80
      protocol = "TCP"
    },
    liveness_probe = {
      http_get = {
        path = "/health"
        port = 80
      }
      initial_delay_seconds = 100
      period_seconds      = 5
      failure_threshold = 3
      success_threshold = 1
    }
  }
]
}
Inputs
Variable Type Description Required Default
name string Name of the resources yes
resource_group_name string The name of the resource group in which to create the resources yes
create_new_resource_group bool If true, a new resource group with the name resource_group_name will be created. Otherwise the deployment will use an existing resource group named resource_group_name. no false
dns_resource_group string Resource group where the DNS zone is located no null
dns_a_record_name string The name of the DNS A record no null
dns_zone_name string The name of the DNS zone no null
dns_record_ttl number The TTL of the DNS record no 300
location string The Azure region where the resources should be created yes
enable_log_analytics bool Enables the creation of the resource log analytics workspace for the container group no false
sku_log_analytics string The SKU of the log analytics workspace no PerGB2018
log_retention number The number of days to retain logs in the log analytics workspace no 30
registry_credential object The credentials for the container registry no null
ip_address_type string The type of IP address that should be used yes Public
os_type string The os type that should be used yes Linux
exposed_port list(object) The list of ports that should be exposed no []
containers.name string Name of the container yes
containers.image string Image of the container yes
containers.cpu string The required number of CPU cores of the containers yes
containers.memory string The required memory of the containers in GB yes
containers.environment_variables map(string) A list of environment variables to be set on the container no
containers.ports object A set of public ports for the container no
containers.liveness_probe object The definition of a liveness probe for this container no
containers.readiness_probe object The definition of a readiness probe for this container no
tags map(string) Map of tags to assign to the resources no {}
Outputs
Output Type Description
container_endpoint string Endpoint of the container. It gives a public IP if no DNS is indicated

Contributing

Prerequisites

Environment Variables

export AWS_PROFILE=nameOfProfile
export AZURE_TENANT_ID=tenantId #<see https://portal.azure.com/#settings/directory for the correct Directory ID>
export TF_VAR_tenant_id=$AZURE_TENANT_ID

Local Dev

Install dependencies:

cd examples/aws/.packages
yarn install

cd examples/azure/.packages
yarn install

If you want to import and test changes you made without merging them first into main, you can use the git commit hash as the version in the source URL when importing the module within other projects. Don't forget to remove the hash when you are done ;)

module "module_with_unmerged_changes" {
  source = "github.com/THEY-Consulting/they-terraform//aws/lambda?ref=ee423515"
}

Deployment

Currently, we only use a single workspace within each cloud provider. To deploy each example execute:

cd examples/aws/<example>
terraform apply

cd examples/azure/<example>
terraform apply

The resources used to manage the state of the resources deployed within the examples folder can be found at examples/.setup-tfstate. If you want to setup your own Terraform state management system, remove any .terraform.lock.hcl files within the examples folder, and deploy the resources at examples/.setup-tfstate/ in your own AWS account.

Clean-up

When you are done testing, please destroy the resources with terraform destroy.

examples/aws/setup-tfstate is a bit more complicated to clean up. terraform destroy can not remove the S3 bucket (due to prevent_destroy = true).

Therefore, you need to delete the bucket manually in the AWS console. After that you can remove the remaining resources with terraform destroy. Keep in mind that after destroying a bucket it can take up to 24 hours until the name is available again.