├── .gitignore ├── Readme.md ├── api_gateway.tf ├── api_gateway_plus_domain.tf ├── docdb.tf ├── instance.tf ├── lambda.tf ├── lambda └── src │ └── lambda.js ├── main.tf ├── outputs.tf ├── vars.tf └── vpc.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform/ 2 | *.backup 3 | *.tfstate 4 | .vs/ 5 | 6 | set_env_vars.sh 7 | lambda.zip 8 | tf-geek-api-ec2 -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # [terraform-aws-documentdb-lambda](https://medium.com/p/34a5d1061c15) 2 | 3 | > 4 | ## create resources on AWS: 5 | ```bash 6 | # 7 | $ export AWS_ACCESS_KEY_ID= 8 | $ export AWS_SECRET_ACCESS_KEY= 9 | 10 | # set variable as env vars or in vars.tf file 11 | $ export TF_VAR_name= # by default geek_api 12 | $ export TF_VAR_docdb_password= 13 | # if you have a domain on AWS 14 | # lambda will be available at https://. 15 | $ export TF_VAR_certificate_arn= 16 | $ export TF_VAR_zone_id=Z1NT1BON8JUIVM= 17 | $ export TF_VAR_main_domain= 18 | 19 | $ terraform init 20 | $ terraform apply 21 | ``` 22 | 23 | ## Outputs: 24 | - aws_instance_public_dns 25 | - url 26 | - docdb_endpoint 27 | - docdb_username 28 | - bucket 29 | - bucket_key 30 | - name 31 | 32 | ## Connect to Amazon DocumentDB 33 | ```bash 34 | $ ssh -i tf--ec2 ubuntu 35 | $ mongo \ 36 | --host \ 37 | --username \ 38 | --password 39 | ``` 40 | 41 | ## update function code: 42 | ```bash 43 | $ export AWS_DEFAULT_REGION= 44 | $ aws s3 cp lambda.zip s3:/// 45 | $ aws lambda update-function-code --function-name tf- --s3-bucket --s3-key 46 | ``` 47 | 48 | ## [Blog post](https://geekrodion.com/blog/documentdb-terraform) 49 | 50 | ## License 51 | 52 | MIT © [RodionChachura](https://geekrodion.com) 53 | -------------------------------------------------------------------------------- /api_gateway.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_rest_api" "service" { 2 | name = "tf-${var.name}" 3 | } 4 | 5 | resource "aws_api_gateway_method" "service_root" { 6 | rest_api_id = "${aws_api_gateway_rest_api.service.id}" 7 | resource_id = "${aws_api_gateway_rest_api.service.root_resource_id}" 8 | http_method = "ANY" 9 | authorization = "NONE" 10 | } 11 | 12 | resource "aws_api_gateway_integration" "service_root" { 13 | rest_api_id = "${aws_api_gateway_rest_api.service.id}" 14 | resource_id = "${aws_api_gateway_rest_api.service.root_resource_id}" 15 | http_method = "${aws_api_gateway_method.service_root.http_method}" 16 | 17 | integration_http_method = "POST" 18 | type = "AWS_PROXY" 19 | uri = "${aws_lambda_function.service.invoke_arn}" 20 | } 21 | 22 | resource "aws_api_gateway_resource" "service" { 23 | rest_api_id = "${aws_api_gateway_rest_api.service.id}" 24 | parent_id = "${aws_api_gateway_rest_api.service.root_resource_id}" 25 | path_part = "{proxy+}" 26 | } 27 | 28 | resource "aws_api_gateway_method" "service" { 29 | rest_api_id = "${aws_api_gateway_rest_api.service.id}" 30 | resource_id = "${aws_api_gateway_resource.service.id}" 31 | http_method = "ANY" 32 | authorization = "NONE" 33 | } 34 | 35 | resource "aws_api_gateway_integration" "service" { 36 | rest_api_id = "${aws_api_gateway_rest_api.service.id}" 37 | resource_id = "${aws_api_gateway_method.service.resource_id}" 38 | http_method = "${aws_api_gateway_method.service.http_method}" 39 | 40 | integration_http_method = "POST" 41 | type = "AWS_PROXY" 42 | uri = "${aws_lambda_function.service.invoke_arn}" 43 | } 44 | 45 | data "aws_caller_identity" "current" {} 46 | 47 | resource "aws_lambda_permission" "apigw" { 48 | statement_id = "AllowAPIGatewayInvoke" 49 | action = "lambda:InvokeFunction" 50 | function_name = "${aws_lambda_function.service.arn}" 51 | principal = "apigateway.amazonaws.com" 52 | 53 | source_arn = "arn:aws:execute-api:${var.region}:${data.aws_caller_identity.current.account_id}:${aws_api_gateway_rest_api.service.id}/*/*" 54 | } 55 | 56 | module "cors" { 57 | source = "github.com/carrot/terraform-api-gateway-cors-module" 58 | resource_id = "${aws_api_gateway_resource.service.id}" 59 | rest_api_id = "${aws_api_gateway_rest_api.service.id}" 60 | } 61 | 62 | resource "aws_api_gateway_deployment" "service" { 63 | depends_on = ["module.cors", "aws_api_gateway_integration.service"] 64 | rest_api_id = "${aws_api_gateway_rest_api.service.id}" 65 | stage_name = "${var.name}" 66 | } 67 | -------------------------------------------------------------------------------- /api_gateway_plus_domain.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_domain_name" "service" { 2 | count = "${var.main_domain != "" ? 1 : 0}" 3 | certificate_arn = "${var.certificate_arn}" 4 | domain_name = "${var.name}.${var.main_domain}" 5 | } 6 | 7 | resource "aws_route53_record" "service" { 8 | count = "${var.main_domain != "" ? 1 : 0}" 9 | name = "${aws_api_gateway_domain_name.service.domain_name}" 10 | type = "A" 11 | zone_id = "${var.zone_id}" 12 | 13 | alias { 14 | evaluate_target_health = true 15 | name = "${aws_api_gateway_domain_name.service.cloudfront_domain_name}" 16 | zone_id = "${aws_api_gateway_domain_name.service.cloudfront_zone_id}" 17 | } 18 | } 19 | 20 | resource "aws_api_gateway_base_path_mapping" "service" { 21 | count = "${var.main_domain != "" ? 1 : 0}" 22 | api_id = "${aws_api_gateway_rest_api.service.id}" 23 | stage_name = "${aws_api_gateway_deployment.service.stage_name}" 24 | domain_name = "${aws_api_gateway_domain_name.service.domain_name}" 25 | } -------------------------------------------------------------------------------- /docdb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_docdb_subnet_group" "service" { 2 | name = "tf-${var.name}" 3 | subnet_ids = ["${module.vpc.public_subnets}"] 4 | } 5 | 6 | resource "aws_docdb_cluster_instance" "service" { 7 | count = 1 8 | identifier = "tf-${var.name}-${count.index}" 9 | cluster_identifier = "${aws_docdb_cluster.service.id}" 10 | instance_class = "${var.docdb_instance_class}" 11 | } 12 | 13 | resource "aws_docdb_cluster" "service" { 14 | skip_final_snapshot = true 15 | db_subnet_group_name = "${aws_docdb_subnet_group.service.name}" 16 | cluster_identifier = "tf-${var.name}" 17 | engine = "docdb" 18 | master_username = "tf_${replace(var.name, "-", "_")}_admin" 19 | master_password = "${var.docdb_password}" 20 | db_cluster_parameter_group_name = "${aws_docdb_cluster_parameter_group.service.name}" 21 | vpc_security_group_ids = ["${aws_security_group.service.id}"] 22 | } 23 | 24 | resource "aws_docdb_cluster_parameter_group" "service" { 25 | family = "docdb3.6" 26 | name = "tf-${var.name}" 27 | 28 | parameter { 29 | name = "tls" 30 | value = "disabled" 31 | } 32 | } -------------------------------------------------------------------------------- /instance.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "service" { 2 | most_recent = true 3 | 4 | filter { 5 | name = "name" 6 | values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*"] 7 | } 8 | 9 | filter { 10 | name = "virtualization-type" 11 | values = ["hvm"] 12 | } 13 | 14 | owners = ["099720109477"] 15 | } 16 | 17 | resource "aws_instance" "service" { 18 | ami = "${data.aws_ami.service.id}" 19 | vpc_security_group_ids = ["${aws_security_group.service.id}"] 20 | subnet_id = "${module.vpc.public_subnets[0]}" 21 | instance_type = "t2.micro" 22 | key_name = "${aws_key_pair.service.key_name}" 23 | 24 | provisioner "remote-exec" { 25 | inline = [ 26 | "sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5", 27 | "echo 'deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.6 multiverse' | sudo tee /etc/apt/sources.list.d/mongodb-org-3.6.list", 28 | "sudo apt-get update", 29 | "sudo apt-get install -y mongodb-org-shell" 30 | ] 31 | } 32 | 33 | connection { 34 | type = "ssh" 35 | user = "ubuntu" 36 | private_key = "${tls_private_key.service.private_key_pem}" 37 | } 38 | } 39 | 40 | resource "tls_private_key" "service" { 41 | algorithm = "RSA" 42 | rsa_bits = 4096 43 | } 44 | 45 | resource "aws_key_pair" "service" { 46 | key_name = "tf-${var.name}-ec2" 47 | public_key = "${tls_private_key.service.public_key_openssh}" 48 | } 49 | 50 | resource "local_file" "service_private_key" { 51 | content = "${tls_private_key.service.private_key_pem}" 52 | filename = "${aws_key_pair.service.key_name}" 53 | provisioner "local-exec" { 54 | command = "chmod 400 ${aws_key_pair.service.key_name}" 55 | } 56 | } -------------------------------------------------------------------------------- /lambda.tf: -------------------------------------------------------------------------------- 1 | data "archive_file" "local_zipped_lambda" { 2 | type = "zip" 3 | source_dir = "${path.module}/lambda" 4 | output_path = "${path.module}/lambda.zip" 5 | } 6 | 7 | resource "aws_s3_bucket_object" "zipped_lambda" { 8 | bucket = "${aws_s3_bucket.lambda_storage.bucket}" 9 | key = "lambda.zip" 10 | source = "${data.archive_file.local_zipped_lambda.output_path}" 11 | } 12 | 13 | resource "aws_s3_bucket" "lambda_storage" { 14 | bucket = "tf-${var.name}-storage" 15 | } 16 | 17 | resource "aws_lambda_function" "service" { 18 | function_name = "tf-${var.name}" 19 | 20 | s3_bucket = "${aws_s3_bucket.lambda_storage.bucket}" 21 | s3_key = "${aws_s3_bucket_object.zipped_lambda.key}" 22 | 23 | handler = "src/lambda.handler" 24 | runtime = "nodejs8.10" 25 | role = "${aws_iam_role.service.arn}" 26 | 27 | vpc_config { 28 | subnet_ids = ["${module.vpc.private_subnets}"] 29 | security_group_ids = ["${aws_security_group.service.id}"] 30 | } 31 | 32 | environment { 33 | variables = { 34 | DB_CONNECTION_STRING = "mongodb://${aws_docdb_cluster.service.master_username}:${aws_docdb_cluster.service.master_password}@${aws_docdb_cluster.service.endpoint}:${aws_docdb_cluster.service.port}" 35 | } 36 | } 37 | } 38 | 39 | resource "aws_iam_role" "service" { 40 | name = "tf-${var.name}" 41 | 42 | assume_role_policy = < { 2 | const response = { 3 | statusCode: 200, 4 | body: JSON.stringify({ 5 | message: 'Hello from AWS Lambda!' 6 | }) 7 | } 8 | callback(null, response) 9 | } -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.region}" 3 | } -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "url" { 2 | value = "${var.main_domain != "" ? "https://${var.name}.${var.main_domain}" : "${aws_api_gateway_deployment.service.invoke_url}"}" 3 | } 4 | 5 | output "aws_instance_public_dns" { 6 | value = "${aws_instance.service.public_dns}" 7 | } 8 | 9 | output "docdb_endpoint" { 10 | value = "${aws_docdb_cluster.service.endpoint}" 11 | } 12 | 13 | output "docdb_username" { 14 | value = "${aws_docdb_cluster.service.master_username}" 15 | } 16 | 17 | output "bucket" { 18 | value = "${aws_s3_bucket.lambda_storage.bucket}" 19 | } 20 | 21 | output "bucket_key" { 22 | value = "${aws_s3_bucket_object.zipped_lambda.key}" 23 | } 24 | 25 | output "name" { 26 | value = "${var.name}" 27 | } -------------------------------------------------------------------------------- /vars.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | default = "us-east-1" 3 | } 4 | 5 | variable "name" { 6 | default = "geek-api" 7 | } 8 | 9 | variable "docdb_instance_class" { 10 | default = "db.r4.large" 11 | } 12 | 13 | variable "docdb_password" {} 14 | 15 | # optional 16 | variable "certificate_arn" {} 17 | variable "zone_id" {} 18 | variable "main_domain" {} -------------------------------------------------------------------------------- /vpc.tf: -------------------------------------------------------------------------------- 1 | module "vpc" { 2 | source = "terraform-aws-modules/vpc/aws" 3 | 4 | name = "tf-${var.name}" 5 | cidr = "10.0.0.0/16" 6 | 7 | azs = ["${var.region}a", "${var.region}b", "${var.region}c"] 8 | private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] 9 | public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] 10 | 11 | enable_dns_hostnames = true 12 | enable_dns_support = true 13 | enable_nat_gateway = true 14 | enable_vpn_gateway = true 15 | single_nat_gateway = false 16 | } 17 | 18 | resource "aws_security_group" "service" { 19 | name = "tf-${var.name}" 20 | vpc_id = "${module.vpc.vpc_id}" 21 | 22 | ingress { 23 | from_port = 0 24 | to_port = 0 25 | protocol = "-1" 26 | cidr_blocks = ["0.0.0.0/0"] 27 | } 28 | 29 | egress { 30 | from_port = 0 31 | to_port = 0 32 | protocol = "-1" 33 | cidr_blocks = ["0.0.0.0/0"] 34 | } 35 | } --------------------------------------------------------------------------------