{"id":1428,"date":"2019-09-27T16:00:59","date_gmt":"2019-09-27T13:00:59","guid":{"rendered":"https:\/\/artem.services\/?p=1421"},"modified":"2020-12-06T23:51:57","modified_gmt":"2020-12-06T20:51:57","slug":"1428","status":"publish","type":"post","link":"https:\/\/artem.services\/?p=1428&lang=en","title":{"rendered":"\u00a0\u00a0Terraform\/Terragrunt &#8212; Create a module. Part 1"},"content":{"rendered":"<p><img loading=\"lazy\" class=\"aligncenter wp-image-99 size-full\" src=\"https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform.png\" alt=\"\" width=\"1210\" height=\"418\" srcset=\"https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform.png 1210w, https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform-300x104.png 300w, https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform-768x265.png 768w, https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform-1024x354.png 1024w, https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform-954x330.png 954w\" sizes=\"(max-width: 1210px) 100vw, 1210px\" \/><\/p>\n<p>In this example, we will create a <strong>Terraform<\/strong> module for the <strong>AWS<\/strong> provider, which will be launched for 3 different environments using <strong>Terragrunt<\/strong>.<\/p>\n<p>&nbsp;<\/p>\n<p>Requirements:<\/p>\n<ul>\n<li><a href=\"https:\/\/artem.services\/?p=152\" target=\"_blank\" rel=\"noopener noreferrer\">Installed AWS CLI<\/a><\/li>\n<li><a href=\"https:\/\/artem.services\/?p=97\" target=\"_blank\" rel=\"noopener noreferrer\">Installed Terraform<\/a> (version above 12.0)<\/li>\n<li>Installed Terragrunt<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p><!--more--><\/p>\n<p>Create the required files for <strong>Terragrunt<\/strong>:<\/p>\n<h3>terragrunt.hcl<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nterraform {\r\n  extra_arguments &quot;custom_vars&quot; {\r\n    commands = get_terraform_commands_that_need_vars()\r\n    arguments = [\r\n      &quot;-var-file=${get_terragrunt_dir()}\/common.tfvars&quot;,\r\n    ]\r\n  }\r\n}\r\n\r\nremote_state {\r\n  backend = &quot;s3&quot;\r\n  config = {\r\n    bucket         = &quot;tfstate.eu-west-2.artem&quot;\r\n    key            = &quot;${path_relative_to_include()}\/terraform.tfstate&quot;\r\n    region         = &quot;eu-west-2&quot;\r\n    encrypt        = true\r\n    dynamodb_table = &quot;my-lock-table&quot;\r\n  }\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Set the <strong>S3<\/strong> bucket name that will store <strong>Terragrunt<\/strong>\u2019s state, you don\u2019t need to create it first, <strong>Terragrunt<\/strong> will create it yourself. We also set the region.<\/p>\n<p>Create a file to store <strong>Terraform<\/strong> and <strong>Terragrunt<\/strong> variables:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ntouch common.tfvars variables.tf\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>We will write the first module that will create the <strong>VPC<\/strong> and <strong>Internet Gateway<\/strong> for it.<\/p>\n<p>We create a directory for modules and for <strong>VPC in<\/strong> particular:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nmkdir -p modules\/vpc\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Modules work on the principle of a function; when accessing them, we pass a number of variables.<\/p>\n<p>To create a <strong>VPC<\/strong>, we need to know the following:<\/p>\n<ul>\n<li>VPC name<\/li>\n<li>CIDR<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Create a separate file for the variables &quot;<strong>modules\/vpc\/variables.tf<\/strong>&quot; and add the following:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nvariable &quot;vpc_name&quot; {}\r\nvariable &quot;vpc_cidr&quot; {}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>We create the module itself, for this we create the file &quot;<strong>modules\/vpc\/main.tf<\/strong>&quot; with the following contents:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n#################### CREATE VPC ####################\r\n\r\nresource &quot;aws_vpc&quot; &quot;vpc&quot; {\r\n  cidr_block = var.vpc_cidr\r\n  enable_dns_hostnames = &quot;true&quot;\r\n  enable_dns_support = &quot;true&quot;\r\n\r\n  tags = {\r\n    Name = var.vpc_name\r\n  }\r\n}\r\n \r\n############# CREATE INTERNET GATEWAY ##############\r\n\r\nresource &quot;aws_internet_gateway&quot; &quot;igw&quot; {\r\n  vpc_id = aws_vpc.vpc.id\r\n \r\n  tags = {\r\n    Name = &quot;${var.vpc_name}-IGW&quot;\r\n  }\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>We will create a template for three different environments:<\/p>\n<ul>\n<li>development<\/li>\n<li>staging<\/li>\n<li>production<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Fill out the variable file for <strong>Terragrunt<\/strong>:<\/p>\n<h3>common.tfvars<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n### DEVELOPMENT ENVIRONMENT ####\r\n\r\ndevelopment_aws_region = &quot;us-east-1&quot;\r\ndevelopment_vpc_name = &quot;ARTEM-DEVELOPMENT-USE1&quot;\r\ndevelopment_vpc_cidr = &quot;192.168.0.0\/16&quot;\r\n\r\n######### END OF BLOCK #########\r\n\r\n##### STAGING ENVIRONMENT ######\r\n\r\nstaging_aws_region = &quot;us-east-1&quot;\r\nstaging_vpc_name = &quot;ARTEM-STAGING-USE1&quot;\r\nstaging_vpc_cidr = &quot;192.168.0.0\/16&quot;\r\n\r\n######### END OF BLOCK #########\r\n\r\n#### PRODUCTION ENVIRONMENT ####\r\n\r\nproduction_aws_region = &quot;eu-central-1&quot;\r\nproduction_vpc_name = &quot;ARTEM-PRODUCTION-EUC1&quot;\r\nproduction_vpc_cidr = &quot;192.168.0.0\/16&quot;\r\n\r\n######### END OF BLOCK #########\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Now fill in the <strong>Terraform<\/strong> variables file:<\/p>\n<h3>variables.tf<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n### DEVELOPMENT ENVIRONMENT ####\r\n\r\nvariable &quot;development_aws_region&quot; {}\r\nvariable &quot;development_vpc_name&quot; {}\r\nvariable &quot;development_vpc_cidr&quot; {}\r\n\r\n######### END OF BLOCK #########\r\n\r\n##### STAGING ENVIRONMENT ######\r\n\r\nvariable &quot;staging_aws_region&quot; {}\r\nvariable &quot;staging_vpc_name&quot; {}\r\nvariable &quot;staging_vpc_cidr&quot; {}\r\n\r\n######### END OF BLOCK #########\r\n\r\n#### PRODUCTION ENVIRONMENT ####\r\n\r\nvariable &quot;production_aws_region&quot; {}\r\nvariable &quot;production_vpc_name&quot; {}\r\nvariable &quot;production_vpc_cidr&quot; {}\r\n\r\n######### END OF BLOCK #########\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>We are ready to create the first environment, we will begin with the <strong>development<\/strong> of the environment<\/p>\n<p>Create the structure:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nmkdir -p environments\/development\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Let&#8217;s go to the created directory:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ncd environments\/development\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Create the required file &quot;<strong>terragrunt.hcl<\/strong>&quot; and paste the following into it:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ninclude {\r\n  path = find_in_parent_folders()\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>And create symbolic links to variable files located in the root:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nln -s ..\/..\/variables.tf variables.tf\r\nln -s ..\/..\/common.tfvars common.tfvars\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Create the file &quot;<strong>main.tf<\/strong>&quot; with the following contents:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nterraform {\r\n  backend &quot;s3&quot; {}\r\n}\r\n\r\nprovider &quot;aws&quot; {\r\n  region  = var.development_aws_region\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>That&#8217;s it, now you can create a <strong>Terraform<\/strong> file to call the module to create a <strong>VPC<\/strong>.<\/p>\n<h3>vpc.tf<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nmodule &quot;vpc&quot; {\r\n  source        = &quot;..\/..\/modules\/vpc&quot;\r\n  vpc_name      = var.development_vpc_name\r\n  vpc_cidr      = var.development_vpc_cidr\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Run:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nterragrunt init\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>At the first start, <strong>Terragrunt<\/strong> will say that the bucket for storing states does not exist, and will offer to create it, we agree.<\/p>\n<p>After <strong>init<\/strong>, we look at the version of <strong>Terraform<\/strong> and the <strong>AWS<\/strong> provider, it is better to specify these versions in the &quot;<strong>main.tf<\/strong>&quot; file, if you plan to start from another computer (since we create a basket to store the state, we mean launch from any computer keeping current state)<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nterraform --version\r\nTerraform v0.12.9\r\n+ provider.aws v2.30.0\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h3>main.tf<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nterraform {\r\n  backend &quot;s3&quot; {}\r\n  required_version = &quot;~&gt; 0.12.9&quot;\r\n}\r\n\r\nprovider &quot;aws&quot; {\r\n  region  = var.development_aws_region\r\n  version = &quot;~&gt; 2.30&quot;\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Now, following the example of a <strong>development<\/strong> environment, we will create <strong>staging<\/strong> and <strong>production<\/strong>. Go back to the root of the project, delete the &quot;<strong>.terraform<\/strong>&quot; directory in &quot;<strong>envarinments\/development<\/strong>&quot; and copy the <strong>development<\/strong> environment:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nrm -rf environments\/development\/.terraform\/\r\ncp -aR environments\/development environments\/staging\r\ncp -aR environments\/development environments\/production\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Change the variables for <strong>staging<\/strong>:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nsed -i 's\/development\/staging\/g' environments\/staging\/{main.tf,vpc.tf}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Change the variables for <strong>production<\/strong>:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nsed -i 's\/development\/production\/g' environments\/production\/{main.tf,vpc.tf}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Now, being at the root of the project, we can start planning for all environments with one command:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nterragrunt plan-all\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>You can apply this template for each environment in particular, being in the directory of the necessary environment, run:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nterragrunt apply\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Or for all at once, being at the root of the project, run:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nterragrunt apply-all\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Our final structure is as follows:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n.\r\n\u251c\u2500\u2500 common.tfvars\r\n\u251c\u2500\u2500 environments\r\n\u2502   \u251c\u2500\u2500 development\r\n\u2502   \u2502   \u251c\u2500\u2500 common.tfvars -&amp;gt; ..\/..\/common.tfvars\r\n\u2502   \u2502   \u251c\u2500\u2500 main.tf\r\n\u2502   \u2502   \u251c\u2500\u2500 terragrunt.hcl\r\n\u2502   \u2502   \u251c\u2500\u2500 variables.tf -&amp;gt; ..\/..\/variables.tf\r\n\u2502   \u2502   \u2514\u2500\u2500 vpc.tf\r\n\u2502   \u251c\u2500\u2500 production\r\n\u2502   \u2502   \u251c\u2500\u2500 common.tfvars -&amp;gt; ..\/..\/common.tfvars\r\n\u2502   \u2502   \u251c\u2500\u2500 main.tf\r\n\u2502   \u2502   \u251c\u2500\u2500 terragrunt.hcl\r\n\u2502   \u2502   \u251c\u2500\u2500 variables.tf -&amp;gt; ..\/..\/variables.tf\r\n\u2502   \u2502   \u2514\u2500\u2500 vpc.tf\r\n\u2502   \u2514\u2500\u2500 staging\r\n\u2502       \u251c\u2500\u2500 common.tfvars -&amp;gt; ..\/..\/common.tfvars\r\n\u2502       \u251c\u2500\u2500 main.tf\r\n\u2502       \u251c\u2500\u2500 terragrunt.hcl\r\n\u2502       \u251c\u2500\u2500 variables.tf -&amp;gt; ..\/..\/variables.tf\r\n\u2502       \u2514\u2500\u2500 vpc.tf\r\n\u251c\u2500\u2500 modules\r\n\u2502   \u2514\u2500\u2500 vpc\r\n\u2502       \u251c\u2500\u2500 main.tf\r\n\u2502       \u2514\u2500\u2500 variables.tf\r\n\u251c\u2500\u2500 terragrunt.hcl\r\n\u2514\u2500\u2500 variables.tf\r\n\r\n6 directories, 20 files\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>In order to store our template in the repository, add the following to the &quot;<strong>.gitignore<\/strong>&quot; file:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n.terraform\/\r\nterraform.*\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>This example can be downloaded from the <strong>Git<\/strong> repository:<\/p>\n<pre>https:\/\/bitbucket.org\/artem-gatchenko\/example-terraform-module-part1<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>In this example, we will create a Terraform module for the AWS provider, which will be launched for 3 different environments using Terragrunt. &nbsp; Requirements: Installed AWS CLI Installed Terraform (version above 12.0) Installed Terragrunt &nbsp;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[541],"tags":[543,1253,555,1255],"_links":{"self":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/1428"}],"collection":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1428"}],"version-history":[{"count":7,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/1428\/revisions"}],"predecessor-version":[{"id":2116,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/1428\/revisions\/2116"}],"wp:attachment":[{"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1428"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1428"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1428"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}