{"id":18518488,"url":"https://github.com/sugaliudaykiran/terraform","last_synced_at":"2026-05-02T02:34:42.978Z","repository":{"id":160842630,"uuid":"605087346","full_name":"sugaliudaykiran/Terraform","owner":"sugaliudaykiran","description":"Terraform lab work","archived":false,"fork":false,"pushed_at":"2023-10-25T12:34:16.000Z","size":53,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-14T15:54:16.700Z","etag":null,"topics":["aws","aws-cloudformation","infrastructure-as-code","terraform","terraform-modules"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sugaliudaykiran.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-02-22T12:19:56.000Z","updated_at":"2023-10-25T12:41:29.000Z","dependencies_parsed_at":"2025-05-15T03:31:44.108Z","dependency_job_id":null,"html_url":"https://github.com/sugaliudaykiran/Terraform","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sugaliudaykiran/Terraform","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sugaliudaykiran%2FTerraform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sugaliudaykiran%2FTerraform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sugaliudaykiran%2FTerraform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sugaliudaykiran%2FTerraform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sugaliudaykiran","download_url":"https://codeload.github.com/sugaliudaykiran/Terraform/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sugaliudaykiran%2FTerraform/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32521108,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","aws-cloudformation","infrastructure-as-code","terraform","terraform-modules"],"created_at":"2024-11-06T17:13:32.331Z","updated_at":"2026-05-02T02:34:42.962Z","avatar_url":"https://github.com/sugaliudaykiran.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Infrastructure as Code - Tools Overview\n\nThere are a lot of tools that allow you to deploy infrastructure as code:\nTerraform\nCloudFormation\nHeat\nAnsible\nSaltStack\nChef, Puppet, Others\n\nNot all of these tools are targeted for the same purpose. It is important to understand the difference between Configuration Management and Infrastructure Orchestration:\nAnsible, Chef and Puppet are configuration management tools which means they are primarily designed to install and manage software on existing servers\nTerraform and CloudFormation are infrastructure orchestration tools which are designed to provision servers and infrastructure themselves.\n\nYou can use infrastructure orchestration tools and configuration management tools in tandem. For example you could use Terraform to create a new EC2 instance on AWS, Terraform can then call Ansible to install and configure software and applications on the EC2 instance. \n\nTerraform Simple Benefits Overview\nTerraform supports multiple platforms - AWS, GCP, Azure etc. \nTerraform has a simple language and a fast learning curve. The syntax is easy to read and understand.\nTerraform is easy to integrate with configuration management tools like Ansible.\nIt is easily extensible with plugins. \nIt is free.\n\n\n\n\nCreating a Simple EC2 Instance with Terraform\nWhen launching a resource in AWS there are 3 things to consider:\nHow will you authenticate to AWS?\nWhich region will the resource be launched in?\nWhich resource do you want to launch?\n\nThere are various ways to authenticate to AWS but the easiest when first using Terraform is static credentials. These consist of a Key-Pair.\nTo use this you create a key pair for an AWS IAM User which Terraform will use to authenticate to AWS.\nIt is important to note that while static credentials in your main file is an easy method and useful as a tutorial or for hobby use it is not considered best practice. \n\nFirst create a .tf file in this case I will use first_ec2.tf\nNow you can edit the file to add Terraform code.\n\nExample code for a simple EC2 instance:\nprovider \"aws\" {\n    region = \"us-west-2\" \n    access_key = \"ExAmpLEKey21fjwljsfjkrf\"\n    secret_key = \"EXamPLeSECreTKEySDAHhhhsJJn23Ksda\"\n}\nresource \"aws_instance\" \"my_ec2\" {\n    ami = \"ami-05b622b5fa0269787\"\n    instance_type = \"t2.micro\"\n}\n\nAs you can see in this example code we have included answers to the \"3 important things to consider\".  Lets run through the code step-by-step:\n\nStatement 1 (defines what platform, where to launch and method of authentication):\nprovider  - In this case it is \"aws\", the provider has to be specified and it is simply the platform that terraform will be provisioning infrastructure on. \nregion - This is the AWS Region we want to launch our EC2 instance into. In this case we have chosen \"us-west-2\"\naccess_key -  This is the 'access key' from the key pair we created for our AWS IAM User, it is part of what allows Terraform to authenticate to AWS.\nsecret_key - This is the 'secret key' from the key pair we created for our AWS IAM User.\n\nStatement 2 (defines resource specfications):\nresource - In this case we have specified \"aws_instance\" the terraform name for an AWS EC2 Instance and secondly \"my_ec2\" which is what we want to name the instance. Two resource blocks cannot have the same name \"my_ec2\" will be a unique name in the file. \nami - Specifies the Amazon Machine Image, ie. what image (OS specifics) we want to load on the EC2 Instance.\ninstance_type - In this case we have specified \"t2.micro\" this specifies what type of Amazon EC2 we want our instance to be. (How much memory, storage etc.)\n\nEvery AWS resource has mandatory elements and optional elements to include in a Terraform resource statement. For EC2 you need to specify the resource, ami and instance type. \nHowever there are far more elements that optionally can be specified. They are not included here as this is as simple as it gets for launching an EC2. \n\nYou can view the requirements for each AWS resource at the Terraform Registry Documentation.\n\nNow we have written our code and saved it with a .tf file type we can run the code with Terraform.  \n\nStep 1: Run \"terraform init\" on the command line in the folder containing your tf file - This will initialise Terraform and install any dependencies your file requires. \n\nStep 2: Run \"terraform plan\" this will generate a plan of the infrastructure your file will create.\n\nNotice how the ami and instance_type are specified but a whole load of other options are \"known after apply\". These could have been specified but were optional. Bear in mind these options run a lot longer than this screenshot shows.\n\nStep 3: If you are happy with the plan, run \"terraform apply\" to create the infrastructure. \n\nStep 4: Terraform will show you the plan again and ask if you want to perform these actions. Say 'yes'. \n\nProvided there are no errors during apply if you check your management console there will now be an EC2 Instance running.\n\n\n\n\nProviders\nTerraform supports multiple providers:\n\nDependant on what type of infrastructure you want to launch you will have to use the appropriate provider in Terraform.\n\nThis is important for the initialisation phase:\nWhenever you add a provider it is important to run \"terraform init\"  which will download plugins associated with the provider.\n\nThis is from the EC2 example above, notice how Terraform has realised we are using the AWS provider and has downloaded the associated AWS plugins. \n\nIn Terraform there are certain providers that are either \"Hashicorp Maintained\" (AWS, Azure etc.) or \"Non-Hashicorp Maintained\". Below is best practice syntax for establishing providers in your Terraform, notice how there are two blocks, one for what providers are required and another for each provider specific config. \nterraform {\n  required_providers {\n    aws = {\n      source = \"hashicorp/aws\"\n      version = \"3.33.0\"\n    }\n  }\n}\n\nprovider \"aws\" {\n  # Configuration options\n}\nIn Terraform v13+ this provider syntax is actually deemed best practice for all providers. You can get this code block ready made from the Terraform Registry website. \n\nIf you are comfortable in Terraform in one provider it is easy to switch between providers as the overall Terraform syntax is the same and only provider specific terms and resources change. \n\n\nResources\nResources are references to individual services a particular provider offers. \nEg. in AWS for an EC2 instance the Terraform resource is aws_instance\n\nResources in terraform can get very specific, below is a snapshot from the Terraform Registry of some AWS EC2 resource options:\n\nYou can see aws_instance which we used in the example in this list. However there are so many more resource options which can be used. \n\n\n\n\nUsing a Non-AWS Provider (Example)\nIn this example we will create a simple GitHub repository with terraform. Information on the provider code block and syntax is easily found at the GitHub TF Registry\n\nBelow is the Terraform code to create a GitHub RepositoryCode:\n\nYou can see in the above we are using  a separate code block to define the provider. As discussed this is actually now best practice and the simpler form used in the earlier EC2 AWS example is now slightly outdated. \nThe provider block can just be copied from the provider documentation on the TF Registry, click the \"Use Provider\" button to get the block with the above syntax. You can do this for any provider.\nNote in the above example GitHub authentication is a \"token\" rather than access keys. The authentication method does change between providers and you can check what is needed in the TF Documentation. In this case all you have to do is request a token from your GitHub account in your browser and use it in Terraform. \n\nTo run this code and create a new GitHub repo we simply have to use: \nterraform init - This downloads the necessary packages for the GitHub provider\nterraform plan - Create a plan for what infrastructure will be changed\nterraform apply - Create the infrastructure\n\n\n\n\nDestroying Resources With Terraform\n\nterraform destroy - allows us to destroy all resources created within the folder.\n\nBut what if I don't want to destroy all resources? Currently I have the EC2 Instance I created as well as a GitHub Repo. terraform destroy will just destroy both of these.\nLet's say I only want to destroy the EC2 Instance.\nIn this case we can make use of a \"target flag\" to direct Terraform on what resources to destroy:\nterraform destroy -target aws_instance.my_ec2\n\nThe target option can be used to focus Terraform's attention on only a subset of resources. You use a combination of Resource Type + Local Resource Name.\nThese are the Resource Types and LRNs we have created so far:\nResource Type\nLocal Resource Name\naws_instance\nmy_ec2\ngithub_repository\nexample\n\nBelow is the code where Resource Type and Local Resource Name are defined:\n\n\nResource Type - An unchangeable resource type used by Terraform that refers to a specific resource type for a provider\nLocal Resource Name - The name you give to the resource you create, it is a custom value and can be anything you want. Do note this name only applies locally within your terraform code as a reference, it is not the name given to the resource itself. \n\nSo when using terraform destroy -target aws_instance.my_ec2  - the \"aws_instance.my_ec2\" at the end tells Terraform exactly what resource to destroy. The syntax being \"resource_type.local_resource_name\".\n\nWhen we run the destroy command with the target flag. Terraform will let us know it is specifically going to destroy only the resources we flagged:\n\nNext we say \"yes\" please do destroy this.\n\nIt will run the destroy which may take a minute. Now if we check AWS there will no longer be an instance running. It's that easy!\n\nIt is important to note that while we have destroyed this resource we have not removed the code to create the aws_instance from our first_ec2.tf  config file. \nDue to this if we now ran terraform plan again then Terraform will plan to create the aws_instance we just destroyed again. \n\nIf you don't want this to be the case then you can remove the code creating the aws_instance from the config file entirely or you could just comment it out. \n\n\n\n\nTerraform State Files\nTerraform stores the state of the infrastructure that are created in the .tf files in the Terraform State File.\n\nThis state file allows Terraform to map real world resources to your configuration files.\n\nWhen we first ran terraform init on our initial first_ec2.tf file it created a state file within the folder.\n\nOnce we used terraform apply to bring up the EC2 instance Terraform then added the state of that EC2 Instance to the state file.\n\nThis is how Terraform knows what needs creating and what doesn't when subsequent changes are made in the folder. For example when we then created the GitHub repo, Terraform didn't also try to spin up another EC2 instance when we ran terraform apply. That is because while we had first_ec2.tf and github.tf in the same folder, all the resources in first_ec2.tf had already been created and were up and running, this information was stored in the state file. So Terraform checked this file and said \"Oh the EC2 instance is already here, no need to create that. But the GitHub repo is not in the state file so I must still need to create that\". \n\nOnce the GitHub repository was created its state was stored in the state file. So if we tried to run terraform plan it would say no changes need to be made as Terraform can see that all the requested infrastructure in the config files is up and running in the exact way the config files want it to be. \n\nHowever when we destroyed the EC2 Instance, Terraform removed it from the state file. This is why if we tried to run terraform plan after this destruction Terraform would plan to recreate the EC2 instance. The code to create the AWS instance is still in the first_ec2.tf config file but its state is no longer in the state file so Terraform assumes it still needs to create it. \nThe state file is maintained by Terraform itself is not something you should ever really be directly editing. \nTerraform Desired \u0026 Current State\nTerraform's primary function is to create, modify, and destroy infrastructure resources to match the desired state described in a Terraform configuration.\nThe current state is the actual state of a resource that is currently deployed. Terraform will try to make sure that the deployed infrastructure is always based on the desired state defined by the .tf config files. \n\nIf there is a difference between the desired state and current state then terraform plan will present a description of the changes necessary to bring the current state in line with the desired state.\n\nIf the current state and desired state match then terraform plan will output that no changes are needed. \n\nAn important thing to note is that if something is not specified in the .tf config file then it is not part of the desired state.  \n\nFor example in our AWS EC2 instance created at the start we have specified only the aspects that are mandatory to specify:\n\nWe have not for example specified what security group this should be part of. As such Terraform has put it in the default security group. However if I went on to the AWS  Browser Console and manually changed the security group then the current state of the EC2 Instance would differ from what was in the state file. The state file will still show that the security group is default while the actual, physical current state of the EC2 security group is that it's a custom one (not default). \n\nSo you might think that if we now run terraform plan that Terraform will pick this change up and plan to change the security group back to the default. However that is not the case, even though the EC2 instance would differ from the state file it doesn't actually differ from the desired state. We have not specified the security group in the config file so there is not a \"desired state\" for the security group. \n\nAs such all terraform will do when we run terraform Plan is request the current state of the EC2 see that it doesn't differ from the desired state and so not plan any changes, however it will see that it does differ from the state file so Terraform will update the state file to show the EC2 instance has a custom security group rather than the default. \n\nProvider Versioning\nDuring terraform init, if the version argument is not specified in the config file, the most recent provider plugin will be downloaded during initialisation.\n\nFor production use you should constrain the acceptable provider versions via configuration, this ensures that new versions with breaking changes will not be installed. \n\nYou specify this in the tf config file in the version field for the provider:\n\nThere is syntax for describing versions:\n\u003e=1.0 - Greater than or equal to the version\n\u003c=1.0 - Less than or equal to the version\n~\u003e2.0 - Any version in the 2.X range\n\u003e=2.10, \u003c=2.30 - Any version between 2.10 and 2.30\n\nDependency Lock File:\nThe Terraform dependency lock file (terraform.lock.hcl) allows us to lock to a specific version of the provider. \nIf a particular provider already has a version selection recorded in the lock file then Terraform will always re-select that version for installation, even if a newer version becomes available. \n\nYou can override that behaviour by adding -upgrade when you run terraform init\n\nAttributes \u0026 Output Values\nNote that the code written in this section is now in a separate folder to the EC2 and GitHub examples above.\nTerraform can output the attributes of a resource that has been created. \n\nOutputted attributes can be used as input for other resources being created by Terraform. \n\nFor instance lets say you are creating an Elastic IP Address with Terraform. \nOnce Terraform creates that EIP it can output the IP Address attribute and feed it into Terraform code that whitelists that IP within a security group. \n\nIn the below Terraform code we create both an EIP and an s3 bucket with outputs:\n(Bare in mind this code is preceded by an AWS provider block)\n\nHere you can see we create an EIP with aws_eip and call it lb\nThen we have the output block instructing Terraform to output the Public IP of the EIP we created. You can see that because we gave our EIP a terraform name of lb, we can reference that name with aws_eip.lb when asking for output of that particular resource. This comes in handy when you have multiple aws_eip resources in the script and want the output of one of them. \n\nNext we create an s3 bucket with aws_s3_bucket and call it mys3, now we also specify a globally unique name for the s3 bucket within AWS.\nImportant to note that mys3 is the name we have given to this s3 bucket within our Terraform code but the buckets actual name on AWS will be zeal-tf-tutorial-hk112j.\n\nFollowing on from the s3 resource block is the s3 output block. This is written to output the domain name of the bucket. \n\nIn the output blocks for the s3 bucket .bucket_domain_name is the attribute we want to output and .public_ip is the attribute to output for the EIP. If we don't specify a particular attribute at the end of the line then it would output all of the attributes for that resource. \n\nSo the structure of the output will be the key we gave for the output blocks \"eip\" and \"mys3bucket\" (these are custom and can be whatever you want). One on each line followed by the actual output. \n\nWhen we run terraform apply on this code we get the following output:\n\nSo the Terraform creates the EIP and s3 buckets as expected. Then it outputs the attributes we asked for. \n\nYou can find the attributes you can request to be outputted for a particular resource in the Terraform registry.\n\n\n\n\nReferencing Cross-Account Attributes\nOutputted attributes of resources can not only be used for user reference but also as inputs for other resources being created by Terraform.\n\nIn the below example we create an AWS Elastic IP and and an EC2 instance, we will use the outputted attributes of these two resources to allocate the EIP to the EC2. \n\nBear in mind that as always this was preceded by an AWS provider block. In the above code the first block we create is a very basic EC2 and we give it the name \"my_ec2\". Second block we create the EIP and give it the name \"lb\".\n\nThe third block is what we want to look at here. We are creating a \"resource\" which is an aws_eip_assocation. This can be found in the AWS Documentation on the Terraform registry.\nIt will essentially allow us to automatically allocate the EIP to the EC2 we have created. \n\nYou can see the standard syntax of resource \"resource_type\" \"resource_name\" in the first line of the block.  \n\nIn the next line instance_id we are clarifying the resource we want the EIP to attach to aws_instance.my_ec2 this refers to the instance we have created, however it requires the instance id, this is an attribute outputted once the EC2 is created. We can call this by adding the .id attribute reference at the end. So aws_instance.my_ec2.id\nThe final line allocation_id we are specifying exactly which EIP we want to allocate by its id. So aws_eip.lb.id, lb being the name we gave to the resource.\n\nRun terraform apply on this and we now have an EIP and an EC2 running in AWS and the EIP is allocated to the EC2.\n\n\n\n\nTerraform Variables Basics\nJust like in any language repeated static values can create work in future if they need changing. \nLets say in you have a few Terraform projects that reference that same IP address 116.30.45.50 say this IP comes up 8 times across your Terraform projects.\n\nThat is fine when you are first writing it, but what happens a few months down the line and this IP needs changing? You have to go back in and change it 8 separate times which is effort spent and opens the door for human error. \n\nInstead of repeating the IP value over and over again. When you first need it you can put the value in a central source from which you can import values in Terraform projects. Now if you needed to update the value you can just update it in the central source and it will update in all your projects. \n\n\nHere we are creating a simple AWS security group. The syntax to construct it isn't overly relevant, what is important is repetition of the IP address in the CIDR block. In production this might be repeated 10 or 20 times in various projects. \n\nSo what we do is create a separate .tf file to use as a central source for variables. In this case I have created a file called variables.tf and created a variable for this IP value. \n\n\nNo we can reference that variable directly in our cidr_blocks value with the syntax var.variable_name.\n\nTerraform is smart enough to look for the vpn_ip variable and insert it where we have referenced it. \n\nNow if we run terraform plan you can see that Terraform replaces the variable with the value we want (look at + cidr_blocks) :\n\n\nNow if you wanted to change the IP Address, instead of changing in multiple places in multiple projects you can just change it in the one variable file. \n\n\n\n\nVarious Ways to Assign Variables\nThere are multiple ways to assign values to variables:\n\nEnvironment Variables\nCommand Line Flags\nFrom a File\nVariable Defaults\n\nVariable Defaults:\nVariable Defaults are exactly as we have used in the IP example above, they are the default value you give to a variable when you create it.\nAnother example of this is the code below creating a very simple EC2 (as always this is preceded by a provider block):\n\nAs you can see we have used a variable that we have called instancetype (though it could be called anything) and assigned it to the instance_type.\nWe have defined this variable as \"t2.micro\" in a separate file (t2.micro is a type of EC2 in AWS):\n\nWe have given the value \"t2.micro\" as the default value for the \"instancetype\" variable. Unless explicitly told otherwise, when Terraform sees a reference to this variable it will supplement the default value we have specified above: \"t2.micro\"\n\nA terraform plan on this code shows that Terraform does indeed swap var.instancetype for t2.micro:\n\n\nThis value t2.micro is the default value for the variable, we can override this in a couple of ways:\n\nOverriding defaults directly in the Terraform commands:\nThis method uses command line flags:\nterraform plan -var=\"instancetype=t2.small\"\n\nif we use this command for the code above here is the output:\n\nYou can see we have changed the value for the instancetype variable and now the EC2 will be created as type t2.small\n\nIf we ran terraform plan by itself again it would plan to create a t2.micro as that is still the default value of the variable.\n\nThis method is not best practice for overriding defaults\n\n\nOverriding defaults with a terraform.tfvars file:\nYou can create a separate file called terraform.tfvars - it is always called exactly that. \n\nWithin this file you can specify different values from the default values you have already put in your variables file:\n\nNote that the syntax is different to defining the variable. This is closer to reassigning it, it still needs to have been initially defined though.\n\nNow if we run terraform plan:\n\nTerraform realises there is a terraform.tfvars file and looks there first for the instancetype variable value. If it weren't there it would use the default value \"t2.micro\" we defined in another file. \n\nThe terraform.tfvars file is not a replacement for the file where you define variables and give them default values, it is additional and allows you to specify values other than the default. \n\nIt is possible to pass a .tfvars file to use on the command line when you run terraform apply.\n\nThis is often used in production environments to have one config file that can be applied to multiple environments (UAT, PreProd, Prod). You would have your one config file and in order to provision or make changes to Prod you would pass the prod.tfvars file.\n\nThe command line syntax for this is as follows:\nterraform apply -var-file=\"prod.tfvars\"\n\nFinally Environment Variables:\nYou can define a variable within your environment with the linux syntax:\nexport TF_VAR_variablename=\"value\"\n\neg. export TF_VAR_instancetype=\"t2.nano\"\nEnvironment variables is the first place Terraform will look for values. So if instancetype has a value set in the environment that is the value Terraform will use even if it is also specified in the terraform.tfvars file. \n\nBest Practice for variables in production is to define defaults in a variables.tf file and then put any necessary overrides in a terraform.tfvars file.\n\n\n\n\nData Types for Variables\nVariable Types:\nstring: a sequence of Unicode characters representing some text, like \"hello\".\nnumber: a numeric value. The number type can represent both whole numbers like 15 and fractional values like 6.283185.\nbool: a boolean value, either true or false. bool values can be used in conditional logic.\nlist (or tuple): a sequence of values, like [\"us-west-1a\", \"us-west-1c\"]. Elements in a list or tuple are identified by consecutive whole numbers, starting with zero.\nmap (or object): a group of values identified by named labels, like {name = \"Mabel\", age = 52}.\nThe type argument in a variable block allows you to restrict the type of value that can be assigned to the variable. \n\nFor example:\n\nNow the above variable my_id will only ever accept values with a type of string.\n\nIt is considered best practice to always specify the type that a variable will expect. \n\n\nUsing Maps and Lists with Variables\nIn the below code we are creating an EC2 and have two different variables we can use to choose the instance_type.\nThere is the \"list\" variable, a list of 3 instance types. There is also the \"types\" variable, a map of 3 more instance types.\n\nCurrently you can see at the top instance_type is set to var.types but this corelates to the entire types map, 3 different instance types.\nSo when referring to a list or map how can we choose a specific value?\n\n\nWith Maps you can specifically choose a value by using its key with the following syntax:\n\nThis will make the instance_type equal to the us-east-1 value in the types map. If you look at the code with the map itself, us-east-1 is the key for the \"t2.micro\" value.\nAs such instance_type in this case will be equal to t2.micro. \n\n\nWith Lists you can choose the value by index, exactly like Python. \n\nIndex starts at 0 so in this case the instance_type will be set to the first item in our list \"m5.large\".  Please note that the syntax we have used here var.list is because we have named the list of 3 instance types \"list\". This could easily be var.mylist or var.list1.\n\n\n\n\nTerraform Count\n\nThe count parameter on resources can simplify configurations and let you scale resources by simply incrementing a number. \n\nIf you wanted to create two identical EC2 instances, one approach you could take is to use two separate resource blocks:\n\nThis approach can certainly be painful once you need lots of the same resource. What if we needed 5 EC2 instances? We don't want to copy and paste the block 5 times, especially if we will be changing anything in the instances, then we will have to make any changes to 5 different blocks. \n\n\nWhat we can do is make use of the count parameter:\n\nThis simple addition tells Terraform to create 5 of this resource. If we now run terraform plan it will indeed plan to create 5 EC2 Instances of Type \"t2.micro\"\n\nAn important thing to note is that we have still only specified one name \"instance-1\" but terraform will create 5 resources.  What it will do is append an indexing number to the given name for each resource it plans to create, so in this case the 5 EC2 instances in Terraform would be named: instance-1[0], instance-1[1], instance-1[2], instance-1[3] and instance-1[4].\n\nThe names above (instance-1[0] etc.) are just Terraform references so you can refer to a specific instance within Terraform. These names will not actually appear in the AWS console once they have been made. However it is important to note that some resources require actual names in AWS.  \n\nIf a resource requires a unique value / identifier and you want to use the count parameter to build multiple of them, then you can use count index to make them unique.\n\nBelow is an example using Terraform to create multiple IAM Users:\n\nThe ${count.index} syntax will append a number to the name of  each IAM User created by that block.\n\nHere we can see the first two outputs of a terraform plan on the above code:\n\n Notice how the name in each is unique: user.0 and user.1 the full output carries on for all 5 IAM Users created. \n\nYou can probably already see that there will be some occasions where it would be far more useful to have more specific names than user.1, user.2 etc. Well Count Index can help with that as well. We can do this using a list variable, which I have put in the same file for ease of screenshotting:\n\n Here you can see in the first block we have a list \"iam_users\" and it has 3 values in it. We use these values as the names for our IAM Users we create in the next block.  \n\nIn the second block we are creating the IAM Users, we give them the terraform reference name of \"user\" but we give each one an actual name with:\nname  =  var.iam_users[count.index]\n\nRemember that specific items in lists can be referred to in Terraform via their index. So in the list iam_users above we have [\"dev-user\", \"staging-user\", \"prod-user\"] so var.iam_users[0] is equal to the first item in the list \"dev-user\".\n\nWith this in mind think that for each resource created count.index iterates once starting at zero. So the first IAM User created will have a name equivalent to var.iam_users[0] which as we have just seen equates to \"dev-user\". The next resource count.index will be equal to 1 so this IAM User will have a name equivalent to var.iam_users[1] or \"staging-user\".\n\n\n\n\nConditional Expressions \nConditional expressions select one of two Boolean values (true or false).\n\nStructure of a conditional expression:\ncondition ? value_if_true : value_if_false\n\nAn example of when you might use this is having two resource blocks one for Dev and one for Prod, but you only need one depending on the environment you are building.\nYou can use a variable called \"istest\" and when the variable is set to true Terraform should build from the Dev resource block and when it is set to false Terraform should build from the Prod resource block.\n\nThis is shown in the code below:\n\nThis looks kind of complicated but it really isn't. \nFirst we have defined a variable called \"istest\", but currently it doesn't have a set value. \n\nThe first resource block is a standard block. Things are normal until the line beginning with count. Lets go over it:\ncount = var.istest == true ? 1 : 0\n\nRemember the count parameter tells Terraform how many of the resource to make.\nSo line this essentially says if the istest variable is equal to true then count equals 1, else count equals 0. If istest is true make one of this resource, else make none. \n\nThe line has the exact same meaning as this python syntax:\n\nBare in mind though that unlike Python, Terraform does not capitalise Boolean expressions it's true \u0026 false not True \u0026 False\n\nThe second resource block has the same conditional expression just switched around. If the istest variable is set to false then this isn't a test environment and the prod environment should be built so count will be set to 1 in that resource block. \n\nNow we will set the value of istest in a terraform.tfvars file to be true and run terraform plan:\n\nYou can see that as \"istest\" is set to true Terraform is planning to create the Dev AWS instance and won't create the Prod one at all. \n\nYou can use higher numbers just like you normally would with count:\ncount = var.istest == true ? 4 : 0\nIf is test = true then make 4 of this resource.\n\n\n\n\nLocal Values\nLocal values assign a name to an expression, allowing it to be used multiple times within a module without repeating it.\n\nIn the code below we have defined the common_tags local value and have then used that in the following two resource blocks to minimise repetition with the syntax tags = local.common_tags\nNote you define local values with the locals block but you call a local value with \"local.\" (no S).\n\n\nThis is cool but it doesn't seem all that different to a standard variable. One of the main benefits of locals is that you can use expressions within them. Standard variables only take static values.\n\nAn example of using expressions to define a local value:\n\nThe expression here means If var.name does not equal an empty string, then name_prefix is equal to var.name, else name_prefix is equal to var.default. \nNotice here how the expression is referring to two standard variables of var.name and var.default within the conditional expression. These two standard variables would be variables we have defined elsewhere. \n\nThis is really useful as without the ability to assign conditional expressions to a value we would have to hard code that expression into every block we need it in. With this we can just use the \"name_prefix\" local value instead.\nLocals can also use Terraform Functions  to define values (we haven't gone through them yet).\n\n\n\n\nTerraform Functions\nTerraform includes built-in functions that can be used to transform and combine values.\n\nThe general syntax for functions is a function name followed by comma separated arguments in parentheses:\nfunction(argument1, argument2) \n\nA specific example of this:\n\u003e max(5, 12, 9)\n\u003e Output: 12\nThe max function outputs the largest argument.\n\nTerraform doesn't support user defined functions. Only those that are built-in to the language.\nA complete list of supported functions can be found here: Terraform Registry - Functions\n\nYou can test out functions before using them in your Terraform files with the use of the terraform console command.\n\nType the command into your terminal and then you can test out functions to see how they operate and what their outputs will be. \nAbove we have tested both the max and the min functions.\n\n\n\n\nData Sources\nData sources allow data to be fetched or computed for use elsewhere in the Terraform configuration.\n\nThe best way to understand this is through example. \nEvery aws_instance resource block we have used so far has used a hardcoded ami.\n\nNotice how the AMI (Amazon machine image) is hardcoded specifically to one AMI code. \n\nThis is the AMI for Ubuntu in the us-west-2 region. AMI codes change between regions even for the same type of AMI. So if we wanted to use the Ubuntu AMI in the us-east-1 region it would be a completely different code. \n\nRemember that the region to deploy into is defined in the provider block:\n\n\nSo what will happen to our code if someone comes in and wants to change the region to us-east-1? Well they'd also need to change all of the references to region specific AMIs. They would also have to change any other region specific references for resources. \n\nData sources give us a work around for this:\n\nYou define the data source within the data block. So the data source is \"aws_ami\" and we have named the data source \"app_ami\".\n\nThe code within the data block is to specify which AMI type you want to call. For instance in this case we always want the Amazon Ubuntu AMI no matter the region (the AMI code will change between regions).\n\nmost_recent = true - we want the most recent version of the AMI we are requesting.\n\nowners  = [\"Amazon\"] - Specifies the 'owner' of the AMI. Basically the entity which created it, Amazon produce some of their own AMIs and we are using one of them.\n\nfilter { - Where you can filter the type down further eg. the exact instance type we want (Amazon Ubuntu). \n\nvalues = [\"amzn2-ami-hvm*] - specifying the exact instance type we want (you can find the AMI ID needed in the AWS docs)\n\nWe have then called this data source within the aws_instance resource block to get the correct AMI for whatever region we have specified in the provider block.\n\nNow no matter what region we select Terraform will always pull the correct AMI for that region from the \"aws_ami\" data source.\n\n\n\nTerraform Debugging\nThere are detailed logs in terraform which can be enabled by setting the TF_LOG environment variable to any value. \nYou can set TF_LOG to one of TRACE, DEBUG, INFO, WARN or ERROR to change the verbosity of the logs. \n\nFor instance in Linux you would set this variable on the Command Line to TRACE with:\nTF_LOG=TRACE\n\nThis will print logs to the terminal and the different levels of verbosity will allow you choose the detail you would like to see the logs in. \n\nYou can output the logs to a file rather than output to terminal with the following command:\nexport TF_LOG_PATH=/path/to-log-file\n\nTRACE is the most verbose, it is the default if TF_LOG is set to something other than a log level name. \n\n\nTo disable logging set the TF_LOG variable to empty:\nexport TF_LOG=\n\n\n\n\nTerraform Formatting\n\nYou can use the terraform fmt command to rewrite your Terraform configuration files with best practice formatting. \n\nHere is some example code for a simple EC2 instance in a file called my_ec2.tf  before using terraform fmt\n\n\nNow here is the same code after inputting terraform fmt my_ec2.tf  in the command line:\n\nYou can just use terraform fmt on it's own and not specify the file. This will format all config files in the directory. \n\n\n\n\nTerraform Validate\nterraform validate checks whether a configuration file is syntactically valid. It can check for issues including unsupported arguments, undeclared variables etc. \n\nHere is some code with an invalid argument of \"cheese\":\n\n\nIf we now run terraform validate on this file we get the following output:\n\n\nThis allows you to quickly identify if there are any errors in your Terraform code. \n\nNote that when you run terraform plan the command will also perform a validate to check that your code is valid before it makes the plan.\n\n\n\n\nLoad Order and File Structure\nTerraform generally loads all config files within a directory in alphabetical order.\nIt will only load files ending with either .tf or .tf.json (this specifies the format in use).\n\nWhen using Terraform in production generally you will have a few different config files for different aspects of the code. \n\nLets say we have the following code all in one config file called test.tf:\n\n\nIn this test.tf file we have:\nTwo provider blocks\nA variable block\nAn EC2 resource block\nAn IAM resource block\n\nWhat we should do is split these blocks into their own relevant files:\nWe will put the provider blocks into a file called providers.tf (all provider blocks would go here)\nWe will put the variable block into a file called variables.tf (all variables would go here)\nWe will put the EC2 resource block into a file called ec2.tf  (all EC2 resource blocks could go in here)\nWe will put the IAM resource block into a file called iam_user.tf (all IAM resource blocks could go in here) \nWe will remove the initial semantics.tf file\n\nThis leaves us with the below structure:\n\nSo if tomorrow there was a requirement to add another IAM User we could simply open up iam_user.tf and add the code in. \n\n This is good practice if you are not using modules. However if you are using modules within you terraform config (as you should in most production cases) there is a different practice. \n\n\n\n\nDynamic Blocks\nThese allow us to dynamically construct repeatable nested blocks, this is supported within resource, data, provider and provisioner blocks.\nWhat we mean by nested blocks is that these are not top level, for instance in the example below the ingress blocks are nested within an AWS Security Group resource block. \n\n\nLet's say we are defining a security group and we need to define multiple ingress rules:\n\nHere we only have two, but what if we needed 40 ingress rules to cover different ports? \nThat would require 40 different blocks with the current syntax/structure of the code. \n\nSo we can use a dynamic block instead:\n\nYou can see in the first block we have defined the variable \"ingress_ports\" this is a list of ports.\n\nThe second block is a resource block for an AWS Security Group. Within that we have a nested dynamic block of type \"ingress\".\n\nWithin the dynamic block we have \"for_each = var.ingress_ports\"\nThis syntax is basically saying \"for each item in the list ingress_ports\" which is the list variable we have defined at the top. \n\nSo for each item in ingress_ports construct a new ingress inbound rule for the security group  with the following values defined with in content {}\n\nfrom_port = ingress.value\nto_port = ingress.value\n\nThese are dynamic values, an ingress resource will be created for every item in the ingress_ports list. This ingress.value will represent each item in that list as it is iterated through. \nSo for the first iteration ingress.value will be 8200, next it will be 8201, then 8300 and so on for all the items in the list.\n\nIt might seem confusing that it is ingress.value and not ingress_ports.value. Doesn't the value refer to the name of the list variable we have created? So why isn't it referred to directly in the code? \n\nWell the for_each = var.ingress_ports syntax essentially assigns the value on each iteration through ingress_ports to the Dynamic ingress block itself, so the ingress in ingress.value refers to the actual dynamic block the code is in rather than the variable the value is pulled from.  \n\nYou can change this through the use of iterator. \n\nNotice how we define the iterator as port now instead of being ingress.value it is port.value. This can make dynamic blocks easier to read and understand. \n\nFinally the protocol and cidr _blocks will be the same for every ingress resource created by the dynamic block so these can stay as static values.\n\nSo now if we ran terraform plan this one dynamic block will lead to 5 separate ingress resources appearing in the plan. One for each port value in the ingress_ports list variable.\n\nWhat if you want multiple different dynamic values in one dynamic block? For instance say we wanted to alter the port value and cidr_blocks value. We couldn't use a simple list, but we could use a list of maps:\n\nThis way we can define the values like: \nfrom_port = ingress.value[\"port\"]\nto_port = ingress.value[\"port\"]\nprotocol = \"tcp\"\ncidr_blocks = ingress.value[\"cidr\"]\n\n\n\n\nTerraform Taint\nLets say we create a new EC2 resource in Terraform then users make a lot of manual changes both to the infrastructure and inside the server. \nNow the EC2 resource will not match the Terraform code. \n\nThere are two ways to handle this:\nImport the changes to Terraform\nDelete and Recreate the resource\n\nWith the terraform taint command we can manually mark a Terraform-managed resource as tainted, this will force it to be destroyed and recreated on the next apply.\n\neg. terraform taint aws_instance.my_ec2  note how this applies to a specific resource rather than a whole template. \n\nThe terraform taint command does modify the infrastructure, it merely alters its status in the state file to be 'tainted'. This marks it for destruction and recreation on the next terraform apply. \n\nWhy use taint?\nWhen a resource declaration is modified, Terraform usually attempts to update the existing resource in place (although some changes can require destruction and re-creation, usually due to upstream API limitations).\nIn some cases, you might want a resource to be destroyed and re-created even when Terraform doesn't think it's necessary.\n\n Note that Terraform Taint has been deprecated in v0.15.2 onwards, now the best practice is considered to be using terraform apply with the \"-replace\" tag.\neg. terraform apply -replace aws_instance.my_ec2\n\n  \n\n\nSplat Expressions\nSplat expressions (*) allow us to output attributes of multiple resources created by a single block. \n\nThe code below creates 3 IAM Users through the use of count and \"count.index\". \n\nRemember count specifies how many of a resource to make. \"count.index\" merely represents the number of iterations so far : [0, 1, 2] in this case, so the AWS name for the 3 IAM resources will be iamuser.1, iamuser.2 \u0026 iamuser.3. \n\nWhile the name in AWS will be iamuser.1 etc., the name we have given the resources in Terraform is lb. So to refer to each IAM resource created by this block individually within the Terraform code we would use lb[0], lb[1] \u0026 lb[2]. \n\nThe second block of this code is the output block and this contains the splat expression. We have 3 IAM resources created by the first block all with the name \"lb\". In order to get the ARN (Amazon Resource Name) attribute for all three we could do:\nvalue = aws_iam_user.lb[0].arn\nvalue = aws_iam_user.lb[1].arn\nvalue = aws_iam_user.lb[2].arn\nEach line individually asks for the ARN of one of the IAM User resources we created. However this is quite repetitive, and if we needed 3 or 4 attributes the number of lines required would really add up.\n\nSo instead in the code we have used the splat expression * to essentially say give me ARN of all the IAM User resources with the name lb.\nvalue = aws_iam_user.lb[*].arn\n\nNo when we apply the code we will receive an output to terminal with the ARNs of all three IAM resources we created in the first block.\n\n\n\n\nTerraform Graph\nThe terraform graph command allows us to generate a visual representation of a configuration or execution plan. \n\nThe output of this command is in DOT format which can be converted into an image. \n\nOnce you have your code written you can use terraform graph \u003e filnename.dot\nYou can then use graphviz to convert that file into an image. \n\nUbuntu graphviz installation: sudo apt install graphviz\nYou can also download it directly from the graphviz website on windows.\n\nExample output of terraform graph in image format:\n\n\n\n\n\nSaving Terraform Plan to File\n\nYou can save the output of terraform plan to a file.\nThen you can specify this file when using terraform apply to be certain that only the changes shown in the plan file will be made and no others.\n\nSyntax:\nterraform plan -out=filepath\n\nThen to use the file with apply, be in the directory with your plan file:\nterraform apply filename\nWhere \"filename\" is replaced with whatever you named your plan file. \n\nThis feature is very useful and often used in production when working with Terraform in conjuction with CI/CD.\n\n\n\n\nTerraform Output\nThe terraform output command is used to extract the value of an output variable from the state file.\n\nLets use the splat expression example again:\n\nWhen you run terraform apply on this it will create the resources but due to the output block it will also output to terminal:\niam_arn = [\n\"arn:aws:iam::795574780076:user/system/iamuser.0\"\n\"arn:aws:iam::795574780076:user/system/iamuser.1\"\n\"arn:aws:iam::795574780076:user/system/iamuser.2\"\n]\n\nNow what if you wanted to see this output again a few days later? Well for that you can use terraform output:\nterraform output iam_arn\nThis will print to terminal the exact same output as above. \n\nAlternatives:\nAlternatively to see the output again you could run another terraform apply as even if there are no changes to make this command will still provide the outputs specified in the code. Or you could directly look at the state file. \n\n\n\n\nTerraform Settings\nThere is a special terraform configuration block type which is used to configure behaviours of Terraform itself. \n\nTerraform settings are gathered into terraform blocks:\n\nThe required_version setting specifies which versions of Terraform can be used with your configuration. This means if the Terraform version used to run this code is not compatible with this setting then Terraform will produce an error and exit without taking any action.\n\n\nAnother useful setting is required_providers, you will have seen this one in this guide multiple times already:\n\nThis specifies all of the providers required by the current module and maps each local provider name to a source address and version constraint. \n\n\n\n\nHandling Large Infrastructure\nWhen you have a large infrastructure you will inevitably face issues related to API limits for a provider. \n\nTo help solve this you can switch to smaller configurations which can each be applied independently. \nSo instead of having one file containing all your VMs, DBs and Security Groups, Networking infrastructure etc.\n\nYou can actually split this out into separate .tf files where each can be independently applied.\n\n\nExample of smaller config benefits:\nFor example when you use terraform plan  the command has to refresh the state of each physical resource in the config file from your provider's API to see if any changes need to be made.\n\nNow  say you have all of your infrastructure in one big .tf file. You make some changes to the EC2 instances and nothing else, well now when you use terraform plan Terraform still has to request the state of every resource in that file from the provider API, not just the EC2 instances. \n\nIf instead you had split out all the resources into appropriate files and the needed to make EC2 changes. Then you can go into ec2.tf (or whatever you named it) and make those changes, now when you can run terraform plan ec2.tf and Terraform will only have to request the state of the resources in ec2.tf, which is far less API calls. \n\nSpecify the target resource type to update:\nIf you do have a really large config file then you can use the target flag to specify the resources you want refreshed:\nSyntax: -target=resource\n\nFor example:\nterraform plan -target=ec2\nThis will allow you to operate on an isolated portion of a very large config file. With this flag the terraform plan command will only refresh the state of the EC2 instances.\n\n\nPrevent refresh altogether:\nYou can use the refresh flag to prevent terraform plan refreshing the state of resources when making a plan. This will do an \"update in place\"\n\nsyntax:\nterraform plan -refresh=false\nYou can use this when you want a plan but don't need to refresh the state of the resources. \n\nImportant for the exam to note is that when you do this the resources in the plan will be preceded by a yellow tilde mark ~ \nThis tilde mark lets you know that the resources have been updated in place and not refreshed. \n\n\n\nZipmap Function\nThe zipmap function constructs a map from a list of keys and a corresponding list of values. \n\nList of Keys\nList of Values\nZipmap\npineapple\nyellow\npineapple=yellow\norange\norange\norange=orange\nstrawberry\nred\nstrawberry=red\n\nAn example of this in action:\n\nHere you can see we are making use of the terraform console command which allows us to test Terraform functions in the terminal.\nWe have provided zipmap with two lists as in the table above. It has then \"zipped\" those two lists into a map of key-value pairs.\n\nExample of real usage:\n1. We have the below code which constructs 3 IAM Users and then outputs the ARN and Name for each of them:\n\n\n2. When we run terraform apply we get the following output:\n\nAs you can see this gives us two lists, one list of the IAM Users ARNs and a second list of IAM Users Names\n\n3. What we can do instead is combine these outputs into a Map with zipmap:\n\nNotice how we have replace the two original output blocks with just one. Where we have used zipmap on the original two values. \n\n4. The output of this is:\n\nNow we have both the ARNs and Names of each resource mapped to each other in one simple output. \n\n\n\n\nTerraform Provisioners\nUp until now all examples and information have centred around creation and destruction of infrastructure scenarios. \n\nLets say we create an EC2 instance with Terraform, it is just an instance and nothing else. What if we want to have software installed on it automatically? \n\nProvisioners are used to execute scripts on a local or remote machine as part of resource creation or destruction.\n\nFor example:\nWe can code in our config file that when we create a new EC2 instance, Terraform should also execute a script which installs Nginx web-server.\n\n\nTypes of Provisioners:\nThere are two main types of provisioners: local-exec and remote-exec.\n\n\nLocal Exec:\nThese allow us to invoke local executable after a resource is created, what this means is that a local-exec provisioner can run an executable (command) on the machine that is applying the Terraform (local):\n\nIn this code we are creating an EC2 instance and within the EC2 resource block we have a Provisioner block.  This is a local-exec provisioner that will run a command that places the Private IP of the instance created by the resource block in a file on the local machine called private_ips.txt\n\nThis is what a local-exec does, it allows Terraform to run commands on the machine that is applying the Terraform. \n\nLocal provisioners are really powerful and one of their most important uses is to run ansible playbooks once resources have been created. \n\n\nRemote Exec:\nRemote-exec provisioners function very similarly and allow us to invoke scripts directly on a remote server. \n\n\n\n\nImplementing Remote-Exec Provisioners\nBelow is an example of the code you would use to have a remote-exec install Nginx on an EC2:\nThis code has 3 blocks: A resource block, a nested provisioner block and a nested connection block. Nested meaning that they are within the resource block. \n\n\nResource Block - This is pretty standard from what we've seen so far. It does however have the addition of key_name. This is because remote exec requires access to the EC2 being created, it can either do this with a user, password or via an access key pair over SSH. We are using the latter and so the resource block needs to know the key name we are using. This is just the name of the key pair within AWS:\n\n\nProvisioner Block (nested) - We have specified this as a \"remote-exec\" provisioner block, this is the block that will run our remote commands. Then we are using \"inline []\"  this allows us to specify a list of commands to be run by the provisioner on the EC2 we are creating. These commands should be listed in order and you can see we have 2 commands: 1 to install Nginx and 2 to start Nginx. \n\nConnection Block (nested) - This is the block that specifies how the provisioner block should connect to the EC2 instance. Within this we have 4 elements defined:\ntype = \"ssh\" - We want to use SSH to connect\nuser = \"ec2-user\" - Every Amazon Machine Image has a default user. With the Linux AMI we are using the default is \"ec2-user\", it defines what user the remote-exec should run commands from. \nprivate_key = file(\"./TF-keys.pem\") - We use this to tell the connection block what the access key is to the EC2. When you create a key pair within AWS it automatically gives you .pem file with your keys. We have put a copy of this file in the same directory with our TF config files. Now we can use the file command with the filepath of \"./TF-keys.pem\". This access key is used for all EC2s on the AWS account. \nhost = self.public_ip - This tells the provisioner the address of the instance we want to connect to. Connection blocks can use the method self to refer to the parent resource block. So this basically says the host address is the public IP of the parent resource of the connection block. \n\nIf you run terraform apply with the above code it will create an AWS EC2 Instance and then proceed to install Nginx and start Nginx up. If it loops when \"connecting to remote host via SSH\" and never connects this is likely because you haven't configured SSH on port 22 to be open in your EC2s security group, you can do this manually in the AWS console if just trying to get this example to work.\n\n\nCreation Time \u0026 Destroy Time Provisioners\n\nCreation-time provisioners are only run during creations, not during updating or any other lifecycle. If a creation-time provisioner fails, the resource is marked as tainted. This means that the next time you run terraform apply the resource parent of the provisioner will be destroyed and recreated to try and fix the issue. \n\nDestroy-time provisioners are run before the resource is destroyed. \n\nThe code below shows an example:\n\nHere we are creating an EC2 resource with two remote-exec provisioner blocks. One is a standard creation-time and the other is destroy-time. Upon running terraform apply this EC2 resource will be created and then the provisioner block will install Nano on the EC2 instance. \n\nThe when within the destroy provisioner allows us to specify that it is a destroy-time provisioner. If this is not included it is assume the provisioner is creation-time. \n\nWhen we then run terraform destroy it will first remove Nano before destroying the instance. \n\nNote that below these two provisioner blocks is also a connection block that isn't shown. \n\n\nOn failure:\nYou can use on failure in creation time provisioners. It essentially gives the code instructions on what to do if the command fails:\n\non_failure can either be set to continue or fail. If it is set to continue then even if this code failed to install Nano the resource will not be marked as tainted and will carry on as normal. If it is set to fail  then  the normal behaviour of marking the resource as tainted will occur, fail is the default if not specified. \n\n\n\n\nThe DRY Principle \u0026 Modules\nIn software engineering don't repeat yourself (DRY) is a principle of software development aimed at reducing repetition of software patterns.\n\nTerraform helps us be DRY by using modules. Modules allow us to centralise the structure of resources so we can call them into our config files easily. For instance you will have seen in this guide that we have used the same EC2 resource block many times in different files. \n\nHere is an example EC2 resource block with tags and a security group specified:\n\n\nRather than including the above in every single config file we create we can instead put this code in a central location and call it in a module to be used in any tf file we want.\nLets say we put the above code in a directory called ec2 and that in a directory called modules (the names aren't relevant). Then we can call it in another file like so:\n\n\n\nModules are just a centralised source where code is defined for resource blocks.  These can then be used in multiple Terraform projects. In this way we don't have to write out the same resource blocks over and over again in each project. \nThis is different to count as count allows us to avoid repeating ourselves within a config file and modules allow us to avoid repeating ourselves between two or more different projects (different sets of infrastructure or environments). \n\nWhen you use modules in a project you will need to use terraform init to install those modules in the project. This is because the modules you create you will store in a central source which is a separate directory or remote repository to the project you'll be working on.\n\n\n\n\nVariables and Terraform Modules\nIt is common in infrastructure management to build multiple environments, eg. one for staging, production, testing etc. With these you will want a similar setup but will want to change some variables. \n\nFor example in your staging environment you may want a small EC2 instance, but in your production you may want a large EC2 instance. \n\nIf you have hardcoded details in your module you will not be able to change it within a project you use it in:\n\nLets say we have 'Project A' and in Project A we use the above module. This EC2 module is a t2.micro, within Project A there is nothing we can do to change that. We can not specify a different instance size as it is hard coded into the module.\n\nHowever we can use variables within modules which we can then alter from within Project A:\n\nHere you can see we have now changed the EC2 module to have instance_type set to a variable called type_of_instance.\n\nWe have also created a separate file within the same directory as the EC2 module called variables.tf. Within that we have given a default value to the type_of_instance variable in case the user does not specify:\n\n\nNow within Project A when the user calls this EC2 Module they can specify the variable value within the module block:\n\n\nNow if we ran terraform plan we would get a plan output for an EC2 instance of type t2.large but where all other details specified in the EC2 module are the same.  \n\n\n\n\nTerraform Registry \u0026 Modules\nThe Terraform Registry has a library of pre written modules that you can browse. So rather than writing  one from scratch you can look for a module on the registry that is a close fit and alter it to your liking. The majority of these are community modules written by the community. \n\nThe registry also has verified modules which are maintained by third party providers like AWS, Azure etc. \n\n\n\n\nTerraform Workspace\nTerraform allows us to have multiple workspaces, within each workspace we can have a different set of variable values. You can have the same infrastructure in two different workspaces and each workspace would be isolated with its own variable values. \n\nFor instance you could have the variable type_of_instance.\n\nThen if you have two environments put in two workspaces:\nWorkspace A - Staging Environment: type_of_instance = t2.micro\nWorkspace B - Production Environment: type_of_instance = t2.large\n\nWorkspaces allow you to have the same variable set to different values dependent on the workspace you are in. In the above example the value of type_of_instance changes dependent on the workspace that it is in.  So if we were in Workspace A , then when we ran terraform plan it would plan to create an EC2 of type t2.micro. Whereas in Workspace B it would create one of type t2.large.\n\n\n\n\nTerraform Collaboration | Remote Repositories\nMost enterprise usage of terraform will involve collaboration through a central GIT repository like GitHub or GitLab.\n\nWhen sharing Terraform code it is never a good idea to push your .tfstate files to a remote repository as they often contain sensitive information. \n\nSource Argument:\nYou can use Modules collaboratively through remote repositories using the source argument of the module block:\n\nThe source argument can either be for a remote or local module. In the above code the source we are using is for a remote GitHub repository. Note that when using a git repository URL whether via SSH or HTTPS the full format is for the URL to be preceded by git::. However Terraform is smart enough to understand it is a git repo even if this is left out.\nRef Argument to pull specific branch:\nWhen using a git repository it is also possible to specify which branch you want to pull as a source through the ref argument:\n\n\nTerraform \u0026 .gitignore:\nYou may want to ignore the following files when adding your Terraform code to Git:\nTerraform directory itself as this is created when terraform init is run.\nterraform.tfvars which is likely to contain sensitive informaiton. However this is dependent on how you have set it up. \nterraform.tfstate secrets (passwords, keys) are stored in plain text in the tfstate file so you don't want to commit them.\ncrash.log\n\n\n\nTerraform Remote Backend\nTerraform has a feature called remote backend which can be used to store a tf state file if you need to do remote state management. This will allow you to refer to a remote TF State file within your terraform code. For example you can store your state file in an S3 Bucket.\n\nThis is useful as it means that everyone in a team can work with the same state file. This prevents unwanted issues like two people working with slightly different state files or trying to perform a Terraform Apply at the exact same time and breaking the infrastructure. \n\nWith one shared state file every time an apply is done the state file is locked so two apply processes can not be run at the same time. \nUsing a remote backend for state files is the recommended process for collaborative environments. Nearly all businesses using Terraform use remote backend for state files.  \n\n\nBelow is an example of the code you might use to establish an s3 remote backend for your Terraform state files. \n\n\n\n\n\nState Locking\n Whenever you are performing a write operation Terraform locks the state file. This is extremely useful when collaborating on Terraform in a team.\nThis is important as during an ongoing terraform apply if others also do an apply it can corrupt your state file. \n\nFor example Person A is terminating an RDS resource which has an associated rds.tfstate file\nPerson B is also at the same time trying to resize the RDS resource. \n\nThis will cause issues in the tfstate file which may lead to corruptions. For this exact reason during a terraform apply the state files are locked so two contradictory actions cannot be run at the same time. \n\nThis is also why it is important to have a shared remote backend for state files when working as a team. This way everyone works off the same state files rather than local copies, so if an apply is occurring the one state file the entire team uses is locked until the apply is done. \n\nWhen using a remote backend you will need to create a lock for the Terraform to use. This can be done in AWS with DynamoDB, if this lock is not created then there can still be issues with simultaneous applies and corruptions\n\n\n\n\nTerraform State Management\nThere may be some cases where we need to modify the Terraform state. It is important to never modify the state file directly, instead we can make use of the terraform state command. \n\nThe terraform state command has multiple sub commands:\nSub Command\nDescription\nlist\nList resources within the Terraform state file\nmv\nMoves item with terraform state (can use for renaming)\npull\nManually download and output the state from remote state\npush\nManually upload a local state file to remote state\nrm\nRemove items from the terraform state\nshow\nShow the attributes of a single resource in the state\n\n\n\n\nUsing Terraform Import\nThere may be a time when there is a resource which has already been created manually and not in Terraform. If we wanted to change something in that resource we would have to do it manually. \nIdeally we can bring that existing resource into infrastructure as code. \nTo do this we can use terraform import\n\nHowever this command will only create a state file for the manual resource it will not create the .tf file so we can edit the code in future. \n\nTo use terraform import correctly we have to write the Terraform code for the manual resource in a tf file ourselves and then link that code to the manual resource when we use terraform import.\n\n\n\n\nTerraform Cloud \nTerraform Cloud manages \"Terraform runs\" in a consistent and reliable environment with various features like access controls, private registry for sharing modules, policy controls and others. \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\nPrevious\nAWS Solutions Architect Associate - Everything You Need To Know\nNext\nBash Scripting Cheat Sheet\nMade with Squarespace\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsugaliudaykiran%2Fterraform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsugaliudaykiran%2Fterraform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsugaliudaykiran%2Fterraform/lists"}