Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deployment with terraform #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
node_modules
*.DS_Store
*config.js
.terraform
terraform.tfstate*
build/
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
clean:
@rm -rf build/
@mkdir -p build

package: clean
cd lambda/functions/createBusinessCard && npm install && zip -r ../../../build/lambda.zip .
cp -r s3/ build/s3

upload:
sed -i -e 's,{ENDPOINT},$(ENDPOINT),g' build/s3/public/js/main.js
sed -i -e 's,{RECAPTCHA_SITE_KEY},$(RECAPTCHA_SITE_KEY),g' build/s3/index.html
aws s3 cp --recursive build/s3 s3://$(BUCKET_URL)

taint:
terraform taint aws_lambda_function.fnb_lambda
terraform taint null_resource.upload
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ Create your own digital business card at https://firstnamebasis.app.

### For developers

#### Prerequisites

- `npm`:
- AWS CLI
- AWS account
- reCAPTCHA site key

To replicate the digital business card service, you need to use the following services:

1. AWS S3
2. AWS API Gateway and AWS Lambda

Expand All @@ -23,13 +31,26 @@ The assets for the individual business cards are found in the s3/assets folder.

Upon submission of the form on the main landing page, a POST API is called from the client browser and is sent to the AWS API Gateway. The lambda function is then called via the API Gateway to do the following:

* Create a new folder in the S3 bucket with a user-specific UUID to store all the relevant files
* Upload the user's profile picture and company logo into the abovementioned folder
* Render the user's individual business card HTML page
* Generate the user's VCF
* Generate the QR code pointing to the user's business card HTML page
* Upload all the files generated into the user's folder
- Create a new folder in the S3 bucket with a user-specific UUID to store all the relevant files
- Upload the user's profile picture and company logo into the abovementioned folder
- Render the user's individual business card HTML page
- Generate the user's VCF
- Generate the QR code pointing to the user's business card HTML page
- Upload all the files generated into the user's folder

The relevant code and assets for the lambda function are in the lambda/functions/createBusinessCard folder.

#### Alternative: Using Terraform

From the command line, perform the following:

```shell
make package
terraform apply -var="site_name=<YOUR_SITE_NAME>" -var="recaptcha_site_key=<YOUR_RECAPTCHA_SITE_KEY>"
```

If you are running it for the first time, perform the following first

```shell
terraform init
```
160 changes: 160 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
variable "site_name" {}
variable "recaptcha_site_key" {}

provider "aws" {
region = "ap-southeast-1"
}

# Lambda

resource "aws_lambda_function" "fnb_lambda" {
filename = "build/lambda.zip"
function_name = "FirstNameBasis"

handler = "index.handle"
runtime = "nodejs10.x"
timeout = 15

role = aws_iam_role.lambda_exec.arn
}

resource "aws_iam_role" "lambda_exec" {
name = "fnb_lambda_exec_role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_lambda_permission" "apigw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.fnb_lambda.function_name
principal = "apigateway.amazonaws.com"

source_arn = "${aws_api_gateway_rest_api.fnb.execution_arn}/*/*"
}

# API Gateway

resource "aws_api_gateway_rest_api" "fnb" {
name = "FirstNameBasis"
description = "API Gateway for FirstNameBasis"
}

resource "aws_api_gateway_resource" "proxy" {
rest_api_id = aws_api_gateway_rest_api.fnb.id
parent_id = aws_api_gateway_rest_api.fnb.root_resource_id
path_part = "{proxy+}"
}

resource "aws_api_gateway_method" "proxy" {
rest_api_id = aws_api_gateway_rest_api.fnb.id
resource_id = aws_api_gateway_resource.proxy.id
http_method = "ANY"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "lambda" {
rest_api_id = aws_api_gateway_rest_api.fnb.id
resource_id = aws_api_gateway_method.proxy.resource_id
http_method = aws_api_gateway_method.proxy.http_method

integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.fnb_lambda.invoke_arn
}

resource "aws_api_gateway_method" "proxy_root" {
rest_api_id = aws_api_gateway_rest_api.fnb.id
resource_id = aws_api_gateway_rest_api.fnb.root_resource_id
http_method = "ANY"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "lambda_root" {
rest_api_id = aws_api_gateway_rest_api.fnb.id
resource_id = aws_api_gateway_method.proxy_root.resource_id
http_method = aws_api_gateway_method.proxy_root.http_method

integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.fnb_lambda.invoke_arn
}

resource "aws_api_gateway_deployment" "fnb_v1_deployment" {
depends_on = [
aws_api_gateway_integration.lambda,
aws_api_gateway_integration.lambda_root,
]

rest_api_id = aws_api_gateway_rest_api.fnb.id
stage_name = "v1"
}

# Site S3

resource "aws_s3_bucket" "site_bucket" {
bucket = "${var.site_name}"
acl = "public-read"
website {
index_document = "index.html"
error_document = "error.html"
}
}

resource "aws_s3_bucket_policy" "site_bucket_policy" {
bucket = aws_s3_bucket.site_bucket.id

policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::${aws_s3_bucket.site_bucket.bucket}/*"
}
]
}
POLICY
}

resource "null_resource" "upload" {
provisioner "local-exec" {
working_dir = path.module
command = "make upload"

environment = {
ENDPOINT = aws_api_gateway_deployment.fnb_v1_deployment.invoke_url
BUCKET_URL = aws_s3_bucket.site_bucket.bucket
RECAPTCHA_SITE_KEY = var.recaptcha_site_key
}

interpreter = ["/bin/bash", "-c"]
}
}

# Output

output "base_url" {
value = aws_api_gateway_deployment.fnb_v1_deployment.invoke_url
}

output "bucket_url" {
value = "http://${aws_s3_bucket.site_bucket.website_endpoint}"
}
4 changes: 2 additions & 2 deletions s3/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ <h4>Office address</h4>
</div>
</div>
</section>
<div class="g-recaptcha" data-sitekey="6Le_S2IUAAAAAEEhadu_y-FGzCxprYt3gt-jKbvM"></div>
<div class="g-recaptcha" data-sitekey="{RECAPTCHA_SITE_KEY}"></div>
<br>
<button class="btn btn-secondary text-primary" id="form-submit" type="submit">Submit</button>
</form>
Expand All @@ -246,4 +246,4 @@ <h4>Office address</h4>
</div>
</div>
<!-- --------------------------------------- -->
</body>
</body>
4 changes: 2 additions & 2 deletions s3/public/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function getBase64(file) {

function sendPostRequest(body) {
console.log("sending data to api endpoint")
var endpoint = 'https://30nl04a0ki.execute-api.ap-southeast-1.amazonaws.com/v1/create';
var endpoint = '{ENDPOINT}/create';

return $.ajax({
type: 'POST',
Expand Down Expand Up @@ -81,7 +81,7 @@ $(document).ready(function() {

var postToEndpoint = formPostObject.then(function(postObject) {
// return sendPostRequest(postObject)
var endpoint = 'https://0sm4cknhsl.execute-api.ap-southeast-1.amazonaws.com/prod/create';
var endpoint = '{ENDPOINT}/create';

// POST request to endpoint
return axios({
Expand Down