lesson ── terraform-beginner ── ~12 мин ── 5 шагов
In the next 10 minutes you will run the core Terraform cycle from end to end: write the HCL for a single S3 bucket, initialize the working directory, read the plan, apply it, and confirm that state knows about the resource you created.
The cloud here is LocalStack (see localstack-provider). No real AWS, no bills. You are free to break things, tear them down, and start over.
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
The working directory ~/tf-hello already contains a provider.tf file,
the AWS provider settings pointed at LocalStack (endpoints { ... }). Your
job now is to create a main.tf next to it that describes a single S3
bucket.
Create the file ~/tf-hello/main.tf with the following contents:
resource "aws_s3_bucket" "demo" { bucket = "linuxlab-hello-${random_id.suffix.hex}" tags = {Owner = "student"
Project = "terraform-hello"
}
}
resource "random_id" "suffix" {byte_length = 4
}
Why random_id? S3 bucket names have to be globally unique. We keep that
discipline even in LocalStack, so the habit stays good.
For the shape of resource "..." "..." { ... } blocks, see tf-resource-block
and hcl-syntax.
You can use `cat > main.tf <<EOF ... EOF` or the `nano main.tf` editor.
✓ The HCL is written. Now Terraform needs to be initialized.
The init command reads provider.tf and downloads the AWS plugin from the
Terraform registry. After it runs you will have:
.terraform/ directory, with the plugin inside..terraform.lock.hcl file, with the plugin version pinned.Without init the other commands will not run.
Run:
cd /home/student/tf-hello
terraform init
For more on init and the lockfile, see tf-init and tf-lockfile.
Remember to `cd /home/student/tf-hello` before `terraform init`.
✓ The AWS provider is downloaded, the lockfile is created.
The lockfile is your guarantee of reproducibility. It records the exact
provider version and its SHA hashes. If you or a colleague run init
again, Terraform downloads exactly that version. If a hash does not match,
it fails with an error (protection against a swapped package).
plan compares HCL with state. State is empty right now, so plan will show
"+ 2 to add": one aws_s3_bucket and one random_id.
Run:
terraform plan
Look at the lines with + resource, those are the resources to be created.
At the end you get a summary like Plan: 2 to add, 0 to change, 0 to destroy.
Plan changes nothing in the cloud. You can run it as many times as you like. See tf-plan.
If plan complains "provider not initialized": go back and check that `init` ran.
✓ Plan showed the plan: 2 resources to create. Now let's apply it.
apply runs the plan against a real cloud. Here the "cloud" is LocalStack:
a container next door that pretends to be AWS.
Run it with -auto-approve (no interactive "yes", this is a practice task):
terraform apply -auto-approve
After apply you should see lines like:
random_id.suffix: Creation complete after Xs
aws_s3_bucket.demo: Creation complete after Xs
Apply complete! Resources: 2 added
If something fails, check that LocalStack is responding (the
wait-for-localstack.sh init script should have waited for it when the
session started). See tf-apply and localstack-provider.
If you get a TLS error: the provider is trying to reach real AWS. Check that `provider.tf` has the endpoints block.
✓ The bucket is created in LocalStack. State is updated. Open it and take a look.
OpenTofu keeps its CLI and state compatible with Terraform for the
commands in this step: migration usually goes through mv .terraform .terraform.bak; tofu init -upgrade. On your first switch, though, back up
state and run on a feature branch, since the differences cluster in the
newer features (variables in the backend, state encryption, OCI
registry-backed modules). See tf-opentofu-parity for the full matrix.
The main rule of Terraform: after apply a second plan should show "No changes". That is an invariant. If it shows changes, then state and HCL disagree, or there is drift.
Run it once more:
terraform plan -detailed-exitcode
The -detailed-exitcode flag matters: exit 0 = no changes, exit 2 = there
are some. This check passes only if the plan is clean.
If plan shows changes: look at `terraform show -json | jq` and compare it with your HCL.
✓ The plan is clean: state and HCL match. The Terraform invariant holds.
Drift is when the state in the cloud differs from what Terraform recorded in state. It happens: someone deleted the bucket by hand through the AWS console, or auto-scaling adjusted the capacity on its own. Terraform sees the drift on the next plan or refresh. The strategies for handling it live in lifecycle.ignore_changes.
You saw the declarative approach of Terraform: you described the desired state, and Terraform worked out what to create. You met the three required commands (init, plan, apply) and learned what each one does.
команды
terraform initdownload providers and create the lockfileterraform planshow the diff between HCL and stateterraform apply -auto-approveapply the planterraform showprint the contents of stateконцепции