Hello, my name is Gabriel and this is my solution for the challenge.
If you're planning to see how I solve this challenge, and are willing to follow allong, you have to be aware that using AWS Cloud may have some costs $$$.- Terraform
- GitHub Actions
- AWS
- Javascript and NodeJS
Have a domain name registered In my case I registered at dominios.pt
I used Terraform to create a Route53 DNS zone, to manage it. This gives me 4 nameservers, that I used to setup my domain register at dominios.pt.
I need this setup and working, in order to be able to validate certificates with DNS challenge.
Also, this is a one time setup. No need to change it.
I decided to brake the challenge in separate deployable parts. I did this to solve dependencies issues, and deployment times. 1: the frontend must call the backend to get visits counter, so we need backend url 2: the frontend must use a custom domain, so I had to register a domain (which I did outside aws, and therefore I need Route53 nameservers url's to configure my domain register provider). Also this is a relative static and durable setup 3: deploying a cloudfront cdn takes some time, so I keep it to last. Also I had some issues with custom certificates (it is required that certificate for cloudfront is created in N. Virginia region)
Having this in consideration, I structured my project in 3 main parts, each one with it's own terraform IaC code.
.
└── my_project/
├── backend/
│ ├── api
│ └── terraform
├── frontend/
│ ├── website
│ └── terraform
└── network/
├── a_setup_domain/
│ └── terraform
└── b_setup_cdn/
└── terraform
As I continue my learning path, I created a second branch "v2", where I structured my terraform code using modules. I will annotate the diferences, ahead.
Create the necessary files/credentials to access the cloud provider:
On AWS create a new user. Then select that user and on "security credentials > Access Keys" create an access key. Select "Application running outside AWS" since it will be used by terraform. This will give you two things: 1- Access key 2- Secret Access Key
(save them)
Select your user again and add him the built_in policy of "AdministratorAccess".
Create a profile:
# create a folder
$ mkdir ~/.aws
# create the file
$ nano credentials
# add this content
# [my-aws-profile]
# aws_access_key_id = <use your value>
# aws_secret_access_key = <use your value>
#
# save and exit
No we can use this crdentials in terraform aws provider:
provider "aws" {
region = "us-west-1" # replace with yours
profile = "my-aws-profile" # replace with yours
shared_credentials_files = ["~/.aws/credentials"] # replace with yours
}
Requirement You must have a Route53 hosted zone created, and you domain nameservers updated.
On branch v2, terraform will query the hosted_zone_id, using data resource, but in branch v1 you have to set that value in terraform.tfvars file.
Init terraform (to download required dependencies)
# enter terraform folder
$ cd terraform
# init
$ terraform init
# deploy infrastructure
$ terraform plan
# And that's it.
# If you are following branch v2, you don't have to do anything more
# If you are following branch v1, continue reading
Init terraform (to download required dependencies)
# enter terraform folder
$ cd terraform
# init
$ terraform init
# deploy infrastructure
$ terraform plan
after terraform it, you should be able to test. terraform will show one output "api_base_url" use it with curl:
# PUT /visits + body = { "count": 10 }
# the last .../visits is the endpoint defined in api gateway routes
$ curl -X PUT -H "Content-Type: application/json" -d '{"count":18}' <outputs_api_base_url>/visits
# r/ "Put item 18 OK"
# GET /visits
# the last .../visits is the endpoint defined in api gateway routes
$ curl <outputs_api_base_url>/visits
# r/ {"id":"gabtec.fun","visitsCount": 18}
# CORS issues ?!
# check that you don't miss cors configs when setting up "aws_apigatewayv2_api" resource
# and also in lambda response headers (js code)
after terraform it, you should be able to test. terraform will show one output "bucket_url" and "bucket_regional_name" use it with your browser:
# browser:
$ http://<outputs_bucker_url>
# in part 3 we will be using a custom domain, but for now,
# this bucket_url will do
Until now, with part 1 (backend) and part 2 (frontend) we have a fully functional service BUT... we do not have https and also we are not using a custom domain.
So, first I registered a domain: gabtec.fun I did it outside aws, in dominios.pt (I will need to setup in their services the Route53 nameservers that will manage my dns)
We also have to create and validate a ssl certificate, and we will setup a CloudFront content delivery network (cdn).
This last one (cdn) may take some time to deploy and became available, so be patient...
we need to setup part 3 "terraform.tfvars" with the previous outputs infos
after terraform it, we should have a DNS zone and we can setup our nameservers at the domain registry
This is not expected to change, so it's "set and forget"
NOTE: this can take some time to propagate
terraform will also output "route53_zone_id" needed for next step
This will create a CDN CloudFront Distribution, add a new "A" record on my domain zone to point my custom domain to cdn url
This part requires a acme certificate issued and validated. I had some issues with this:
- the first one, the fact that the certificate must be issued in N. Virginia region ???
- the second, terraform created it and output success, but I was not able to use it or see it in aws dashboard ???
- I wasn't understanding it right, and I wasn't creating the CNAME DNS records for the validation challenge (which I have to setup with terraform)
- SO I created it manually and validated it, then I passed it as a terraform var
Start this part (branch v1) by setting up terraform.tfvars with:
# aws_s3_bucket_website_configuration.*.website_endpoint
BUCKET_WEBSITE_URL = "<bucket-name>.s3-website-<region>.amazonaws.com"
# aws_s3_bucket.*.bucket_regional_domain_name
BUCKET_REGIONAL_NAME = "<bucket-name>.s3.<region>.amazonaws.com"
# aws_route53_zone.*.zone_id
ROUTE53_ZONE_ID = "the-zone-id-from-setp-3.1"
# aws_acme_certificate (in N. Virginia region)
CERTIFICATE_ARN = "sdasdasdasdasd"
After terraform it, we should be able to access the website resume with our custom domain. and...
# verify dns with
$ dns-sd -G v4 <my-domain.com>
This was a lot of content. But I think But I think I've achieved the main goals. Of course some more improvements may be done. I didn't invest much more time in complete automated CI/CD because to be able to call terraform using github actions, I'll need to have my terraform state file in a central repo (like Terraform Cloud). That will allow me to create a CI/CD runner machine, pull the state, run terraform, save new state, and destroy runner. For now I just run it locally and just put some mock tests in github actions pipeline.
Best, Gabriel