Skip to content
/ tf-starter Public template

This Terraform Starter has been created to bootstrap your PHP project (Symfony, Laravel, Api Platform, or whatever ...) with Terraform and quickly build a local Docker stack. Lambda deployment is optimized to be close to the AWS free tiers (lambda/DynamoDB).

Notifications You must be signed in to change notification settings

matthieuwerner/tf-starter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

3 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

tf-starter tests suite

Introduction

This project has been created to bootstrap your PHP project (Symfony, Laravel, Api Platform, or whatever ...) with Terraform.

Install dependencies and run the start command to have a full PHP stack running locally ๐Ÿฅณ. Local configuration is described via the Docker provider in the terraform/docker directory.

After that, just run deploy-[provider] command to build your stack on your favorite provider. AWS-Lambda is for now the only available provider.

This project compile stuff from JoliCode Docker Starter, Bref.sh layers and Terraform learning resources.

๐Ÿ˜Ž Pssst: Use this project as a Github template

Quickstart ๐Ÿš„

Install the following dependencies:

Replace variables in Makefle:

export TF_VAR_project_name=terraform
export TF_VAR_project_domain=terraform.test
export TF_VAR_php_version=8.1

You can additionally add context or custom variables in your terraform/docker/variables.tfvars:

env = {                              # Environnement variables
  APP_NAME  = "tf-starter"
  APP_DEBUG = true
  APP_ENV   = "prod"
}

Replace variables in terraform/docker/variables.tfvars:

project_name      = "terraform"      # Your project name, used as a prefix of images and containers
project_domain    = "terraform.test" # The local domain
php_version       = "8.1"            # The PHP version to use

env = {                              # Environnement variables
  APP_NAME  = "tf-starter"
  APP_DEBUG = true
  APP_ENV   = "prod"
}

Configure your local host in the /etc/hosts file:

# Use the same domain as "project_domain" in the previous block
echo "127.0.0.1 terraform.test www.terraform.test" >> /etc/hosts

You optionally can install your favorite framework with installers:

make install-symfony

Start the local stack:

make start

Go to your project_domain, you should now see your website content.

To configure deployment, you now have to read the appropriate section:

Providers

AWS - Lambda

AWS Lambda provider use Bref.sh layers on top of AWS Lambda.

Quickstart

Replace or add variables in terraform/aws/variables.tfvars if needed:

env = {
  DEFAULT_REGION = "eu-west-3"
  APP_NAME       = "tf-starter"
  APP_DEBUG      = true
  APP_ENV        = "prod"
}

Deploy your application:

make lambda-deploy

This will output something like :

Apply complete! Resources: x added, x changed, x destroyed.

Outputs:

base_url = "https://**********.execute-api.eu-west-3.amazonaws.com/"
function_name = "terraform"
lambda_bucket_name = "terraform-dev"

Go to the provided base_url, you should now see an :

Hello world!

Variables

project_name: Project name used as function name.

php_layer: Bref.sh layer arn for your PHP version. The layer have to be in the same region as your lambda. You will find a complete list here: https://bref.sh/docs/runtimes/index.html#lambda-layers-in-details

entrypoint: The entrypoint of your script. Default is public/index.php.

aws_region: AWS region. Default is eu-west-3.

aws_profile: AWS profile. Default is default.

env: Environment variables as an object. Ex: {FOO = "BAR"}

Cookbook ๐Ÿค 

To go further ... (and you will)

Installers

Use installers to initialize the application folder with your favorite framework.

โš  It will clear the "application" folder content, and backup the old one with .old exentension.

Install Symfony

make install-symfony

Continue the magic with the maker bundle. Example:

make builder
cd application
bin/console make:controller

Install API Platform

make install-apip

Install Laravel

make install-laravel

Add more services

Elasticsearch

Local stack

Add the following code to terraform/docker/resources.tf:

// Elasticsearch --------------------------------
resource "docker_image" "elasticsearch" {
  name = "elasticsearch:7.16.2"
}

resource "docker_container" "elasticsearch" {
  name  = "${var.project_name}_elasticsearch"
  image = docker_image.elasticsearch.latest
  network_mode = "tf-starter_network"
  env = ["discovery.type=single-node"]
  volumes {
    volume_name = "elasticsearch-data"
    container_path = "/usr/share/elasticsearch/data"
  }
  labels {
    label = "traefik.enable"
    value = "true"
  }
  labels {
    label = "traefik.http.routers.${var.project_name}-elasticsearch.rule"
    value = "Host(`elasticsearch.${var.project_domain}`)"
  }
  labels {
    label = "traefik.http.routers.${var.project_name}-elasticsearch.tls"
    value = "true"
  }
}

resource "docker_image" "kibana" {
  name = "kibana:7.16.2"
}

resource "docker_container" "kibana" {
  name  = "${var.project_name}_kibana"
  image = docker_image.kibana.latest
  depends_on = [docker_container.elasticsearch]
  network_mode = "tf-starter_network"
  labels {
    label = "traefik.enable"
    value = "true"
  }
  labels {
    label = "traefik.http.routers.${var.project_name}-kibana.rule"
    value = "Host(`kibana.${var.project_domain}`)"
  }
  labels {
    label = "traefik.http.routers.${var.project_name}-kibana.tls"
    value = "true"
  }
}

Add the following code to terraform/docker/output.tf:

output "Elasticsearch" {
  description = "Elasticsearch informations"
  value="https://elasticsearch.${var.project_domain}"
}

output "Kibana" {
  description = "Elasticsearch informations"
  value="https://kibana.${var.project_domain}"
}

Provider: AWS Lambda

Add the following code to terraform/aws-lambda/resources.tf:

resource "aws_elasticsearch_domain" "es" {
  domain_name = local.elk_domain
  elasticsearch_version = "7.7"

  cluster_config {
      instance_count = 3
      instance_type = "r5.large.elasticsearch"
      zone_awareness_enabled = true

      zone_awareness_config {
        availability_zone_count = 3
      }
  }

  vpc_options {
      subnet_ids = [
        aws_subnet.nated_1.id,
        aws_subnet.nated_2.id,
        aws_subnet.nated_3.id
      ]

      security_group_ids = [
          aws_security_group.es.id
      ]
  }

  ebs_options {
      ebs_enabled = true
      volume_size = 10
  }

  access_policies = <<CONFIG
{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Action": "es:*",
          "Principal": "*",
          "Effect": "Allow",
          "Resource": "arn:aws:es:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:domain/${local.elk_domain}/*"
      }
  ]
}
  CONFIG

  snapshot_options {
      automated_snapshot_start_hour = 23
  }

  tags = {
      Domain = local.elk_domain
  }
}

Add the following code to terraform/aws-lambda/output.tf:

output "elk_endpoint" {
  value = aws_elasticsearch_domain.es.endpoint
}

output "elk_kibana_endpoint" {
  value = aws_elasticsearch_domain.es.kibana_endpoint
}
Redis

Local stack

Add the following code to terraform/docker/resources.tf:

// Redis --------------------------------
resource "docker_image" "redis" {
  name = "redis:6.2"
}

resource "docker_container" "redis" {
  name  = "${var.project_name}_redis"
  image = docker_image.redis.latest
  network_mode = "tf-starter_network"
  volumes {
    volume_name = "redis-data"
    container_path = "/data"
  }
}

resource "docker_image" "redis-insight" {
  name = "redislabs/redisinsight"
}

resource "docker_container" "redis-insight" {
  name  = "${var.project_name}_redis_insight"
  image = docker_image.redis-insight.latest
  depends_on = [docker_container.redis]
  network_mode = "tf-starter_network"
  volumes {
    volume_name = "redis-insight-data"
    container_path = "/db"
  }
  labels {
    label = "traefik.enable"
    value = "true"
  }
  labels {
    label = "traefik.http.routers.${var.project_name}-redis.rule"
    value = "Host(`redis.${var.project_domain}`)"
  }
  labels {
    label = "traefik.http.routers.${var.project_name}-redis.tls"
    value = "true"
  }
}

Add the following code to terraform/docker/output.tf:

output "Redis" {
  description = "Redis informations"
  value="https://redis.${var.project_domain}"
}

Provider: AWS Lambda

Add the following layer (match layer-version and region with https://raw.githubusercontent.com/brefphp/extra-php-extensions/master/layers.json):

arn:aws:lambda:<region>:403367587399:layer:redis-php-81:<layer-version>

# ex: arn:aws:lambda:eu-west-3:403367587399:layer:redis-php-81:4
RabbitMQ

Local stack

Add the following code to terraform/docker/resources.tf:

// RabbitMQ --------------------------------
resource "docker_image" "rabbitmq" {
  name = "rabbitmq:3.9-management-alpine"
}

resource "docker_container" "rabbitmq" {
  name  = "${var.project_name}_redis_insight"
  image = docker_image.rabbitmq.latest
  network_mode = "tf-starter_network"
  env = ["RABBITMQ_VM_MEMORY_HIGH_WATERMARK=1024MiB"]
  volumes {
    volume_name = "rabbitmq-data"
    container_path = "/var/lib/rabbitmq"
  }
  labels {
    label = "traefik.enable"
    value = "true"
  }
  labels {
    label = "traefik.http.routers.${var.project_name}-rabbitmq.rule"
    value = "Host(`rabbitmq.${var.project_domain}`)"
  }
  labels {
    label = "traefik.http.routers.${var.project_name}-rabbitmq.tls"
    value = "true"
  }
  labels {
    label = "traefik.http.services.rabbitmq.loadbalancer.server.port"
    value = "15672"
  }
}

Add the following code to terraform/docker/output.tf:

output "Rabbitmq" {
  description = "Rabbitmq informations"
  value="https://rabbitmq.${var.project_domain}"
}

Provider: AWS Lambda

Add the following layer (match layer-version and region with https://raw.githubusercontent.com/brefphp/extra-php-extensions/master/layers.json):

arn:aws:lambda:<region>:403367587399:layer:amqp-php-80:<layer-version>

# ex: arn:aws:lambda:eu-west-3:403367587399:layer:amqp-php-80:4

Manage environments

There are a lot a way to deal with multiple environments like preprod or prod. If your stack are basically similar between environments, you could create one workspace for each.

# Create a preprod workspace
terraform workspace new preprod

# Create a prod workspace
terraform workspace new prod

# List workspaces
terraform workspace list

After that, create in your provider working directory an environment folder with 2 tfvars files, named as your workspaces. (ex: aws-lambda/environment/preprod.tfvars.).

Edit your Makefile commands for chosen provider and replace -var-file=variables.tfvars by -var-file=environment/$$(terraform workspace show).tfvars.

Now you juste have to adapt your .tfvars files by environment, and navigate between your workspaces with:

# To use "prod" environment
terraform workspace select prod

Your commands will now automatically use the variable configuration who match the current workspace ๐Ÿ™Œ๐Ÿป.

Cleanup the mess

After initialization, you can (should) safely remove useless providers, installer commands, replace the readme, etc.


Usefull stuff used in this repo (thanks to them):
- Docker starter: https://github.com/jolicode/docker-starter
- Bref.sh: https://bref.sh/
- TF learning respources: https://learn.hashicorp.com/terraform
- Makefile tips: https://gist.github.com/prwhite/8168133?permalink_comment_id=3785627#gistcomment-3785627

About

This Terraform Starter has been created to bootstrap your PHP project (Symfony, Laravel, Api Platform, or whatever ...) with Terraform and quickly build a local Docker stack. Lambda deployment is optimized to be close to the AWS free tiers (lambda/DynamoDB).

Topics

Resources

Stars

Watchers

Forks

Packages