Глава 4. Погружаемся глубже в Terraform
Содержание
В своей предыдущей главе мы обсудили поставщиков Terraform, что в основном помогает Terraform понимать какой API применять для развёртывания. Также мы обсудили ресурсы Terraform, что помогает вам употреблять API службы поставщика для обеспечения соответствующих служб. Двигаясь далее, мы обсудили получение ввода от пользователей через определение переменных Terraform, что превращает код Terraform в повторно применяемый. Мы к тому же показали как вы можете подтверждать свой вывод от тех ресурсов, которые вы обеспечили или уже имеются при помощи вывода Terraform и, наконец, мы обсудили как вы можете применять уже имеющиеся ресурсы через вызов блока данных Terraform в вашем коде конфигурации.
В этой главе мы намерены обсудить собственно серверную основу Terraform, что
поможет вам хранить ваш файл Terraform tfstate
. Более того, мы будем обсуждать
оснастки Terraform, которые помогают вам исполнять сценарий внутри своего
кода конфигурации Terraform. Далее мы обсудим различные циклы (итерации) Terraform,
которые могут применяться в коде конфигурации Terraform. Мы также увидим чем являются различные встроенные
функции Terraform и как вы можете действенно применять эти функции в своём коде конфигурации. Наконец, вы увидите некоторые варианты,
которые предоставляет Terraform в плане отладки.
Данная глава рассмотрит следующие вопросы:
-
Введение в серверную основу Terraform
-
Основы оснасток Terraform
-
Основы циклов Terraform
-
Основы функций Terraform
-
Основы отладки Terraform
Чтобы следовать данной главой вам требуется обладать представлением о написании кода конфигурации Terraform через определение поставщиков, ресурсов, переменных, данных и вывода. Наряду с этим, на протяжении всего курса будут полезны некоторые базовые знания об основных поставщиках услуг, таких как GCP, AWS и Azure. Вес применяемый в этой главе код вы можете отыскать по такой ссылке: https://github.com/PacktPublishing/HashiCorp-Infrastructure-Automation-Certification-Guide/tree/master/chapter4.
Чтобы увидеть видео "Код в действии", перейдите по следующей ссылке: https://bit.ly/2SY35WS.
В этом разделе мы намерены поговорить о файле состояния Terraform и его серверной
основы Terraform. Как вы знаете, Terrafor следует модели конфигурации желательного состояния, в которой ы описываете ту среду,
которую вы бы желали собрать применяя код описания и попыток Terraform для превращения желательного состояния в реальность. Некий критически
важный компонент такого желательной модели состояния это соответствие того что уже имеется в вашей среде и того, что выражено в вашем коде
описания. Terraforms отслеживает это состояние через структуру данных в формате JSON с названием файл
состояния. Мы намерены посмотреть на то где может храниться такой файл состояния Terraform, как он может настраиваться и как к
нему осуществлять доступ, а также каковы рекомендации по удержанию файла Terraform tfstate
.
Вы уже осведомлены о том, что когда вы пишите файл конфигурации Terraform и как при исполнении terraform
init
, plan
и apply
он применяется для выработки
файла состояния, который хранит сведения относительно вашей полной инфраструктуры или тех служб, которые вы пытаетесь развернуть при помощи
Terraform. Этот файл состояния применяется Terraform для установления соответствия ресурсов реального мира с вашим файлом конфигурации
Terraform. По умолчанию, Terraform сохраняет terraform.tfstate
в своём текущем рабочем каталоге;
вы также можете хранить этот файл состояния в неком месте удалённого хранилища, таком как хранилище Amazon S3 или Azure Blob. Terraform
сочетает свою конфигурацию с таким файлом состояния, а также обновляя значение текущего состояния элементов в своём файле состояния для
создания планов.
Он выполняет второй раунд обновления после выполнения этапа apply
.
Файлы состояния Terraform это просто файлы JSON; в таком файле состояния не рекомендуется выполнять редактирование или производить изменения.
Обычно файлы состояния применяются для фиксации связывания один- к- одному ресурсов конфигурации с удалёнными объектами. Terraform создаёт
каждый объект и записывает его идентификационные сведения в таком файле состояния. Если вы желаете уничтожить какие бы то ни было ресурсы,
тогда удалите конфигурацию этого ресурса из своего файла конфигурации и исполните terraform apply
,
что удалит этот конкретный ресурс из соответствующего файла состояния. Помните, что не рекомендуется удалять привязку ресурса из своего
файла состояния.
Когда вы добавляете или удаляете ресурсы некими иными средствами - допустим, вы создали некий ресурс вручную и желаете применять этот
ресурс в Terraform - тогда вам бы потребовалось создать некую конфигурацию соответствующую такому ресурсу и затем импортируем этот ресурс в
свой файл состояния при помощи cmdlet terraform import
. Таким же образом, если вы хотите чтобы
Terraform забыл об одном из таких объектов, тогда вы можете воспользоваться cmdlet terraform state rm
,
что удалило бы этот объект из имеющегося файла состояния.
Как вы знаете, Terraform вырабатывает файл состояния. Вы можете задуматься, зачем IaC (Infrastructure as Code) Terraform требуется некий файл состояния и что бы произошло, если бы Terraform не обладал бы файлом состояния? Для ответа на этот вопрос файл состояния в Terraform хранит текущее знание своей настраиваемой инфраструктуры,что снижает сложность самого развёртывания ресурса когда вы обрабатываете большие Корпоративные инфраструктуры. Мы собираемся обсудить каковы основные цели обладания файлом состояния Terraform и какие преимущества он предоставляет нам:
-
Установление соответствия реальному миру: Terraform нуждается в обладании некой базой данных для установления соответствия конфигурации Terraform реальному миру. При определении в вашей конфигурации некого ресурса, например,
resource "azurerm_resource_group" "example"
, Terraform определяет это блок кода в формате JSON в своём файле состояния устанавливая соответствие конкретному объекту. Это сообщит вам что Terraform создал в некой группе ресурсов Azure и в дальнейшем, когда вы произведёте какие бы то ни было изменения в своём блоке кода конфигурации, он попробует проверить такой имеющийся ресурс в своём файле состояния и позволит вам таким образом знать был ли создан, исправлен или уничтожен такой ресурс при каждом выполненииterraform plan
. Это помогает понимать любой вид отклонений инфраструктуры от уже имеющихся ресурсов. -
Метаданные: Помимо установления соответствия между ресурсами и объектами, которые представлены в имеющемся файле состояния, Terraform нуждается в поддержке метаданных, таких как зависимости ресурсов. Terraform достаточно интеллектуален для понимания того какой ресурс требуется создавать или уничтожать и в какой последовательности. Для выполнения таких действий Terraform хранит зависимости ресурсов в своём файле состояния.
-
Производительность: Terraform обладает базовым соответствием. Он также хранит некий кэш всех своих атрибутов в своём файле состояния, что является одной из не обязательных функциональных возможностей Terraform и применяется только для улучшений производительности. Когда вы выполняете команду
terraform plan
, Terraform требуется знать значение текущего состояния своих ресурсов чтобы знать те изменения, которые ему требуется выполнить для достижения желательной конфигурации.Для малых инфраструктур Terraform запросто способен получать самые последние атрибуты от весх ваших ресурсов, что является поведением по умолчанию Terraform, для синхронизации всех ресурсов в своём состоянии перед выполнением любых операций, таких как
plan
иapply
.Для больших корпоративных инфраструктур Terraform может занимать многие часы для запроса всех своих атрибутов и ресурсов, что целиком зависит от размера своей инфраструктуры и способно удивить вас в плане значения общего времени, которое ему потребуется. Множество поставщиков облачных решений не предоставляет никакого API, который способен запрашивать множество ресурсов одновременно. Итак, в подобной ситуации многие пользователи устанавливают
-refresh=false
, а также флаг-target
и по этой причине его кэшированное состояние трактуется как истинная запись, что помогает Terraform отсечь время этапа планирования практически наполовину. -
Синхронизация: По умолчанию Terraform хранит свой файл состояния в своём текущем рабочем каталоге, в котором вы оставляете свой файл конфигурации. Это приемлемо когда вы единственный пользователь, который применяет код конфигурации Terraform. Но что если у вас имеется множество участников команд, совместно работающих с одни и тем же кодом конфигурации Terraform? Тогда, в подобной ситуации, вы просто обязаны удерживать свой файл состояния Terraform в неком удалённом состоянии. Terraform применяет удалённое блокирование с тем, чтобы только одна персона способна запускать в некий момент времени Terraform. Когда в то же самое время прочие пользователи пытаются пытаются исполнять имеющийся код Terraform, тогда Terraform распространит ошибку, сообщающую что его файл состояния пребывает в неком блокированном состоянии. Такая функциональность Terraform гарантирует что при каждом запуске кода Terraform, он обязан ссылаться на самый последний файл состояния.
Мы ознакомились с файлами состояния Terraform и их свойствами. Теперь давайте попробуем разобраться с типами серверных основ Terraform.
Файл состояния необходимо хранить либо в некой локальной, либо в удалённой серверной основе. Мы собираемся обсудить как локальные, так и удалённые серверные основы.
Локальные серверные основы Terraform
Terraform вырабатывает некоторый файл состояния и он должен где- то храниться. При отсутствии конфигурации с удалённой серверной основой, он будет хранится в том каталоге, в котором вам приходится удерживать свой файл конфигурации Terraform, что является поведением по умолчанию для Terraform. Давайте рассмотрим структуру папку для некого образца конфигурации:
.
├── .terraform
├── main.tf
└── terraform.tfstate
Эта конфигурация Terraform была инициализирована и работает через plan
и
apply
. В результате, наш каталог .terraform
удерживает те
сведения подключаемого модуля, который вы определили в своём файле конфигурации. В нашем случае мы выбрали поставщика Azure, поэтому в каталог
.terraform
, Terraform выгружает своего поставщика azurerm
.
Соответствующий файл terraform.tfstate
содержит положения состояния этой конфигурации.
В нашем следующем фрагменте кода мы показали ка вы способны изменять значение местоположения такого локального файла состояния при помощи
флага командной строки -state=statefile
для terraform plan
и
terraform apply
:
root@terraform-vm:~# terraform apply -state=statefile
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
inmishrar@terraform-vm:~#
inmishrar@terraform-vm:~# ls
main.tf statefile terraform-lab
inmishrar@terraform-vm:~# cat statefile
{
"version": 4,
"terraform_version": "1.0.0",
"serial": 1,
"lineage": "03e0af5c-c3a0-c2ae-9d40-d067358067dd",
"outputs": {},
"resources": []
}
Давайте попробуем разобраться что произойдёт в вашем файле состояния если вы воспользуетесь terraform
workspace
. В нашем случае мы создали некое новое рабочее пространство Terraform с названием
development
. После создания нового рабочего пространства и исполнения
terraform plan
и terraform apply
мы можем наблюдать, что
Terraform создал каталог terraform.tfstate.d
и подкаталог для каждого рабочего пространства. В каждом
рабочем пространстве получается созданным файл terraform.tfstate
:
inmishrar@terraform-vm:~# terraform workspace new development
Created and switched to workspace "development"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
inmishrar@terraform-vm:~# terraform workspace list
default
* development
inmishrar@terraform-vm:~# terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
inmishrar@terraform-vm:~# tree
.
├── main.tf
├── .terraform.lock.hcl
├── terraform.tfstate
└── terraform.tfstate.d
└── development
└── terraform.tfstate
Из определённого выше блока кода мы можем понять, что один и тот же файл конфигурации может применяться в различных рабочих пространствах Terraform (то есть неком ландшафте, например разработки или тестирования).
Удалённые серверные основы Terraform
Как мы уже упоминали ранее, Terraform по умолчанию применяет серверную основу локального файла. Имеются некоторые преимущества применения удалённых серверных основ. Некоторые из них мы поясняем ниже:
-
Работа в команде: Удалённые серверные основы помогают вам хранить файлы состояния в неком удалённом местоположении и защищать их при помощи функциональной возможности блокирования с тем, чтобы такой файл состояния никогда не разрушался. Такой файл состояния переходит в состояние блокированного когда всякий участник команды запускает этот файл конфигурации с тем, чтобы не было никаких конфликтов, что означает, что никакие два участника команды не будут способны одновременно исполнять имеющийся код конфигурации Terraform. Некоторые серверные основы Terraform, такие как Облачное решение Terraform, удерживают полную историю всех ревизий положения состояния.
-
Удержание конфиденциальных сведений на диске: Файл состояния Terraform обычно обладает чувствительными данными, такими как пароли, которые очень критически важны. Итак, когда вы применяете удалённые серверные основы, тогда эти сведения будут храниться в удалённом хранилище и вы можете выполнять их выборку с удалённой серверной основы по запросу и обеспечивать что они хранятся исключительно в памяти. Большинство серверных основ в состоянии покоя обеспечивают шифрование. Таким образом, файл состояния внутри своей удалённой серверной основы будет безопасным и конфиденциальным.
-
Удалённые операции: Допустим, вы имеете дело с очень большими инфраструктурами и выполняете некоторые изменения конфигурации. По этой причине, команда
terraform apply
может потребовать для своего исполнения времени больше ожидаемого. Некоторые удалённые серверные основы поддерживают удалённые операции, которые допускают для них удалённое выполнение действий. В таком случае вы можете выключить свой компьютер и вы обнаружите, что эти операции Terraform продолжают исполняться удалённо вплоть до своего завершения. Такие удалённые операции поддерживаются исключительно продуктом SaaS HashiCorp, то есть Облачным решением Terraform.
Удалённые серверные основы хранят ваши сведения состояния в неком удалённом местоположении на основе конфигурации той серверной основы, которую вы определили в своей конфигурации. Не все серверные основы одинаковы и HashiCorp определяет два класса серверных основ:
-
Стандартные: Включают управление состоянием и возможно блокируются
-
Расширенные: Включают удалённые операции поверх стандартных функциональных возможностей
Расширенные удалённые серверные основы это либо Облачные решения Terraform, либо Корпоративный Terraform, которые будут рассмотрены в
Главе 10, Облачные решения Terraform и корпоративный Terraform. Обе эти службы позволяют
вам выполнять ваши операции Terraform, такие как plan
и apply
в имеющейся удалённой службе, а также локально.
Прохождение по всем стандартным серверным основам выходит за рамки данной книги. Всё же мы обсудим пару из них, такие как AWS S3, Azure Storage и Google Cloud Storage (GCS).
Давайте рассмотрим в качестве примера серверную основу Хранилища Azure:
terraform {
backend "azurerm" {
storage_account_name = "terraform-stg"
container_name = "tfstate"
key = "terraform.tfstate"
access_key = "tyutyutyutryuvsd68564568..."
}
}
В приведённом выше блоке кода вам потребуется определить некий ключ доступа для получения полного доступа к Azure Blob. Обычно, не
рекомендуется в самом фале конфигурации определять access_key
в такой конфигурации кода серверной основы.
Определение полномочий в таком фале конфигурации имеет пару проблем; например, вам может потребоваться изменять эти полномочия через регулярные
промежутки времени, что означает что вам потребуется удерживать изменения своего файла конфигурации серверной основы. Также рекомендуется
определять полномочия в обычном тексте и хранить их в неком управлении источником или в локальной машине.
Теперь, основная проблема в том как вы будете определять полномочия в своём файле конфигурации серверной основы. Вы не обладаете неким вариантом их определения в качестве переменной, потому как такой файл конфигурации серверной основы запускается в процессе самой его инициализации и вплоть до этого Terraform не сможет разобраться с такими переменными. Для преодоления такой проблемы вы можете определять частичную конфигурацию серверной основы в его корневом модуле, а все остальные сведения в самой среде времени выполнения.
В нашем определённом ранее блоке кода мы увидели как определять код конфигурации серверной основы для хранилища Azure Blob, но мы определили
access_key
в обычном тексте в конфигурации серверной основы, что совсем не рекомендуется применять.
Итак, мы определим код конфигурации серверной основы применяя свой частичный метод. Следующий фрагмент кода снабдит вас некой идеей того, как
вы предполагаете определять частичную конфигурацию серверной основы:
terraform {
backend "azurerm" {
container_name = "tfstate"
key = "terraform.tfstate"
}
}
Сами значения для storage_account_name
и access_key
могут
поддерживаться одним из трёх способов:
-
Когда вы выполняете
terraform init
, вы можете передавать значения в некотором интерактивном терминале или в CLI. -
Через флаг
-backend-config
с неким набором пар ключ- значение. -
Через флаг
-backend-config
с путём к файлу, содержащему пары ключ- значения.
Вы даже обладаете вариантом получения необходимых значений из своих переменных среды; например, вы можете определить
ARM_ACCESS_KEY
в качестве переменной среды в своей локальной машине и в процессе инициализации Terraform,
Terraform будет способен получать значения из таких переменных среды. Вы можете обратиться к справочным сведениям серверной основы
Хранилища Azure для поиска поддерживаемых параметров в переменных среды: https://www.terraform.io/docs/backends/types/azurerm.html.
После выполнения инициализации Terraform в самый первый раз со всеми необходимыми значениями серверной основы, вам не потребуется предоставлять
эти значения снова для прочих команд. Необходимые сведения серверной основы и прочие данные инициализации сохраняются в локальном файле в
соответствующем подкаталоге .terraform
вашей конфигурации. Когда вы активно доставляет свой код конфигурации в
управление источником, убедитесь что вы не доставляете этот каталог .terraform
, поскольку он может
содержать чувствительные сведения, такие как полномочия аутентификации вашей конфигурации серверной основы.
![]() | Замечание |
---|---|
Когда вы создаёте в GitHub некий новый репозиторий, в этом репозитории, когда вы создаёте некий файл |
Давайте попробуем разобраться как такая удалённая серверная основа Terraform поспособствует вам в совместной работе при помощи некого примера.
Допустим, вы работаете совместно с Джоном, сетевым администратором, с некой сетевой конфигурацией.
Вы оба будете обновлять свои локальные копии имеющейся конфигурации и затем активно доставлять её в управление версиями. Когда наступает время
для обновления реальной среды, вы исполняете этапы plan
и apply
.
В протяжении этого периода имеющийся удалённый файл состояния будет в заблокированном состоянии, поэтому Джон не будет способен одновременно
выполнять какие бы то ни было изменения. Когда Джон запускает свой следующий план, он будет применять обновлённое данные удалённого состояния
из вашей самой последней фазы apply
. Хранилище Azure Blob обладает естественной способностью поддержки
блокированного состояния и согласованности данных.
Итак , применяя следующий блок кода, вы способны выделять вывод из своей имеющейся удалённой серверной основы при помощи блока данных Terraform. Для получения дополнительных сведений относительно конфигурации и аутентификации хранилища серверной основы Azure Blob вы можете найти на https://www.terraform.io/docs/backends/types/azurerm.html:
data "terraform_remote_state" "example" {
backend = "azurerm"
config = {
storage_account_name = "terraform-stg"
container_name = "tfstate"
key = "terraform.tfstate"
}
}
Теперь давайте попробуем рассмотреть конфигурацию удалённой серверной основы для сегмента AWS S3. Как и в случае с хранилищем Azure Blob, AWS S3 также поддерживает конфигурацию удалённой серверной основы для хранения файла состояния Terraform, однако если вы желаете обладать блокированным и согласованным состоянием, тогда вам потребуется применять Amazon DynamoDB:
terraform {
backend "s3" {
bucket = "terraform-state-dev"
key = "network/terraform.tfstate"
region = "us-east-1"
}
}
Давайте попробуем считать вывод из нашей конфигурации серверной основы AWS S3, применяя источник данных. Следующий блок кода поможет вам разобраться с тем как вы можете выделять соответствующий вывод:
data "terraform_remote_state" "example" {
backend = "s3"
config = {
bucket = "terraform-state-dev"
key = "network/terraform.tfstate"
region = "us-east-1"
}
}
Соответствующий источник данных terraform_remote_state
вернёт следующие значения:
data.terraform_remote_state.example:
id = 2016-10-29 01:57:59.780010914 +0000 UTC
addresses.# = 2
addresses.0 = 52.207.220.222
addresses.1 = 54.196.78.166
backend = s3
config.% = 3
config.bucket = terraform-state-dev
config.key = network/terraform.tfstate
config.region = us-east-1
elb_address = web-elb-790251200.us-east-1.elb.amazonaws.com
public_subnet_id = subnet-1e05dd33
Давайте рассмотрим как мы можем определять необходимую серверную основу Terraform при помощи GCS. Для вашего сведения, GCS сохраняет свой файл состояния и по умолчанию поддерживает блокировку состояния. Вот фрагмент кода:
terraform {
backend "gcs" {
bucket = "tf-state-prod"
prefix = "terraform/state"
}
}
Теперь основной вопрос состоит в том, как вы можете считывать вывод из уже настроенной серверной основы Terraform в CGS? Вы можете воспользоваться таким блоком кода:
data "terraform_remote_state" "example" {
backend = "gcs"
config = {
bucket = "terraform-state"
prefix = "prod"
}
}
resource "template_file" "terraform" {
template = "${greeting}"
vars {
greeting = "${data.terraform_remote_state.example.greeting}"
}
}
В этом разделе мы изучили серверную основу Terraform, как локальную серверную основу, так и удалённую серверную основу. По умолчанию, Terraform сохраняет свой файл состояния в своей локальной серверной основе, однако если вы в поиске совместной работы и безопасности, тогда Terraform предоставляет вам соответствующий вариант для сохранения необходимого файла состояний в своей удалённой серверной основе. Мы обсудили как вы можете настраивать свою удалённую серверную основу при помощи AWS S3, Azure Blob storage и GCS. Помимо этого, мы также обсудили как вы можете считывать всё содержимое своих настроенных серверных основ при помощи источников данных Terraform. При помощи всего этого вы будете обладать крепким пониманием серверной основы Terraform и её важности. В идущем следом разделе мы намерены обсудить поставщиков Terraform и попытаться обсудить некоторые образцы реального времени поставщиков Terraform.
В этом разделе мы намерены обсудить оснастки Terraform (Terraform provisioners). Давайте попробуем разобраться с ними на неком примере. Предположим, вы работаете с одним из своих коллег, Марком. Вы оба получили некую задачу развёртывания полной корпоративной инфраструктуры в Microsof Azure, которая содержит 10 серверов Ubuntu, 10 виртуальных сетевых сред, 1 подсеть для каждой виртуальной сети и балансировщиков нагрузки, причём все эти виртуальные машины должны обладать установленным по умолчанию Apache прежде чем обрабатываться вашей командой приложений. Марк предполагает, что для того чтобы обладать установленным Apache на этих серверах, вы можете применять оснастки Terraform. Теперь вы задумались, что представляет собой такая оснастка Terraform и как мы можем применять её?
Давайте попробуем разобраться с оснастками Terraform. Допустим, вы создаёте некоторые ресурсы и вам требуется запускать некий вид сценария или операции, которую вы желаете выполнять локально или в неком удалённом ресурсе. Применяя оснастки Terraform вы можете удовлетворить такому ожиданию. Собственно выполнение оснасток Terraform не требует идемпотентности или атомарности, поскольку она выполняет некий произвольный сценарий или инструкцию. Terraform не будет способен отслеживать результаты и состояние оснасток аналогично тому как это применяется для осуществления в прочих ресурсах. По этой причине HashiCorp рекомендует применять оснастки HashiCorp в качестве последнего средства, когда у вас нет никакой другой возможности для осуществления своей цели.
![]() | Замечание |
---|---|
Не применяйте оснастки Terraform пока не остаётся абсолютно никакого иного способа выполнения вашей цели по той причине, что Terraform не хранит подробности оснасток в своём файле состояния, что делает затруднительным исправление неисправностей. Итак, в подобной ситуации для осуществления некоторых операций или запуска кого бы то ни было сценария вы можете воспользоваться неким сторонним инструментом, таким как CircleCI или Jenkins. Это также может помочь вам в получении более подробных регистрационных записей отладки. |
Давайте попробуем разобраться с некоторыми из вариантов применения оснасток Terraform. Может иметься множество вариантов, целиком зависящие от различных ситуаций. Некоторые из них таковы:
-
Загрузка данных в виртуальную машину
-
Самораскрутка виртуальной машины для диспетчера конфигурации
-
Локальное сохранение данных в вашей системе
Оснастка remote-exec
соединяет некоторую удалённую машину через WinRM или SSH и помогает вам запускать
удалённо некий сценарий. Такая удалённая машина должна допускать удалённые подключения; в противном случае оснастка
remote-exec
Terraform не будет способна запускать такой предоставляемый сценарий. Вместо применения
remote-exec
для передачи данных в некую виртуальную машину, большинство поставщиков облачных решений
предоставляет для передачи данных встроенные инструменты, например, user_data
в AWS или
custom_data
в Azure. Все общедоступные облачные решения поддерживают некий вид обмена данных, который не
требует удалённого доступа к такой машине; для дальнейшего чтения относительно встроенных инструментов для передачи данных в различные облачные решения,
можете воспользоваться справочными материалами https://www.terraform.io/docs/language/resources/provisioners/syntax.html. Здесь показаны некоторые из них:
Как вы могли видеть на этом рисунке, разными поставщиками облачных решений поддерживаются различные аргументы обмена данных. Это поможет вам
отыскать данные, которые вы ищите для таких ресурсов в процессе своего создания или обновления. Имеется большое число образов ОС Linux,
таких как Red Hat, Ubuntu и SUSE, которые обладают встроенным программным обеспечением с названием cloud-init
,
позволяющим вам запускать произвольные сценарии и выполнять некоторые базовые системные конфигурации в процессе изначальной разработки самого
такого сервера, и, благодаря этому, вам не требуется получать SSH или такой сервер в явном виде.
Дополнительно к такой оснастке remote-exec
, также имеются такие оснастки управления конфигурацией как
Chef, Puppet, Salt
и тому подобные {Прим. пер.: см, например, наш перевод 3 издания Полное руководство Ansible Джеймса Фримана и Джесса
Китинга}. они позволяют раскручивать загрузку соответствующей виртуальной машины для применения в качестве диспетчера конфигурации
по вашему выбору. Одной из наилучших альтернатив выступает создание некого персонального образа с уже установленным программным обеспечением
необходимого диспетчера конфигурации и получения его регистрации в качестве вашего сервера управления конфигурацией при запуске, применяя один из
упомянутых в нашем предыдущем параграфt вариантов загрузки данных. Мы намерены подробнее обсудить все имеющиеся типы оснасток Terraform.
Теперь, когда мы получили некое представление относительно оснасток Terraform, давайте рассмотрим какие различные типы оснасток доступны нам.
Оснастка local-exec
Применяя оснастку local-exec
, вы будете локально запускать код конфигурации Terraform для выделения
некоторых сведений. В некоторых ситуациях имеется некий поставщик, который уже обладает той функциональностью, которую вы ищите. Например, некий
локальный поставщик способен взаимодействовать с файлами в вашей локальной системе. Тем не мене, всё ещё могут существовать некие ситуации, в
которых вам бы приходилось запускать некий локальный сценарий при помощи оснастки local-exec
. HashiCorp
рекомендует применять local-exec
в качестве временного обходного пути когда та функциональная возможность,
которую вы ищете, не доступна у такого поставщика.
Приводимый далее фрагмент кода поможет вам определить блок кода local-exec
в вашем коде
конфигурации Terraform:
resource "aws_instance" "example" {
# ...
provisioner "local-exec" {
command = "echo The EC2 server IP address is ${self.private_ip}"
}
}
В приведённом выше блоке кода, если вы обратили внимание, мы задали блок кода оснастки local-exec
внутри своего блока кода ресурса имеющегося экземпляра AWS. Это поможет выделить значение IP адреса этого сервера после его создания.
Мы определили некий объект с названием self
, который предоставляет собственно родительский ресурс
оснастки и получили возможность выделять все атрибуты этого ресурса. В нашем случае мы определили
self.public_ip
, который ссылается на атрибут
public_ip
для aws_instance
.
Предположим, вы пытаетесь исполнить оснастку local-exec
и получаете отказ. Тогда, давайте рассмотрим как
вы можете определить блок кода, обрабатывающий поведение отказа такой оснастки. Для всех оснасток может применяться аргумент
on_failure
:
resource "aws_instance" "example" {
# ...
provisioner "local-exec" {
command = "echo The EC2 server IP address is ${self.private_ip}"
on_failure = continue
}
}
В своём предыдущем коде мы определили on_failure = continue
. Мы обладаете двумя поддерживаемыми значениями
аргументов n_failure
, либо continue
, либо
fail
:
-
continue
: продолжает при помощи создания или разрушения, путём игнорирования всякой ошибки, когда она встречается. -
fail
: Когда имеется какая бы то ни было ошибка, останавливает применение установленного по умолчанию поведения. Если происходит подобное при подобном создании оснастки, это запятнает такой ресурс.
Вам может быть интересно, что же произойдёт с вашей оснасткой когда вы захотите уничтожить свой ресурс? Следующий код это образец того как
вы можете определять when
, некоторый параметр, со значением destroy
(то есть when = destroy
):
resource "google_compute_instance" "example" {
# ...
provisioner "local-exec" {
when = destroy
command = "echo We are discussing Destroy-time provisioner"
}
}
Уничтожения оснасток исполняются до уничтожения самого ресурса. В случае их отказа, Terraform возбудит некую ошибку и вернёт необходимые
оснастки снова, когда вы выполните terraform apply
в следующий раз. Благодаря такому поведению, вам
требуется быть аккуратным при запуске уничтожения оснасток множество раз.
Оснастки времени уничтожения могут исполняться только когда они имеются в соответствующем файле конфигурации в то время, когда ресурс подлежит уничтожению. Если вы удаляете соответствующий блок кода ресурса с оснасткой времени уничтожения, конфигурация его оснастки также будет удалена совместно с ней и, по этой причине, у вас не будет возможности запуска такой оснастки уничтожения.
Давайте посмотрим как мы можем определять множество оснасток. Приводимый далее блок кода снабдит вас пониманием того как определять множество оснасток:
resource "google_compute_instance" "example" {
# ...
provisioner "local-exec" {
command = "echo We have executed first command successfully"
}
provisioner "local-exec" {
command = "echo We have executed second command successfully"
}
}
Из нашего определённого выше блока кода, мы можем понять, что мы можем определять столько блоков кода оснасток внутри своего блока кода ресурса, сколько пожелаем.
Давайте воспользуемся ещё одним примером local-exec
, в котором мы покажем как вы можете определять некий
сценарий PowerShell, пребывающий в вашем локальном каталоге, и как вы можете исполнять такой сценарий, определяя его в неком блоке кода
null_resource
:
resource "null_resource" "script" {
triggers = {
always_run = "${timestamp()}"
}
provisioner "local-exec" {
command = "${path.module}/script.ps1"
interpreter = ["powershell", "-File"]
}
}
Допустим, вы желаете экспортировать некий вывод из своего ресурса в соответствующий локальный файл. Тогда вы можете написать следующий блок
кода. Как вы можете видеть из этого блока кода, мы будем способны получать частный IP адрес своего экземпляра AWSв наш локальный файл
с названием private_ips.txt
и что он будет создан в соответствующем локальном каталоге:
resource "aws_instance" "example" {
# ...
provisioner "local-exec" {
command = "echo ${aws_instance.example.private_ip} >> private_ips.txt"
}
}
Для дополнительных сведений относительно оснастки local-exec
, вы можете прочесть
https://www.terraform.io/docs/provisioners/local-exec.html.
Оснастка file
Теперь, когда вы разобрались с оснастками Terraform, имеется один тип оснастки, с названием оснастки file, которая помогает нам копировать
файлы или каталоги с одной машины, на которой подлежит исполнению Terraform, во вновь создаваемый ресурс. Такая оснастка file поддерживает
оба вида подключений, то есть, ssh
и winrm
. Следующий блок кода
поможет вам разобраться с тем, как вы можете пользоваться такой оснасткой file:
resource "aws_instance" "example" {
# ...
# Copies the app.conf file to /etc/app.conf
provisioner "file" {
source = "conf/app.conf"
destination = "/etc/app.conf"
}
# Copies the string in content into /tmp/amifile.log
provisioner "file" {
content = "ami used: ${self.ami}"
destination = "/tmp/amifile.log"
}
# Copies the configs.d folder to /etc/configs.d
provisioner "file" {
source = "conf/configs.d"
destination = "/etc"
}
# Copies all files and folders in apps/apptest to D:/IIS/webapp
provisioner "file" {
source = "apps/apptest/"
destination = "D:/IIS/webapp"
}
}
В нашем определяемом выше блоке кода вы можете наблюдать некоторые параметры, которые поддерживаются оснастками file:
-
source
: Это исходные папка или файл, которые вы желаете копировать. Вы можете определятьsource
относительно текущего рабочего каталога или в качестве некого абсолютного пути. У вас нет возможности определения атрибутаsource
с соответствующим атрибутомcontent
. В оснастках file может определяться либо атрибутsource
, либо отрибутcontent
. -
Code
: Этот атрибут помогает вам копировать содержимое в его получателя. Когда получателем выступает некий файл, данное содержимое будет записано в такой файл. В случае некого каталога, создаётся какой- то файл с названиемtf-file-content
. Для применения в качестве получателя рекомендуется применять файл с тем, чтобы ваше содержимое копировалось в такой заданный файл. У вас нет возможности применять данный атрибут с атрибутомsource
. -
destination
(необходим): Некий абсолютный путь, в который вы желаете выполнить копирование.
Оснастка file может применяться вместе с определённым вложенным блоком кода соединения. Следующий код
оснастки даст вам внутреннее представление относительно того как вы можете определять подробности подключения, такого как
ssh
или winrm
в самом коде:
# Copies the file as the root user using SSH
provisioner "file" {
source = "conf/app.conf"
destination = "/etc/app.conf"
connection {
type = "ssh"
user = "root"
password = var.root_ssh_password
host = var.host_name
}
}
# Copies the file as the Administrator user using WinRM
provisioner "file" {
source = "conf/app.conf"
destination = "C:/App/app.conf"
connection {
type = "winrm"
user = "TerraformAdmin"
password = var.windows_admin_password
host = var.host_name
}
}
Этот код целиком может быть определён в самом ресурсе или в его оснастке. Если вы нуждаетесь в дополнительных сведения относительно подключений внутри оснастки, вы можете обратиться за справкой к https://www.terraform.io/docs/provisioners/connection.html.
Оснастка remote-exec
Оснастка remote-exec
помогает вам исполнять некий сценарий в вашем удалённом ресурсе, после его создания.
вы можете воспользоваться оснасткой remote-exec
для исполнения некого инструмента управления
конфигурацией, самораскрутки в какой- то кластер и тому подобного. Она также поддерживает типы соединения как
ssh
, так и winrm
. Приводимый далее блок кода снабдит вас пониманием
того, как вы можете определять свою оснастку remote-exec
:
resource "aws_instance" "example" {
# ...
provisioner "remote-exec" {
inline = [
"puppet apply",
"consul join ${aws_instance.example.private_ip}",
]
}
}
Приводимое определение блока кода обладает такими аргументами:
-
inline
: Это список команд, которые исполняются в той последовательности, в которой вы определите их в своём блоке кода. Они не могут быть написаны с применениемscript
илиscripts
. -
script
: Это значение относительного пути, которое будет скопировано в ваш удалённый ресурс и затем исполнено. Им не может быть сочетание сinline
илиscripts
. -
scripts
: Это перечень относительных путей к локальным сценариям, который копируется в ваш удалённый ресурс и затем исполняется. Он применяется для выполнения в том установленном порядке, который вы задаёте в своём блоке кода. У вас нет возможности определять его сinline
илиscript
.
Для дополнительного ознакомления с remote-exec
оснасток, вы можете посетить
https://www.terraform.io/docs/language/resources/provisioners/remote-exec.html.
В данном разделе мы обсудили оснастки Terraform. Теперь вы разбираетесь в различных типах оснасток, таких как оснастки file, локальные оснастки
и удалённые оснастки. Мы также упомянули, что вам надлежит выбирать применение некой оснастки исключительно когда у вас нет другого
варианта. В своей сердцевине оснастка Terraform помогает вам запускать некий вид сценария, либо локально, либо удалённо, для достижения некоторой
цели. В идущем следом разделе мы намерены обсудить различные типы циклов, поддерживаемые Terraform, с тем, чтобы вы были способны определять блок
кода для соответствия определённому числу N
требований.
В этом разделе мы будем обсуждать различные методы циклы Terraform. Как и прочие
языки программирования, Terraform также поддерживает некоторые виды циклов и это будет помогать вам очень гладко выполнять число
N
операций Terraform. Давайте попробуем разобраться с этим на неком примере. Допустим, вы работаете
с коллегой, Марком в одной из MNC
(multinational companies, мультинациональных компаний). Вы оба обсуждаете одно из
требований - необходимость развёртываания виртуальной сети Azure с 10 различными подсетями совместно с некой
Network Security Group
(NSG, группой сетевой безопасности), связанной с этими подсетями. Вы говорите
Марку, что это запросто можно сделать при помощи циклов Terraform вместо того чтобы писать один и тот же блок кода для такой подсети снова и
снова.
Мы объясним как мы можем действенно писать свой блок кода конфигурации Terraform при помощи циклов Terraform. Terraform поддерживаются следующие циклы:
-
count
: Циклы по ресурсам -
for_each
: Циклы по ресурсам и встроенным блокам внутри некого ресурса -
for
: Циклы по определённым спискам и соответствиям
Давайте подробно рассмотрим каждый из них.
Давайте разберёмся как мы можем применять параметры count
. Для лучшего понимания мы имеем следующий
блоком кода Terraform, который поможет нам создать некую группу ресурсов в Azure:
# To Create Resource Group
resource "azurerm_resource_group" "example" {
name = "Terraform-rg"
location = "West Europe"
}
Теперь давайте предположим, что вы хотите создать в Azure три группы ресурсов. Затем, как бы вы достигли этого требования? Вы могли бы планировать применение традиционного подхода циклов, который вы бы могли видеть в прочих языках программирования, однако Terraform не поддерживает такой подход:
# Этот псевдокод не работает в Terraform.
for (i = 0; i < 3; i++) {
resource "azurerm_resource_group" "example" {
name = "Terraform-rg"
location = "West Europe"
}
}
Как вы знаете, этот определённый выше блок кода не будет работать в Terraform. Теперь основной вопрос это как на самом деле мы определяем это
в Terraform? Для создания трёх групп ресурсов в Azure вы можете применять параметр count
. Такое задание
для нас будет способен выполнять следующий фрагмент кода:
# To Create Resource Group
resource "azurerm_resource_group" "example" {
count = 3
name = "Terraform-rg"
location = "West Europe"
}
В нашем определённом выше коде остаётся одна проблема: название соответствующей группы ресурсов в Azure обязано быть уникальным. Вы можете
воспользоваться count.index
чтобы превратить название такой группы ресурсов в уникальное. Наш
следующий фрагмент кода снабдит вас пониманием того как определять count
и
count.index
:
# To Create Resource Group
resource "azurerm_resource_group" "example" {
count = 3
name = "Terraform-rg${count.index}"
location = "West Europe"
}
Если вы выполняете terraform plan
, вы можете увидеть следующий фрагмент кода, который ясно
показывает что он обеспечит три различные группы ресурсов в Azure при выполнении команды terraform apply
:
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# azurerm_resource_group.example[0] will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "Terraform-rg0"
}
# azurerm_resource_group.example[1] will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "Terraform-rg1"
}
# azurerm_resource_group.example[2] will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "Terraform-rg2"
}
Plan: 3 to add, 0 to change, 0 to destroy.
Названия групп ресурсов Terraform-rg0
, Terraform-rg1
и
Terraform-rg2
не выглядят привлекательными. Мы можем давать им свои собственные названия, вместо того,
чтобы получать их через count.index
. Мы можем выполнить такую персонализацию определяя блок кода
некоторой переменной:
variable "rg_names" {
description = "list of the resource group names"
type = list(string)
default = ["Azure-rg", "AWS-rg", "Google-rg"]
}
Теперь мы определили значения названий групп ресурсов в своём блоке кода переменной, наш блок кода реального ресурса будет выглядеть так:
# To Create Resource Group
resource "azurerm_resource_group" "example" {
count = length(var.rg_names)
name = var.rg_names[count.index]
location = "West Europe"
}
Как вы можете видеть в нашем определённом выше блоке кода, мы пользуемся функцией length
, которая
возвращает значение числа элементов в заданном списке. Когда вы выполняете команду terraform plan
,
вы можете обнаружить, что Terraform желает создать в Azure три группы ресурсов с названиями Azure-rg
AWS-rg
и Google-rg
:
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# azurerm_resource_group.example[0] will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "Azure-rg"
}
# azurerm_resource_group.example[1] will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "AWS-rg"
}
# azurerm_resource_group.example[2] will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "Google-rg"
}
Plan: 3 to add, 0 to change, 0 to destroy.
Обратите внимание, что поскольку вы в своём ресурсе воспользовались count
, это больше не отдельный
ресурс, а вместо этого он превращается в список ресурсов. Так как это список ресурсов, если вы желаете считать некий атрибут из конкретного
ресурса, тогда вам бы потребовалось определять их следующим способом:
<PROVIDER>_<TYPE>.<NAME[INDEX]>.ATTRIBUTE
Давайте попробуем получить значение атрибута id
из соответствующей группы ресурсов
Azure-rg
. Следующий блок кода снабдит нас значением атрибута
id
:
output "rg_id" {
value = azurerm_resource_group.example[0].id
description = "The Id of the resource group"
}
Если вам нужны все идентификаторы всех имеющихся групп ресурсов, вам потребуется воспользоваться вместо индекса выражением звёздочки,
*
:
output "All_rg_id" {
value = azurerm_resource_group.example[*].id
description = "The Id of all the resource group"
}
Когда вы выполняете команду terraform apply
, rg_id
снабдит нас выводом группы ресурсов Azure-rg
, а All_rg_id
снабдит нас выводом для всех имеющихся групп ресурсов:
$ terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
Terraform will perform the following actions:
Plan: 0 to add, 0 to change, 0 to destroy.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
All_rg_id = [
"/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Azure-rg",
"/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/AWS-rg",
"/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Google-rg",
]
rg_id = /subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Azure-rg
Мы уже видели как мы можем определять значение параметра count
, однако имеются некоторые
ограничения на это и по такой причине, применение данного параметра count
ослаблено.
Самое первое ограничение когда применяется count
над всем ресурсом целиком, вы не будете способны
применять count
внутри некого ресурса для обхода циклом некого встроенного блока кода. Например,
скажем, вы планируете создать виртуальную сеть в Azure и множество подсетей внутри такой виртуальной сети. Такой блок кода подсети
содержит необходимые свойства и обладает собственной конфигурацией. Поэтому, когда мы желаем выполнять итерации по таким свойствам,
тогда мы не могли бы иметь возможности применения выражения count
.
Второе ограничение с count
состоит в том, чт произойдёт если вы попытаетесь выполнить любые
изменения в определённом вами списке. Давайте продолжим с тем же самым кодом переменной, который мы определяем для своих групп ресурсов:
variable "rg_names" {
description = "list of the resource group names"
type = list(string)
default = ["Azure-rg", "AWS-rg", "Google-rg"]
}
Рассмотрим как вам придётся удалять из вашего списка AWS-rg
. Поэтому наш код переменной выглядел
бы так:
variable "rg_names" {
description = "list of the resource group names"
type = list(string)
default = ["Azure-rg", "Google-rg"]
}
Теперь давайте попробуем разобраться как в точности Terraform будет вести себя когда мы исполним
terraform-plan
:
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
-/+ destroy and then create replacement
Terraform will perform the following actions:
# azurerm_resource_group.example[1] must be replaced
-/+ resource "azurerm_resource_group" "example" {
~ id = "/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/AWS-rg" -> (known after apply)
location = "westeurope"
~ name = "AWS-rg" -> "Google-rg" # forces replacement
- tags = {} -> null
}
# azurerm_resource_group.example[2] will be destroyed
- resource "azurerm_resource_group" "example" {
- id = "/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Google-rg" -> null
- location = "westeurope" -> null
- name = "Google-rg" -> null
- tags = {} -> null
}
Plan: 1 to add, 0 to change, 2 to destroy.
При выполнении terraform-plan
вы можете наблюдать что это намерено уничтожить
Google-rg
и повторно создать его. Однако это не что-то то, что мы собирались сделать; мы бы не хотели
чтобы Google-rg
был уничтожен и повторно создан когда мы удаляем
AWS-rg
из своего списка переменных.
Когда мы в неком ресурсе определяем параметр count
, Terraform трактует такой ресурс как перечень или
массив ресурсов и по этой причине он применяется для идентификации каждого ресурса, определённого внутри такого массива по его индексу позиции
в этом массиве. Внутреннее представление такой группы ресурсов при выполнении terraform apply
выглядит примерно так:
azurerm_resource_group.example[0]: Azure-rg
azurerm_resource_group.example[1]: AWS-rg
azurerm_resource_group.example[2]: Google-rg
Когда вы удаляете некий элемент из середины какого- то массива, все элементы после него сдвигаются обратно на один, поэтому после выполнения
plan
с всего лишь двумя элементами, внутреннее представление Terraform станет подобным следующему:
azurerm_resource_group.example[0]: Azure-rg
azurerm_resource_group.example[1]: Google-rg
Вы могли заметить как Google-rg
переместился с индекса 1 на индекс 0 и по этой причине Terraform
понимает это как "переименовать элемент по индексу 1 в Google-rg
и удалить элемент с индексом 2".
Иными словами, всякий раз, когда вы применяете count
в своём коде для обеспечения списка ресурсов и
предполагаете что вы удаляете какой- то элемент из середины элементов в своём списке, Terraform будет снова с нуля повторно создавать те ресурсы,
которые следуют после такого среднего элемента, что несомненно является не тем, чего бы мы желали. Для решения таких ограничений нашего параметра
count
, Terraform предоставляет выражение for_each
.
Terraform поставляется с выражением for_each
, которое помогает вам обходить в цикле некие наборы
строк или соответствий. Применяя for_each
вы способны создавать множество копий своего ресурса целиком или
множество копий встроенного блока кода, который определяется внутри своего блока кода ресурса.
Давайте попробуем разобраться как мы можем писать блок кода for_each
просто для соответствующего
блока кода for_each
. Для своего справочного руководства мы намерены рассмотреть тот же самый пример создания
трёх групп ресурсов в Azure:
# To Create Resource Group
resource "azurerm_resource_group" "example" {
for_each = toset(var.rg_names)
name = each.value
location = "West Europe"
}
Для преобразования своего списка var.rg_names
в некий набор строк мы применяем функцию Terraform
toset
. for_each
в соответствующем блоке кода поддерживает
только набор строк или соответствий. Когда вы применяете над таким набором цикл for_each
, тогда
each.value
или each.key
обеспечат значение имён соответствующих
групп ресурсов. В целом, each.key
применяется для соответствий когда у вас имеются пары ключ- значение.
После того, как вы определили в блоке кода ресурса for_each
, он превращается в некое соответствие
ресурсов вместо отдельного ресурса. Чтобы увидеть о чем именно мы говорим, давайте определим некую переменную вывода с названием
all_rg
и какую- то переменную ввода с названием rg_names
со значениями по умолчанию:
output "all_rg" {
value = azurerm_resource_group.example
}
variable "rg_names" {
description = "list of the resource group names"
type = list(string)
default = ["Azure-rg", "AWS-rg", "Google-rg"]
}
Вот что мы можем ожидать после выполнения команды terraform apply
:
$ terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# azurerm_resource_group.example["AWS-rg"] will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "AWS-rg"
}
# azurerm_resource_group.example["Azure-rg"] will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "Azure-rg"
}
# azurerm_resource_group.example["Google-rg"] will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "Google-rg"
}
Plan: 3 to add, 0 to change, 0 to destroy.
Outputs:
all_rg = {
"AWS-rg" = {
"id" = "/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/AWS-rg"
"location" = "westeurope"
"name" = "AWS-rg"
"tags" = {}
}
"Azure-rg" = {
"id" = "/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Azure-rg"
"location" = "westeurope"
"name" = "Azure-rg"
"tags" = {}
}
"Google-rg" = {
"id" = "/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Google-rg"
"location" = "westeurope"
"name" = "Google-rg"
"tags" = {}
}
}
Как вы можете видеть, Terraform создаёт в Azure три группы ресурсов, а наша переменная вывода all_rg
выдаёт некое соответствие, в котором значение ключа представляет само название группы ресурсов. Давайте представим, что вы ищете значение
идентификатора с конкретным названием all_id
во всех соответствующих группах ресурсов. Тогда, вам
потребуется сделать нечто дополнительное; то есть, вам потребуется определить values
, которая является
встроенной функцией Terraform, для выделения точных значений. Код вывода Terraform может быть определён следующим образом:
output "all_id" {
value = values(azurerm_resource_group.example)[*].id
}
Теперь, после того как вы снова исполните команду terraform apply
, вы можете ожидать такой вывод:
$ terraform apply
(...)
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
all_id = [
"/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/AWS-rg",
"/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Azure-rg",
"/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Google-rg",
]
Самый лучший момент в выражении for_each
заключается в том, что вы получаете некое соответствие имеющихся
ресурсов вместо списка ресурсов, который был получен при помощи count
. Давайте попробуем удалить то же
самое название средней группы ресурсов, то есть AWS_rg
, из своей входной переменной и затем попробуем
выполнить свою команду terraform plan
. Вы можете ожидать следующий фрагмент кода:
$ terraform plan
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# azurerm_resource_group.example["AWS-rg"] will be destroyed
- resource "azurerm_resource_group" "example" {
- id = "/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/AWS-rg" -> null
- location = "westeurope" -> null
- name = "AWS-rg" -> null
- tags = {} -> null
}
Plan: 0 to add, 0 to change, 1 to destroy.
Changes to Outputs:
~ all_id = [
- "/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/AWS-rg",
"/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Azure-rg",
"/subscriptions/97c3799f-2753-40b7-a4bd-157ab464d8fe/resourceGroups/Google-rg",
]
Как вы можете видеть из terraform plan
, удаляется только само название группы ресурсов
AWS_rg
и не затрагиваются прочие группы ресурсов. Это означает, что ту проблему, которую мы имели
в выражении count
, можно обойти при помощи выражения for_each
.
Давайте попробуем разобраться как мы можем применять выражение for_each
для своего встроенного
блока внутри его блока кода ресрса. Динамическим образом мы попытаемся получать множество подсетей внутри своей виртуальной сетевой среды
в Azure. Наш приводимый ниже блок кода снабдит вас внутренним пониманием того, как вы можете определять множество подсетей внутри виртуальной
сети при помощи выражения for_each
:
resource "azurerm_resource_group" "example" {
name = "Terraform-rg"
location = "West Europe"
}
resource "azurerm_virtual_network" "vnet" {
name = var.vnet_name
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
address_space = var.address_space
dynamic "subnet" {
for_each = var.subnet_names
content {
name = subnet.value.name
address_prefix = subnet.value.address_prefix
}
}
}
variable "subnet_names" {
default = {
subnet1 = {
name = "subnet1"
address_prefix = "10.0.1.0/24"
}
subnet2 = {
name = "subnet2"
address_prefix = "10.0.2.0/24"
}
}
}
variable "vnet_name" {
default = "terraform-vnet"
}
variable "address_space" {
default = ["10.0.0.0/16"]
}
Если вы запустите команду terraform apply
, вы обнаружите, что она создаст некую новую виртуальную сеть с
двумя подсетями. Аналогично, когда вы хотите обладать множеством подсетей внутри ресурсов виртуальной сети, вы можете применять динамическое
выражение for_each
:
$ terraform apply
(...)
# azurerm_virtual_network.vnet will be created
+ resource "azurerm_virtual_network" "vnet" {
+ address_space = [
+ "10.0.0.0/16",
]
+ guid = (known after apply)
+ id = (known after apply)
+ location = "westeurope"
+ name = "terraform-vnet"
+ resource_group_name = "Terraform-rg"
+ subnet = [
+ {
+ address_prefix = "10.0.1.0/24"
+ id = (known after apply)
+ name = "subnet1"
+ security_group = ""
},
+ {
+ address_prefix = "10.0.2.0/24"
+ id = (known after apply)
+ name = "subnet2"
+ security_group = ""
},
]
}
В своём предыдущем блоке кода мы показали как вы можете применять динамическое выражение блока, которое применяется для производства одного
или большего числа встраиваемых блоков. Устанавливаемое название метки подсети позволяет Terraform знать что это будет некий поднабор
встраиваемых блоков каждого типа подсети. Внутри такого блока нам приходится предоставлять некие сведения для их применения под создание
соответствующих встраиваемых блоков. Мы сохранили конфигурацию своей подсети в переменной с названием subnet_names
.
Итак, мы рассмотрели цикл for_each
. Давайте теперь разберёмся с выражением
for
.
Мы обсудили выражения цикла count
и for_each
в Terraform.
Теперь давайте попробуем разобраться с выражением for
. Вы можете применять выражение
for
как для list
, так и для
map
. Для выражения for
в блоке кода определён следующий
синтаксис:
[for <ITEM> in <LIST> : <OUTPUT>]
[for <KEY>, <VALUE> in <MAP> : <OUTPUT>]
Здесь ITEM
это название локальной переменной для каждого элемента в
LIST
и LIST
это перечень по которому вы будете выполнять
итерации. OUTPUT
это преобразованный ITEM
.
Аналогично, для соответствия, KEY
и VALUE
это названия
локальных переменных, назначаемых каждой паре в MAP
. MAP
это соответствие, по которому вы будете выполнять итерации, а OUTPUT
это преобразуемое из
KEY
и VALUE
выражение.
Наш следующий блок кода поможет вам с представлением того, как вы можете определять в неком списке выражение
for
:
variable "cloud" {
description = "A list of cloud"
type = list(string)
default = ["azure", "aws", "gcp"]
}
output "cloud_names" {
value = [for cloud_name in var.cloud : upper(cloud_name)]
}
Когда вы выполните команду terraform apply
, вы можете ожидать следующее:
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Changes to Outputs:
+ cloud_names = [
+ "AZURE",
+ "AWS",
+ "GCP",
]
Давайте рассмотрим как определять выражение for
в блоке кода для некого соответствия. В таком кодовом блоке
нам придётся определить некую переменную с названием cloud_map
и предоставить некие величины по
умолчанию ключа и значения:
variable "cloud_map" {
description = "map"
type = map(string)
default = {
Azure = "Microsoft"
AWS = "Amazon"
GCP = "Google"
}
}
output "cloud_mapping" {
value = [for cloud_name, company in var.cloud_map : "${cloud_name} cloud is founded by ${company}"]
}
Когда вы исполните команду terraform apply
, вы можете ожидать следующее:
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
cloud_mapping = [
"AWS cloud is founded by Amazon",
"Azure cloud is founded by Microsoft",
"GCP cloud is founded by Google",
]
В данном разделе мы обсудили различные доступные в Terraform циклы, такие как count
,
for_each
и for
. Теперь вы будете понимать как выполнять итерации
в коде конфигурации Terraform. В своём следующем разделе мы обсудим различные доступные в Terraform встроенные функции, которые вы можете
применять для преобразования своих требований в код конфигурации в тот момент когда это требуется.
Имеется большое число встроенных функций Terraform,которые вы можете применять для преобразования данных в тот формат, который вы можете искать для употребления в своём соответствующем коде конфигурации. Зачастую может оказаться так, что тот формат, который данных, которые возвращаются вашим источником данных и ресурсом не совместим с тем форматом данных, который вам требуется передавать в прочие ресурсы. Terraform предоставляет встроенные функции в следующих категориях:
-
Числа
-
Строки
-
Коллекции
-
Кодированные
-
Файловая система
-
Дата и время
-
Хэш и шифр
-
Сеть IP
-
Преобразование типа
Нет возможности обсуждать здесь все встроенные функции Terraform. Если вам интересно узать обо всех имеющихся функциях Terraform, вы можете обратиться к https://www.terraform.io/docs/configuration/functions.html. Наиболее важно для вас иметь представление о том, как применять функции и как проверять имеющиеся функции Terraform при помощи консоли Terraform.
Если вы уже знакомы с каким- нибудь языком программирования, то определение функции Terraform работает точно так же. Базовый формат для
написания функции это function(arguments,...)
.
Давайте рассмотрим несколько синтаксисов функций Terraform:
-
max(number1, number2, number3,…)
: Для определения наивысшего числа из всех представленных чисел -
lower("text")
: Преобразовать строку к нижнему регистру
Всякая функция Terraform получает определённый тип аргументов и возвращает некоторое значение конкретного типа. Вы даже можете комбинировать множество функций
воедино для получения желаемого типа данных, например, abs(max(-1,-10,-5))
. Здесь, в данном примере,
функция max
вернёт наивысшее значение из заданных чисел, а затем функция abs
снабдит вас абсолютным значением этого вывода.
Применяя консоль Terraform вы можете проверять все имеющиеся функции Terraform. Вы можете просто открыть любой CLI, такой как терминал CMD
Windows и затем набрать terraform console
. Помните об одном консоль Terraform никогда не вносит
изменения в имеющийся файл состояния. Если вы открыли терминал в своём текущем каталоге, в котором у вас имеется файл конфигурации Terraform,
тогда будет загружено текущее состояние с тем, чтобы вы получили доступ к имеющимся данным состояния и вы могли проверять соответствующим
образом функции.
Давайте разберёмся с этим на неком образце:
variable "address_space" {
type = list(string)
default = ["10.0.0.0/16"]
}
resource "azurerm_resource_group" "azure-rg" {
name = "Terraform-rg"
location = "eastus"
}
resource "azurerm_virtual_network" "vnet" {
name = "prod-vnet"
location = azurerm_resource_group.azure-rg.location
resource_group_name = azurerm_resource_group.azure-rg.name
address_space = var.address_space
subnet {
name = "subnet1"
address_prefix = cidrsubnet(var.address_space[0], 8, 1)
}
}
В определённом выше коде мы задали некое значение пространства виртуальных сетевых адресов при помощи переменной с названием
address_space
, а затем для вычисления своего значения подсети
address_prefix
, мы воспользовались соответствующей функцией Terraform
cidrsubnet
.
Давайте рассмотрим как мы можем проверять значение address_prefix
при помощи консоли Terraform.
Для выполнения такой проверки вам просто требуется открыть любой CLI и исполнить команду terraform console
,
которая загрузит имеющийся файл конфигурации и снабдит вас таким выводом:
$ terraform console
> var.address_space[0]
10.0.0.0/16
> cidrsubnet(var.address_space[0],8,1)
10.0.1.0/24
Как вы можете видеть, консоль Terraform помогает нам проверять встроенные функции Terraform. Итак, всякий раз когда вы планируете употребить какую бы то ни было встроенную функцию Terraform, вы можете проверить её и определить её подобающим образом в своём блоке кода конфигурации.
В данном разделе мы изучили встроенные функции Terraform и то, как мы можем действенно применять функции Terraform, а также мы даже получили знание о том, как мы способны проверять некую функцию Terraform при помощи консоли Terraform. Далее мы обсудим вариант отладки Terraform и получим представление о том, как применять имеющиеся варианты отладки Terraform.
Terraform предоставляет множество вариантов для отладки. Вы можете получат подробные журналы Terraform включая переменную среды с
названием TF_LOG
. TF_LOG
поддерживает любой из следующих
уровней регистрации: TRACE
, DEBUG
,
INFO
, WARN
или ERROR
.
По умолчанию включён уровень регистрации TRACE
, который рекомендуется Terraform по той причине, что он
предоставляет наиболее подробные журналы.
Если вы желаете сохранять эти регистрации в неком определённом месте, вы можете определить TF_LOG_PATH
в соответствующей переменной среды Terraform и указать ей соответствующее местоположение, в котором вы желаете сохранять свой файл журнала. Просто
помните, что для включения своего журнала вам потребуется воспользоваться TF_LOG
с одним из описанных
выше уровней регистрации, например, с TRACE
или DEBUG
. Для установки
переменной среды TF_LOG
вы можете воспользоваться следующим:
$ export TF_LOG=TRACE
Аналогично, если вы хотите установить TF_LOG_PATH
, тогда вы можете сделать это следующим образом:
$ export TF_LOG_PATH=./terraform.log
Допустим, при исполнении своего кода конфигурации Terraform вы обнаруживаете, что Terraform внезапно испытывает крушение. Тогда он
сохранит некий файл журнала с названием crash.log
, который будет обладать всеми регистрациями отладки
данного сеанса, а также тревожным сообщением и отслеживанием. Эти файлы журналов не предназначены для нас, как для конечных пользователей
Terraform. Вам требуется передать эти журналы самому разработчику через страницу проблем GitHub (https://github.com/hashicorp/terraform/issues). Однако, если вам интересно
обнаружить что не так с Terraform, тогда вы можете проверить свои тревожное сообщение и отслеживание, которые будут содержать сведения, относящиеся
к возникшей проблеме. Вы обнаружите нечто подобное следующему:
panic: runtime error: invalid memory address or nil pointer dereference
goroutine 123 [running]:
panic(0xabc100, 0xd93000a0a0)
/opt/go/src/runtime/panic.go:464 +0x3e6
github.com/hashicorp/terraform/builtin/providers/aws.resourceAwsSomeResourceCreate(...) /opt/gopath/src/github.com/hashicorp/terraform/builtin/providers/aws/resource_aws_some_resource.go:123 +0x123
github.com/hashicorp/terraform/helper/schema.(*Resource).Refresh(...) /opt/gopath/src/github.com/hashicorp/terraform/helper/schema/resource.go:209 +0x123
github.com/hashicorp/terraform/helper/schema.(*Provider).Refresh(...) /opt/gopath/src/github.com/hashicorp/terraform/helper/schema/provider.go:187 +0x123
github.com/hashicorp/terraform/rpc.(*ResourceProviderServer).Refresh(...)
/opt/gopath/src/github.com/hashicorp/terraform/rpc/resource_provider.go:345 +0x6a
reflect.Value.call(...)
/opt/go/src/reflect/value.go:435 +0x120d
reflect.Value.Call(...)
/opt/go/src/reflect/value.go:303 +0xb1
net/rpc.(*service).call(...)
/opt/go/src/net/rpc/server.go:383 +0x1c2
created by net/rpc.(*Server).ServeCodec
/opt/go/src/net/rpc/server.go:477 +0x49d
Самые первые две строки содержат ключевые сведения относительно hashicorp/terraform
. Рассмотрим
следующий пример:
github.com/hashicorp/terraform/builtin/providers/aws.resourceAwsSomeResourceCreate(...)
/opt/gopath/src/github.com/hashicorp/terraform/builtin/providers/aws/resource_aws_some_resource.go:123 +0x123
Самая первая строка сообщает нам в каком методе произошёл отказ, коим в данном примере выступает
resourceAwsSomeResourceCreate
. Что это, мы можем догадаться, что нечто пошло не так в процессе создания
ресурса AWS.
Наша вторая строка сообщает вам точный номер строки кода, которая вызвала эту тревогу. Данного тревожного сообщения достаточно для разработчика чтобы быстро выявить причину данной проблемы.
Вам, как пользователю, эти сведения помогут выполнять некий уровень устранения неисправностей всякий раз, когда вы наблюдает крушение Terraform.
![]() | Замечание |
---|---|
При выгрузке журналов Terraform с любой проблемой в GitHub (https://github.com/hashicorp/terraform/issues), убедитесь что они не содержать никаких конфиденциальных сведений или секретов. |
В этом разделе мы изучили журналы Terraform, которые вы можете собирать устанавливая различные уровни TF_LOG
и что вы можете сохранять эти файлы журналов определяя TF_LOG_PATH
в переменных среды Terraform.
Мы также обсудили файл crash.log
, который вы можете отправлять разработчикам при помощи проблем
GitHub с тем, чтобы они имели возможность помочь вам найти выход.
В этой главе мы изучили конфигурацию серверной основы Terraform, которая применяется для хранения файла состояния Terraform, оснастки,
которые в основном применяются для запуска некого вида сценария, а также встроенные функции, которые используются для преобразования данных
в желательный вид с тем, чтобы их можно было бы применять в прочих блоках кода конфигурации. Продвигаясь по тексту, мы изучили как можно
осуществлять различные виды итераций при помощи for
и прочих циклов Terraform, которые являются
полезными когда вы ищите снабжение множества ресурсов за раз. Затем, наконец, мы обсудили отладку Terraform, где вы можете установить
переменную среды Terraform TF_LOG
на некоторый уровень регистрации и сохранять их в предоставляемом
получателе в соответствии с определением такой переменной среды Terraform, как
TF_LOG_PATH
.
В своей следующей главе мы обсудим аутентификацию Terraform для Azure, AWS и GCP. По ходу действия мы также обсудим применение CLI AWS, где мы увидим как мы можем запускать различные команды рабочего процесса Terraform и соответствующий вывод из них.
Ответы на данные вопросы можно обнаружить в разделе Аттестация в самом конце этой книги:
-
Какие из следующих переменных среды необходимо установить для получения подробных журналов Terraform?
-
TF_LOG
-
TF_TRACE
-
TF_DEBUG
-
TF_INFO
-
-
Каков результат следующей функции Terraform?
max(2, 5, 10, -10)
-
2
-
5
-
10
-
-10
-
-
"Серверная основа" Terraform используется для удержания файла состояния Terraform и в помощь операциям Terraform. Что из приводимого ниже не является поддерживаемым типом серверной основы:
-
S3
-
Artifactory
-
azurerm
-
GitHub
-
-
Вам требуется выполнить некий сценарий PowerShell в той виртуальной машине, которую вы планируете создавать при помощи кода конфигурации Terraform. Какими бы из следующих свойств вы бы воспользовались?
-
Ресурсы Terraform
-
Источник данных Terraform
-
Оснастка Terraform (
alocal-exec
илиremote-exec
) -
Функция Terraform
-
-
Вас попросили создать множество подсетей с неким особенным атрибутом внутри некой виртуальной сети Azure. Какой из следующих методов итераций способен вам достичь такой цели?
-
Выражение
count
-
Выражение
for
-
Выражение
if
-
Динамическое выражение
-
Для получения дополнительных сведений по рассматривавшимся в этой главе темам вы можете обратиться к следующим ссылкам: