Последняя проверка: май 2026 г.
Разверните сервисы AWS для экзамена 004 с помощью чистого Terraform: пошаговое руководство с привязкой каждого блока к разделам экзамена. Код также совместим с OpenTofu.
К концу этой лабораторной работы вы полностью выполните основной рабочий процесс Terraform и затронете каждую область экзамена Associate — без облачной учетной записи и единого цента затрат. Мы используем провайдеры random и local, которые не требуют учетных данных, поэтому основное внимание уделяется тому, что фактически проверяет экзамен: как ведет себя сам Terraform.
Вы напишете свой первый ресурс, параметризуете его переменными и выходными данными, сгенерируете множество ресурсов с помощью for_each, рефакторизуете повторяющийся код в переиспользуемый модуль, проверите и перенесете состояние, и, наконец, направите конфигурацию на HCP Terraform для удаленного выполнения. Каждый фрагмент — это чистый Terraform — тот же код работает без изменений на OpenTofu. Поместите блоки в один файл main.tf (мы укажем те немногие, которые находятся в отдельных файлах), запустите terraform init один раз, затем terraform apply шаг за шагом.
>= 1.5 или OpenTofu >= 1.6 в вашем PATH (terraform version). Мы используем управляемые конфигурацией блоки import в Шаге 6, для которых требуется версия 1.5+.random и local полностью работают на вашей машине.mkdir tf-associate-lab && cd tf-associate-lab).Эта лабораторная работа полностью бесплатна. Провайдеры random и local не создают облачных ресурсов — только несколько небольших файлов на вашем диске и записи в локальном terraform.tfstate. Шаг 7 (HCP Terraform) использует бесплатный уровень, которого вполне достаточно для одного рабочего пространства лаборатории. Здесь нет ничего, что взимало бы плату в режиме ожидания.
Прежде чем что-либо запустить, мы объявляем, какую версию Terraform мы ожидаем и от каких провайдеров мы зависим. Закрепление версии — это излюбленная тема экзамена — домены «Основы Terraform» и «Основной рабочий процесс» проверяют ваше понимание required_version, блока required_providers и роли terraform init в загрузке плагинов провайдеров в .terraform/.
Мы намеренно выбираем random и local. Ни один из них не требует входа в облако, поэтому вся лабораторная работа остается бесплатной и воспроизводимой, а экзамен все равно никогда не спрашивает о конкретном облаке — он спрашивает о Terraform. Поместите этот код в новый файл main.tf и запустите terraform init; вы увидите, как Terraform разрешает и блокирует оба провайдера в файл .terraform.lock.hcl, что само по себе является важным моментом в домене «Основы» (зафиксируйте файл блокировки, чтобы каждый запуск использовал идентичные версии плагинов).
terraform {
required_version = ">= 1.5"
required_providers {
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
local = {
source = "hashicorp/local"
version = "~> 2.5"
}
}
}
# Both providers run locally and need no "provider" configuration
# block at all - a useful reminder that providers are just plugins.Теперь мы переходим к сути домена «Использование основного рабочего процесса Terraform» (18% экзамена). Мы объявляем ресурс random_pet, который придумывает дружелюбное имя, а затем ресурс local_file, который записывает это имя на диск. Ресурс local_file.greeting ссылается на random_pet.name.id, и именно эта ссылка сообщает Terraform, что файл зависит от питомца — неявный порядок зависимостей, depends_on не требуется.
Выполните рабочий процесс по порядку: terraform plan показывает вам разницу того, что изменится до того, как что-либо произойдет, а terraform apply реализует это и записывает результат в terraform.tfstate. Запустите terraform apply второй раз, ничего не меняя, и вы увидите «Изменений нет» — это идемпотентность, и экзамен очень любит спрашивать, почему второе применение не приводит к операциям. Имея рабочий ресурс и свежий файл состояния, мы можем начать делать конфигурацию гибкой.
resource "random_pet" "name" {
length = 2
separator = "-"
}
resource "local_file" "greeting" {
filename = "${path.module}/hello.txt"
content = "Hello from ${random_pet.name.id}!\n"
}Жестко закодированные значения подходят для демонстрации, но домен «Чтение, создание и изменение конфигурации» (19%) ожидает, что вы будете использовать параметры. Мы добавляем входную variable с type, default и блоком validation, который отклоняет все, что выходит за рамки разрешенных сред — валидация выполняется во время планирования и является частым вопросом на экзамене. Мы вычисляем карту locals один раз и повторно используем ее, а также выставляем результаты с помощью блоков output, чтобы другие конфигурации (и terraform output) могли их читать.
Обратите внимание на слои, которые проверяет экзамен: variable — это входные данные, locals — производное/промежуточное значение, а output — опубликованный результат. Примените еще раз и попробуйте terraform output pet_name, чтобы прочитать одно значение, или terraform output -json, чтобы использовать его в скриптах. С настроенными входами и выходами мы готовы перестать писать ресурсы по одному и генерировать целый набор.
variable "environment" {
description = "Deployment environment label."
type = string
default = "dev"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "environment must be one of: dev, staging, prod."
}
}
locals {
common_tags = {
environment = var.environment
managed_by = "terraform"
}
}
output "pet_name" {
description = "The generated pet name."
value = random_pet.name.id
}
output "tags" {
value = local.common_tags
}Реальные конфигурации редко объявляют по одному экземпляру каждого ресурса. Здесь мы углубляемся в домен «Чтение, создание и изменение конфигурации», управляя созданием ресурсов из коллекции. Переменная set(string) перечисляет логические сервисы; затем for_each создает по одному random_string и local_file для каждого сервиса, доступных как random_string.suffix["api"] и так далее.
Этот шаг также демонстрирует выражения и встроенные функции, которые экзамен ожидает от вас распознавать: each.key для текущего элемента, интерполяцию строк для создания имени корзины для каждого сервиса и jsonencode() для преобразования объекта HCL в файл JSON на диске. Мы повторно используем var.environment и local.common_tags из Шага 3, чтобы каждый сгенерированный файл содержал согласованные метаданные. Очевидный следующий вопрос — «это становится повторяющимся, как мне это упаковать?» — именно на это отвечают модули.
variable "services" {
description = "Logical services to generate a config file for."
type = set(string)
default = ["api", "web", "worker"]
}
resource "random_string" "suffix" {
for_each = var.services
length = 6
special = false
upper = false
}
resource "local_file" "service_config" {
for_each = var.services
filename = "${path.module}/config/${each.key}.json"
content = jsonencode({
service = each.key
environment = var.environment
bucket = "${each.key}-${random_string.suffix[each.key].result}"
tags = local.common_tags
})
}Домен «Взаимодействие с модулями Terraform» требует от вас создания модуля, его вызова и передачи значений. Мы перемещаем логику для каждого сервиса в дочерний модуль в ./modules/service, даем ему свои входные variable и output, затем вызываем его из корня с помощью for_each — один экземпляр модуля на каждый сервис.
Два файла ниже ясно показывают границу: дочерний модуль ничего не знает о том, какие сервисы существуют (это задача вызывающего модуля через var.name), а корень ничего не знает о том, как построен сервис (это инкапсулировано в модуле). Запустите terraform init снова после добавления модуля — экзамен проверяет, что новые источники модулей требуют повторной инициализации для установки. Теперь, когда наша конфигурация модульная, последняя важная тема для уровня Associate — это то, что Terraform тихо отслеживал все это время: состояние.
# modules/service/main.tf
variable "name" {
type = string
}
variable "environment" {
type = string
}
resource "random_string" "suffix" {
length = 6
special = false
upper = false
}
resource "local_file" "config" {
filename = "${path.root}/config/${var.name}.json"
content = jsonencode({
service = var.name
environment = var.environment
bucket = "${var.name}-${random_string.suffix.result}"
})
}
output "bucket_name" {
value = "${var.name}-${random_string.suffix.result}"
}
# main.tf (root) - call the module once per service
module "service" {
source = "./modules/service"
for_each = var.services
name = each.key
environment = var.environment
}
output "service_buckets" {
value = { for k, m in module.service : k => m.bucket_name }
}Здесь представлены домены «Реализация и поддержание состояния» (19%) и «Использование Terraform вне основного рабочего процесса» (9%). Состояние — это JSON-реестр, сопоставляющий адреса вашей конфигурации с реальными объектами; terraform state list перечисляет его, а terraform state show <addr> выводит одну запись. Экзамен ожидает, что вы будете знать, что переименование ресурса в конфигурации обычно привело бы к его уничтожению и повторному созданию — если вы не сообщите Terraform, что адрес переместился.
Блок moved делает именно это декларативно: переименуйте local_file.greeting (из Шага 2) в local_file.welcome, и блок moved перенесет состояние на месте, так что plan покажет перемещение, а не уничтожение и повторное создание. (Императивный эквивалент — terraform state mv local_file.greeting local_file.welcome.) Мы также показываем управляемый конфигурацией блок import — способ версии 1.5+ для включения существующего объекта в состояние без старой команды CLI terraform import. Когда состояние под контролем, осталась одна возможность: запустить все это удаленно.
# Renaming a resource? A "moved" block migrates state in place
# instead of destroying and recreating the object. Replace the
# Step 2 "greeting" resource with this renamed "welcome" one.
moved {
from = local_file.greeting
to = local_file.welcome
}
resource "local_file" "welcome" {
filename = "${path.module}/hello.txt"
content = "Hello from ${random_pet.name.id}!\n"
}
# Config-driven import (Terraform 1.5+): adopt an object that
# already exists into state, no "terraform import" CLI command.
# terraform_data is a built-in resource - nothing to provision.
import {
to = terraform_data.tracked
id = "existing-id"
}
resource "terraform_data" "tracked" {}Последний домен, «Понимание возможностей HCP Terraform» (5%), завершает экзамен. Один блок cloud внутри terraform {} меняет локальное выполнение на HCP Terraform: состояние хранится удаленно и блокируется во время запусков, terraform plan/apply выполняются на ранерах HashiCorp, а вывод запуска (плюс сохраненный план) отображается в веб-интерфейсе. Здесь также экзамен сравнивает функции HCP Terraform — удаленное состояние, историю запусков, принудительное применение политик с помощью Sentinel/OPA и частный реестр модулей — с чисто локальным рабочим процессом, который мы использовали в Шагах 1–6.
Замените my-org вашей собственной организацией, запустите terraform login один раз, чтобы сохранить токен API, затем terraform init, чтобы перенести состояние в удаленное рабочее пространство. Все, что вы написали в этой лабораторной работе, теперь работает без изменений — изменилось только место выполнения. Этот полный цикл, от чистого main.tf до удаленного рабочего пространства, составляет весь путь Associate за один присест.
terraform {
cloud {
organization = "my-org"
workspaces {
name = "terraform-associate-lab"
}
}
}
# Run once to authenticate, then re-init to migrate state:
# terraform login
# terraform initВсе находится на вашей машине, поэтому очистка быстрая:
terraform destroy, чтобы удалить сгенерированные файлы и очистить их из состояния.terraform-associate-lab из пользовательского интерфейса HCP Terraform (или сначала выполните terraform destroy для него), затем удалите блок cloud.cd .. && rm -rf tf-associate-lab. Локальный кэш плагинов .terraform/, .terraform.lock.hcl и terraform.tfstate удаляются вместе с ней.Экзамен Associate посвящен Terraform как инструменту, а не какому-либо конкретному облаку, поэтому эта лабораторная работа намеренно не предусматривает развертывания облачной инфраструктуры. Мы пропускаем ресурсы AWS / Azure / GCP специально: они требуют учетных данных, могут повлечь за собой расходы и отвлекали бы от механики, которую фактически проверяет экзамен — рабочий процесс, состояние, язык конфигурации и модули.
Некоторые темы Associate лучше изучать теоретически, а не на практике: бэкенды, отличные от HCP Terraform (S3, Azure Blob, GCS, Consul), провижионеры, такие как remote-exec (которые HashiCorp перечисляет как последнее средство), и рабочие пространства для управления несколькими экземплярами состояния. Для них разделы Просмотр и Справочник этой страницы сертификации содержат концептуальное описание. Практическая ценность здесь заключается в мышечной памяти init → plan → apply, чтении файла состояния и безопасной рефакторизации с помощью moved.