This folder contains an example of how to create an EC2 instance with two EBS volumes that are persisted between
redeploys. This is useful for servers that persist data to the local hard disk and need that data to still be available
after a redeploy, such as a database like MySQL, CI server like Jenkins, or CMS like WordPress. Although it should be
possible to do this with native Terraform using the aws_ebs_volume and aws_volume_attachment resources, due to a
bug, the aws_volume_attachment resource
does not work correctly and cannot be used. Therefore, this example includes a Packer template that installs the
persistent-ebs-volume scripts and uses those scripts to attach and mount the volume
when the instance is booting.
Note, an EBS volume can only be associated with a single EC2 Instance, so if you need the data on the disk to be shared
amongst multiple servers, check out the Amazon Elastic File System, which provides a
service built on top of NFS.
How do you run this example?
To run this example, you need to do the following:
Build an AMI using Packer
Deploy the AMI using Terraform
These steps are described in detail next.
Build an AMI using Packer
The code that runs the EC2 instance in this example is an Amazon Machine Image
(AMI) that has been defined in a Packer
template under packer/build.json. To build an AMI from this template:
Open up vars.tf and set secrets at the top of the file as environment variables and fill in any other variables in
the file that don't have defaults. This includes the ami variable which you should fill in with the ID of the
AMI you just built with Packer.
terraform get.
terraform plan.
If the plan looks good, run terraform apply.
When the templates are applied, Terraform will output the IP address of the EC2 instance. If you SSH to the instance,
it should have a /data folder. This is the EBS volume. It should persist whatever data you put into it in between
restarts of this EC2 instance.
Questions? Ask away.
We're here to talk about our services, answer any questions, give advice, or just to chat.
{"treedata":{"name":"root","toggled":true,"children":[{"name":".circleci","children":[{"name":"config.yml","path":".circleci/config.yml","sha":"edb2e1f7416276a2f1dcf8bc174bf805b235163c"}]},{"name":".github","children":[{"name":"ISSUE_TEMPLATE","children":[{"name":"bug_report.md","path":".github/ISSUE_TEMPLATE/bug_report.md","sha":"d2e87e27c601e423865ed660ec697082470ca60f"},{"name":"feature_request.md","path":".github/ISSUE_TEMPLATE/feature_request.md","sha":"023a33099be2336476930c96e17ff1ba5dc55348"}]},{"name":"pull_request_template.md","path":".github/pull_request_template.md","sha":"1ed18daca8d8613b91574ddd1641f2d129204f50"}]},{"name":".gitignore","path":".gitignore","sha":"1b77db107bd9abb565bd5adafce570dd59adf016"},{"name":".pre-commit-config.yaml","path":".pre-commit-config.yaml","sha":"6c5b735f0db5a0b8d732e9fc612255e3f181d7d5"},{"name":"CODEOWNERS","path":"CODEOWNERS","sha":"555c0c6e23a7502acbef94fb0b77bfa759ba11e8"},{"name":"LICENSE.txt","path":"LICENSE.txt","sha":"f4e3d9bd4717a044ed31ad847a300eee74371a78"},{"name":"README.adoc","path":"README.adoc","sha":"c2288c93a58bebc4cf4c6f7ff7de1134fe8f0f6e"},{"name":"_docs","children":[{"name":"aws-ec2.png","path":"_docs/aws-ec2.png","sha":"861e17a7d1df585b37d10e3211f71f6a6e182115"},{"name":"single-ec2-instance-architecture.png","path":"_docs/single-ec2-instance-architecture.png","sha":"c44f7efdb936c53fc4988f286ddcf31be8a6fdc9"}]},{"name":"core-concepts.md","path":"core-concepts.md","sha":"74ee29b0f03ed842c70a271d1a58be0c61b1c358"},{"name":"examples","children":[{"name":"attach-eni","children":[{"name":"README.md","path":"examples/attach-eni/README.md","sha":"86ce148cc900ce48f3debc490ce771ff5cd9fded"},{"name":"main.tf","path":"examples/attach-eni/main.tf","sha":"97e8294ba1990b1c930810fd082317b5275c2e83"},{"name":"outputs.tf","path":"examples/attach-eni/outputs.tf","sha":"cf9472109a00b477ec4eb950e5b4e5ce9d21f4d1"},{"name":"packer","children":[{"name":"build.json","path":"examples/attach-eni/packer/build.json","sha":"c80651ff8b260e9edf73c8e8602d2e964575cebf"}]},{"name":"user-data","children":[{"name":"user-data-1.sh","path":"examples/attach-eni/user-data/user-data-1.sh","sha":"978ca952d47f3dcd84881deb3723614339edbd6f"},{"name":"user-data-2.sh","path":"examples/attach-eni/user-data/user-data-2.sh","sha":"898fd7a3c7179cc0c6d595b6ccbb4b92b1b2cedb"}]},{"name":"vars.tf","path":"examples/attach-eni/vars.tf","sha":"c8bd5aa7b155cf011bd03e3c6bf76a79fd1be4d5"}]},{"name":"bastion-host","children":[{"name":"README.md","path":"examples/bastion-host/README.md","sha":"0326f3c2c0707a3e2a67ffb6e002aec3eb30e1fa"},{"name":"main.tf","path":"examples/bastion-host/main.tf","sha":"86b9f77a4d50b5edee8837c579707ebc4d745820"},{"name":"outputs.tf","path":"examples/bastion-host/outputs.tf","sha":"5f0c2d739b9646c39e19bfd05cacb852c6080c0c"},{"name":"user-data.sh","path":"examples/bastion-host/user-data.sh","sha":"40484d6463cf88d4d5b174cbc3ca759548b9d788"},{"name":"vars.tf","path":"examples/bastion-host/vars.tf","sha":"cec5c3c5cc8041b2165059c3f8378a6415d72b3a"}]},{"name":"ec2-backup","children":[{"name":"README.md","path":"examples/ec2-backup/README.md","sha":"782667373dc4d7cf602fea0e05d6b78ed47d2c42"},{"name":"main.tf","path":"examples/ec2-backup/main.tf","sha":"a93b214edd82f1e36d0113d7727239c95e28bfac"},{"name":"outputs.tf","path":"examples/ec2-backup/outputs.tf","sha":"6023311f87f6757e60e5d47600095cbc6dce324a"},{"name":"variables.tf","path":"examples/ec2-backup/variables.tf","sha":"e6e9fa9520608ef0a5cc0aadc70d118775543b03"}]},{"name":"persistent-ebs-volume","children":[{"name":"README.md","path":"examples/persistent-ebs-volume/README.md","sha":"7acf321a8a3183bfe4d7187be94208ed281bcd66","toggled":true},{"name":"main.tf","path":"examples/persistent-ebs-volume/main.tf","sha":"e38e758d9bb4d438633e840003478803e0a7cde8"},{"name":"outputs.tf","path":"examples/persistent-ebs-volume/outputs.tf","sha":"54f33c96b796373b12b9702f46d30a1c85043f65"},{"name":"packer","children":[{"name":"build.json","path":"examples/persistent-ebs-volume/packer/build.json","sha":"d9448a8beecb2b12a1e7ddfbb6ad59d9997988e9"}]},{"name":"user-data","children":[{"name":"user-data.sh","path":"examples/persistent-ebs-volume/user-data/user-data.sh","sha":"9c5d08f440596a978418d09aea275d8c0ffc16fb"}]},{"name":"vars.tf","path":"examples/persistent-ebs-volume/vars.tf","sha":"47a3c46579f095eaed913046ebd972edd7bb525e"}],"toggled":true},{"name":"route53-helpers","children":[{"name":"README.md","path":"examples/route53-helpers/README.md","sha":"6b212ee9299ea98770f423799c73583e617e07fa"},{"name":"main.tf","path":"examples/route53-helpers/main.tf","sha":"9c2307438841e137cd8de9e6885283c054290959"},{"name":"outputs.tf","path":"examples/route53-helpers/outputs.tf","sha":"4c3c3ccccf964155ffbce7546fcbc9ef6e886de6"},{"name":"packer","children":[{"name":"build.json","path":"examples/route53-helpers/packer/build.json","sha":"d36a48b043b3398dff52c286dd404f411054bacd"}]},{"name":"user-data","children":[{"name":"user-data.sh","path":"examples/route53-helpers/user-data/user-data.sh","sha":"fcd5070b46e6428d81dbb1e5ba5f4bef3b5b8c0c"}]},{"name":"vars.tf","path":"examples/route53-helpers/vars.tf","sha":"3c990b1e4ae4da54aac558cf24124e29cd362931"}]}],"toggled":true},{"name":"modules","children":[{"name":"attach-eni","children":[{"name":"README.md","path":"modules/attach-eni/README.md","sha":"5c32557105967e1b0e82f50fe693c12370e905cc"},{"name":"bin","children":[{"name":"attach-eni","path":"modules/attach-eni/bin/attach-eni","sha":"616849047aad575aca81659bbf45d99d72ec9e46"}]},{"name":"install.sh","path":"modules/attach-eni/install.sh","sha":"542cbd93ca08fd9cbe6f121f4d8fea2b1d76ca8b"}]},{"name":"disable-instance-metadata","children":[{"name":"README.md","path":"modules/disable-instance-metadata/README.md","sha":"eacec50007d78aec20335d85b05a9582200c9a66"},{"name":"bin","children":[{"name":"disable-instance-metadata","path":"modules/disable-instance-metadata/bin/disable-instance-metadata","sha":"a2b5e85b40225bc693a46978dd2ca2ea9406283f"}]},{"name":"install.sh","path":"modules/disable-instance-metadata/install.sh","sha":"bdce5d29adc4041375ffc9bf7eabb93a0059583c"}]},{"name":"ec2-backup","children":[{"name":"README.md","path":"modules/ec2-backup/README.md","sha":"e5f22ab6bdd9f34dacf71428bc04fef1d5415bad"},{"name":"main.tf","path":"modules/ec2-backup/main.tf","sha":"8153eee6c6285bdd67884372cc069a0cb4bf5fa8"},{"name":"outputs.tf","path":"modules/ec2-backup/outputs.tf","sha":"19b7bd464c3c0916e03bff6f4f2cf69b671041ed"},{"name":"variables.tf","path":"modules/ec2-backup/variables.tf","sha":"d590cf90b9a1060c9f003f64d82085d183c5e8f2"}]},{"name":"persistent-ebs-volume","children":[{"name":"README.md","path":"modules/persistent-ebs-volume/README.md","sha":"6d8c0c80d3e674fcda15a9a95a7fc2f4de7d6e9d"},{"name":"bin","children":[{"name":"mount-ebs-volume","path":"modules/persistent-ebs-volume/bin/mount-ebs-volume","sha":"1650101d5c5e7aeaa885bab9b80816b6417720e4"},{"name":"unmount-ebs-volume","path":"modules/persistent-ebs-volume/bin/unmount-ebs-volume","sha":"576ef38dc65ac1ef80c83b837efd6313bfd02741"}]},{"name":"install.sh","path":"modules/persistent-ebs-volume/install.sh","sha":"e0ce5862cd6975992dc011a6dfe94fdf14a9b607"}]},{"name":"require-instance-metadata-service-version","children":[{"name":"README.md","path":"modules/require-instance-metadata-service-version/README.md","sha":"e44bae312f899c7b88cb7745e0732cf71f28fe8e"},{"name":"bin","children":[{"name":"require-instance-metadata-service-version","path":"modules/require-instance-metadata-service-version/bin/require-instance-metadata-service-version","sha":"49481dbbd668025409ae6e9fa7903276f3145948"}]},{"name":"install.sh","path":"modules/require-instance-metadata-service-version/install.sh","sha":"b819422b6cd7475666945112202fb71adbc6bb60"}]},{"name":"route53-helpers","children":[{"name":"README.md","path":"modules/route53-helpers/README.md","sha":"52d0b942bba658065589567e9c9d78c98b4ca028"},{"name":"bin","children":[{"name":"add-dns-a-record","path":"modules/route53-helpers/bin/add-dns-a-record","sha":"2fd418252934a48c78f910a739b5d681d1585941"}]},{"name":"install.sh","path":"modules/route53-helpers/install.sh","sha":"535ed35d64611c5f12e9924b3cb8df0a77180ab8"}]},{"name":"single-server","children":[{"name":"README.md","path":"modules/single-server/README.md","sha":"afcb5405b0aca7dd4438538ddfa3d87b79846b4a"},{"name":"main.tf","path":"modules/single-server/main.tf","sha":"7cc8374d9ee72061cc84792d43e82b2618c505a1"},{"name":"outputs.tf","path":"modules/single-server/outputs.tf","sha":"e042d24d9e54a2df2011c736bfa462d9f1175be5"},{"name":"vars.tf","path":"modules/single-server/vars.tf","sha":"3d7a6911c59b3554cfa3699e7e58ae98c18dec2e"}]}]},{"name":"terraform-cloud-enterprise-private-module-registry-placeholder.tf","path":"terraform-cloud-enterprise-private-module-registry-placeholder.tf","sha":"ae586c0fe830819580e1009d41a9074f16e65bed"},{"name":"test","children":[{"name":"README.md","path":"test/README.md","sha":"ef26d3851db2fff0b36dfa61379724c0db9ff281"},{"name":"attach_eni_test.go","path":"test/attach_eni_test.go","sha":"6cff173ca858ec9765e837cdd7943489f1a0cc75"},{"name":"bastion_host_test.go","path":"test/bastion_host_test.go","sha":"f8d38f1cd893ef2097cababd4053bfd71710fa4d"},{"name":"ec2_backup_test.go","path":"test/ec2_backup_test.go","sha":"73ae620eb4bbec7771bfc614e676935956134e9d"},{"name":"go.mod","path":"test/go.mod","sha":"b275b1103e930e7ea7c08e9a3ca4a108f8efdee6"},{"name":"go.sum","path":"test/go.sum","sha":"76a7d496d54ca2743c46bd66a4ff7d260de9cb32"},{"name":"persistent_ebs_volume_test.go","path":"test/persistent_ebs_volume_test.go","sha":"ac3adb89e68c61e73b9cb31d179fefb13a404788"},{"name":"route53_helpers_test.go","path":"test/route53_helpers_test.go","sha":"6ab270f015bf4b8261278c8789a346159ee7eb9b"},{"name":"test_helpers.go","path":"test/test_helpers.go","sha":"16156727df11949125c635b75612ae6fa527df3d"},{"name":"validation","children":[{"name":"validate_all_modules_and_examples_test.go","path":"test/validation/validate_all_modules_and_examples_test.go","sha":"33d73c385b64c4fc870033e99427e683c31dc45a"}]}]}]},"detailsContent":"<h1 class=\"preview__body--title\" id=\"persistent-ebs-volume-example\">Persistent EBS Volume Example</h1><div class=\"preview__body--border\"></div><p>This folder contains an example of how to create an EC2 instance with two EBS volumes that are persisted between\nredeploys. This is useful for servers that persist data to the local hard disk and need that data to still be available\nafter a redeploy, such as a database like MySQL, CI server like Jenkins, or CMS like WordPress. Although it should be\npossible to do this with native Terraform using the <code>aws_ebs_volume</code> and <code>aws_volume_attachment</code> resources, <a href=\"https://github.com/hashicorp/terraform/issues/2957#issuecomment-150613677\" class=\"preview__body--description--blue\" target=\"_blank\">due to a\nbug</a>, the <code>aws_volume_attachment</code> resource\ndoes not work correctly and cannot be used. Therefore, this example includes a Packer template that installs the\n<a href=\"/repos/v0.9.0/module-server/modules/persistent-ebs-volume\" class=\"preview__body--description--blue\">persistent-ebs-volume scripts</a> and uses those scripts to attach and mount the volume\nwhen the instance is booting.</p>\n<p>Note, an EBS volume can only be associated with a single EC2 Instance, so if you need the data on the disk to be shared\namongst multiple servers, check out the <a href=\"https://aws.amazon.com/efs/\" class=\"preview__body--description--blue\" target=\"_blank\">Amazon Elastic File System</a>, which provides a\nservice built on top of NFS.</p>\n<h2 class=\"preview__body--subtitle\" id=\"how-do-you-run-this-example\">How do you run this example?</h2>\n<p>To run this example, you need to do the following:</p>\n<ol>\n<li>Build an AMI using Packer</li>\n<li>Deploy the AMI using Terraform</li>\n</ol>\n<p>These steps are described in detail next.</p>\n<h3 class=\"preview__body--subtitle\" id=\"build-an-ami-using-packer\">Build an AMI using Packer</h3>\n<p>The code that runs the EC2 instance in this example is an <a href=\"http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html\" class=\"preview__body--description--blue\" target=\"_blank\">Amazon Machine Image\n(AMI)</a> that has been defined in a <a href=\"https://www.packer.io/\" class=\"preview__body--description--blue\" target=\"_blank\">Packer\ntemplate</a> under <code>packer/build.json</code>. To build an AMI from this template:</p>\n<ol>\n<li>Install <a href=\"https://www.packer.io/\" class=\"preview__body--description--blue\" target=\"_blank\">Packer</a>.</li>\n<li>Set up your <a href=\"https://www.packer.io/docs/builders/amazon.html\" class=\"preview__body--description--blue\" target=\"_blank\">AWS credentials as environment variables</a>.</li>\n<li>Run <code>packer build build.json</code> to create the AMI in your AWS account. Note down the ID of this new AMI.</li>\n</ol>\n<p><strong>NOTE</strong>: If you wish to try the example on <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html#ec2-nitro-instances\" class=\"preview__body--description--blue\" target=\"_blank\">a Nitro based EC2\ninstance</a>, use\n<code>nvme_build.json</code> instead of <code>build.json</code>.</p>\n<h3 class=\"preview__body--subtitle\" id=\"deploy-the-ami-using-terraform\">Deploy the AMI using Terraform</h3>\n<p>Now that you have an AMI, use Terraform to deploy it:</p>\n<ol>\n<li>Install <a href=\"https://www.terraform.io/\" class=\"preview__body--description--blue\" target=\"_blank\">Terraform</a>.</li>\n<li>Open up <code>vars.tf</code> and set secrets at the top of the file as environment variables and fill in any other variables in\nthe file that don't have defaults. This includes the <code>ami</code> variable which you should fill in with the ID of the\nAMI you just built with Packer.</li>\n<li><code>terraform get</code>.</li>\n<li><code>terraform plan</code>.</li>\n<li>If the plan looks good, run <code>terraform apply</code>.</li>\n</ol>\n<p>When the templates are applied, Terraform will output the IP address of the EC2 instance. If you SSH to the instance,\nit should have a <code>/data</code> folder. This is the EBS volume. It should persist whatever data you put into it in between\nrestarts of this EC2 instance.</p>\n","repoName":"module-server","repoRef":"v0.13.8","serviceDescriptor":{"serviceName":"Single EC2 Instance","serviceRepoName":"module-server","serviceRepoOrg":"gruntwork-io","cloudProviders":["aws"],"description":"Run a single EC2 instance for stateless or stateful apps. Supports IAM roles, EBS volumes, ENIs, and EIPs.","imageUrl":"single-service.png","licenseType":"subscriber","technologies":["Terraform","Bash"],"compliance":[],"tags":[""]},"serviceCategoryName":"Server orchestration","fileName":"README.md","filePath":"/examples/persistent-ebs-volume","title":"Repo Browser: Single EC2 Instance","description":"Browse the repos in the Gruntwork Infrastructure as Code Library."}