├── .gitignore
├── LICENSE
├── README.md
├── Vagrantfile
├── hugo-example
├── config.toml
├── content
│ └── _index.md
└── public
│ ├── 404.html
│ ├── _index
│ └── index.html
│ ├── categories
│ └── general
│ │ ├── index.html
│ │ └── index.xml
│ ├── css
│ ├── bootie-docs.css
│ ├── bootstrap-theme.css
│ ├── bootstrap-theme.min.css
│ ├── bootstrap.min.css
│ ├── highlight.js.min.css
│ └── theme.css
│ ├── favicon.ico
│ ├── index.html
│ ├── index.xml
│ ├── js
│ ├── bootstrap.min.js
│ ├── highlight.min.js
│ ├── ie-emulation-modes-warning.js
│ ├── ie10-viewport-bug-workaround.js
│ └── jquery-1.11.2.min.js
│ ├── sitemap.xml
│ └── tags
│ └── document
│ ├── index.html
│ └── index.xml
├── images
└── hugo-site.png
├── modules
└── hugo
│ ├── iam.tf
│ ├── lambda.tf
│ ├── route53.tf
│ ├── s3.tf
│ └── variables.tf
└── terraforms
├── artifacts
└── policies
│ ├── html-bucket-policy-ip-restricted.tmpl
│ ├── html-bucket-policy.tmpl
│ └── lambda-role-policy.tmpl
├── delete-s3-trigger.sh
├── main.tf
├── provider.tf.tmpl
└── set-s3-trigger.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | .tfstatus
2 | .backup
3 | *.pem
4 | *.key
5 | .DS_Store
6 | .git
7 | .terraform
8 | .vagrant
9 | .zip
10 | provider.tf
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Xu Wang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Create a Hugo website generator on AWS Lambda with Terraform
3 |
4 | This project demonstrates how to use [Terraform](https://www.terraform.io/intro/index.html) to manage AWS resources needed to create Hugo static website using AWS Lambda service. See the excellent blog [hugo-awslambda-static-website/](http://bezdelev.com/post/hugo-aws-lambda-static-website/) about how does it work. In this project we use Terraform to automate the AWS resource creation. This gives you a repeatable process so you can build and tear down as you wish.
5 |
6 | Resources managed are:
7 |
8 | * Source, destination and log buckets on AWS, buckets's policies, static website configuration
9 | * Lambda function, IAM role and policies
10 | * Lambda S3 trigger
11 | * Route53 record for static website pointing to the S3 bucket
12 |
13 | This tutorial uses content and ideas from a number of open source projects. See [Acknowledgements](#Acknowledgements) for details.
14 |
15 | ## Prerequisites
16 |
17 | Install the _awscli_, _jq_, _terraform_, and optionally _vagrant_ as described on https://github.com/xuwang/install-tools page.
18 |
19 | When setting up AWS credentials in the above steps, use IAM user assumptions for this project:
20 |
21 | 1. Create a group `myhugo` with `AWSLambdaFullAccess`, `IAMFullAccess`, `AmazonS3FullAccess`, and `AmazonRoute53FullAccess` policy.
22 | 2. Create a user `myhugo` and __Download__ the user credentials.
23 | 3. Add user `myhugo` to group `myhugo`.
24 |
25 | ## Clone the repo
26 | ```
27 | $ git clone git@github.com:xuwang/aws-hugo
28 | $ cd aws-hugo
29 | ```
30 |
31 | ## Run Vagrant ubuntu box with terraform installed (Optional)
32 | If you use Vagrant, instead of install tools on your host machine,
33 | there is Vagranetfile for a Ubuntu box with all the necessary tools installed:
34 | ```
35 | $ vagrant up
36 | $ vagrant ssh
37 | $ cd aws-hugo
38 | ```
39 |
40 | ## Customization
41 |
42 | First setup parameters for your site. There is only one file you need to customize.
43 |
44 | ```
45 | $ cd terraforms
46 | $ cp provider.tf.tmpl provider.tf
47 | ```
48 | Edit `terraforms/provider.tf` to set up AWS region, profile and root domain for your hugo site, for example:
49 |
50 | ```
51 | # Customize this to your account configuration.
52 | provider "aws" {
53 | profile = "myhugo"
54 | region = "us-west-2"
55 | }
56 | variable "hugo_site" {
57 | # your static site FQDN
58 | default = {
59 | root_domain = "example.com"
60 | }
61 | }
62 | ```
63 |
64 | Polices for buckets and lambda function role are located under _terraform/artifacts/policies_ directory. The [lambda code](http://bezdelev.com/post/hugo-aws-lambda-static-website/) will be downloaded under _terraform/artifacts_ directory.
65 |
66 | ## Apply Terraforms
67 |
68 | Run plan first to see what resources will be created. Still under _terraform_ directory:
69 |
70 | ```
71 | $ terraform get
72 | $ terraform plan
73 | ```
74 | Verify all the resoucre to be created. These are example resouces:
75 |
76 | ```
77 | + module.hugo.aws_iam_policy_attachment.hugo_lambda_attach
78 | + module.hugo.aws_iam_role.lambda_role
79 | + module.hugo.aws_iam_role_policy.lambda_policy
80 | + module.hugo.aws_lambda_function.hugo_lambda
81 | + module.hugo.aws_route53_record.root_domain
82 | + module.hugo.aws_route53_zone.main
83 | + module.hugo.aws_s3_bucket.html
84 | + module.hugo.aws_s3_bucket.input
85 | + module.hugo.aws_s3_bucket.log
86 | + module.hugo.null_resource.lambda_download
87 | + module.hugo.template_file.lambda_policy
88 | ```
89 |
90 | ```
91 | $ terraform apply
92 | ...
93 | ```
94 |
95 | You may see the following errors:
96 | ```
97 | 1 error(s) occurred:
98 | aws_lambda_function.hugo_lambda: Error creating Lambda function: timeout while waiting for state to become '[success]'
99 | ```
100 | This is caused by [terraform issue 4926](issue https://github.com/hashicorp/terraform/issues/4926), but the function actually is created alright. You can ignore this error.
101 |
102 | To verify Terraform output created:
103 |
104 | ```
105 | $ terraform output -module=hugo
106 | aws_route53_record_fqdn = example.com
107 | html_bucket_id = example.com
108 | html_domain = s3-website-us-west-2.amazonaws.com
109 | html_endpoint = example.com.s3-website-us-west-2.amazonaws.com
110 | hugo_lambda_name = hugo-lambda
111 | input_bucket_id = input.example.com
112 | ```
113 |
114 | ## Install S3 event trigger
115 |
116 | There are some bugs arouond Terraform lambda resource management. To workaround the issues, let's use
117 | awc cli to create lambda event trigger and s3 bucket invoke policy:
118 |
119 | ```
120 | $ ./set-s3-trigger.sh
121 | ```
122 |
123 | ## Let's have fun
124 |
125 | * Upload Hugo page content to S3 bucket
126 |
127 | There is a _hugo-example_ diretory in this repo, which has a simple "Hello World" website:
128 |
129 | $ cd hugo-example
130 |
131 | Edit _config.toml_ so the baseurl matches your root_domain:
132 |
133 | baseurl = "http://example.com"
134 |
135 | Then upload hugo-example directory to the input bucket we created previously:
136 |
137 | $ cd ..
138 | $ aws --profile myhugo s3 sync hugo-example/ s3://input.example.com/
139 |
140 | * You should be able to go to http://example.com to see the page,like this:
141 | 
142 |
143 | Use your favoriate editor to update files under hugo _content_ directory, upload to S3 input bucket, lambda function will be invoked to re-generate your websites. You can view Lambda function logs as instructed [here] (http://docs.aws.amazon.com/lambda/latest/dg/monitoring-functions-logs.html)
144 |
145 | ## Tear down
146 |
147 | You can use the setup for production implementation. If you no longer need the resources, you can easily wipe out everything you created, including the buckets and contents:
148 |
149 | ```
150 | $ cd terraform
151 | $ ./delete-s3-trigger.sh
152 | $ terraform destroy
153 | Do you really want to destroy?
154 | Terraform will delete all your managed infrastructure.
155 | There is no undo. Only 'yes' will be accepted to confirm.
156 |
157 | Enter a value: yes
158 | ```
159 |
160 | If you are in Vagrant my-hugo-box:
161 | ```
162 | $ logout
163 | $ vagrant destroy
164 | ```
165 |
166 | ## Acknowledgements
167 | * [hugo-aws-lambda-static-website](http://bezdelev.com/post/hugo-aws-lambda-static-website/)
168 | * [Terraform](http://www.terraform.io/downloads.html)
169 | * [Hugo](gohugo.io)
170 |
171 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | Vagrant.configure("2") do |config|
5 | config.vm.box = "ubuntu/trusty64"
6 | config.vm.define vm_name = "my-hugo-box"
7 |
8 | # app ports
9 | # config.vm.network :forwarded_port, guest: 8080, host: 80, auto_correct: true
10 | # config.vm.network :forwarded_port, guest: 8443, host: 443, auto_correct: true
11 | # Create a private network, which allows host-only access to the machine
12 | # using a specific IP.
13 | config.vm.network :private_network, ip: "192.168.33.10"
14 |
15 | # Create a public network, which generally matched to bridged network.
16 | # Bridged networks make the machine appear as another physical device on
17 | # your network.
18 | # config.vm.network :public_network
19 |
20 | # Share an additional folder to the guest VM. The first argument is
21 | # the path on the host to the actual folder. The second argument is
22 | # the path on the guest to mount the folder. And the optional third
23 | # argument is a set of non-required options.
24 | config.vm.synced_folder ".", "/home/vagrant/aws-hugo"
25 | config.vm.synced_folder "~/.aws", "/home/vagrant/.aws"
26 | config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
27 |
28 | # To enable ssh agent fowarding and
29 | # add vagrant insecure_private_key to ssh-agent for fleet ssh
30 | # if you are using a different private_keys vs the default vagrant's insecure_private_key,
31 | # add them to ssh-agent:
32 | # ssh-add
33 | config.ssh.forward_agent = true
34 |
35 | # Get rid of stdin: not tty error
36 | config.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'"
37 |
38 | # Provider-specific configuration so you can fine-tune various
39 | # backing providers for Vagrant. These expose provider-specific options.
40 | # Example for VirtualBox:
41 | #
42 | config.vm.provider :virtualbox do |vb|
43 | vb.customize [
44 | "modifyvm", :id,
45 | "--memory", "2048"
46 | ]
47 | end
48 | config.vm.provision "shell", inline: $install_tools
49 | end
50 |
51 | $install_tools = <
132 |
133 |
134 |
135 |
136 |
137 |
138 |