diff --git a/README.md b/README.md index 87b0792cd980400585fee6df4eeb2880fc4d9bf7..6561ce3bdc46a3864453ffa820508a4785779406 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Lab with Terraform and any Cloud Lab template for a Cloud provisioning/orchestration exercise with Terraform -(TF) and AWS. Originally written by Marcel Graf (HEIG-VD). +(TF) and AWS. ## Pedagogical objectives ## @@ -25,6 +25,13 @@ conventions about the *command line prompt*: * `lcl`: your local machine * `ins`: your VM instance +TF CLI commands' output follow a diff-style convention of prefixing lines with +special marks to explain what is (or would be) going on: + * `+`: something is added + * `-`: something is removed/destroyed + * `-/+`: a resource is destroyed and recreated + * `~`: something is modified in place + ### Task #1: install Terraform CLI and AWS CLI ### @@ -85,14 +92,14 @@ resource "aws_instance" "app_server" { <a name="AMI-query"></a>To find `<your-AWS-AMI-ID>`, you can query the AMI Catalog via a. [AWS dashboard](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#AMICatalog:), searching "Ubuntu", selecting "Quickstart AMIs" and filtering by "Free-tier", b. or (preferred) with CLI, and get the 2 most recent AMIs: - ``` shell - lcl$ aws ec2 describe-images --region us-east-1 \ - --filters "Name=root-device-type,Values=ebs" \ - "Name=name,Values= ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server*" \ - --query "reverse(sort_by(Images, &ImageId))[:2].[ImageId]" --output text - ami-0fa37863afb290840 - ami-0e2512bd9da751ea8 - ``` + ``` shell + lcl$ aws ec2 describe-images --region us-east-1 \ + --filters "Name=root-device-type,Values=ebs" \ + "Name=name,Values= ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server*" \ + --query "reverse(sort_by(Images, &ImageId))[:2].[ImageId]" --output text + ami-0fa37863afb290840 + ami-0e2512bd9da751ea8 + ``` :bulb: In the following task(s), we use the first AMI found. @@ -116,7 +123,6 @@ that Terraform can guarantee to make the same selections by default when you run Terraform has been successfully initialized! ... ``` -Take note of what the file` .terraform.lock.hcl` is used for. Have a look inside the newly created sub-directory `~/terraform/AWS/.terraform/`, you'll find the required `aws` provider module @@ -312,5 +318,116 @@ lcl$ terraform show | grep instance_state instance_state = "running" ``` -Modify the resource's `ami` in `main.tf` (here we use the second one found -from the [catalog query done above](#AMI-query)) +Replace the resource's `ami` in `main.tf` with the second one found from the +[catalog query done above](#AMI-query) (or another one available with your +account). Before applying our new plan, let's see what TF thinks of it: + +``` shell +lcl$ terraform plan -out=change-AMI.tfplan +aws_instance.app_server: Refreshing state... [id=i-0155ba9d77ee0a854] + +Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: +-/+ destroy and then create replacement + +Terraform will perform the following actions: + + # aws_instance.app_server must be replaced +-/+ resource "aws_instance" "app_server" { + ~ ami = "ami-0fa37863afb290840" -> "ami-0e2512bd9da751ea8" # forces replacement + +... + +Plan: 1 to add, 0 to change, 1 to destroy. + +Saved the plan to: change-AMI.tfplan + +To perform exactly these actions, run the following command to apply: + terraform apply "change-AMI.tfplan" +``` + +:bulb: Remarks: + * The change we want to apply is destructive! + * We saved our plan. :question: Why? It is not really necessary in a simple + scenario like ours, however a more complex IaC workflow might require plan + artifacts to be programmatically validated and versioned. + +Apply the saved plan: +``` shell +lcl$ terraform apply change-AMI.tfplan +aws_instance.app_server: Destroying... [id=i-0155ba9d77ee0a854] +... +aws_instance.app_server: Destruction complete after 33s +aws_instance.app_server: Creating... +... +aws_instance.app_server: Creation complete after 48s [id=i-0470db35749548101] +``` + +:bulb: What? Not asking for confirmation? Indeed, a saved plan is intended for +automated workflows! Moreover, a saved plan will come handy for rolling back a +broken infrastructure to the last working setup. + +:question: What if we did not save our plan, and called a plain apply command? +Would the result be the same? + + +### Task #5: input variables ### + +**Goal:** make a TF plan more flexible via input variables. + +Our original plan has all its content hard-coded. Let's make it more flexible +with some input variables stored in a separate `variables.tf` file inside your +TF sandbox: + +``` hcl +variable "instance_name" { + description = "Value of the Name tag for the EC2 instance" + type = string + default = "AnotherAppServerInstance" +} +``` + +Then modify the `main.tf` as follows: + +``` hcl +resource "aws_instance" "app_server" { + ami = "ami-0e2512bd9da751ea8" + instance_type = "t2.micro" + + tags = { +- Name = "ExampleAppServerInstance" ++ Name = var.instance_name + } +} +``` + +Apply the change: +``` shell +lcl$ terraform apply -auto-approve +aws_instance.app_server: Refreshing state... [id=i-0470db35749548101] +... + + ~ update in-place + +Terraform will perform the following actions: + + # aws_instance.app_server will be updated in-place + ~ resource "aws_instance" "app_server" { + id = "i-0470db35749548101" + ~ tags = { + ~ "Name" = "ExampleAppServerInstance" -> "AnotherAppServerInstance" + } + ~ tags_all = { + ~ "Name" = "ExampleAppServerInstance" -> "AnotherAppServerInstance" + } +... + } + +Plan: 0 to add, 1 to change, 0 to destroy. +... +Apply complete! Resources: 0 added, 1 changed, 0 destroyed. +``` + +:bulb: Input variables can also be passed on the `apply` command line. **As an +exercise**, find how to do that with another different value for +`instance_name`. :question: Would that last change be persistent if we rerun a +plain `terraform apply`?