# مرحباً

الهدف من هذا التوثيق هو توصيف  أفضل الممارسات لاستعمال أداة Terraform وتوفير توصيات للتعامل مع المشكلات الأكثر شيوعاً التي يواجهها مستخدمو هذه الأداة.

يعد الـ [Terraform](https://www.terraform.io/)مشروع جديد نسبياً (مثل معظم أدوات الـ DevOps )، صدر في عام 2014.

تعد أداة Terraform قوية (إن لم تكن الأقوى الآن) وواحدة من أكثر الأدوات استعمالاً لتوصيف البنى التحتية المعلوماتية باستعمال الكود (Infrastructure as code). تسمح هذه الأداة للمطورين بالقيام بالكثير من الوظائف ولا تمنعهم من فعل الأشياء بطرق يصعب دعمها أو التواصل معها.

بعض المعلومات في هذا الكتاب قد تبدو وكأنها لا تصنف كأفضل الممارسات. أعلم هذا. ولمساعدة القراء على التفريق بين ما يعد أفضل ممارسة أو ما هو مجرد طريقة أخرى لفعل الأشياء، فإني سأقوم باستعمال التلميحات (hints) في بعض الأحيان وتوفير الأيقونات لتحديد مستوى النضج في كل قسم فرعي يتعلق بأفضل الممارسات.

تم البدء بكتابة هذا الكتاب في مدريد في سنة 2018 وهو متوفر مجاناً على الرابط -

<https://www.terraform-best-practices.com/>

تم التعديل على هذا الكتاب بعد عدة سنين لإضافة أفضل الممارسات المتوفرة لإصدار Terraform 1.0 ، هذا الكتاب يجب أن يحتوي أفضل الممارسات والتوصيات الغير القابلة للجدل ليتم استعمالها من قبل مستخدمي الـ Terraform

## الرعاة

Please [contact me](https://github.com/antonbabenko/terraform-aws-devops#social-links) if you want to become a sponsor.

| [![](/files/ciLnpxYxSerNRVCRhtz6)](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) | [Compliance.tf](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) — Terraform Compliance Simplified. Make your Terraform modules compliance-ready. |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

## الترجمات

{% content-ref url="/spaces/PJbgKPAX0ohEMLpETpg7" %}
[Bosanski (Bosnian)](https://www.terraform-best-practices.com/ba/)
{% endcontent-ref %}

{% content-ref url="/spaces/B48qUSNPO2XmkIySLzfr" %}
[Português (Brazilian Portuguese)](https://www.terraform-best-practices.com/ptbr/)
{% endcontent-ref %}

{% content-ref url="/spaces/e1Mp2scOX6OnQbifCen3" %}
[English](https://www.terraform-best-practices.com/)
{% endcontent-ref %}

{% content-ref url="/spaces/6shyPtr2KrqW4ANbFXYg" %}
[Français (French)](https://www.terraform-best-practices.com/fr/)
{% endcontent-ref %}

{% content-ref url="/spaces/DyguS0uZfMW7X7m9BWx1" %}
[ქართული (Georgian)](https://www.terraform-best-practices.com/ka/)
{% endcontent-ref %}

{% content-ref url="/spaces/PKopCWJZbhpQ9FT0W8tL" %}
[Deutsch (German)](https://www.terraform-best-practices.com/de/)
{% endcontent-ref %}

{% content-ref url="/spaces/5c1kFpqxaDZC2g9e6rtT" %}
[ελληνικά (Greek)](https://www.terraform-best-practices.com/el/)
{% endcontent-ref %}

{% content-ref url="/spaces/4bq6CyY8vYiEHkjN63rT" %}
[עברית (Hebrew)](https://www.terraform-best-practices.com/he/)
{% endcontent-ref %}

{% content-ref url="/spaces/Mgong4S6IjtibE055zUM" %}
[हिंदी (Hindi)](https://www.terraform-best-practices.com/hi/)
{% endcontent-ref %}

{% content-ref url="/spaces/ZLCz7lNWbSJxDGuNOI44" %}
[Bahasa Indonesia (Indonesian)](https://www.terraform-best-practices.com/id/)
{% endcontent-ref %}

{% content-ref url="/spaces/8VlMHbHDbW6qRWdgN5oU" %}
[Italiano (Italian)](https://www.terraform-best-practices.com/it/)
{% endcontent-ref %}

{% content-ref url="/spaces/3vykLOWgdQLPLgHtxqQH" %}
[日本語 (Japanese)](https://www.terraform-best-practices.com/ja/)
{% endcontent-ref %}

{% content-ref url="/spaces/BoZVs6O2OJFQLNV1utmm" %}
[ಕನ್ನಡ (Kannada)](https://www.terraform-best-practices.com/kn/)
{% endcontent-ref %}

{% content-ref url="/spaces/bJnDvAqIyVgo7LDHgxYJ" %}
[한국어 (Korean)](https://www.terraform-best-practices.com/ko/)
{% endcontent-ref %}

{% content-ref url="/spaces/9yChMGbFo2G47Wiow1yY" %}
[Polski (Polish)](https://www.terraform-best-practices.com/pl/)
{% endcontent-ref %}

{% content-ref url="/spaces/sFM1GW5TPCGsskQ03mTm" %}
[Română (Romanian)](https://www.terraform-best-practices.com/ro/)
{% endcontent-ref %}

{% content-ref url="/spaces/5VD4NK4mHOY8SWjC9N5e" %}
[简体中文 (Simplified Chinese)](https://www.terraform-best-practices.com/zh/)
{% endcontent-ref %}

{% content-ref url="/spaces/fTxekzr50pIuGmrPkXUD" %}
[Español (Spanish)](https://www.terraform-best-practices.com/es/)
{% endcontent-ref %}

{% content-ref url="/spaces/Fedpbc5NbKjynXI8xTeF" %}
[Türkçe (Turkish)](https://www.terraform-best-practices.com/tr/)
{% endcontent-ref %}

{% content-ref url="/spaces/tXRvMPILxeJaJTM2CsSq" %}
[Українська (Ukrainian)](https://www.terraform-best-practices.com/uk/)
{% endcontent-ref %}

{% content-ref url="/spaces/dcjhau04KQIKHUJA90iN" %}
[اردو (Urdu)](https://www.terraform-best-practices.com/ur/)
{% endcontent-ref %}

اتصل بي إذا كنت تود المساعدة بالترجمة إلى لغات أخرى

## المساهمات

أرغب دائمًا في الحصول على تعليقات وتحديثات لهذا الكتاب بينما ينضج المجتمع ويتم تنفيذ الأفكار الجديدة والتحقق منها بمرور الوقت.

إذا كنت مهتمًا بموضوعات معينة فالرجاء طرح مشكلتك [open an issue](https://github.com/antonbabenko/terraform-best-practices/issues) أو إبداء الإعجاب بالمشكلة التي تريد تغطيتها أكثر من غيرها. إذا كنت تشعر أن لديك محتوى وتريد المساهمة، فاكتب مسودة وأرسل Pull request (لا تقلق بشأن كتابة نص جيد في هذه المرحلة!)

## المؤلفون

هذا الكتاب من إعداد [Anton Babenko](https://github.com/antonbabenko) بمساعدة مساهمين ومترجمين مختلفين.

## الترخيص

هذا العمل مرخص بموجب ترخيص Apache 2. انظر LICENSE للحصول على التفاصيل الكاملة.

لا يمكن للمؤلفين والمساهمين في هذا المحتوى ضمان صحة المعلومات الموجودة هنا. يرجى التأكد من أنك تفهم أن المعلومات المقدمة هنا يتم توفيرها بحرية ، وأنه لا يوجد أي نوع من الاتفاقية أو العقد بينك وبين أي أشخاص مرتبطين بهذا المحتوى أو المشروع. لا يتحمل المؤلفون والمساهمون ، وبموجب هذا ، أي مسؤولية تجاه أي طرف عن أي خسارة أو ضرر أو اضطراب ناتج عن أخطاء أو سهو في المعلومات الواردة في هذا المحتوى أو المرتبطة به أو المرتبطة به ، سواء كانت هذه الأخطاء أو الإغفالات ناتجة عن إهمال أو حادث أو أي سبب آخر.

حقوق النشر © 2018-2023 Anton Babenko.


# المفاهيم الأساسية

تصف [وثائق Terraform الرسمية](https://developer.hashicorp.com/terraform/language) جميع مصطلحات تعريف البنى التحتية بالتفصيل. اقرأها بعناية لفهم بقية هذا القسم.

يشرح هذا القسم المفاهيم الأساسية المستخدمة في الكتاب

## المورد (Resource)

المورد هو aws\_vpc، aws\_db\_instance الخ...، ينتمي المورد إلى موفر معين (provider) ويقبل الوسيطات (arguments) ويولد المخرجات (outputs) وله دورات حياة (lifecycles). يمكن إنشاء مورد واسترجاع معلوماته وتحديثه وحذفه.&#x20;

## وحدة الموارد (Resource module)

وحدة الموارد هي مجموعة من الموارد المرتبطة ببعضها والتي تقوم مع بعضها بتنفيذ وظيفة معينة (لنأخذ [AWS VPC Terraform module](https://github.com/terraform-aws-modules/terraform-aws-vpc/) كمثال، تقوم هذه الوحدة ببناء VPC، subnets, NAT gateway والعديد من الموارد التي نستعملها للتشبيك)، تعتمد وحدة الموارد على الموفر، ويتم تعريفها أما في الموفر أو في الهياكل عالية المستوى (على سبيل المثال ، في وحدة البنية التحتية). &#x20;

## وحدة البنية التحتية (Infrastructure module)

وحدة البنية التحتية هي مجموعة وحدات موارد والتي قد لا تتربط منطقياً ولكنها توجد في مشروع أو نظام يخدم نفس الهدف. تعرف هذه الوحدة الـتهيئة (configuration)  للموفرين (providers)، والتي تقوم بتمريرها إلى وحدات الموارد والموارد المؤلفة لهذه الوحدة.  عادةً ما يقتصر العمل في وحدة بنية تحتية واحدة لكل كيان منفصل (على سبيل المثال ، AWS Region ، Google Project).&#x20;

كمثال فإن وحدة البنية التحتية [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis/) تستعمل وحدات الموارد مثل [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc/) و [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group/) لإدارة البنية التحتية اللازمة لتشغيل [Atlantis](https://www.runatlantis.io) on [AWS Fargate](https://aws.amazon.com/fargate/).&#x20;

كمثال أخر فإن وحدة البنية التحتية [terraform-aws-cloudquery](https://github.com/cloudquery/terraform-aws-cloudquery) يتم استعمالها لإدارة البنية التحتية كما تقوم باستعمال أداة Docker لإدارة Docker images في وحدة واحدة.

## التركيب (Composition)

التركيب هو مجموعة من وحدات البنية التحتية والتي يمكن أن تمدد على العديد من المناطق المنفصلة (AWS Regions, AWS Accounts) . يتم استعمال التركيب لوصف كامل البنية التحتية لمؤسسة أو مشروع. &#x20;

كما يبين الشكل يتألف تركيب البنية التحتية (infrastructure composition) من مجموعة وحدات بنية تحتية (infrastructure module) والتي تتكون من وحدات موارد (resources module) والتي تنجز عدة موارد محددة&#x20;

![Simple infrastructure composition](/files/gRXjhRxnTLLcdax83v0g)

## مصدر البيانات (Data source)

يوفر مصدر البيانات مورداً قابلاً للقراءة فقط (read-only) ويعتمد على نمط الموفر (provider) الذي نتعامل معه، يتم استعماله من قبل كل من وحدة البنية التحتية ووحدة الموارد.&#x20;

يعتبر مصدر البيانات `terraform_remote_state`أداة لربط الوحدات عالية المستوى والتراكيب المختلفة مع بعضها

يسمح مصدر البيانات [external](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/data_source) بالبرامج الخارجية بالعمل كمصدر للبيانات ليتم التعامل معها في مكان أخر من ملفات التهئية الخاصة بأداة Terraform، يمكن أن نجد مثالاً في [terraform-aws-lambda module](https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/258e82b50adc451f51544a2b57fd1f6f8f4a61e4/package.tf#L5-L7) حيث يتم الحصول على اسم الملف من خلال استدعاء كود Python خارجي

يحصل مصدر البيانات [http](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) على معلوماته من خلال إرسال طلب HTTP GET لرابط معين ويصدر الجواب الذي حصلنا عليه ويعتبر هذه المصدر مفيداً في حال عدم وجود موفر Terraform.

## ملف الحالة المخرن عن بعد (Remote state)

يجب تخزين [Terraform state](https://www.terraform.io/docs/language/state/index.html) لكل من وحدات البنية التحتية والتراكيب عن بعد حيث يمكن استرجاعه من قبل كل الأشخاص العاملين عليه ويجب أن تتم إدارته بشكل يضمن السرية والتنظيم (e.g. specify ACL, versioning, logging). &#x20;

## الموفر (Provider, provisioner)

&#x20;يوجد توصيف مجزي لمفهوم الموفر في وثائق Terraform الرسمية، ولا يوجد لازمة لتكرارها هنا، وبرأي أن ليس لها علاقة بكتابة وحدات Terraform جيدة.

## أين الصعوبة؟

بينما يمكننا اعتبار أن الموارد هي عبارة عن ذرات فإن "وحدات الموارد" تعتبر الجزئيات. "وحدة الموارد" هي أصغر وحدة يمكننا مشاركتها وإنشاء إصدرات منها، لديها قائمة محددة من الوسيطات، وتنجز وظيفة معينة. إذا أخذنا الوحدة  [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group) كمثال فإننا سنجد أنها تقوم بإنشاء الموارد `aws_security_group` و `aws_security_group_rule` بالاعتماد على الدخل، يمكن استعمال هذه الوحدة مع وحدات أخرى لإنشاء وحدة بنية تحتية.

حتى نتمكن من الوصول إلى بيانات فإننا نستعمل مخرجات الوحدات (outputs) بالإضافة إلى مصادر البيانات (data sources)&#x20;

حتى نتمكن من تبادل البيانات من تركيب إلى تركيب أخر يجب علينا استعمال مصدر البيانات `terraform_remote_state،`([كما يوجد العديد من الطرق لتبادل البيانات](https://developer.hashicorp.com/terraform/language/state/remote-state-data#alternative-ways-to-share-data-between-configurations))

وأخيراً يمكننا تمثيل المفاهيم المشروحة سابقاً بهذا الشكل

```
composition-1 {
  infrastructure-module-1 {
    data-source-1 => d1

    resource-module-1 {
      data-source-2 => d2
      resource-1 (d1, d2)
      resource-2 (d2)
    }

    resource-module-2 {
      data-source-3 => d3
      resource-3 (d1, d3)
      resource-4 (d3)
    }
  }

}
```


# بنية الكود

الأسئلة المتعلقة ببنية كود Terraform هي الأكثر شيوعًا في المجتمع. في الكثير من الأحيان فكر الجميع في أفضل بنية كود ممكن الحصول عليها  للمشروع.

## كيف يجب هيكلة مشروع Terraform؟

هذا نوع من الأسئلة التي يمكن الإجابة عليها بالعديد من الطرق ومن الصعب جداً تقديم نصيحة تنفع لجميع الحالات، لذلك لنبدأ بفهم ما نتعامل معه.

* ما هو مدى تعقيد مشروعك؟
  * عدد الموارد المرتبطة ببعضها
  * عدد الموفرين (انظر الملاحظة أدناه عن "logical providers")
* &#x20;كم مرة تحتاج إلى تغيير في البنية التحتية؟
  * **من** مرة في الشهر/الأسبوع/اليوم؟
  * **إلى** تغيير مستمر (عند كل commit جديدة)
* مصادر تغيير الكود، هل تسمح لمخدم CI بتعديل repository عند بناء artifact جديدة؟
  * يسمح فقط للمطورين بإضافة تغييرات إلى repository الخاصة بالبنية التحتية
  * يسمح لأي أحد باقتراح تغيير من خلال فتح PR (متضمناً المهام الأتوماتيكية التي يقوم بها مخدم CI)
* ما هي المنصة أو الخدمة التي تستعملها لعملية deployment؟
  * &#x20;AWS CodeDeploy, Kubernetes, OpenShift (يحتاج كل حل إلى معاملة مختلفة)
* كيف يتم فرز البيئات المختلفة؟
  * من خلال بيئة العمل(environment)، المنطقة (region)، المشروع (project)

{% hint style="info" %}
*Logical providers*

هي الموفرات التي تعمل كلياً ضمن Terraform ولا تتعامل غالباً مع أي خدمة خارجية، لذا يمكن أن نعتبرها أدوات بتعقيد O(1). أكثر هذه الموفرات شهرة هي [random](https://registry.terraform.io/providers/hashicorp/random/latest/docs), [local](https://registry.terraform.io/providers/hashicorp/local/latest/docs), [terraform](https://www.terraform.io/docs/providers/terraform/index.html), [null](https://registry.terraform.io/providers/hashicorp/null/latest/docs), [time](https://registry.terraform.io/providers/hashicorp/time/latest).
{% endhint %}

## مقدمة إلى هيكلة مشروع Terraform

عند بداية التعامل مع Terraform أو عندما تريد أن تكتب مثالاً فإن وضع كل الكود في ملف `main.tf` تعتبر فكرة جيدة، ولكن في كل الحالات الأخرى يجب تقسيم الكود منطقياً إلى ملفات مختلفة كالتالي:

* ملف `main.tf`, والذي يستدعي الوحدات المختلفة (modules), والوسطاء المحلية (locals)، ومصادر البيانات (data) لتوليد كافة الموارد (resources)
* &#x20;ملف `variables.tf`، الذي يحتوي على تعريف للوسطاء (variables) المراد استعمالها في`main.tf`&#x20;
* ملف`outputs.tf`، الذي يعرف خرج الموارد المولدة من قبل`main.tf`&#x20;
* ملف`versions.tf` ، الذي يحتوي متطلبات الإصدار الخاصة بـ Terraform و الموفرين

لا يجب استعمال `terraform.tfvars`في أي مكان ما عدا [composition](https://antonbabenko.gitbook.io/ar/key-concepts#altrkyb-composition).

## كيف يجب أن نفكر عند هيكلة مشروع Terraform

{% hint style="info" %}
الرجاء التأكد من فهم المفاهيم الرئيسية [resource module](/ar/key-concepts#resource-module), [infrastructure module](/ar/key-concepts#infrastructure-module), [composition](/ar/key-concepts#composition) لأهميتها لفهم الأمثلة التالية &#x20;
{% endhint %}

### التوصيات العامة لهيكلة الكود&#x20;

* إنه من الأسهل والأسرع التعامل مع عدد قليل من الموارد
  * إن كل من تعليمتي `terraform plan`و`terraform apply` تقوم باستدعاء cloud API للتحقق من حالة الموارد
  * إذا كانت كل بنيتك التحتية في تركيب واحد ستأخذ هذه التعليمات وقتاً طويلاً للتنفيذ
* مدى الضرر يكون أصغر في حال تعريف موارد قليلة
  * عزل الموارد غير المرتبطة ببعضها في تراكيب مختلفة يقلل الخطر في حال حدوث مشكلة ما
* استعمل ملف الحالة المخرن عن بعد
  * حاسبك الشخصي لا يمثل عكس لحقيقة البنية التحتية
  * إدارة tfstate في git أمر كارثي
  * لاحقاً عند توسع البنية في اتجاهات عدة (عدد الموارد وعدد dependencies)، سيكون استعمال هذا الملف أسهل للتحكم بهذه البنية
* &#x20;تمرن على بنية وطرق تسمية متسقة
  * مثل الكود العادي، فإن كود Terraform يجب أن يكون مقروءاً، الاتساق سيساعد عندما تريد التحقق من تعديلات تمت إضافتها من 6 أشهر ماضية
  * من الممكن أن تنقل موارد لم يتم تعريفها سابقاً إلى ملف Terraform State، لكن وجود كود غير متسق سيصعب الأمر &#x20;
* قم ببناء وحدة (modules) بسيطة قدر الإمكان
* لا تقم باستعمال قيم ثابتة (hardcoded) إذا كنت تستطيع تمريرها كمتحول أو الحصول عليها من مصدر بيانات
* استعمل مصادر البيانات و `terraform_remote_state` كغراء بين وحدات البنية التحتية في التركيب

في هذا المشروع قمنا بفرز المشروعات بحسب تعقيدها من البنى الصغيرة إلى الكبيرة جداً، لا يعتبر هذا التجميع ملزماً، لذا ابحث عن طرق أخرى أيضاً.

### تنسيق وحدات وتركيبات البنى التحتية

وجود بنية تحتية صغيرة يعني وجود عدد صغير من الاعتماديات (dependencies) والموارد، كلما كبر المشروع فإن أهمية ربط البنى المختلفة وتمرير المتحولات في التركيب تصبح واضحة اكثر.

يوجد على الأقل 5 أدوات تنسيقات مستقلة التي يمكن للمطور أن يستعملها:

1. يكفي للمطور أن يستعمل Terraform
2. &#x20; استعمال  أداة Terragrunt، وهي أداة orchestration تستعمل لتنسيق كل البنية التحتية والاعتماديات، تعمل هذه الأداة مع مختلف الوحدات (modules) والتراكيب (compositions) لذلك تخفف من تكرار الكود (إعادة اختراع العجلة)
3. استعمال scripts خاصة بالشركة (In-house scripts)، تم استعمالها كبداية لعمليات التنسيق قبل صدور Terragrunt
4. استعمال أداة Ansible أو أي أداة أتمتة (Autoamtion tools) . يتم استعمالها في حال تم تبني Terraform بعد استعمال Ansible، أو في حال استعمال Ansible UI.
5. &#x20;أستعمال أداة [Crossplane](https://crossplane.io/) و الأدوات الملهمة من قبل Kubernetes. من المنطقي أحياناً استعمال Kubernetes لتنسيق Terraform.  لمزيد من المعلومات شاهد هذا الفيديو   [Crossplane vs Terraform](https://www.youtube.com/watch?v=ELhVbSdcqSY)&#x20;

في هذا الكتاب نقوم فقط بعرض الحلين الأولين (Terraform only and Terragrunt.)

شاهد أمثلة عن  [Terraform](/ar/examples/terraform) أو [Terragrunt](/ar/examples/terragrunt) في الفصل القادم&#x20;


# أمثلة عن بنية الكود

## بنى الكود في Terraform

{% hint style="info" %}
تُظهر هذه الأمثلة استعمال لموفر AWS ولكن يمكن تطبيق غالبية المبادئ الموضحة في الأمثلة على موفري السحابة الآخرين بالإضافة إلى أنواع أخرى من مقدمي الخدمات (DNS ، DB ، المراقبة ، إلخ)
{% endhint %}

<table><thead><tr><th width="231.33333333333331">النمط</th><th width="291">الوصف</th><th>قابلة القراءة من الكتاب</th></tr></thead><tbody><tr><td><a href="/pages/EW1oMujrlc87h5Q3N1RK">صغير</a></td><td>بعض الموارد، لا وجود لاعتماديات خارجية، استعمال حساب AWS واحد، استعمال منطقة وحيدة، استعمال بيئة وحيدة </td><td>نعم</td></tr><tr><td><a href="/pages/LXOFU87XaO9Gyya6zxfZ">متوسط</a></td><td>عدة حسابات AWS وعدة بيئات، استعمال وحدات جاهزة  باستخدام Terraform</td><td>نعم</td></tr><tr><td><a href="/pages/4DxId8ocZTTPvjmgckXI">كبير</a></td><td>العديد من حسابات AWS، العديد من المناطق، حاجة ملحة لتقليل عمليات النسخ واللصق، استعمال وحدات مخصصة، استعمال كبير للتراكيب باستخدام Terraform</td><td>جاري العمل عليه</td></tr><tr><td>كبير جداً</td><td>العديد من الموفرين (AWS, GCP, Azure). استعمال للعديد من الخدمات السحابية في عملية deployment باستخدام Terraform</td><td>لا</td></tr></tbody></table>

## بنى الكود في Terragrunt

<table><thead><tr><th>Type</th><th width="288.3333333333333">Description</th><th>Readiness</th></tr></thead><tbody><tr><td>متوسط</td><td>عدة حسابات AWS وعدة بيئات، استعمال وحدات جاهزة  باستخدام Terragrunt</td><td>لا</td></tr><tr><td>كبير</td><td>العديد من حسابات AWS، العديد من المناطق، حاجة ملحة لتقليل عمليات النسخ واللصق، استعمال وحدات مخصصة، استعمال كبير للتراكيب باستخدام Terragrunt</td><td>لا</td></tr><tr><td>كبير جداً</td><td>العديد من الموفرين (AWS, GCP, Azure). استعمال للعديد من الخدمات السحابية في عملية deployment باستخدام Terragrunt.</td><td>لا</td></tr></tbody></table>


# أداة Terragrunt


# أداة Terraform


# البنى الصغيرة باستعمال Terraform

المصدر:  <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/small-terraform>

يحتوي هذا المثال على كود لهيكلة كود Terraform لبنية تحتية صغيرة، حيث لا وجود لاعتمادات خارجية

{% hint style="success" %}

* ممتاز للبدء بتعلم Terraform وإعادة هيكلة الكود (refactoring)
* ممتاز لبناء الوحدات الصغيرة
* جيد لاستعمال الوحدات الصغيرة (eg, [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis))
* جيد عند وجود عدد صغير من الموارد (أقل من 20-30)
  {% endhint %}

{% hint style="warning" %}
وجود ملف حالة وحيد Single state file من أجل كل الموارد سيجعل أداة Terraform بطيئة كلما زاد عدد الموارد المعرفة (خذ بعين الاعتبار استعمال الوسيط `target-` للحد من الموارد التي تتعامل معها عند طلب الأداة)&#x20;
{% endhint %}


# البنى المتوسطة باستعمال Terraform

المصدر:  <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/medium-terraform>

يحتوي هذا المثال على كود لهيكلة كود Terraform لبنية تحتية متوسطة والتي تستعمل:

* حسابين AWS
* بيئتين مختلفتين (`prod` and `stage` لا وجود لشيء مشترك بينهما). كل بيئة موجودة في حساب AWS مختلف
* كل بيئة تستعمل إصدارات مختلفة للوحدات الجاهزة (alb) مصدرها  [Terraform Registry](https://registry.terraform.io/)
* كل بيئة تستعمل الإصدار نفسه للوحدات الداخلية `modules/network` مصدره المجلد المحلي

{% hint style="success" %}

* ممتاز للمشاريع التي تحتاج إلى فصل منطقي لبيئاتها (باستعمال حسابات AWS مختلفة)
* جيد عندما لا يوجد حاجة لتعديل الموارد المشتركة بين حسابات AWS المختلفة (بيئة واحدة = حساب AWS واحد = ملف حالة وحيد)
* جيد عندما لا يوجد حاجة لتنسيق التعديلات بين البيئات المختلفة&#x20;
* جيد عند الاختلاف المتعمد للموارد بين البيئات والذي لا يمكن تعريف حالة عامة له (كوجود بعض الموارد في بيئة وغيابها في بيئة أخرى)&#x20;
  {% endhint %}

{% hint style="warning" %}
&#x20;مع نمو المشروع ، سيكون من الصعب الحفاظ على تحديث هذه البيئات مع بعضها البعض. خذ بعين الاعتبار استخدام وحدات البنية التحتية (الجاهزة أو الداخلية) للمهام المتكررة.
{% endhint %}

##


# البنى الكبيرة باستعمال Terraform

المصدر:  <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/large-terraform>

يحتوي هذا المثال على كود لهيكلة كود Terraform لبنية تحتية كبيرة والتي تستعمل:

* حسابين AWS
* منطقتين
* بيئتين مختلفتين (`prod` and `stage` لا وجود لشيء مشترك بينهما). كل بيئة موجودة في حساب AWS مختلف وتوزع الموارد على المنطقتين
* كل بيئة تستعمل إصدارات مختلفة للوحدات الجاهزة (alb) مصدرها [Terraform Registry](https://registry.terraform.io/)&#x20;
* كل بيئة تستعمل الإصدار نفسه للوحدات الداخلية `modules/network` مصدره المجلد المحلي

{% hint style="info" %}
في المشاريع الكبيرة مثل المشروع أعلاه تظهر أهمية استعمال أداة Terragrunt. انظر إلى [Code Structures examples with Terragrunt](/ar/examples/terragrunt). &#x20;
{% endhint %}

{% hint style="success" %}

* ممتاز للمشاريع التي تحتاج إلى فصل منطقي لبيئاتها (باستعمال حسابات AWS مختلفة)
* جيد عندما لا يوجد حاجة لتعديل الموارد المشتركة بين حسابات AWS المختلفة (بيئة واحدة = حساب AWS واحد = ملف حالة وحيد)
* جيد عندما لا يوجد حاجة لتنسيق التعديلات بين البيئات المختلفة&#x20;
* جيد عند الاختلاف المتعمد للموارد بين البيئات والذي لا يمكن تعريف حالة عامة له (كوجود بعض الموارد في بيئة وغيابها في بيئة أخرى)&#x20;
  {% endhint %}

{% hint style="warning" %}
&#x20;مع نمو المشروع ، سيكون من الصعب الحفاظ على تحديث هذه البيئات مع بعضها البعض. خذ بعين الاعتبار استخدام وحدات البنية التحتية (الجاهزة أو الداخلية) للمهام المتكررة.
{% endhint %}

##


# قواعد التسمية

## القواعد العامة&#x20;

{% hint style="info" %}
يجب ألا يكون هناك سبب لعدم اتباع هذه القواعد على الأقل :)
{% endhint %}

{% hint style="info" %}
كن حذراً أن الموارد الحقيقية التي يتم تعريفها في Terraform غالباً ما يكون لها قيود على الأسماء المسموح بها. بعض الموارد كمثال لا تقبل الخطوط (dashes)، أو يجب أن يكون بعضها Camel-cased، القواعد في هذا الكتاب تشير إلى الأسماء المستخدمة في Terraform. &#x20;
{% endhint %}

1. استعمل \_ (underscore) بدلاً من - (dash) في كل مكان (أسماء الموراد، أسماء مصادر البيانات، أسماء المتحولات، أسماء المخرجات الخ..)
2. فضل استعمال الأحرف الصغيرة (lowercase) والأرقام فقط (حتى لو كان نظام UTF-8 مدعوماً)

## أسماء الموراد ومصادر البيانات&#x20;

1. لا تكرر نوع المورد في اسم المورد (ليس جزئيًا أو كليًا):

   <div data-gb-custom-block data-tag="hint" data-style="success" class="hint hint-success"><p><code>resource "aws_route_table" "public" {}</code></p></div>

   <div data-gb-custom-block data-tag="hint" data-style="danger" class="hint hint-danger"><p><code>resource "aws_route_table" "public_route_table" {}</code></p></div>

   <div data-gb-custom-block data-tag="hint" data-style="danger" class="hint hint-danger"><p><code>resource "aws_route_table" "public_aws_route_table" {}</code></p></div>
2. يجب تسمية المورد باسم`this`إذا لم يكن هناك اسم وصفي وعام متاح، أو إذا كانت وحدة الموارد تنشئ موردًا واحدًا من هذا النوع (كمثال في [AWS VPC module](https://github.com/terraform-aws-modules/terraform-aws-vpc) يوجد فقط مورد وحيد من النوع`aws_nat_gateway` وعدة موارد من النوع `aws_route_table` لذلك يجب تسمية المورد من نوع`aws_nat_gateway` باسم `this`ويجب أن  نستعمل أسماء وصفية اكثر من أجل موارد النوع `aws_route_table` مثل`private`, `public,` `database)`&#x20;
3. استخدم دائمًا الأسماء المفردة للتسمية.
4. استعمل - (dash) داخل قيم الوسيطات وفي الأماكن التي ستتعرض فيها القيمة للبشر (على سبيل المثال ، اسم DNS لخادم افتراضي RDS).&#x20;
5. استعمل الوسيطان `count` / `for_each` داخل المورد أو داخل مصدر البيانات كأول وسيط وقم بإضافة سطر فارغ بعده&#x20;
6. استعمل الوسيط`tags` إذا كان مدعوماً من قبل المورد كأخر وسيط متبوع بالوسيطات`depends_on, lifecycle`إذا احتجت إليها، كل منها مفصول عن الأخر بسطر فارغ.
7. عند استعمال شروط للوسيطان `count` / `for_each`ففضل استعمال القيم المنطقية عوضاً عن`length`أو أي تعابير أخرى&#x20;

## أمثلة كود لأسماء المصادر

### استعمال `count` / `for_each`في الكود

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  count = 2

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}

resource "aws_route_table" "private" {
  for_each = toset(["one", "two"])

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  vpc_id = "vpc-12345678"
  count  = 2

  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

### وضعية `tags`في الكود

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  allocation_id = "..."
  subnet_id     = "..."

  tags = {
    Name = "..."
  }

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }
}   
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  tags = "..."

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }

  allocation_id = "..."
  subnet_id     = "..."
}
```

{% endcode %}
{% endhint %}

### &#x20;استعمال الشروط في `count`في الكود

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
resource "aws_nat_gateway" "that" {    # Best
  count = var.create_public_subnets ? 1 : 0
}

resource "aws_nat_gateway" "this" {    # Good
  count = length(var.public_subnets) > 0 ? 1 : 0
}
```

{% endcode %}
{% endhint %}

## أسماء المتحولات

1. لا تعيد اختراع العجلة في وحدات الموارد: استخدم الاسم`name`والوصف`description`والقيمة الافتراضية `default`للمتحولات كما هو محدد في قسم "Argument Reference"  للمورد الذي تعمل معه.
2. عملية التحقق (Validation) من المتحولات محدود نوعًا ما (على سبيل المثال ، لا يمكن الوصول إلى متحولات أخرى أو إجراء عمليات بحث). خطط وفقًا لذلك لأنه في كثير من الحالات تكون هذه الميزة غير مجدية.
3. استخدم صيغة الجمع في اسم متحول  عند يكون نمطه`list`أو`map`.&#x20;
4. قم بترتيب الأقسام في المتحول كالتالي:`description`ثم`type`ثم`default`وأخيراً`validation`
5. دائماً قم بإضافة قسم`description` إلى كل المتحولات حتى لو كنت تظن أنه واضح (ستحتاجه في المستقبل)
6. فضل استعمال الأنواع البسيطة (`number`, `string`, `list(...)`, `map(...)`, `any`) على الأنواع الأخرى مثل`object،`إلا إذا كنت تحتاج قيود صارمة على كل key
7. استعمل الأنماط المحددة مثل`map(string)` في حال كانت كل العناصر الموجودة داخلها من نفس النمط أو كان يمكن تحويلها إلى هذا النمط (مثلاً النمط`number`ممكن تحويله إلى النمط`string)`&#x20;
8. استعمل النمط `any`لتعطيل التحقق من النوع بدءاً من عمق معين أو عندما يجب دعم أنواع متعددة
9. القيمة {} هي عبارة عن map في بعض الأحيان وobject في أحيان أخرى. استعمل ()tomap لجعلها من النمط map دائماً.

## أسماء المخرجات

اجعل المخرجات متسقة ومفهومة خارج سياقها ( عندما يتم استعمال وحدة من قبل مستخدم يجب على المخرجات أن تكون واضح ما هو نمط وما صفات القيمة التي ترجعها)

1. يجب على اسم الخرج أن يصف القيمة التي يرجعها وأن تكون أقل حرية مما تريد عادة.
2. الشكل الجيد لاسم الخرج يكون كالتالي `{attribute}_{type}_{name}` حيث:
   1. &#x20;إن `{name}` هو اسم المورد أو اسم مصدر البيانات بدون اسم الموفر. كمثال للمورد `aws_subnet` يكون الاسم هو`subnet`وللمورد`aws_vpc` يكون `vpc`
   2. &#x20;`إن {type}`هو نمط الخرج الئي نتعامل معه&#x20;
   3. إن`{attribute}`هو الصفة المخرجة&#x20;
   4. [انظر الأمثلة](#code-examples-of-output).
3. إذا كان الخرج يعيد قيمة مع استعمال interpolation functions  وعدة موارد فيجب على `{name} و{type}`  أن تكون معممة قدر الإمكان (`this يجب حذفها)`[انظر الأمثلة](#code-examples-of-output)&#x20;
4. إذا كانت قيمة الخرج عبارة عن list فإنه يجب أن نستعمل اسم جمع [انظر الأمثلة](#use-plural-name-if-the-returning-value-is-a-list)
5. دائماً قم بإضافة قسم`description` إلى كل المخرجات حتى لو كنت تظن أنه واضح
6. تجنب وضع وسيط `sensitive`إلا إذا كنت تملك تحكم كامل باستعمال هذا الخرج في كل الأماكن في كل الوحدات
7. فضل استعمال ()`try` (متوفرة منذ الإصدار 0.13) على استعمال `element(concat(...))`(التي كانت تستعمل قبل 0.13)&#x20;

### أمثلة كود لأسماء المخرجات

تعيد على  الأكثر Security group ID وحيد

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "security_group_id" {
  description = "The ID of the security group"
  value       = try(aws_security_group.this[0].id, aws_security_group.name_prefix[0].id, "")
}
```

{% endcode %}
{% endhint %}

عند وجود عدة مصادر من نفس النمط، يجب حذف`this`من الخرج:&#x20;

{% hint style="danger" %}
{% code title="outputs.tf" %}

```hcl
output "this_security_group_id" {
  description = "The ID of the security group"
  value       = element(concat(coalescelist(aws_security_group.this.*.id, aws_security_group.web.*.id), [""]), 0)
  
```

{% endcode %}
{% endhint %}

إذا كانت قيمة الخرج عبارة عن list فإنه يجب أن نستعمل اسم جمع :

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "rds_cluster_instance_endpoints" {
  description = "A list of all cluster instance endpoints"
  value       = aws_rds_cluster_instance.this.*.endpoint
}
```

{% endcode %}
{% endhint %}


# تنسيق الكود

{% hint style="info" %}

* يجب أن تحتوي أمثلة ووحدات Terraformعلى توثيق لشرح الخصائص التي تقدمها وكيفية استعمالها
* كل الروابط في ملف README.md يجب أن تكون مطلقة لجعل موقع Terraform Registry يعرضها بشكل صحيح
* يمكن أن يحتوي التوثيق على رسومات تم إنشاؤها باستخدام [mermaid](https://github.com/mermaid-js/mermaid) أو مخططات تم إنشاؤها باستخدام [cloudcraft.co](https://cloudcraft.co). &#x20;
* قم باستعمال [Terraform pre-commit hooks](https://github.com/antonbabenko/pre-commit-terraform) للتأكد من أن الكود صالح، ومنسق بشكل صحيح، وموثق تلقائيًا قبل دفعه إلى Git واستعراضه من قبل البشر. &#x20;
  {% endhint %}

## التوثيق

### التوثيق مولداً تلقائياً

إن [pre-commit](https://pre-commit.com/) هو إطار عمل لإدارة وصيانة pre-commit hooks متعددة اللغات، مكتوبة بلغة بايثون وهي أداة قوية للقيام ببعض المهام بشكل أتوماتيكي على جهاز المطور قبل الدفع بالكود إلى git repository. تستعمل عادةً لتشغيل linters ولتنسيق الكود ( انظر إلى [supported hooks](https://pre-commit.com/hooks.html))

مع ملفات Terraform يمكننا استعمال `pre-commit`لتنسيق الكود والتحقق منه بالإضافة إلى تعديل التوثيق

تحقق من [pre-commit-terraform repository](https://github.com/antonbabenko/pre-commit-terraform/blob/master/README.md) ومن  [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc) الذي يقوم باستعماله

### أداة terraform-docs

إن [terraform-docs](https://github.com/segmentio/terraform-docs) هي أداة تقوم بتوليد التوثيق من وحدات Terraform وتولد أشكال مختلفة، يمكنك أن تشغلها يدوياً (بدون pre-commit hooks) أو تستعمل إطار عمل [pre-commit-terraform hooks](https://github.com/antonbabenko/pre-commit-terraform) لجعل التوثيق يتكون أتوماتيكياً.

@todo: Document module versions, release, GH actions

## الموراد

1. [pre-commit framework homepage](https://pre-commit.com/)
2. [Collection of git hooks for Terraform to be used with pre-commit framework](https://github.com/antonbabenko/pre-commit-terraform)
3. Blog post by [Dean Wilson](https://github.com/deanwilson): [pre-commit hooks and terraform - a safety net for your repositories](https://www.unixdaemon.net/tools/terraform-precommit-hooks/)


# الأسئلة الأكثر تكراراً

FTP (Frequent Terraform Problems اكثر المشاكل التي تعاني منها الأداة)

## ما هي الأدوات التي يجب معرفتها واستخدامها؟

* أداة [**Terragrunt**](https://terragrunt.gruntwork.io/) -أداة تنسيق
* &#x20;أداة [**tflint**](https://github.com/terraform-linters/tflint)
* أداة [**tfenv**](https://github.com/tfutils/tfenv) - إدارة الإصدارات
* أداة [**Atlantis**](https://www.runatlantis.io/) أتمتة العمل مع PR
* أداة [**pre-commit-terraform**](https://github.com/antonbabenko/pre-commit-terraform) مجموعة من git-hooks خاصة بأداة Terraform التي يتم استعمالها مع إطار العمل [pre-commit framework](https://pre-commit.com/)

## ما هي الحلول لمشكلة [dependency hell](https://en.wikipedia.org/wiki/Dependency_hell) مع الوحدات؟

يجب تحديد إصدار الوحدة التي نتعامل معها. يجب تعريف الموفرات خارج الوحدات وفقط في التراكيب، يجب أيضاً  تحديد إصدار الموفرات وإصدار Terraform أالذي نتعامل معه.&#x20;

لا يوجدا أداة إدارة Dependency، لكن يوجد بعض النصائح التي تجعل هذه المشكلة أقل إشكالاً. كمثال يمكن استعمال أداة [Dependabot](https://dependabot.com/) لأتمتة تحديث الارتباطات. تقوم هذه الأداة بإنشاء PR للحفاظ على الارتباطات بشكل أمن ومحدث. تدعم هذه الأداة ملفات Terraform.&#x20;


# المراجع

{% hint style="info" %}
يوجد الكثير من الناس الذين قاموا بإنشاء محتوى عظيم وإدارة مشاريع مفتوحة المصدر عن Terraform، ولكن لا يمكنني أن أجد تنسيقاً لقائمة روابط هذه الأعمال أفضل من [awesome-terraform](https://github.com/shuaibiyy/awesome-terraform).&#x20;
{% endhint %}

<https://twitter.com/antonbabenko/lists/terraform-experts> -  قائمة بالناس الذين عملوا مع الأداة بشكل كبير ومن الممكن أن تتعلم الكثير منهم (إذا سألت) &#x20;

<https://github.com/shuaibiyy/awesome-terraform> - قائمة مختارة من مصارد تعلم الأداة

<http://bit.ly/terraform-youtube> - "جرعتك الأسبوعية من الأداة"&#x20;

بث مباشر من قبل مؤلف هذه الكتاب. مراجعات، مقابلات، Q\&A، جلسات تكويد وكل شيء عن الأداة

<https://weekly.tf> - "الرسائل الأسبوعية عن الأداة"

أخبار مختلفة عن عالم الأداة (مشاريع جديدة، إعلانات، نقاشات) ترسل إليك من قبل مؤلف الكتاب.


# كتابة ملفات أداة Terraform

## استعمل locals لتحديد الاعتماديات الصريحة بين الموارد

من الطرق المساعدة لإخبار Terraform أنه يجب حذف بعض الموارد حتى عندما لا يوجد اعتمادية مباشرة عليها&#x20;

<https://raw.githubusercontent.com/antonbabenko/terraform-best-practices/master/snippets/locals.tf>

## &#x20;إصدار Terraform 0.12 - الوسيطات الإجبارية والاختيارية&#x20;

1. الوسيط `index_document`هو وسيط إجباري يجب تحديده، إذا كانت`var.website`ليست`map`فارغة
2. الوسيط `error_document`هو وسيط اختياري من الممكن عدم ذكره

{% code title="main.tf" %}

```hcl
variable "website" {
  type    = map(string)
  default = {}
}

resource "aws_s3_bucket" "this" {
  # omitted...

  dynamic "website" {
    for_each = length(keys(var.website)) == 0 ? [] : [var.website]

    content {
      index_document = website.value.index_document
      error_document = lookup(website.value, "error_document", null)
    }
  }
}
```

{% endcode %}

{% code title="terraform.tfvars" %}

```hcl
website = {
  index_document = "index.html"
}
```

{% endcode %}


# ورشة عمل

يوجد أيضاً ورشة عمل للناس التي تريد أن تتمرن على الأشياء التي تعلمناها في هذا المرجع

هنا يوجد المحتوى - <https://github.com/antonbabenko/terraform-best-practices-workshop>&#x20;


# Dobro došli

Ovaj dokument ima za cilj da se sistematski opišu najbolje prakse prilikom korištenja Terrafrom alata kao i da se daju preporuke za najčešće probleme sa kojima se susreću korisnici Terraforma.

[Terraform](https://www.terraform.io) je relativno nov alat (kao i većina ostalih DevOps alata) koji je započet 2014. godine.

Terrraform je moćan (ako ne i najmoćniji) i jedan od najkorištenijih alata koji vam dozvoljavaju upravljanje infrastrukturom kao kodom. Dozvoljava programerima da rade mnoge stvari i ne ograničava ih da te stvari rade na način koji bi bio težak za podršku ili integraciju.

Neke informacije opisane u ovoj knjizi možda ne izgledaju kao najbolje prakse. Ja to znam, i da bi pomogao čitaocima da razdvoje šta su ustaljene najbolje prakse, a šta je samo još jedno misljenje o tome kako treba raditi stvari, ponekad koristim pomoćne dijelove koda kako bi dao širi kontekst. Također ponekad koristim savjete kako bi dao dodatni kontekst i ikone da bi specificirao o kojem nivou zrelosti se radi za svaku podsekciju vezanu za najbolje prakse

Ova knjiga je započeta u sunčanom Madridu, 2018. godine i dostupna je besplatno za preuzimanje preko sljedećeg linka [https://www.terraform-best-practices.com/](https://www.terraform-best-practices.com).

Nekoliko godina kasnije je ažurirana sa aktuelnim najboljim praksama dostupnim sa Terrafrmom 1.0. U konačnici ova knjiga bi trebala sadržavati većinu neospornih najboljih praksi za korisnike Terrafroma.

## Sponzori

Please [contact me](https://github.com/antonbabenko/terraform-aws-devops#social-links) if you want to become a sponsor.

| [![](/files/ZR7lJ2rL4ny1Dv9nDQYs)](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) | [Compliance.tf](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) — Terraform Compliance Simplified. Make your Terraform modules compliance-ready. |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![](https://github.com/antonbabenko/terraform-best-practices/blob/ba/.gitbook/assets)](/ba)                    | —                                                                                                                                                                             |

## Prevodi

{% content-ref url="/spaces/u3iITRIHQx97ro2PkfdC" %}
[العربية (Arabic)](https://www.terraform-best-practices.com/ar/)
{% endcontent-ref %}

{% content-ref url="/spaces/B48qUSNPO2XmkIySLzfr" %}
[Português (Brazilian Portuguese)](https://www.terraform-best-practices.com/ptbr/)
{% endcontent-ref %}

{% content-ref url="/spaces/e1Mp2scOX6OnQbifCen3" %}
[English](https://www.terraform-best-practices.com/)
{% endcontent-ref %}

{% content-ref url="/spaces/6shyPtr2KrqW4ANbFXYg" %}
[Français (French)](https://www.terraform-best-practices.com/fr/)
{% endcontent-ref %}

{% content-ref url="/spaces/DyguS0uZfMW7X7m9BWx1" %}
[ქართული (Georgian)](https://www.terraform-best-practices.com/ka/)
{% endcontent-ref %}

{% content-ref url="/spaces/PKopCWJZbhpQ9FT0W8tL" %}
[Deutsch (German)](https://www.terraform-best-practices.com/de/)
{% endcontent-ref %}

{% content-ref url="/spaces/5c1kFpqxaDZC2g9e6rtT" %}
[ελληνικά (Greek)](https://www.terraform-best-practices.com/el/)
{% endcontent-ref %}

{% content-ref url="/spaces/4bq6CyY8vYiEHkjN63rT" %}
[עברית (Hebrew)](https://www.terraform-best-practices.com/he/)
{% endcontent-ref %}

{% content-ref url="/spaces/Mgong4S6IjtibE055zUM" %}
[हिंदी (Hindi)](https://www.terraform-best-practices.com/hi/)
{% endcontent-ref %}

{% content-ref url="/spaces/ZLCz7lNWbSJxDGuNOI44" %}
[Bahasa Indonesia (Indonesian)](https://www.terraform-best-practices.com/id/)
{% endcontent-ref %}

{% content-ref url="/spaces/8VlMHbHDbW6qRWdgN5oU" %}
[Italiano (Italian)](https://www.terraform-best-practices.com/it/)
{% endcontent-ref %}

{% content-ref url="/spaces/3vykLOWgdQLPLgHtxqQH" %}
[日本語 (Japanese)](https://www.terraform-best-practices.com/ja/)
{% endcontent-ref %}

{% content-ref url="/spaces/BoZVs6O2OJFQLNV1utmm" %}
[ಕನ್ನಡ (Kannada)](https://www.terraform-best-practices.com/kn/)
{% endcontent-ref %}

{% content-ref url="/spaces/bJnDvAqIyVgo7LDHgxYJ" %}
[한국어 (Korean)](https://www.terraform-best-practices.com/ko/)
{% endcontent-ref %}

{% content-ref url="/spaces/9yChMGbFo2G47Wiow1yY" %}
[Polski (Polish)](https://www.terraform-best-practices.com/pl/)
{% endcontent-ref %}

{% content-ref url="/spaces/sFM1GW5TPCGsskQ03mTm" %}
[Română (Romanian)](https://www.terraform-best-practices.com/ro/)
{% endcontent-ref %}

{% content-ref url="/spaces/5VD4NK4mHOY8SWjC9N5e" %}
[简体中文 (Simplified Chinese)](https://www.terraform-best-practices.com/zh/)
{% endcontent-ref %}

{% content-ref url="/spaces/fTxekzr50pIuGmrPkXUD" %}
[Español (Spanish)](https://www.terraform-best-practices.com/es/)
{% endcontent-ref %}

{% content-ref url="/spaces/Fedpbc5NbKjynXI8xTeF" %}
[Türkçe (Turkish)](https://www.terraform-best-practices.com/tr/)
{% endcontent-ref %}

{% content-ref url="/spaces/tXRvMPILxeJaJTM2CsSq" %}
[Українська (Ukrainian)](https://www.terraform-best-practices.com/uk/)
{% endcontent-ref %}

{% content-ref url="/spaces/dcjhau04KQIKHUJA90iN" %}
[اردو (Urdu)](https://www.terraform-best-practices.com/ur/)
{% endcontent-ref %}

Kontaktirajte me ukoliko želite prevesti ovu knjigu i na druge jezike.

## Kontribucije

Uvijek želim da dobijem povratnu informaciju kako bi mogao da ovu knjigu držim ažuriranom. Kako zajednica napreduje, i dolaze nove ideje i prijedlozi za ažuriranje, ažuriranja su impletirane i verifikovane sa vremena na vrijeme.

Ako ste zainteresovani za neku posebnu temu koja već nije obrađena unutar ove knjige, molim vas da ga je predložite [ovdje,](https://github.com/antonbabenko/terraform-best-practices/issues) ili glasajte za neku od vec predloženih tema koje bi željeli da vidite u ovoj knjizi. Ako mislite da **imate sadržaj** koji bi se trebao naći u ovoj knjizi i želite da doprinesete, napišite prijedlog i napravite zahtijev za izmjenom (nemojte se opterećivati o stilu pisanja teksta u ovom trenutku!).

## Autori

Ova knjiga je održavana od strane [Antona Babenka](https://github.com/antonbabenko) uz pomoć različitih kontributora i prevoditelja.

## Licenca

Rad na ovoj knjizi je licenciran pod Apache 2 licencom. Pogledajte LICENCU za vise informacija.

Autori i oni koji su doprinijeli pisanju sadržaja ove knjige ne mogu garantovati za ispravnost informacija koje ćete ovdje pronaći. Molim vas vodite računa da razumijete, da informacije koje su ponuđene u ovo knjizi su proizvod slobodne volje, i nikakva vrsta dogovora ili ugovora nije napravljena između vas i osoba koje su povezane sa sadržajem knjige ili samim projektom. Autori i oni koji su doprinijeli pisanju knjige nisu odgovorni za bilo kakve eventulne posljedice koje mogu proisteći iz korištenja sadržaja ove knjige.

Autorska prava © 2018-2023 Anton Babenko.


# Ključni koncepti

Zvanična Terrafrom dokumentacija detaljno opisuje sve detalje vezane za [konfiguraciju](https://www.terraform.io/docs/configuration/index.html). Pročitajte je pazljivo kako bi razumijeli ostatak ovog poglavlja.

Ovo poglavlje opisuje ključne koncepte koji su korišteni unutar ove knjige.

## Resursi

Resursi (eng. resource) su `aws_vpc`, `aws_db_instance`, itd. Resurs pripada pružatelju usluga u oblaku odnosno cloudu (eng. cloud provider), prihvata argumente, kao izlaznu informaciju pruža atribute i ima svoj životni ciklus. Resursi mogu biti kreirani, možete dohvatiti ranije kreirane resurse, raditi njihovo ažuriranje i brisanje.

## Resurs moduli

Resurs moduli (eng. resource module) predstavljaju kolekciju resursa, koji skupa mogu napraviti neku zajedničku akciju (npr. [AWS VPC Terraform modul ](https://github.com/terraform-aws-modules/terraform-aws-vpc/)kreira VPC, podmreže, NAT, itd). Od konfiguracije cloud provajdera zavisi koji resursi mogu biti definisani unutar modula, ili unutar struktura na većem nivou (npr. unutar infrastrukturnog modula).

## Infrastrukturni moduli

Infrastrukturni moduli predstavljaju kolekciju resurs modula, oni logički ne moraju biti povezani ali u odredjenoj situaciji ili konfiguraciji projekta služe istoj svrsi. Definišu kofiguraciju za cloud provajdera koja je prosljeđena resurs modulima a dalje prema samim resursima. Limitirani su na jedan entitet unutar logičke cjeline. (npr. AWS region, Google projekat)

Na primjer, modul [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis/) koristi resurs module kao što su  [terraform-aws-vpc ](https://github.com/terraform-aws-modules/terraform-aws-vpc/)i [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group/) kako bi se kreirala infrastruktura potrebna za pokretanje [Atlantisa](https://www.runatlantis.io) unutar [AWS Fargate](https://aws.amazon.com/fargate/) servisa.

Još jedan primjer je modul [terraform-aws-cloudquery](https://github.com/cloudquery/terraform-aws-cloudquery) gdje su zajedno korišteni drugi razlčiti moduli od strane [terraform-aws-modules ](https://github.com/terraform-aws-modules/)kao i od strane Docker resursa, kako bi se postiglo kreiranje i deplojment Docker slika, sve unutar jednog istog seta komandi.&#x20;

## Kompozicija

Kompozicija (eng. composition) je kolekcija infrastrukturnih modula, koji mogu biti rašireni preko nekoliko logički razdvojenih područja (npr. AWS regioni, različiti AWS računi). Kompozicija se koristi kako bi se opisala kompletna infrastruktura potrebna za čitavu organizaciju ili projekat.

Kompozicija se sastoji od infrastrukturnih modula, koji se dalje sastoje od modula resursa koji dalje implementiraju same resurse.

![Jednostavna infrastruktura kompozicije](/files/gRXjhRxnTLLcdax83v0g)

## Izvor podataka

Izvor podataka (eng. data source) je zadužen samo za operaciju čitanja, i zavisi od pružatelja cloud usluga, koristi se kao resurs modul i kao infrastrukturni modul.&#x20;

Izvor podataka `terraform_remote_state` se ponaša kao vezivno tkivo za module višeg nivoa i kompozicije.

Izvor podataka [external](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/data_source) dozvoljava vanjskim programima da budu izvor podataka, omogućavajući im kasnije korištenje u drugim dijelovima Terrafrom konfiguracije. Ovdje je primjer [terraform-aws-lambda module](https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/258e82b50adc451f51544a2b57fd1f6f8f4a61e4/package.tf#L5-L7) modula gdje je ime datoteke kreirano pozivajuci eksternu Paython skriptu.

Izvor podataka [http](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) pravi HTTP GET zahtjeve prema datom URL-u i eksportuje informacije dobijenog odgovora. Ovaj pristup se obično koristi da bi se dobile informacije izvora podataka od pristupne tačke (eng. endpoint) koja ne podržava Terrafrom.

## Udaljeno stanje

Infrastrukturni moduli i kompozicije bi se trebali nalaziti unutar svog fajla [Terrafrom stanja](https://developer.hashicorp.com/terraform/language/state) koji se pohranjuje na nekoj udaljenoj lokaciji sa koje moze biti dohvaćena od strane drugih programera na kontrolisan način (sa dnevnikom pristupa, specifičnom sigurnosnom politikom itd.)

## Provjader, provisioner, itd

Provajderi dostupni unutar terrafroma, zatim provisioners, kao i nekoliko drugih pojmova su vrlo dobro opisani i objašnjeni unutar zvanične dokumentacije tako da ih nećemo ovdje dodatno opisivati. Po mom mišljenju, oni imaju malo ili nimalo veze sa pisanjem dobrih Terrafrom modula.

## Zašto je tako teško?

Dok su individualni resursi kao atomi unutar infrastrukture, resurs moduli su molekule koje su sastavljene od atoma. Modul je namanja verzionisana i dijeljena jedinica. Posjeduje tačnu listu argumenata, osnovnu implementacijsku logiku da bi se napravila željena funkcionalnost. Npr. modul [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group) kreira `aws_security_group` i `aws_security_group_rule` resurse na osnovu ulaznih podataka. Ovaj resurs modul se sam po sebi može koristiti skupa sa drugim modulima kako bi se kreirao infruastrukturni modul.

Pristup podacima kroz različite molekule (resurs module i infrastrukturne module) je napravljen koristći izlazne podatke dobijene od strane modula i izvora podataka.

Pristup između kompozicija je često napravljen koristeći udaljeni fajl stanja izvora podataka. Postoji [više načina](https://developer.hashicorp.com/terraform/language/state/remote-state-data#alternative-ways-to-share-data-between-configurations) uz pomoć kojih se mogu dijeliti podaci između konfiguracija.

Ako koncepte opisane iznad postavimo u pseudo vezu onda bi to moglo izlgedati ovako:

```
composition-1 {
  infrastructure-module-1 {
    data-source-1 => d1

    resource-module-1 {
      data-source-2 => d2
      resource-1 (d1, d2)
      resource-2 (d2)
    }

    resource-module-2 {
      data-source-3 => d3
      resource-3 (d1, d3)
      resource-4 (d3)
    }
  }

}
```


# Struktura koda

Pitanja vezana za strukturu Terrafrom koda su daelko najecsca pitanja unutar zajednice. Takodjer svi se u odredjenom trenutku nadju u sitauaciji da razmisljaju koji je najbolji nacin da najbolje struktruiraju kod za svoj projekat.

## Kako bi trebali organizovati Terrafrom konfiguraciju?

Ovo je jedno od pitanja za koje postoje razlicita rjesenja i tesko je dati univerzalni savjet, pocnimo prvo sa razumijevanje na sta se sve ovo pitanje odnosi.&#x20;

* Kakava je kompleksnost projekta?
  * Broj povezanih resursa
  * Broj Terraform provajdera (pogledati biljesku ispod o "logickim provajderima")
* Koliko cesto se vasa infrastruktura mijenja
  * **Od** jedanput mjesecno/sedmicno/godisnje
  * **Do** kontinuirano (svaki put kada napravite novu izmjenu)
* Inicijatori izmjene koda? Da li dozvoljavate da vas CI server radi azuiranje repozitorija kada se napravi novi artifakt?
  * Samo programeri mogu praviti izmjene unutar repozitorija u kojem cuvamo infrastruktruni kod
  * Svi mogu predloziti izmjenu praveci zahtijev za izmjenom (eng. Pull Request) ukljucuji i automatske zatatke pokrenute od strane CI servera
* Koju deplojmet platformu odnosno servis koristite?&#x20;
  * AWS CodeDeploy, Kubernetes, ili OpenShift zahtijevaju malo drugaciji pristup
* Kako ste grupisali vasa okruzenja?
  * Po okruzenju (produkcijsko, testno), regionu ili projektu

{% hint style="info" %}
*Logicki provajderi rade u potpusnosti unutar logike Terrafroma i vrlo cesto nemaju interakciju sa bilo kojim drugim servisima. Za njih mozemo smatrati da su binarni 0 ili 1. Najcesci logicki provajderi ukljucuju* [random](https://registry.terraform.io/providers/hashicorp/random/latest/docs), [local](https://registry.terraform.io/providers/hashicorp/local/latest/docs), [terraform](https://www.terraform.io/docs/providers/terraform/index.html), [null](https://registry.terraform.io/providers/hashicorp/null/latest/docs), [time](https://registry.terraform.io/providers/hashicorp/time/latest).
{% endhint %}

## Zapocnite sa organizacijom Terrafrom konfiguracija

Stavljanjem cjelokupnog koda unutar `main.tf` je dobra ideja kada ste na pocetku ili pravite neki jednostavni primjer. U svim drugim slucajevima puno bolja opcija je radvojiti fajlove u nekoliko logickih cjelina kao sto su:

* `main.tf` - poziva module, sadrzi lokalne varijable i izvore podataka da bi se kreirali svi resursi.
* `variables.tf` - sadrzi deklaraciju varijabli koristenih unutar `main.tf`
* `outputs.tf` - sadrzi izlazne podatke za resurse kreirane unutar `main.tf`
* `versions.tf` - sadrzi detalje o verziji Terrafroma i projevajdera

`terraform.tfvars` ne bi trebao biti koristen nigdje osim unutar [kompozija](/ba/key-concepts#composition).

## Kako da razmisljate o organizaciji Terrafrom strukture

{% hint style="info" %}
Pobrinite se da razumijete kljucne koncepte - [resurs module](/ba/key-concepts#resource-module), [infrastrukturne module](/ba/key-concepts#infrastructure-module), i [ckompozicije](/ba/key-concepts#composition), kao sto su koristene u sljecem primjeru.
{% endhint %}

### Ceste preporuke za organizaciju koda

* Lakse je i brze raditi sa manjim brojem resursa
  * `terraform plan` i `terraform apply` prave API pozive da bi projeverili status resursa
  * Ako imate citavu infrastrukturu unutar jedne kompozicije to moze uzeto dosta vremena
* Ranjivos (u slucaju sigurnosnog incidenta) je manja sa manjim brojem resursa
  * Izolacija ne relevantnih resursa jednih od drugih stavljajuci ih unutar razlicitih kompozicija smanjuje rizik ukoliko nesto krene po zlu&#x20;
* Zapocnite svoj projekat koristeci udaljeno stanje zato sto:
  * Vas laptop nije mjesto na kojem cete cuvati jedinu ispravnu verziju vaze infrastrukture
  * Menadzment `tfstate` fajla unutar git-a je nocna mora
  * Kasnije kada slojevi infrastrukture krenu da rastu u vise smjerova (broj njihovih zavisnosti ili resursa) bit ce lakse drzati stvari pod kontrolom&#x20;
* Vjezbajte konzistentnu strukturu i [konvenciju o imenovanjima](/ba/naming):
  * Kao i proceduralni kod, Terrafrom kod treba biti pisan kako bi ga u prvom redu bio citljiv rudima, konzistentnost ce pomoci kada krenete da mijenjate vas kod u buducnosti
  * Moguce je pomjerati resurse unutar Terrafrom fajla stanja ali je to dosta teze raditi ukoliko imate ne konzistentnu sturkturu koda i nacin imenovanja.
* Drzite resurs module sto je moguce jednostavnijim
* Nemojte pisati vrijednosti direktno u kodu ukoliko te vrijednosti mogu biti prosljedjene kao varijabile ili mogu biti procitane direktno iz izvora podataka.&#x20;
* Koristite izvor podataka i `terraform_remote_state` kao poveznicu izmedju infrastrukturnih modula i kompozicija.

U ovoj knjizi primjeri su grupisani po kompleksnosti - od manjih prema vecim infrastrukturama. Ovakvo razdvajanje nije striktrno pa svakako provjerite i druge struktrue koda.

### Orkestracija infrastruktrunih modula i kompozicija

Imati malu infrastrukturu znaci imati mali broj zavisnosti i nekoliko resursa. Kako projekat raste, raste i potreba za lancem izvrsavanja Terrafrom konfigracija, spajajci razlicite infrastukturne module i prosljedjujuci vrijednosti unutar kompozicija koje su postale ocigledne.

Postoji najmanje 5 razlicitih grupa orkestracijskih rjesenja koje bi programeri trebalo da koriste:

1. Samo Terraform. Vrlo jednostavno, programer treba da zna samo Terrafrom kako bi zavrsio posao.&#x20;
2. Terragrunt. Cisti orkestracijski alat koji moze biti koristen za orkestraciju citave infrastrukture kao i za brigu o zavisnostima. Terragrunt po prirodi radi sa infrastukturnim modulima i kompozicijama sto smanjuje ponavljanje koda.
3. Vlastite skripte. Cesto se dogadja na pocetku i prije otkrivanja Terragrunt-a
4. Ansibl ili slicni alati automatizacijski alati. Obicno se koristi kada se sa upotrebom Terrafroma krenulo nakon Ansibl-a ili kada se Ansibl UI aktivno kroisti.
5. [Crossplane](https://crossplane.io) i druga rjesenja inspirisana Kubernetesom. Ponekad ima smisla da koristite Kubernetes eko sistem alata da bi postigli zeljeno stanje vase Terrafrom konfiguracije. Pogledati video [Crossplane vs Terraform](https://www.youtube.com/watch?v=ELhVbSdcqSY) za vise informacija.

Sa tim na umu, u ovoj knjizi je napravljen osvrt na prve dvije vrste strukture projekta, samo Terrafrom i Terragrunt.

Pogledajte primjere organizacije koda za [Terraform](/ba/examples/terraform) ili [Terragrunt](/ba/examples/terragrunt) u sljedecem poglavlju.


# Primjeri organizacije koda

## Terraform struktura koda

{% hint style="info" %}
Ovi primjeri koriste AWS Terrafrom provjader ali vecina principa pokazanih u ovim primjerima moze biti primjenjena i na druge cloud projvadere kao i na druge vrste provajdera (DNS, DB itd.)
{% endhint %}

| Tip                                                                       | Opis                                                                                                                                       | Status        |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------- |
| [mala](/ba/examples/terraform/small-size-infrastructure)                  | Nekoliko resursa, bez vanjskih zavisnosti. Jedan AWS racun. Jedna regija. Jedno okruzenje.                                                 | Zavrseno      |
| [srednja](/ba/examples/terraform/medium-size-infrastructure)              | Nekoliko AWS racuna i okruzenja, skolski primjer infrastrukturnih modula koristeci Terrafrom                                               | Zavrseno      |
| [velika](/ba/examples/terraform/large-size-infrastructure-with-terraform) | Vise AWS racuna, mnogo regiona, hitna potreba da se smanji kopiranje i ponavljanje koda, velika upotreba konpozicija. Koristi se Terrafrom | Rad u toku    |
| vrlo velika                                                               | Nekoliko razlicitih cloud provajdera(AWS, GCP, Azure). Deplojment na vise cloud platformi. Koristi se Terrafrom.                           | Nije zapoceto |

## Terragrunt struktura koda

| Tip     | Opis                                                                                                                                       | Status        |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------- |
| mala    | Nekoliko AWS racuna i okruzenja, skolski primjer infrastrukturnih modula koristeci Terrafrom                                               | Nije zapoceto |
| srednja | Vise AWS racuna, mnogo regiona, hitna potreba da se smanji kopiranje i ponavljanje koda, velika upotreba konpozicija. Koristi se Terrafrom | Nije zapoceto |
| velika  | Nekoliko razlicitih cloud provajdera(AWS, GCP, Azure). Deplojment na vise cloud platformi. Koristi se Terragrunt.                          | Nije zapoceto |


# Terragrunt


# Terraform


# Kreiranje manjih infrastruktura uz pomoc Terraforma

Izvor: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/small-terraform>

Ovaj primjer sadrzi kod koji je primjer organizacije Terrafrom konfiguracije za male infrastrukture bez vanjskih zavisnosti.

{% hint style="success" %}

* Idealan za pocetak i izmjene u hodu
* Idealan za male resurs module
* Dobar za male i linearne infrastrukturne module (npr: [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis))
* Dobar za mali broj resursa (manje od 20-30)
  {% endhint %}

{% hint style="warning" %}
Jedan fajl stanja za sve resurse moze uciniti proces rada sa Terrafromom sporim ukoliko broj resursa poraste (razmislite o upotrebi argumenta -target da bi ogranicili broj resursa)
{% endhint %}


# Kreiranje infrastrukture srednje velicine uz pomoc Terraforma

Izvor: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/medium-terraform>

Ovaj primjer sadrzi kod koji je primjer organizacije Terrafrom konfiguracije za infrastrukture srednje velicine, u ovom primjeru se koriste:

* 2 AWS racuna
* 2 odvojena okruzenja (`produkcijsko` and `testno`). Svako okruzenje je smjesteno unutar posebnog AWS racuna i nemaju dodairnih tacaka.
* Svako okruzenje koristi razlicitu verziju infrastrukturnog modula (`alb`) preuzetog sa [Terraform Registry-a](https://registry.terraform.io/)
* Svako okruzenje koristi istu verziju internog modula `modules/network` posto je taj modul preuzet iz lokalnog direktorija.

{% hint style="success" %}

* Idealan za projekte gdje je infrastruktura logicki razdvojena (radvojeni AWS racuni)&#x20;
* Dobar kada nema potrebe da mijenjate resurse koji su dijeljeni izmedju AWS racuna (jedno okruzenje = jedan AWS racun = jedan Terraform fajl stanja)
* Dobar kada nema potrebe za orkestracijom izmjena izmedju okruzenja
* Dobar kada su resursi infrastrukture u razilicitim okruzenjima sa svrhom i kada se ne mogu generalizovati (npr: neki resursi se ne koriste u jednom od okruzenja ili u nekom od regiona)
  {% endhint %}

{% hint style="warning" %}
Kako projekat raste, bit ce teze odrzati ova okruzenja u azuriranom stanju. Razmislite o upotrebi infrastruktrurnih modula za zadatke koji se ponavljaju.
{% endhint %}

##


# Kreiranje velike infrastrukture uz pomoc Terraforma

Source: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/large-terraform>

Ovaj primjer sadrzi kod koji je primjer organizacije Terrafrom konfiguracije za vece infrastrukture, u primjeru se koriste:

* 2 AWS racuna
* 2 regiona
* 2 odvojena okruzenja (`produkcijsko` and `testno`). Svako okruzenje je smjesteno unutar posebnog AWS racuna i resursi se prostiru izmedju 2 regije.
* Svako okruzenje koristi razlicitu verziju infrastrukturnog modula (`alb`) preuzetog sa [Terraform Registry-a](https://registry.terraform.io/)
* Svako okruzenje koristi istu verziju internog modula `modules/network` posto je taj modul preuzet iz lokalnog direktorija.

{% hint style="info" %}
U velikim projektima kao sto je opisano ovdje prednosti koristenja Terragrunta postaju ocigledne. Pogledajte [Primjeri organizacije koda sa Terragruntom](/ba/examples/terragrunt).
{% endhint %}

{% hint style="success" %}

* Idealan za projekte gdje je infrastruktura logicki razdvojena (radvojeni AWS racuni)&#x20;
* Dobar kada nema potrebe da mijenjate resurse koji su dijeljeni izmedju AWS racuna (jedno okruzenje = jedan AWS racun = jedan Terraform fajl stanja)
* Dobar kada nema potrebe za orkestracijom izmjena izmedju okruzenja
* Dobar kada su resursi infrastrukture u razilicitim okruzenjima sa svrhom i kada se ne mogu generalizovati (npr: neki resursi se ne koriste u jednom od okruzenja ili u nekom od regiona)
  {% endhint %}

{% hint style="warning" %}
Kako projekat raste, bit ce teze odrzati ova okruzenja u azuriranom stanju. Razmislite o upotrebi infrastruktrurnih modula za zadatke koji se ponavljaju.
{% endhint %}

##


# Konvencija o imenovanjima

## Generalna konvencija

{% hint style="info" %}
Ne bi trebao postojati razlog da pratite samo jednu konvenciju :)
{% endhint %}

{% hint style="info" %}
Budite svjesni cinjenice da cloud resursi cesto imaju ogranicenja u dozvoljenim imenima. Neki resursi, npr: ne mogu sadrzavati srednju crtu u imenu. Konvencija u ovoj knizi se odnonosi samo na imenovanje unutar Terrafroma
{% endhint %}

1. Koristite `_` (donja crta) umjesto `-` (srednje crte) na svim mjestima (za imena resursa, imena izvora podataka, imena varijabli, izlaznih vrijednosti itd).
2. Preferirajte upotrebu malih slova i brojeva (iako je UTF-8 podrzan).

## Resursi i argumenti izvora podataka

1. Ne ponavaljajte tip resursa u imenima resursa (u dijelovima ili kompletno):

{% hint style="success" %}

```
`resource "aws_route_table" "public" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_route_table" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_aws_route_table" {}`
```

{% endhint %}

1. Ime resrusa treba biti imenovano sa `this` ako nema neko vise opisujuce ili generalnije ime, ili ako resurs modul kreira jedan resurs tog tipa (npr, u [AWS VPC modulu](https://github.com/terraform-aws-modules/terraform-aws-vpc) postoji jedan resurs tipa `aws_nat_gateway` i vise resursa tipa`aws_route_table`, tako bi `aws_nat_gateway` trebao biti imenovan `this` a`aws_route_table` treba da ima bolje opisujuce ime - kao `private`, `public`, `database`).
2. Uvijek koristite imenice u jednini za imena.
3. Koristite `-` unutar vrijednosti argumenata i na mjestima gdje ce vrijednosti biti izlozene ljudima (npr, unutar DNS imena RDS instance).
4. Ukljucite argument `count` / `for_each`unutar resursa ili blokova izvora podataka kao prvi argument na vrhu i razdvojite novim redom nakon toga.
5. Ukljucite argument `tags,` ako je podrzano od strane resursa, kao zadnji pravi argument pracen sa `depends_on` i `lifecycle`, ako je neophodno. Sve ovo bi trebalo biti razdvojeno sa jednim praznim redom.
6. Kada koristite uslove unutar argumenta`count` / `for_each` praktikujte booelan vrijednosti (true/false) umjesto koristenja`length` ili drugih izraza.

## Primjeri koda za `resource`

### Upotreba `count` / `for_each`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  count = 2

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}

resource "aws_route_table" "private" {
  for_each = toset(["one", "two"])

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  vpc_id = "vpc-12345678"
  count  = 2

  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

### Upotreba `tags`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  allocation_id = "..."
  subnet_id     = "..."

  tags = {
    Name = "..."
  }

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }
}   
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  tags = "..."

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }

  allocation_id = "..."
  subnet_id     = "..."
}
```

{% endcode %}
{% endhint %}

### Uslovi unutar `count`

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
resource "aws_nat_gateway" "that" {    # Best
  count = var.create_public_subnets ? 1 : 0
}

resource "aws_nat_gateway" "this" {    # Good
  count = length(var.public_subnets) > 0 ? 1 : 0
}
```

{% endcode %}
{% endhint %}

## Varijable

1. Don't reinvent the wheel in resource modules: use `name`, `description`, and `default` value for variables as defined in the "Argument Reference" section for the resource you are working with.
2. Support for validation in variables is rather limited (e.g. can't access other variables or do lookups). Plan accordingly because in many cases this feature is useless.
3. Use the plural form in a variable name when type is `list(...)` or `map(...)`.
4. Order keys in a variable block like this: `description` , `type`, `default`, `validation`.
5. Always include `description` on all variables even if you think it is obvious (you will need it in the future).
6. Prefer using simple types (`number`, `string`, `list(...)`, `map(...)`, `any`) over specific type like `object()` unless you need to have strict constraints on each key.
7. Use specific types like `map(map(string))` if all elements of the map have the same type (e.g. `string`) or can be converted to it (e.g. `number` type can be converted to `string`).
8. Use type `any` to disable type validation starting from a certain depth or when multiple types should be supported.
9. Value `{}` is sometimes a map but sometimes an object. Use `tomap(...)` to make a map because there is no way to make an object.

## Outputs

Make outputs consistent and understandable outside of its scope (when a user is using a module it should be obvious what type and attribute of the value it returns).

1. The name of output should describe the property it contains and be less free-form than you would normally want.
2. Good structure for the name of output looks like `{name}_{type}_{attribute}` , where:
   1. `{name}` is a resource or data source name without a provider prefix. `{name}` for `aws_subnet` is `subnet`, for`aws_vpc` it is `vpc`.
   2. `{type}` is a type of a resource sources
   3. `{attribute}` is an attribute returned by the output
   4. [See examples](#code-examples-of-output).
3. If the output is returning a value with interpolation functions and multiple resources, `{name}` and `{type}` there should be as generic as possible (`this` as prefix should be omitted). [See example](#code-examples-of-output).
4. If the returned value is a list it should have a plural name. [See example](#use-plural-name-if-the-returning-value-is-a-list).
5. Always include `description` for all outputs even if you think it is obvious.
6. Avoid setting `sensitive` argument unless you fully control usage of this output in all places in all modules.
7. Prefer `try()` (available since Terraform 0.13) over `element(concat(...))` (legacy approach for the version before 0.13)

### Code examples of `output`

Return at most one ID of security group:

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "security_group_id" {
  description = "The ID of the security group"
  value       = try(aws_security_group.this[0].id, aws_security_group.name_prefix[0].id, "")
}
```

{% endcode %}
{% endhint %}

When having multiple resources of the same type, `this` should be omitted in the name of output:

{% hint style="danger" %}
{% code title="outputs.tf" %}

```hcl
output "this_security_group_id" {
  description = "The ID of the security group"
  value       = element(concat(coalescelist(aws_security_group.this.*.id, aws_security_group.web.*.id), [""]), 0)
}
```

{% endcode %}
{% endhint %}

### Use plural name if the returning value is a list

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "rds_cluster_instance_endpoints" {
  description = "A list of all cluster instance endpoints"
  value       = aws_rds_cluster_instance.this.*.endpoint
}
```

{% endcode %}
{% endhint %}


# Stilovi koda

{% hint style="info" %}

* Primjeri Terrafrom modula trebaju sadrzavati dokumentovana objasnjenja mogucnosti i kako ih koristiti.
* Svi linkovi README.md trebaju biti apsulutni da bi ih Terrafrom Registry web stranica prikazivala ispravno
* Dokumentacija moze ukljucivati dijagrame krierane sa [mermaid](https://github.com/mermaid-js/mermaid) i primjere kreirane uz pomoc [cloudcraft.co](https://cloudcraft.co).
* Koristite [Terraform pre-commit hooks](https://github.com/antonbabenko/pre-commit-terraform) kako bi osigurali da je kod validan, pravilno formatiran i automatski dokumentovan prije nego bude dostupan na gitu i pregledan od strane ljudi.
  {% endhint %}

## Dokumentacija

### Automatski generisana dokumentacija

[pre-commit](https://pre-commit.com/) je framework za menadzmen i odrzavanje visejezicnih okidaca prije nego se kod nadje na gitu. Napisan je u Python programskom jeziku i to je mocan alat koji vam omogucava da odradite nesto automatski na racunaru programera prije nego je kod postavljen na git repozitoriji. Koristi se za pokretanje automatskih formatera koda (pogledajte [podrzane okidace](https://pre-commit.com/hooks.html)).

Sa Terraform konfiguracijama `pre-commit` se moze korisititi da formatira i validira kod kao i da azurira dokumentaciju.&#x20;

Pogledajte [pre-commit-terraform repository](https://github.com/antonbabenko/pre-commit-terraform/blob/master/README.md) kako bi se poblize upoznali sa tim, takodjer pogledajte i postojeci repozitoriji [terraform-aws-vpc ](https://github.com/terraform-aws-modules/terraform-aws-vpc)gdje se to i koristi.

### terraform-docs

[terraform-docs](https://github.com/segmentio/terraform-docs) je alat koji vam omogucava generisanje dokumenatacije iz Terrafrom modula u razlicitim izlaznim formatima. Mozete ga pokrenuti rucno bez upotrebe pre-commit okidaca, ili koristeci [pre-commit-terraform hooks](https://github.com/antonbabenko/pre-commit-terraform) da bi se dokumentacija azurirala automatski..

@todo: Document module versions, release, GH actions

## Izvori

1. [pre-commit framework homepage](https://pre-commit.com/)
2. [Collection of git hooks for Terraform to be used with pre-commit framework](https://github.com/antonbabenko/pre-commit-terraform)
3. Blog post - [Dean Wilson](https://github.com/deanwilson): [pre-commit hooks and terraform - a safety net for your repositories](https://www.unixdaemon.net/tools/terraform-precommit-hooks/)


# Česta pitanja

FTP (Cesti Terraform Problemi)

## Koje alate bi trebali korisititi ili razmisliti o njihovoj upotrebi?

* [**Terragrunt**](https://terragrunt.gruntwork.io/) - Orkestracijski alat
* [**tflint**](https://github.com/terraform-linters/tflint) - Alat za formatiranje koda
* [**tfenv**](https://github.com/tfutils/tfenv) - Menadzer verzija
* [**Atlantis**](https://www.runatlantis.io/) - Automatizacija zahtijeva za izmjene&#x20;
* [**pre-commit-terraform**](https://github.com/antonbabenko/pre-commit-terraform) - Kolekcija git okidaca za Terraform koji mogu biti koristeni sa [pre-commit framework](https://pre-commit.com/)
* [**Infracost**](https://www.infracost.io) - Procjena troskova infrastrukture za Terraform unutar zahtjeva za izmjenu. Radi sa Terragruntom, Atlantisom i pre-commit-terraform.

## Sta su rjesenja za [pakao zavisnosti](https://en.wikipedia.org/wiki/Dependency_hell) izmedju modula?

Verzionisanje resursa i infrastrukturnih modula treba biti specificirano. Provajderi trebaju biti konfigurisani izvan modula, ali samo unutar kompozicija. Verzinisanje provajdera i Terrafroma moze takodjer biti zakljucano.

Ne postoji najbolji alat za odrzavanje zavisnosti i njihov menadzment, ali postoje odredjene upute kako napraviti zavisnosti manje problematicnim. Na primjer, [Dependabot](https://dependabot.com/) moze biti koriste za automatizaciju azuriranja zavisnosti. Dependabot zahtjev za izmjenu da bi drzao vase zavisnosti sigurnim i azuiranim. Dependabot podrzava Terraform konfiguracije.


# Reference

{% hint style="info" %}
Postoji mnogo ljudi koji kreiraju sjajan sadrzaj i odrzavaju projekte otvorenog koda relevantnim za Terrafrom zajednicu ali ja ne mogu smisliti bolji nacin organizacije ovih linkova osim da kopiram linkove koji su navedeni unutar [awesome-terraform](https://github.com/shuaibiyy/awesome-terraform).
{% endhint %}

<https://twitter.com/antonbabenko/lists/terraform-experts> - Lista ljudi koji rade sa Terrafromom veoma aktivno i koji ce vam reci mnogo (ukoliko ih pitate).

<https://github.com/shuaibiyy/awesome-terraform> - Azurna list resursa HashiCorp Terraform.

<http://bit.ly/terraform-youtube> - "Your Weekly Dose of Terraform" YouTube kanal Antonona Babenka. Prenosi uzivo sa osvrtima, interviju, pitanja i odgovori, programiranje uzivo, i neki trikovi sa Terraformom.

<https://weekly.tf> - Terraform sedmicne vijesti. Razlicite vijesti iz Terraform svijeta (projekti, najave, diskusije). Urednik Anton Babenko.


# Pisanje Terraform konfiguracija

## Korisite `locals` Da bi specificirali ekspicitne zavisnosti izmedju resursa

Koristan nacin da ukazete Terrafromu da bi neki resurs trebao biti izbrisan cak i kad nema direktne zavisnosti u Terrafrom konfiguraciji.

<https://raw.githubusercontent.com/antonbabenko/terraform-best-practices/master/snippets/locals.tf>

## Terraform 0.12 - Zahtijevani vs Opcionalni arguments

1. Zahtjevani argument `index_document` mora biti postavljen, ako `var.website` nije prazan.
2. Opcioni argumenti `error_document` mogu biti izostavljeni.

{% code title="main.tf" %}

```hcl
variable "website" {
  type    = map(string)
  default = {}
}

resource "aws_s3_bucket" "this" {
  # omitted...

  dynamic "website" {
    for_each = length(keys(var.website)) == 0 ? [] : [var.website]

    content {
      index_document = website.value.index_document
      error_document = lookup(website.value, "error_document", null)
    }
  }
}
```

{% endcode %}

{% code title="terraform.tfvars" %}

```hcl
website = {
  index_document = "index.html"
}
```

{% endcode %}


# Vježba

Ukoliko zelite vježbati neke od stvari opisanih u ovom upustvu pogledajte link ispod na kojem cete pronaci workshop za vježbu.

Workshop za vježbu je dostupna na sljedecem linku - <https://github.com/antonbabenko/terraform-best-practices-workshop>


# Seja Bem-Vindo(a)

Este documento é uma tentativa de descrever sistematicamente as melhores práticas usando o Terraform, e, fornecer recomendações para os problemas mais frequentes de seus usuários.

O [Terraform](https://www.terraform.io) é um projeto relativamente novo (como a maioria das ferramentas de DevOps, na verdade) que foi iniciado em 2014.

O Terraform é poderoso (se não o mais poderoso que existe atualmente) e uma das ferramentas mais utilizadas que permitem o gerenciamento de infraestrutura como código (IaC). Ele permite que os desenvolvedores realizem uma grande variedade de coisas e não os restringe de fazê-las de forma com que sejam difíceis de integrar ou suportar à longo prazo.

Algumas informações descritas neste livro podem não parecer as melhores práticas. Sei disso, e, para ajudar os leitores a separar as melhores práticas estabelecidas e do que apenas mais uma maneira opinativa, às vezes, dou dicas para fornecer algum contexto e ícones para especificar o nível de maturidade em cada subseção relacionada às melhores práticas.

Este livro começou na ensolarada Madri em 2018 e está disponível gratuitamente aqui - [https://www.terraform-best-practices.com/](https://www.terraform-best-practices.com)

Alguns anos depois, ele foi atualizado com mais práticas atuais recomendadas disponíveis com o Terraform 1.0. Eventualmente, este livro deve conter a maioria das melhores práticas e recomendações incontestáveis para usuários do Terraform.

## Patrocinadores

Please [contact me](https://github.com/antonbabenko/terraform-aws-devops#social-links) if you want to become a sponsor.

| [![](/files/E075iQF6ZYoRfvgo6CoM)](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) | [Compliance.tf](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) — Terraform Compliance Simplified. Make your Terraform modules compliance-ready. |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![](https://github.com/antonbabenko/terraform-best-practices/blob/ptbr/.gitbook/assets)](/ptbr)                | —                                                                                                                                                                             |

## Traduções

{% content-ref url="/spaces/u3iITRIHQx97ro2PkfdC" %}
[العربية (Arabic)](https://www.terraform-best-practices.com/ar/)
{% endcontent-ref %}

{% content-ref url="/spaces/PJbgKPAX0ohEMLpETpg7" %}
[Bosanski (Bosnian)](https://www.terraform-best-practices.com/ba/)
{% endcontent-ref %}

{% content-ref url="/spaces/e1Mp2scOX6OnQbifCen3" %}
[English](https://www.terraform-best-practices.com/)
{% endcontent-ref %}

{% content-ref url="/spaces/6shyPtr2KrqW4ANbFXYg" %}
[Français (French)](https://www.terraform-best-practices.com/fr/)
{% endcontent-ref %}

{% content-ref url="/spaces/DyguS0uZfMW7X7m9BWx1" %}
[ქართული (Georgian)](https://www.terraform-best-practices.com/ka/)
{% endcontent-ref %}

{% content-ref url="/spaces/PKopCWJZbhpQ9FT0W8tL" %}
[Deutsch (German)](https://www.terraform-best-practices.com/de/)
{% endcontent-ref %}

{% content-ref url="/spaces/5c1kFpqxaDZC2g9e6rtT" %}
[ελληνικά (Greek)](https://www.terraform-best-practices.com/el/)
{% endcontent-ref %}

{% content-ref url="/spaces/4bq6CyY8vYiEHkjN63rT" %}
[עברית (Hebrew)](https://www.terraform-best-practices.com/he/)
{% endcontent-ref %}

{% content-ref url="/spaces/Mgong4S6IjtibE055zUM" %}
[हिंदी (Hindi)](https://www.terraform-best-practices.com/hi/)
{% endcontent-ref %}

{% content-ref url="/spaces/ZLCz7lNWbSJxDGuNOI44" %}
[Bahasa Indonesia (Indonesian)](https://www.terraform-best-practices.com/id/)
{% endcontent-ref %}

{% content-ref url="/spaces/8VlMHbHDbW6qRWdgN5oU" %}
[Italiano (Italian)](https://www.terraform-best-practices.com/it/)
{% endcontent-ref %}

{% content-ref url="/spaces/3vykLOWgdQLPLgHtxqQH" %}
[日本語 (Japanese)](https://www.terraform-best-practices.com/ja/)
{% endcontent-ref %}

{% content-ref url="/spaces/BoZVs6O2OJFQLNV1utmm" %}
[ಕನ್ನಡ (Kannada)](https://www.terraform-best-practices.com/kn/)
{% endcontent-ref %}

{% content-ref url="/spaces/bJnDvAqIyVgo7LDHgxYJ" %}
[한국어 (Korean)](https://www.terraform-best-practices.com/ko/)
{% endcontent-ref %}

{% content-ref url="/spaces/9yChMGbFo2G47Wiow1yY" %}
[Polski (Polish)](https://www.terraform-best-practices.com/pl/)
{% endcontent-ref %}

{% content-ref url="/spaces/sFM1GW5TPCGsskQ03mTm" %}
[Română (Romanian)](https://www.terraform-best-practices.com/ro/)
{% endcontent-ref %}

{% content-ref url="/spaces/5VD4NK4mHOY8SWjC9N5e" %}
[简体中文 (Simplified Chinese)](https://www.terraform-best-practices.com/zh/)
{% endcontent-ref %}

{% content-ref url="/spaces/fTxekzr50pIuGmrPkXUD" %}
[Español (Spanish)](https://www.terraform-best-practices.com/es/)
{% endcontent-ref %}

{% content-ref url="/spaces/Fedpbc5NbKjynXI8xTeF" %}
[Türkçe (Turkish)](https://www.terraform-best-practices.com/tr/)
{% endcontent-ref %}

{% content-ref url="/spaces/tXRvMPILxeJaJTM2CsSq" %}
[Українська (Ukrainian)](https://www.terraform-best-practices.com/uk/)
{% endcontent-ref %}

{% content-ref url="/spaces/dcjhau04KQIKHUJA90iN" %}
[اردو (Urdu)](https://www.terraform-best-practices.com/ur/)
{% endcontent-ref %}

Entre em contato se você quer ajudar a traduzir este livro para outros idiomas.

## Contribuições

Continuarei atualizando este livro conforme a comunidade amadurece e novas ideias são implementadas e verificadas. Por favor, deixe seu comentário ou crítica construtiva para que o livro esteja sempre em boa qualidade.

Se você tem interesse em determinados tópicos, [abra um problema no Github](https://github.com/antonbabenko/terraform-best-practices/issues), ou curta um já aberto que você julga ser importante e deva ter prioridade.

## Autores

Este livro é mantido por [Anton Babenko](https://github.com/antonbabenko) com a ajuda de diversos colaboradores e tradutores.

## Licença

Este trabalho está licenciado sob a Licença Apache 2. Veja LICENSE para maiores detalhes.

Os autores e colaboradores deste conteúdo não podem garantir a validade das informações aqui encontradas. Certifique-se de que entende que as informações aqui contidas estão sendo fornecidas livremente, e que, nenhum acordo ou contrato é criado entre você e quaisquer pessoas associadas a este conteúdo ou projeto. Os autores e colaboradores não assumem e, por meio deste, se isentam de qualquer responsabilidade perante qualquer parte, por qualquer perda, dano ou interrupção causada por erros, ou omissões nas informações contidas, associadas ou vinculadas a este conteúdo, sejam tais erros ou omissões resultantes de negligência, acidente ou qualquer outra causa.

Direito autoral © 2018-2023 Anton Babenko.


# Conceitos chave

A documentação oficial do Terraform descreve[ todos os aspectos da configuração em detalhes](https://www.terraform.io/docs/configuration/index.html). Leia-o com atenção para entender o restante desta seção.

## Recursos

Um recurso é `aws_vpc`, `aws_db_instance`, etc. Um recurso pertence a um provedor, aceita argumentos, produz atributos e tem ciclos de vida. Um recurso pode ser criado, recuperado, atualizado e excluído.

## Módulo de Recursos

O módulo de recursos é uma coleção de recursos conectados, que juntos, executam a ação comum (por exemplo, o [módulo AWS VPC Terraform](https://github.com/terraform-aws-modules/terraform-aws-vpc/) cria VPC, sub-redes, gateway NAT, etc.). Depende da configuração do provedor, que pode ser definida nele, ou em estruturas de nível superior (por exemplo, no módulo de infraestrutura).

## Módulo de infraestrutura

Um módulo de infraestrutura é uma coleção de módulos de recursos, que podem ser logicamente não conectados, mas na situação/projeto/configuração atual servem ao mesmo propósito. Ele define a configuração para provedores, passada para os módulos de recursos downstream e para os recursos. Normalmente é limitado a trabalhar em uma entidade por separar lógico (por exemplo, região da AWS, projeto do Google).

Por exemplo, o módulo [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis/) utiliza módulo de recursos tais como o [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc/) e [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group/) para gerenciar infraestrutura necessária para executar o  [Atlantis](https://www.runatlantis.io) no [AWS Fargate](https://aws.amazon.com/fargate/).

Outro exemplo é o módulo [terraform-aws-cloudquery](https://github.com/cloudquery/terraform-aws-cloudquery), onde vários módulos do [terraform-aws-modules](https://github.com/terraform-aws-modules/) estão sendo utilizados juntos para gerenciar a infraestrutura, assim como utilizar recursos do Docker para criar, enviar e implantar imagens Docker. Tudo em um conjunto.

## Composição <a href="#composicao" id="composicao"></a>

Composição é uma coleção de módulos de infraestrutura, que podem abranger várias áreas separadas logicamente (por exemplo, regiões da AWS, várias contas da AWS). A composição é usada para descrever a infraestrutura completa necessária para toda a organização ou projeto.

Uma composição consiste em módulos de infraestrutura, que consistem em módulos de recursos, que implementam recursos individuais.

![Composição de infraestrutura simples](/files/gRXjhRxnTLLcdax83v0g)

## Fonte de Dados

A fonte de dados executa uma operação somente leitura e é dependente da configuração do provedor, é também usada em um módulo de recursos e em um módulo de infraestrutura.

A fonte de dados  `terraform_remote_state` atua como uma “cola” para módulos e composições de nível superior.

Já uma fonte de dados [externa](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/data_source), permite que um programa externo atue como fonte de dados, expondo informações arbitrários para uso em outro lugar na configuração do Terraform. Aqui está um exemplo do módulo [terraform-aws-lambda](https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/258e82b50adc451f51544a2b57fd1f6f8f4a61e4/package.tf#L5-L7), onde o nome do arquivo é calculado chamando um script Python externo.

A fonte de dados [http](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) realiza uma solicitação `HTTP GET` para o URL fornecido e exporta informações sobre a resposta, o que geralmente é útil para obter informações de terminais onde um provedor Terraform nativo não existe.

## Estado Remoto

Módulos de infraestrutura e composições devem manter seu [estado Terraform](https://www.terraform.io/docs/language/state/index.html) em um local remoto, onde possam ser recuperados por outros de maneira controlável (por exemplo, especificar ACL, versionamento, logging).

## Provedor, Aprovisionador, etc

Provedores, provisionadores e alguns outros termos estão muito bem descritos na documentação oficial e não vale a pena repetir aqui. Na minha opinião, eles têm pouco a ver com escrever bons módulos Terraform.

## Por que é tão difícil?

Enquanto os recursos individuais são como átomos na infraestrutura, os módulos de recursos são moléculas. Um módulo é a menor unidade com versão e compartilhável. Possui uma lista exata de argumentos, implementa lógica básica para que tal unidade realize a função necessária. Por exemplo, o módulo [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group) cria recursos `aws_security_group` e `aws_security_group_rule` com base no input. Este módulo de recursos por si só pode ser usado em conjunto com outros módulos para criar o módulo de infraestrutura.

O acesso aos dados entre moléculas (módulos de recursos e módulos de infraestrutura) é realizado utilizando saídas e fontes de dados dos módulos.

O acesso entre composições geralmente é realizado usando fontes de dados de estado remoto. [Existem várias maneiras de compartilhar dados entre as configurações](https://www.terraform.io/docs/language/state/remote-state-data.html#alternative-ways-to-share-data-between-configurations).

Ao colocar os conceitos descritos acima em pseudo-relações, pode-se ficar assim:

```
composition-1 {
  infrastructure-module-1 {
    data-source-1 => d1

    resource-module-1 {
      data-source-2 => d2
      resource-1 (d1, d2)
      resource-2 (d2)
    }

    resource-module-2 {
      data-source-3 => d3
      resource-3 (d1, d3)
      resource-4 (d3)
    }
  }

}
```


# Estrutura do código

As perguntas relacionadas à estrutura de código do Terraform sào de longe as mais frequentes na comunidade. Todos pensaram na melhor estrutura de código para o projeto em algum momento também.

## Como devo estruturar minhas configurações do Terraform?

Esta é uma das questões em que existem muitas soluções e é muito difícil dar conselhos dinâmicos, então vamos começar entendendo com o que estamos lidando.

* Qual é a complexidade do seu projeto?
  * Número de recursos relacionados.
  * Número de provedores Terraform (veja a nota abaixo sobre “provedores lógicos”).
* Com que frequência sua infraestrutura muda?
  * **A partir** de uma vez por mês/semana/dia.
  * **Continuamente** (toda vez que houver um novo commit).
* Iniciadores de mudança de código? *Você permite que o servidor CI atualize o repositório quando um novo artefato é criado?*
  * Somente desenvolvedores podem realizar o push para o repositório de infraestrutura.
  * Todos podem propor uma mudança em qualquer coisa abrindo um PR (incluindo tarefas automatizadas em execução no servidor CI).
* Qual plataforma de implementação ou serviço de implementação você utiliza?
  * AWS CodeDeploy, Kubernetes, ou OpenShift exigem uma abordagem um pouco diferente.
* Como os ambientes são agrupados?
  * Por ambiente, região, projeto...

{% hint style="info" %}
*Os provedores lógicos trabalham inteiramente dentro da lógica do Terraform e, muitas vezes, não interagem com nenhum outro serviço, entao podemos pensar em sua complexidade como* O(1). Os provedores lógicos mais comuns incluem [random](https://registry.terraform.io/providers/hashicorp/random/latest/docs), [local](https://registry.terraform.io/providers/hashicorp/local/latest/docs), [terraform](https://www.terraform.io/docs/providers/terraform/index.html), [null](https://registry.terraform.io/providers/hashicorp/null/latest/docs), [time](https://registry.terraform.io/providers/hashicorp/time/latest).
{% endhint %}

## Introdução à estruturação de configurações do Terraform

Colocar todo o código em um único `main.tf` é uma boa ideia quando você está começando ou escrevendo um código de exemplo. Em todos os outros casos, será melhor ter vários arquivos divididos logicamente assim:

* `main.tf` - chame módulos, locais e fontes de dados para criar todos os recursos.
* `variables.tf` - contém declarações de variáveis utilizadas em `main.tf.`
* `outputs.tf` - contém saídas dos recursos criados em `main.tf.`
* `versions.tf` - contém requisitos de versão para Terraform e provedores.

`terraform.tfvars` não deve ser utilizado em nenhum lugar exceto na [composição](/ptbr/key-concepts#composicao).

## Como pensar sobre a estrutura de configurações do Terraform?

{% hint style="info" %}
Por favor, certifique-se de entender os principais conceitos - [módulo de recursos](/ptbr/key-concepts#modulo-de-recursos),&#x20;

[módulo de infraestrutura](/ptbr/key-concepts#modulo-de-infraestrutura) e [composição](/ptbr/key-concepts#composicao), conforme são utilizados nos exemplos a seguir.
{% endhint %}

### Recomendações comuns para estruturar código

* É mais fácil e rápido trabalhar com um número menor de recursos
  * `terraform plan` e `terraform apply` fazem chamada API na nuvem para verificar o status dos recursos.
  * Se você tiver toda a sua infraestrutura em uma única composição, isso pode levar algum tempo.
* O raio afetado é menor com menos recursos
  * Isolar recursos não relacionados uns aos outros, colocando-os em composições separadas, reduz o risco se algo der errado.
* Inicie seu projeto utilizando o estado remoto porque:
  * Seu notebook não é lugar para sua fonte de verdade de infraestrutura.
  * Gerenciar um arquivo `tfstate` no git é um pesadelo.
  * Mais tarde, quando as camadas de infraestrutura começarem a crescer em várias direções (número de dependências ou recursos), será mais fácil manter as coisas sob controle.
* Pratique uma estrutura consistente e uma convenção de [nomenclatura](/ptbr/naming#convencoes-gerais):
  * Assim como o código procedural, o código do Terraform deve ser escrito para que as pessoas leiam primeiro, a consistência ajudará quando as mudanças ocorrerem daqui a seis meses.
  * É possível mover recursos no arquivo de estado do Terraform, mas pode ser mais difícil de efetuar se você tiver estrutura e nomenclatura inconsistentes.
* Mantenha os módulos de recursos o mais simples possível.
* Não codifique valores que possam ser passados como variáveis ou descobertos usando fontes de dados.
* Use fontes de dados e o `terraform_remote_state` especificamente como uma cola entre os módulos de infraestrutura na composição.

Neste livro, os projetos de exemplo são agrupados por *complexidade* - de infraestruturas pequenas a muito grandes. Essa separação não é rígida, portanto, verifique também outras estruturas.

### Orquestração de módulos e composições de infraestrutura

Ter uma infraestrutura pequena significa haver um pequeno número de dependências e poucos recursos. À medida que o projeto cresce, torna-se óbvia a necessidade de encadear a execução das configurações do Terraform, conectar diferentes módulos de infraestrutura e passar valores em uma composição.

Existem pelo menos 5 grupos distintos de soluções de orquestração que os desenvolvedores usam:

1. Somente o Terraform. Muito simples, os desenvolvedores precisam conhecer apenas o Terraform para realizar o trabalho.
2. Terragrunt. Ferramenta de orquestração pura que pode ser usada para orquestrar toda a infraestrutura, bem como lidar com dependências. O Terragrunt opera com módulos e composições de infraestrutura nativamente, reduzindo assim a duplicação de código.
3. Roteiros internos (in-house scripts). Muitas vezes isso acontece como um ponto de partida para a orquestração e antes de descobrir o Terragrunt.
4. Ansible ou ferramenta de automação de uso geral similar. Geralmente utilizado quando o Terraform é adotado após o Ansible, ou quando a “interface” do usuário do Ansible é usada ativamente.
5. [Crossplane](https://crossplane.io/) e outras soluções inspiradas no Kubernetes. Às vezes, faz sentido utilizar o ecossistema Kubernetes e empregar um recurso de “loop” de reconciliação para atingir o estado desejado de suas configurações do Terraform. Observe o vídeo [Crossplane vs Terraform](https://www.youtube.com/watch?v=ELhVbSdcqSY) para mais informações.

Com isso em mente, este livro analisa às duas primeiras dessas estruturas de projeto, apenas Terraform e Terragrunt.

Veja exemplos de estruturas de código para o [Terraform](/ptbr/examples#estruturas-de-codigo-do-terraform) e/ou [Terragrunt](/ptbr/examples#estruturas-de-codigo-do-terragrunt) no próximo capítulo.


# Exemplos de estrutura de códigos

## Estruturas de código do Terraform

{% hint style="info" %}
Esses exemplos estão mostrando o provedor da AWS, mas a maioria dos princípios mostrados nos exemplos pode ser aplicada a outros provedores de núvem pública, bem como a outros tipos de provedores (DNS, DB, Monitoring, etc).
{% endhint %}

| Tipo                                                                        | Descrição                                                                                                                                                                          | Disponibilidade       |
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| [pequeno](/ptbr/examples/terraform/small-size-infrastructure)               | Poucos recursos, sem dependências externas. Conta única da AWS. Região única. Ambiente único.                                                                                      | Sim                   |
| [médio](/ptbr/examples/terraform/medium-size-infrastructure)                | Diversas contas e ambientes na AWS, módulos de infraestrutura prontos para o uso utilizando o Terraform.                                                                           | Sim                   |
| [grande](/ptbr/examples/terraform/large-size-infrastructure-with-terraform) | Muitas contas na AWS, muitas regiões, necessidade urgente de reduzir copiar e colar, módulos de infraestrutura personalizados, uso intenso de composições. Utilizando o Terraform. | Trabalho em progresso |
| muito grande (nível Enterprise)                                             | Diversos provedores (AWS, GCP, Azure). Implementações em diversas nuvens. Utilizando o Terraform.                                                                                  | Não                   |

## Estruturas de código do Terragrunt

| Tipo                            | Descrição                                                                                                                                                                           | Disponibilidade |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| médio                           | Diversas contas e ambientes na AWS, módulos de infraestrutura prontos para o uso utilizando o Terragrunt.                                                                           | Não             |
| grande                          | Muitas contas na AWS, muitas regiões, necessidade urgente de reduzir copiar e colar, módulos de infraestrutura personalizados, uso intenso de composições. Utilizando o Terragrunt. | Não             |
| muito grande (nível Enterprise) | Diversos provedores (AWS, GCP, Azure). Implementações em diversas nuvens. Utilizando o Terragrunt.                                                                                  | Não             |


# Terragrunt


# Terraform


# Infraestrutura pequena com o Terraform

Fonte: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/small-terraform>

Este exemplo contém código como um exemplo de estruturação de configurações do Terraform para uma infraestrutura de pequeno porte, onde nenhuma dependência externa é utilizada.

{% hint style="success" %}

* Perfeito para começar e refatorar à medida que avança
* Perfeito para pequenos módulos de recursos
* Bom para módulos de infraestrutura pequenos e lineares (ex, [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis))
* Bom para um número pequeno de recursos (menos de 20-30)
  {% endhint %}

{% hint style="warning" %}
Um arquivo de estado único para todos os recursos pode tornar o processo de trabalho com o Terraform lento, se o número de recursos estiver crescendo (considere utilizar o argumento [`-target`](https://learn.hashicorp.com/tutorials/terraform/resource-targeting?in=terraform/cli) para limitar o número de recursos)
{% endhint %}


# Infraestrutura média com o Terraform

Fonte: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/medium-terraform>

Este exemplo contém código como um exemplo de estruturação de configurações do Terraform para uma infraestrutura de médio porte, que utiliza:

* 2 contas na AWS
* 2 ambientes separados (`prod` e `stage` que não compartilham nada entre eles). Cada ambiente está em uma conta separada na AWS.
* Cada ambiente utiliza uma versão diferente do módulo de infraestrutura pronto para uso (`alb`) originado do [Terraform Registry](https://registry.terraform.io/)
* Cada ambiente utiliza a mesma versão de `módulos/rede` de um módulo interno, pois é originado de um diretório local.

{% hint style="success" %}

* Perfeito para projetos em que a infraestrutura é separada logicamente (contas AWS separadas)
* Bom para quando não há necessidade de modificar recursos compartilhados entre contas da AWS (um ambiente = uma conta da AWS = um arquivo de estado)
* Bom para quando não há necessidade na orquestração de mudanças entre os ambientes
* Bom para quando os recursos de infraestrutura são diferentes por ambiente de propósito e não podem ser generalizados (por exemplo, alguns recursos estão ausentes em um ambiente ou em algumas regiões)
  {% endhint %}

{% hint style="warning" %}
À medida que o projeto cresce, será mais difícil manter esses ambientes atualizados entre sí. Considere o uso de módulos de infraestrutura (já prontos ou internos) para tarefas repetíveis.
{% endhint %}

##


# Infraestrutura grande com o Terraform

Fonte: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/large-terraform>

Este exemplo contém código como um exemplo de estruturação de configurações do Terraform para uma infraestrutura de médio porte, que utiliza:

* 2 contas na AWS
* 2 regiões (`ap-southeast-2` e `us-west-1`, por exemplo)
* 2 ambientes separados (`prod` e `stage` que não compartilham nada entre eles). Cada ambiente está em uma conta separada na AWS.
* Cada ambiente utiliza uma versão diferente do módulo de infraestrutura pronto para uso (`alb`) originado do [Terraform Registry](https://registry.terraform.io/)
* Cada ambiente utiliza a mesma versão de `módulos/rede` de um módulo interno, pois é originado de um diretório local.

{% hint style="info" %}
Em um grande projeto como o descrito aqui, os benefócios do uso do Terragrunt se tornam muito visíveis. Veja [Estruturas de código de exemplos com o Terragrunt.](/ptbr/examples/terragrunt)
{% endhint %}

{% hint style="success" %}

* Perfeito para projetos em que a infraestrutura é separada logicamente (contas AWS separadas)
* Bom para quando não há necessidade de modificar recursos compartilhados entre contas da AWS (um ambiente = uma conta da AWS = um arquivo de estado)
* Bom para quando não há necessidade na orquestração de mudanças entre os ambientes
* Bom para quando os recursos de infraestrutura são diferentes por ambiente de propósito e não podem ser generalizados (por exemplo, alguns recursos estão ausentes em um ambiente ou em algumas regiões)
  {% endhint %}

{% hint style="warning" %}
À medida que o projeto cresce, será mais difícil manter esses ambientes atualizados entre sí. Considere o uso de módulos de infraestrutura (já prontos ou internos) para tarefas repetíveis.
{% endhint %}

##


# Convenções de nomenclatura

## Convenções gerais

{% hint style="info" %}
Não deve haver razão alguma para **não** seguir pelo menos essas convenções :)
{% endhint %}

{% hint style="info" %}
Esteja ciente de que os recursos reais da núvem geralmente têm restrições em nomes permitidos. Alguns recursos, por exemplo, não podem conter travessões, alguns devem ser em caixa de camelo (mais conhecido como [CamelCase](https://pt.wikipedia.org/wiki/CamelCase)). As convenções neste livro referem-se aos próprios nomes do Terraform.
{% endhint %}

1. Utilize `_` (subtraço) ao invés do `-` (traço) em todo o lugar (nomes de recursos, nomes de fontes de dados, nomes de variáveis, outputs, etc.).
2. Prefira usar letras minúsculas e números (mesmo que o UTF-8 seja suportado).

## Argumentos de recursos e fontes de dados

1. Não repita a categoria de recurso no nome do recurso (não parcialmente, nem completamente):

   <div data-gb-custom-block data-tag="hint" data-style="success" class="hint hint-success"><p><code>resource "aws_route_table" "public" {}</code></p></div>

   <div data-gb-custom-block data-tag="hint" data-style="danger" class="hint hint-danger"><p><code>resource "aws_route_table" "public_route_table" {}</code></p></div>

   <div data-gb-custom-block data-tag="hint" data-style="danger" class="hint hint-danger"><p><code>resource "aws_route_table" "public_aws_route_table" {}</code></p></div>
2. O nome do recurso deve ser nomeado `this` se não houver mais um nome descritivo e geral disponível ou se o módulo de recurso criar um único recurso desse tipo (por exemplo, no módulo [AWS VPC](https://github.com/terraform-aws-modules/terraform-aws-vpc) há um único recurso do tipo `aws_nat_gateway` e vários recursos do tupo`aws_route_table`, então `aws_nat_gateway`deve ser nomeado `this` e `aws_route_table` deve ter nomes mais descritivos - como `private`, `public`, `database`).
3. Sempre utilize substantivos singulares para nomes.
4. Utilize `-` em valores de argumentos e em locais onde o valor será exposto a um humano (por exemplo, no nome de DNS da instância RDS).
5. Inclua o(s) argumento(s) `count` / `for_each` no bloco de recurso ou fonte de dados como o primeiro argumento na parte superior e separe por uma nova linha depois dele.
6. Inclua o argumento `tags,` se suportadas pelo recurso, como o último argumento real, seguido por `depends_on` e `lifecycle`, se necessário. Estes devem ser separados por uma única linha vazia.
7. Ao utilizar condições em um argumento `count` / `for_each` , prefira valores boleanos (`true` / `false`) em vez de usar `length` ou outras expressões.

## Exemplos de código de `resource`

### Uso do `count` / `for_each`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  count = 2

  vpc_id = "vpc-12345678"
  # ... argumentos restantes omitidos
}

resource "aws_route_table" "private" {
  for_each = toset(["one", "two"])

  vpc_id = "vpc-12345678"
  # ... argumentos restantes omitidos
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  vpc_id = "vpc-12345678"
  count  = 2

  # ... argumentos restantes omitidos
}
```

{% endcode %}
{% endhint %}

### Colocação das `tags`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  allocation_id = "..."
  subnet_id     = "..."

  tags = {
    Name = "..."
  }

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }
}   
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  tags = "..."

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }

  allocation_id = "..."
  subnet_id     = "..."
}
```

{% endcode %}
{% endhint %}

### Condições com o `count`

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
resource "aws_nat_gateway" "that" {    # Perfeito
  count = var.create_public_subnets ? 1 : 0
}

resource "aws_nat_gateway" "this" {    # Bom
  count = length(var.public_subnets) > 0 ? 1 : 0
}
```

{% endcode %}
{% endhint %}

## Variáveis

1. Não reinvente a roda em módulos de recursos: use `name`, `description`, e valor  `default` para variáveis conforme definido na seção “Referência de argumento” para o recurso com o qual você está trabalhando.
2. O suporte para validação em variáveis é bastante limitado (por exemplo, não pode acessar outras variáveis ou fazer pesquisas). Planeje de acordo porque em muitos casos esse recurso é inútil.
3. Use a forma plural em um nome de variável quando o tipo for `list(...)` ou `map(...)`.
4. Chaves de ordem em um bloco variável como: `description`, `type`, `default`, `validation`.
5. Sempre inclua `description` em todas as variáveis, mesmo que você julgue ser óbvio (você precisará disso, no futuro).
6. Prefira usar tipos simples (`number`, `string`, `list(...)`, `map(...)`, `any`) sobre tipos específicos como `object()`, a menos que você precise ter restrições estritas em cada chave.
7. Use tipos específicos como `map(map(string))` se todos os elementos do mapa tiverem o mesmo tipo (ex. `string`) ou podem ser convertidos para ele (ex. `number` pode ser convertido para `string`).
8. Use tipo `any` para desabilitar a validação de tipo a partir de uma determinada profundidade ou quando vários tipos devem ser suportados.
9. O valor `{}` às vezes é um mapa, mas às vezes é um objeto. Use `tomap(...)` para criar um mapa porque não há como criar um objeto.

## Outputs

Torne os outputs consistentes e compreensíveis fora de seu escopo (quando um usuário está usando um módulo, deve ser óbvio que tipo e atributo do valor ele retorna).

1. O nome do output deve descrever a propriedade que ela contém e ser menos livre do que você normalmente desejaria.
2. Uma boa estrutura para o nome do output parece com `{name}_{type}_{attribute}`, onde:
   1. `{name}`  um nome de recurso ou fonte de dados sem um prefixo de provedor. O `{name}` do `aws_subnet` é `subnet`, para o`aws_vpc` é `vpc`.
   2. `{type}` é um tipo de fontes de recursos.
   3. `{attribute}` é um atributo retornado pelo output.
   4. [Veja exemplos](/ptbr/naming#exemplos-de-codigo-do-output).
3. Se o output estiver retornando um valor com funções de interpolação e vários recursos, `{name}` e `{type}` devem ser o mais genéricos possível (`this` como prefixo deve ser omisso). [Veja exemplos](/ptbr/naming#exemplos-de-codigo-do-output).
4. Se o valor retornado for uma lista, deve ter um nome no plural. [Veja exemplos](/ptbr/naming#use-o-nome-no-plural-se-o-valor-de-retorno-for-uma-lista).
5. Sempre inclua `description` para todos os outputs mesmo que você julgue que ser óbvio.
6. Evite definir o argumento `sensitive`, a menos que você controle totalmente o uso desse output em todos os locais em todos os módulos.
7. Prefira `try()` (disponível desde o Terraform 0.13) ao invés de `element(concat(...))` (abordagem herdada para a versão anterior a 0.13).

### Exemplos de código do `output`

Retorne no máximo um ID do `security-group`:

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "security_group_id" {
  description = "The ID of the security group"
  value       = try(aws_security_group.this[0].id, aws_security_group.name_prefix[0].id, "")
}
```

{% endcode %}
{% endhint %}

Quando há vários recursos do mesmo tipo, `this` deve ser omisso no nome do output:

{% hint style="danger" %}
{% code title="outputs.tf" %}

```hcl
output "this_security_group_id" {
  description = "The ID of the security group"
  value       = element(concat(coalescelist(aws_security_group.this.*.id, aws_security_group.web.*.id), [""]), 0)
}
```

{% endcode %}
{% endhint %}

### Use o nome no plural se o valor de retorno for uma lista

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "rds_cluster_instance_endpoints" {
  description = "A list of all cluster instance endpoints"
  value       = aws_rds_cluster_instance.this.*.endpoint
}
```

{% endcode %}
{% endhint %}


# Estilo de código

{% hint style="info" %}

* Os módulos de exemplos e do Terraform devem conter documentação explicando os recursos e como usá-los.
* Todos os links nos arquivos README.md devem ser absolutos para que o site do Terraform Registry os mostre corretamente.
* A documentação pode incluir diagramas criados com [mermaid](https://github.com/mermaid-js/mermaid) e plantas criadas com o [cloudcraft.co](https://cloudcraft.co).
* Utilize o [Terraform pre-commit hooks](https://github.com/antonbabenko/pre-commit-terraform) para garantir que o código seja válido, formatado corretamente e documentado automaticamente antes de ser enviado para o git e revisado por humanos.
  {% endhint %}

## Documentação

### Documentação gerada automaticamente

O [pre-commit](https://pre-commit.com/) é um framework para gerenciar e manter hooks pré-commit multi-idioma. Ele é escrito em Python e é uma ferramente poderosa para fazer algo automaticamente na máquina de um desenvolvedor antes que o código seja enviado para o repositório git. Normalmente, ele é usado para executar linters e formatar código (veja [supported hooks](https://pre-commit.com/hooks.html)).

Com as configurações do Terraform, o `pre-commit` pode ser usado para formatar e validar o código, bem como para atualizar a documentação.

Confirma o repositório [pre-commit-terraform](https://github.com/antonbabenko/pre-commit-terraform/blob/master/README.md) para se familiarizar com ele e os repositórios existentes (por exemplo, [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc)) onde ele já é utilizado.

### terraform-docs

O [terraform-docs](https://github.com/segmentio/terraform-docs) é uma ferramente que faz a geração de documentação a partir de módulos Terraform em vários formatos de saída (output). Você pode executá-lo manualmente (sem ganchos — pre-commit hooks — de pré-commit) ou usar o  [pre-commit-terraform hooks](https://github.com/antonbabenko/pre-commit-terraform) para atualizar a documentação automaticamente.

@todo: Document module versions, release, GH actions

## Recursos

1. [pre-commit framework homepage](https://pre-commit.com/)
2. [Collection of git hooks for Terraform to be used with pre-commit framework](https://github.com/antonbabenko/pre-commit-terraform)
3. Blog post by [Dean Wilson](https://github.com/deanwilson): [pre-commit hooks and terraform - a safety net for your repositories](https://www.unixdaemon.net/tools/terraform-precommit-hooks/)


# FAQ

FTP (Frequent Terraform Problems)

## Quais são as ferramentas que eu deveria estar ciente e considerar utilizar?

* [**Terragrunt**](https://terragrunt.gruntwork.io) - Ferramenta de orquestração
* [**tflint**](https://github.com/terraform-linters/tflint) - Ferramenta de checagem de código
* [**tfenv**](https://github.com/tfutils/tfenv) - Sistema de controle de versão
* [**Atlantis**](https://www.runatlantis.io) - Automação de Pull Requests
* [**pre-commit-terraform**](https://github.com/antonbabenko/pre-commit-terraform) - Coleção de git hooks para Terraform para ser usado com o [framework pre-commit](https://pre-commit.com)
* [**Infracost**](https://infracost.io) - Estimativas de custo de nuvem para Terraform em solicitações de Pull Requests. Funciona com Terragrunt, Atlantis e pré-commits também.

## Quais são as soluções do [Inferno de Dependências](https://pt.wikipedia.org/wiki/Inferno_de_depend%C3%AAncias) com módulos?

As versões dos módulos de recursos e infraestrutura devem ser especificadas. Os provedores devem ser configurados fora dos módulos, mas apenas na composição. A versão dos provedores e do Terraform podem também ser travadas.

Não existe uma ferramenta de gerenciamento de dependência mestre, mas existem algumas dicas para tornar a dependência menos problemática. Por exemplo, o [Dependabot](https://dependabot.com) pode ser usado para automatizar as atualizações de dependências seguras e atualizadas. O Dependabot é compatível com as configurações do Terraform.


# Referências

{% hint style="info" %}
Existem muitas pessoas que criam ótimos conteúdos e gerenciam projetos de código aberto relevantes para a comunidade Terraform, mas não consigo pensar na melhor estrutura para obter esses links listados aqui sem copiar listas como a [awesome-terraform](https://github.com/shuaibiyy/awesome-terraform).
{% endhint %}

<https://twitter.com/antonbabenko/lists/terraform-experts> - Lista de pessoas que trabalham com o Terraform muito ativamente e podem lhe dizer muito sobre (se você perguntar-lhes).

<https://github.com/shuaibiyy/awesome-terraform> - Lista com curadoria de recursos no Terraform da HashiCorp.

<http://bit.ly/terraform-youtube> - O canal do YouTube "Your Weekly Dose of Terraform" de Anton Babenko. Transmissões ao vivo com análises, entrevistas, perguntas e respostas, codificação ao vivo e alguns hacks com o Terraform.

<https://weekly.tf> - Boletim semanal Terraform. Várias notícias no mundo Terraform (projetos, anúncios, discussões) por Anton Babenko.


# Escrevendo configurações do Terraform

## Use `locals` para especificar dependências explícitas entre recursos

Uma maneira útil de dar uma dica ao Terraform de que alguns recursos devem ser excluídos antes mesmo quando não houver dependência direta nas configurações do  Terraform.

<https://raw.githubusercontent.com/antonbabenko/terraform-best-practices/master/snippets/locals.tf>

## Terraform 0.12 - Argumentos obrigarórios vs opcionais

1. O argumento obrigatório `index_document` deve ser definido, se `var.website` não for um mapa vazio.
2. O argumento opcional `error_document` pode ser omitido.

{% code title="main.tf" %}

```hcl
variable "website" {
  type    = map(string)
  default = {}
}

resource "aws_s3_bucket" "this" {
  # omitted...

  dynamic "website" {
    for_each = length(keys(var.website)) == 0 ? [] : [var.website]

    content {
      index_document = website.value.index_document
      error_document = lookup(website.value, "error_document", null)
    }
  }
}
```

{% endcode %}

{% code title="terraform.tfvars" %}

```hcl
website = {
  index_document = "index.html"
}
```

{% endcode %}


# Workshop

Há também um ‘workshop’ para pessoas que desejam praticar algumas das coisas descritas neste guia.

Você pode conferir o material aqui (em inglês) - <https://github.com/antonbabenko/terraform-best-practices-workshop>


# Welcome

This document is an attempt to systematically describe best practices using Terraform and provide recommendations for the most frequent problems Terraform users experience.

[Terraform](https://www.terraform.io) is powerful (if not the most powerful out there now) and one of the most used tools which allow management of infrastructure as code. It allows developers to do a lot of things and does not restrict them from doing things in ways that will be hard to support or integrate with.

Some information described in this book may not seem like the best practices. I know this, and to help readers to separate what are established best practices and what is just another opinionated way of doing things, I sometimes use hints to provide some context and icons to specify the level of maturity on each subsection related to best practices.

The book was started in sunny Madrid in 2018, available for free here at [https://www.terraform-best-practices.com/](https://www.terraform-best-practices.com).

A few years later it has been updated with more actual best practices available with Terraform 1.0. Eventually, this book should contain most of the indisputable best practices and recommendations for Terraform users.

## Sponsors

Please [contact me](https://github.com/antonbabenko/terraform-aws-devops#social-links) if you want to become a sponsor.

| [![](/files/66JjEO3O8uwnrorHnY1Z)](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) | [Compliance.tf](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) — Terraform Compliance Simplified. Make your Terraform modules compliance-ready. |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

## Translations

{% content-ref url="/spaces/u3iITRIHQx97ro2PkfdC" %}
[العربية (Arabic)](https://www.terraform-best-practices.com/ar/)
{% endcontent-ref %}

{% content-ref url="/spaces/PJbgKPAX0ohEMLpETpg7" %}
[Bosanski (Bosnian)](https://www.terraform-best-practices.com/ba/)
{% endcontent-ref %}

{% content-ref url="/spaces/B48qUSNPO2XmkIySLzfr" %}
[Português (Brazilian Portuguese)](https://www.terraform-best-practices.com/ptbr/)
{% endcontent-ref %}

{% content-ref url="/spaces/6shyPtr2KrqW4ANbFXYg" %}
[Français (French)](https://www.terraform-best-practices.com/fr/)
{% endcontent-ref %}

{% content-ref url="/spaces/DyguS0uZfMW7X7m9BWx1" %}
[ქართული (Georgian)](https://www.terraform-best-practices.com/ka/)
{% endcontent-ref %}

{% content-ref url="/spaces/PKopCWJZbhpQ9FT0W8tL" %}
[Deutsch (German)](https://www.terraform-best-practices.com/de/)
{% endcontent-ref %}

{% content-ref url="/spaces/5c1kFpqxaDZC2g9e6rtT" %}
[ελληνικά (Greek)](https://www.terraform-best-practices.com/el/)
{% endcontent-ref %}

{% content-ref url="/spaces/4bq6CyY8vYiEHkjN63rT" %}
[עברית (Hebrew)](https://www.terraform-best-practices.com/he/)
{% endcontent-ref %}

{% content-ref url="/spaces/Mgong4S6IjtibE055zUM" %}
[हिंदी (Hindi)](https://www.terraform-best-practices.com/hi/)
{% endcontent-ref %}

{% content-ref url="/spaces/ZLCz7lNWbSJxDGuNOI44" %}
[Bahasa Indonesia (Indonesian)](https://www.terraform-best-practices.com/id/)
{% endcontent-ref %}

{% content-ref url="/spaces/8VlMHbHDbW6qRWdgN5oU" %}
[Italiano (Italian)](https://www.terraform-best-practices.com/it/)
{% endcontent-ref %}

{% content-ref url="/spaces/3vykLOWgdQLPLgHtxqQH" %}
[日本語 (Japanese)](https://www.terraform-best-practices.com/ja/)
{% endcontent-ref %}

{% content-ref url="/spaces/BoZVs6O2OJFQLNV1utmm" %}
[ಕನ್ನಡ (Kannada)](https://www.terraform-best-practices.com/kn/)
{% endcontent-ref %}

{% content-ref url="/spaces/bJnDvAqIyVgo7LDHgxYJ" %}
[한국어 (Korean)](https://www.terraform-best-practices.com/ko/)
{% endcontent-ref %}

{% content-ref url="/spaces/9yChMGbFo2G47Wiow1yY" %}
[Polski (Polish)](https://www.terraform-best-practices.com/pl/)
{% endcontent-ref %}

{% content-ref url="/spaces/sFM1GW5TPCGsskQ03mTm" %}
[Română (Romanian)](https://www.terraform-best-practices.com/ro/)
{% endcontent-ref %}

{% content-ref url="/spaces/5VD4NK4mHOY8SWjC9N5e" %}
[简体中文 (Simplified Chinese)](https://www.terraform-best-practices.com/zh/)
{% endcontent-ref %}

{% content-ref url="/spaces/fTxekzr50pIuGmrPkXUD" %}
[Español (Spanish)](https://www.terraform-best-practices.com/es/)
{% endcontent-ref %}

{% content-ref url="/spaces/Fedpbc5NbKjynXI8xTeF" %}
[Türkçe (Turkish)](https://www.terraform-best-practices.com/tr/)
{% endcontent-ref %}

{% content-ref url="/spaces/tXRvMPILxeJaJTM2CsSq" %}
[Українська (Ukrainian)](https://www.terraform-best-practices.com/uk/)
{% endcontent-ref %}

{% content-ref url="/spaces/dcjhau04KQIKHUJA90iN" %}
[اردو (Urdu)](https://www.terraform-best-practices.com/ur/)
{% endcontent-ref %}

Contact me if you want to help translate this book into other languages.

## Contributions

I always want to get feedback and update this book as the community matures and new ideas are implemented and verified over time.

If you are interested in specific topics, please [open an issue](https://github.com/antonbabenko/terraform-best-practices/issues), or thumb up an issue you want to be covered. If you feel that **you have content** and you want to contribute, write a draft and submit a pull request (don't worry about writing good text at this point!).

## Authors

This book is maintained by [Anton Babenko](https://github.com/antonbabenko) with the help of different contributors and translators.

## License

This work is licensed under Apache 2 License. See LICENSE for full details.

The authors and contributors to this content cannot guarantee the validity of the information found here. Please make sure that you understand that the information provided here is being provided freely, and that no kind of agreement or contract is created between you and any persons associated with this content or project. The authors and contributors do not assume and hereby disclaim any liability to any party for any loss, damage, or disruption caused by errors or omissions in the information contained in, associated with, or linked from this content, whether such errors or omissions result from negligence, accident, or any other cause.

Copyright © 2018-2023 Anton Babenko.


# Key concepts

The official Terraform documentation describes [all aspects of configuration in details](https://www.terraform.io/docs/configuration/index.html). Read it carefully to understand the rest of this section.

This section describes key concepts which are used inside the book.

## Resource

Resource is `aws_vpc`, `aws_db_instance`, etc. A resource belongs to a provider, accepts arguments, outputs attributes, and has a lifecycle. A resource can be created, retrieved, updated, and deleted.

## Resource module

Resource module is a collection of connected resources which together perform the common action (for e.g., [AWS VPC Terraform module](https://github.com/terraform-aws-modules/terraform-aws-vpc/) creates VPC, subnets, NAT gateway, etc). It depends on provider configuration, which can be defined in it, or in higher-level structures (e.g., in infrastructure module).

## Infrastructure module

An infrastructure module is a collection of resource modules, which can be logically not connected, but in the current situation/project/setup serves the same purpose. It defines the configuration for providers, which is passed to the downstream resource modules and to resources. It is normally limited to work in one entity per logical separator (e.g., AWS Region, Google Project).

For example, [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis/) module uses resource modules like [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc/) and [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group/) to manage the infrastructure required for running [Atlantis](https://www.runatlantis.io) on [AWS Fargate](https://aws.amazon.com/fargate/).

Another example is [terraform-aws-cloudquery](https://github.com/cloudquery/terraform-aws-cloudquery) module where multiple modules by [terraform-aws-modules](https://github.com/terraform-aws-modules/) are being used together to manage the infrastructure as well as using Docker resources to build, push, and deploy Docker images. All in one set.

## Composition

Composition is a collection of infrastructure modules, which can span across several logically separated areas (e.g.., AWS Regions, several AWS accounts). Composition is used to describe the complete infrastructure required for the whole organization or project.

A composition consists of infrastructure modules, which consist of resources modules, which implement individual resources.

![Simple infrastructure composition](/files/gRXjhRxnTLLcdax83v0g)

## Data source

Data source performs a read-only operation and is dependant on provider configuration, it is used in a resource module and an infrastructure module.

Data source `terraform_remote_state` acts as a glue for higher-level modules and compositions.

The [external](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) data source allows an external program to act as a data source, exposing arbitrary data for use elsewhere in the Terraform configuration. Here is an example from the [terraform-aws-lambda module](https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/258e82b50adc451f51544a2b57fd1f6f8f4a61e4/package.tf#L5-L7) where the filename is computed by calling an external Python script.

The [http](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) data source makes an HTTP GET request to the given URL and exports information about the response which is often useful to get information from endpoints where a native Terraform provider does not exist.

## Remote state

Store [Terraform state](https://www.terraform.io/docs/language/state/index.html) for each infrastructure module and composition in a remote backend, configured with ACLs, versioning, and logging. This single, authoritative source of truth keeps environments consistent and typically includes disaster-recovery features such as automated backups. Managing state locally can lead to collaboration issues and race conditions when multiple developers run Terraform at the same time, resulting in unpredictable outcomes.

## Provider, provisioner, etc

Providers, provisioners, and a few other terms are described very well in the official documentation and there is no point to repeat it here. To my opinion, they have little to do with writing good Terraform modules.

## Why so *difficult*?

While individual resources are like atoms in the infrastructure, resource modules are molecules (consisting of atoms). A module is the smallest versioned and shareable unit. It has an exact list of arguments, implement basic logic for such a unit to do the required function. e.g., [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group) module creates `aws_security_group` and `aws_security_group_rule` resources based on input. This resource module by itself can be used together with other modules to create the infrastructure module.

Access to data across molecules (resource modules and infrastructure modules) is performed using the modules' outputs and data sources.

Access between compositions is often performed using remote state data sources. There are [multiple ways to share data between configurations](https://www.terraform.io/docs/language/state/remote-state-data.html#alternative-ways-to-share-data-between-configurations).

When putting concepts described above in pseudo-relations it may look like this:

```
composition-1 {
  infrastructure-module-1 {
    data-source-1 => d1

    resource-module-1 {
      data-source-2 => d2
      resource-1 (d1, d2)
      resource-2 (d2)
    }

    resource-module-2 {
      data-source-3 => d3
      resource-3 (d1, d3)
      resource-4 (d3)
    }
  }
}
```


# Code structure

Questions related to Terraform code structure are by far the most frequent in the community. Everyone thought about the best code structure for the project at some point also.

## How should I structure my Terraform configurations?

This is one of the questions where lots of solutions exist and it is very hard to give universal advice, so let's start with understanding what are we dealing with.

* What is the complexity of your project?
  * Number of related resources
  * Number of Terraform providers (see note below about "logical providers")
* How often does your infrastructure change?
  * **From** once a month/week/day
  * **To** continuously (every time when there is a new commit)
* Code change initiators? *Do you let the CI server update the repository when a new artifact is built?*
  * Only developers can push to the infrastructure repository
  * Everyone can propose a change to anything by opening a PR (including automated tasks running on the CI server)
* Which deployment platform or deployment service do you use?
  * AWS CodeDeploy, Kubernetes, or OpenShift require a slightly different approach
* How environments are grouped?
  * By environment, region, project

{% hint style="info" %}
*Logical providers* work entirely within Terraform's logic and very often don't interact with any other services, so we can think about their complexity as O(1). The most common logical providers include [random](https://registry.terraform.io/providers/hashicorp/random/latest/docs), [local](https://registry.terraform.io/providers/hashicorp/local/latest/docs), [terraform](https://www.terraform.io/docs/providers/terraform/index.html), [null](https://registry.terraform.io/providers/hashicorp/null/latest/docs), [time](https://registry.terraform.io/providers/hashicorp/time/latest).
{% endhint %}

## Getting started with the structuring of Terraform configurations

Putting all code in `main.tf` is a good idea when you are getting started or writing an example code. In all other cases you will be better having several files split logically like this:

* `main.tf` - call modules, locals, and data sources to create all resources
* `variables.tf` - contains declarations of variables used in `main.tf`
* `outputs.tf` - contains outputs from the resources created in `main.tf`
* `versions.tf` - contains version requirements for Terraform and providers

`terraform.tfvars` should not be used anywhere except [composition](/key-concepts#composition).

## How to think about Terraform configuration structure?

{% hint style="info" %}
Please make sure that you understand key concepts - [resource module](/key-concepts#resource-module), [infrastructure module](/key-concepts#infrastructure-module), and [composition](/key-concepts#composition), as they are used in the following examples.
{% endhint %}

### Common recommendations for structuring code

* It is easier and faster to work with a smaller number of resources
  * `terraform plan` and `terraform apply` both make cloud API calls to verify the status of resources
  * If you have your entire infrastructure in a single composition this can take some time
* A blast radius (in case of security breach) is smaller with fewer resources
  * Insulating unrelated resources from each other by placing them in separate compositions reduces the risk if something goes wrong
* Start your project using remote state because:
  * Your laptop is no place for your infrastructure source of truth
  * Managing a `tfstate` file in git is a nightmare
  * Later when infrastructure layers start to grow in multiple directions (number of dependencies or resources) it will be easier to keep things under control
* Practice a consistent structure and [naming](/naming) convention:
  * Like procedural code, Terraform code should be written for people to read first, consistency will help when changes happen six months from now
  * It is possible to move resources in Terraform state file but it may be harder to do if you have inconsistent structure and naming
* Keep resource modules as plain as possible
* Don't hardcode values that can be passed as variables or discovered using data sources
* Use data sources and `terraform_remote_state` specifically as a glue between infrastructure modules within the composition

In this book, example projects are grouped by *complexity* - from small to very-large infrastructures. This separation is not strict, so check other structures also.

### Orchestration of infrastructure modules and compositions

Having a small infrastructure means that there is a small number of dependencies and few resources. As the project grows the need to chain the execution of Terraform configurations, connecting different infrastructure modules, and passing values within a composition becomes obvious.

There are at least 5 distinct groups of orchestration solutions that developers use:

1. Terraform only. Very straightforward, developers have to know only Terraform to get the job done.
2. Terragrunt. Pure orchestration tool which can be used to orchestrate the entire infrastructure as well as handle dependencies. Terragrunt operates with infrastructure modules and compositions natively, so it reduces duplication of code.
3. In-house scripts. Often this happens as a starting point towards orchestration and before discovering Terragrunt.
4. Ansible or similar general purpose automation tool. Usually used when Terraform is adopted after Ansible, or when Ansible UI is actively used.
5. [Crossplane](https://crossplane.io) and other Kubernetes-inspired solutions. Sometimes it makes sense to utilize the Kubernetes ecosystem and employ a reconciliation loop feature to achieve the desired state of your Terraform configurations. View video [Crossplane vs Terraform](https://www.youtube.com/watch?v=ELhVbSdcqSY) for more information.

With that in mind, this book reviews the first two of these project structures, Terraform only and Terragrunt.

See examples of code structures for [Terraform](/examples/terraform) or [Terragrunt](/examples/terragrunt) in the next chapter.


# Code structure examples

## Terraform code structures

{% hint style="info" %}
These examples are showing AWS provider but the majority of principles shown in the examples can be applied to other public cloud providers as well as other kinds of providers (DNS, DB, Monitoring, etc)
{% endhint %}

| Type                                                                  | Description                                                                                                                                     | Readiness |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| [small](/examples/terraform/small-size-infrastructure)                | Few resources, no external dependencies. Single AWS account. Single region. Single environment.                                                 | Yes       |
| [medium](/examples/terraform/medium-size-infrastructure)              | Several AWS accounts and environments, off-the-shelf infrastructure modules using Terraform.                                                    | Yes       |
| [large](/examples/terraform/large-size-infrastructure-with-terraform) | Many AWS accounts, many regions, urgent need to reduce copy-paste, custom infrastructure modules, heavy usage of compositions. Using Terraform. | WIP       |
| very-large                                                            | Several providers (AWS, GCP, Azure). Multi-cloud deployments. Using Terraform.                                                                  | No        |

## Terragrunt code structures

| Type       | Description                                                                                                                                      | Readiness |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------- |
| medium     | Several AWS accounts and environments, off-the-shelf infrastructure modules, composition pattern using Terragrunt.                               | No        |
| large      | Many AWS accounts, many regions, urgent need to reduce copy-paste, custom infrastructure modules, heavy usage of compositions. Using Terragrunt. | No        |
| very-large | Several providers (AWS, GCP, Azure). Multi-cloud deployments. Using Terragrunt.                                                                  | No        |


# Terragrunt


# Terraform


# Small-size infrastructure with Terraform

Source: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/small-terraform>

This example contains code as an example of structuring Terraform configurations for a small-size infrastructure, where no external dependencies are used.

{% hint style="success" %}

* Perfect to get started and refactor as you go
* Perfect for small resource modules
* Good for small and linear infrastructure modules (eg, [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis))
* Good for a small number of resources (fewer than 20-30)
  {% endhint %}

{% hint style="warning" %}
Single state file for all resources can make the process of working with Terraform slow if the number of resources is growing (consider using an argument `-target` to limit the number of resources)
{% endhint %}


# Medium-size infrastructure with Terraform

Source: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/medium-terraform>

This example contains code as an example of structuring Terraform configurations for a medium-size infrastructure which uses:

* 2 AWS accounts
* 2 separate environments (`prod` and `stage` which share nothing). Each environment lives in a separate AWS account
* Each environment uses a different version of the off-the-shelf infrastructure module (`alb`) sourced from [Terraform Registry](https://registry.terraform.io/)
* Each environment uses the same version of an internal module `modules/network` since it is sourced from a local directory.

{% hint style="success" %}

* Perfect for projects where infrastructure is logically separated (separate AWS accounts)
* Good when there is no need to modify resources shared between AWS accounts (one environment = one AWS account = one state file)
* Good when there is no need in the orchestration of changes between the environments
* Good when infrastructure resources are different per environment on purpose and can't be generalized (eg, some resources are absent in one environment or in some regions)
  {% endhint %}

{% hint style="warning" %}
As the project grows, it will be harder to keep these environments up-to-date with each other. Consider using infrastructure modules (off-the-shelf or internal) for repeatable tasks.
{% endhint %}

##


# Large-size infrastructure with Terraform

Source: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/large-terraform>

This example contains code as an example of structuring Terraform configurations for a large-size infrastructure which uses:

* 2 AWS accounts
* 2 regions
* 2 separate environments (`prod` and `stage` which share nothing). Each environment lives in a separate AWS account and span resources between 2 regions
* Each environment uses a different version of the off-the-shelf infrastructure module (`alb`) sourced from [Terraform Registry](https://registry.terraform.io/)
* Each environment uses the same version of an internal module `modules/network` since it is sourced from a local directory.

{% hint style="info" %}
In a large project like described here the benefits of using Terragrunt become very visible. See [Code Structures examples with Terragrunt](/examples/terragrunt).
{% endhint %}

{% hint style="success" %}

* Perfect for projects where infrastructure is logically separated (separate AWS accounts)
* Good when there is no need to modify resources shared between AWS accounts (one environment = one AWS account = one state file)
* Good when there is no need for the orchestration of changes between the environments
* Good when infrastructure resources are different per environment on purpose and can't be generalized (eg, some resources are absent in one environment or in some regions)
  {% endhint %}

{% hint style="warning" %}
As the project grows, it will be harder to keep these environments up-to-date with each other. Consider using infrastructure modules (off-the-shelf or internal) for repeatable tasks.
{% endhint %}

##


# Naming conventions

## General conventions

{% hint style="info" %}
There should be no reason to not follow at least these conventions :)
{% endhint %}

{% hint style="info" %}
Beware that actual cloud resources often have restrictions in allowed names. Some resources, for example, can't contain dashes, some must be camel-cased. The conventions in this book refer to Terraform names themselves.
{% endhint %}

1. Use `_` (underscore) instead of `-` (dash) everywhere (in resource names, data source names, variable names, outputs, etc).
2. Prefer to use lowercase letters and numbers (even though UTF-8 is supported).

## Resource and data source arguments

1. Do not repeat resource type in resource name (not partially, nor completely):

{% hint style="success" %}

```
`resource "aws_route_table" "public" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_route_table" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_aws_route_table" {}`
```

{% endhint %}

2. Resource name should be named `this` if there is no more descriptive and general name available, or if the resource module creates a single resource of this type (eg, in [AWS VPC module](https://github.com/terraform-aws-modules/terraform-aws-vpc) there is a single resource of type `aws_nat_gateway` and multiple resources of type`aws_route_table`, so `aws_nat_gateway` should be named `this` and `aws_route_table` should have more descriptive names - like `private`, `public`, `database`).
3. Always use singular nouns for names.
4. Use `-` inside arguments values and in places where value will be exposed to a human (eg, inside DNS name of RDS instance).
5. Include argument `count` / `for_each` inside resource or data source block as the first argument at the top and separate by newline after it.
6. Include argument `tags,` if supported by resource, as the last real argument, following by `depends_on` and `lifecycle`, if necessary. All of these should be separated by a single empty line.
7. When using conditions in an argument`count` / `for_each` prefer boolean values instead of using `length` or other expressions.

## Code examples of `resource`

### Usage of `count` / `for_each`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  count = 2

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}

resource "aws_route_table" "private" {
  for_each = toset(["one", "two"])

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  vpc_id = "vpc-12345678"
  count  = 2

  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

### Placement of `tags`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  allocation_id = "..."
  subnet_id     = "..."

  tags = {
    Name = "..."
  }

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  tags = "..."

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }

  allocation_id = "..."
  subnet_id     = "..."
}
```

{% endcode %}
{% endhint %}

### Conditions in `count`

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
resource "aws_nat_gateway" "that" {    # Best
  count = var.create_public_subnets ? 1 : 0
}

resource "aws_nat_gateway" "this" {    # Good
  count = length(var.public_subnets) > 0 ? 1 : 0
}
```

{% endcode %}
{% endhint %}

## Variables

1. Don't reinvent the wheel in resource modules: use `name`, `description`, and `default` value for variables as defined in the "Argument Reference" section for the resource you are working with.
2. Support for validation in variables is rather limited (e.g. can't access other variables or do lookups if using a version before Terraform `1.9`). Plan accordingly because in many cases this feature is useless.
3. Use the plural form in a variable name when type is `list(...)` or `map(...)`.
4. Order keys in a variable block like this: `description` , `type`, `default`, `validation`.
5. Always include `description` on all variables even if you think it is obvious (you will need it in the future). Use the same wording as the upstream documentation when applicable.
6. Prefer using simple types (`number`, `string`, `list(...)`, `map(...)`, `any`) over specific type like `object()` unless you need to have strict constraints on each key.
7. Use specific types like `map(map(string))` if all elements of the map have the same type (e.g. `string`) or can be converted to it (e.g. `number` type can be converted to `string`).
8. Use type `any` to disable type validation starting from a certain depth or when multiple types should be supported.
9. Value `{}` is sometimes a map but sometimes an object. Use `tomap(...)` to make a map because there is no way to make an object.
10. Avoid double negatives: use positive variable names to prevent confusion. For example, use `encryption_enabled` instead of `encryption_disabled`.
11. For variables that should never be `null`, set `nullable = false`. This ensures that passing `null` uses the default value instead of `null`. If `null` is an acceptable value, you can omit nullable or set it to `true`.

## Outputs

Make outputs consistent and understandable outside of its scope (when a user is using a module it should be obvious what type and attribute of the value it returns).

1. The name of output should describe the property it contains and be less free-form than you would normally want.
2. Good structure for the name of output looks like `{name}_{type}_{attribute}` , where:
   1. `{name}` is a resource or data source name
      * `{name}` for `data "aws_subnet" "private"` is `private`
      * `{name}` for `resource "aws_vpc_endpoint_policy" "test"` is `test`
   2. `{type}` is a resource or data source type without a provider prefix
      * `{type}` for `data "aws_subnet" "private"` is `subnet`
      * `{type}` for `resource "aws_vpc_endpoint_policy" "test"` is `vpc_endpoint_policy`
   3. `{attribute}` is an attribute returned by the output
   4. [See examples](#code-examples-of-output).
3. If the output is returning a value with interpolation functions and multiple resources, `{name}` and `{type}` there should be as generic as possible (`this` as prefix should be omitted). [See example](#code-examples-of-output).
4. If the returned value is a list it should have a plural name. [See example](#use-plural-name-if-the-returning-value-is-a-list).
5. Always include `description` for all outputs even if you think it is obvious.
6. Avoid setting `sensitive` argument unless you fully control usage of this output in all places in all modules.
7. Prefer `try()` (available since Terraform 0.13) over `element(concat(...))` (legacy approach for the version before 0.13)

### Code examples of `output`

Return at most one ID of security group:

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "security_group_id" {
  description = "The ID of the security group"
  value       = try(aws_security_group.this[0].id, aws_security_group.name_prefix[0].id, "")
}
```

{% endcode %}
{% endhint %}

When having multiple resources of the same type, `this` should be omitted in the name of output:

{% hint style="danger" %}
{% code title="outputs.tf" %}

```hcl
output "this_security_group_id" {
  description = "The ID of the security group"
  value       = element(concat(coalescelist(aws_security_group.this.*.id, aws_security_group.web.*.id), [""]), 0)
}
```

{% endcode %}
{% endhint %}

### Use plural name if the returning value is a list

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "rds_cluster_instance_endpoints" {
  description = "A list of all cluster instance endpoints"
  value       = aws_rds_cluster_instance.this.*.endpoint
}
```

{% endcode %}
{% endhint %}


# Code styling

{% hint style="info" %}

* Examples and Terraform modules should contain documentation explaining features and how to use them.
* All links in README.md files should be absolute to make Terraform Registry website show them correctly.
* Documentation may include diagrams created with [mermaid](https://github.com/mermaid-js/mermaid) and blueprints created with [cloudcraft.co](https://cloudcraft.co).
* Use [Terraform pre-commit hooks](https://github.com/antonbabenko/pre-commit-terraform) to make sure that the code is valid, properly formatted, and automatically documented before it is pushed to git and reviewed by humans.
  {% endhint %}

## Formatting

Terraform’s `terraform fmt` command enforces the canonical style for configuration files. The tool is intentionally opinionated and non-configurable, guaranteeing a uniform format across codebases so reviewers can focus on substance rather than style. Integrate it with [Terraform pre-commit hooks](https://github.com/antonbabenko/pre-commit-terraform) to validate and format code automatically before it reaches version control.

For example:

```yaml
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.99.4
    hooks:
      - id: terraform_fmt
```

In CI pipelines, use `terraform fmt -check` to verify compliance. It exits with status 0 when all files are correctly formatted; otherwise, it returns a non-zero code and lists the offending files. Centralizing formatting in this way removes merge friction and enforces a consistent standard across teams.

## Editor Configuration

* **Use `.editorconfig`**: [EditorConfig](https://editorconfig.org/) helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs. Include an `.editorconfig` file in your repositories to maintain consistent whitespace and indentation.

**Example `.editorconfig`:**

```editorconfig
[*]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

[*.{tf,tfvars}]
indent_style = space
indent_size = 2

[Makefile]
indent_style = tab
```

## Documentation

### Automatically generated documentation

[pre-commit](https://pre-commit.com/) is a framework for managing and maintaining multi-language pre-commit hooks. It is written in Python and is a powerful tool to do something automatically on a developer's machine before code is committed to a git repository. Normally, it is used to run linters and format code (see [supported hooks](https://pre-commit.com/hooks.html)).

With Terraform configurations `pre-commit` can be used to format and validate code, as well as to update documentation.

Check out the [pre-commit-terraform repository](https://github.com/antonbabenko/pre-commit-terraform/blob/master/README.md) to familiarize yourself with it, and existing repositories (eg, [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc)) where this is used already.

### terraform-docs

[terraform-docs](https://github.com/segmentio/terraform-docs) is a tool that does the generation of documentation from Terraform modules in various output formats. You can run it manually (without pre-commit hooks), or use [pre-commit-terraform hooks](https://github.com/antonbabenko/pre-commit-terraform) to get the documentation updated automatically.

### Comment style

Use `#` for comments. Avoid `//` or block comments.

**Example:**

```hcl
# This is a comment explaining the resource
resource "aws_instance" "this" {
# ...
}
```

**Section Headers**: Delimit section headers in code with `# -----` or `######` for clarity.

**Example:**

```hcl
# --------------------------------------------------
# AWS EC2 Instance Configuration
# --------------------------------------------------

resource "aws_instance" "this" {
# ...
}
```

@todo: Document module versions, release, GH actions

## Resources

1. [pre-commit framework homepage](https://pre-commit.com/)
2. [Collection of git hooks for Terraform to be used with pre-commit framework](https://github.com/antonbabenko/pre-commit-terraform)
3. Blog post by [Dean Wilson](https://github.com/deanwilson): [pre-commit hooks and terraform - a safety net for your repositories](https://www.unixdaemon.net/tools/terraform-precommit-hooks/)


# FAQ

FTP (Frequent Terraform Problems)

## What are the tools I should be aware of and consider using?

* [**Terragrunt**](https://terragrunt.gruntwork.io/) - Orchestration tool
* [**tflint**](https://github.com/terraform-linters/tflint) - Code linter
* [**tfenv**](https://github.com/tfutils/tfenv) - Version manager
* [**Atmos**](https://atmos.tools/) - A modern composable framework for Terraform backed by YAML
* [**asdf-hashicorp**](https://github.com/asdf-community/asdf-hashicorp) - HashiCorp plugin for the [asdf](https://github.com/asdf-vm/asdf) version manager
* [**Atlantis**](https://www.runatlantis.io/) - Pull Request automation
* [**pre-commit-terraform**](https://github.com/antonbabenko/pre-commit-terraform) - Collection of git hooks for Terraform to be used with [pre-commit framework](https://pre-commit.com/)
* [**Infracost**](https://www.infracost.io) - Cloud cost estimates for Terraform in pull requests. Works with Terragrunt, Atlantis and pre-commit-terraform too.

## What are the solutions to [dependency hell](https://en.wikipedia.org/wiki/Dependency_hell) with modules?

Versions of resource and infrastructure modules should be specified. Providers should be configured outside of modules, but only in composition. Version of providers and Terraform can be locked also.

There is no master dependency management tool, but there are some tips to make dependency specifications less problematic. For example, [Dependabot](https://dependabot.com/) can be used to automate dependency updates. Dependabot creates pull requests to keep your dependencies secure and up-to-date. Dependabot supports Terraform configurations.


# References

{% hint style="info" %}
There are a lot of people who create great content and manage open-source projects relevant to the Terraform community but I can't think of the best structure to get these links listed here without copying lists like [awesome-terraform](https://github.com/shuaibiyy/awesome-terraform).
{% endhint %}

<https://x.com/i/lists/1042729226057732096> - List of people who work with Terraform very actively and can tell you a lot (if you ask them).

<https://www.hashicorp.com/ambassador/directory?products=Terraform> - A community of individuals who actively share their Terraform knowledge through content, events, and open collaboration.

<https://github.com/shuaibiyy/awesome-terraform> - Curated list of resources on HashiCorp's Terraform.

<http://bit.ly/terraform-youtube> - "Your Weekly Dose of Terraform" YouTube channel by Anton Babenko. Live streams with reviews, interviews, Q\&A, live coding, and some hacking with Terraform.

<https://weekly.tf> - Terraform Weekly newsletter. Various news in the Terraform world (projects, announcements, discussions) by Anton Babenko.


# Writing Terraform configurations

## Use `locals` to specify explicit dependencies between resources

Helpful way to give a hint to Terraform that some resources should be deleted before even when there is no direct dependency in Terraform configurations.

<https://raw.githubusercontent.com/antonbabenko/terraform-best-practices/master/snippets/locals.tf>

## Terraform 0.12 - Required vs Optional arguments

1. Required argument `index_document` must be set, if `var.website` is not an empty map.
2. Optional argument `error_document` can be omitted.

{% code title="main.tf" %}

```hcl
variable "website" {
  type    = map(string)
  default = {}
}

resource "aws_s3_bucket" "this" {
  # omitted...

  dynamic "website" {
    for_each = length(keys(var.website)) == 0 ? [] : [var.website]

    content {
      index_document = website.value.index_document
      error_document = lookup(website.value, "error_document", null)
    }
  }
}
```

{% endcode %}

{% code title="terraform.tfvars" %}

```hcl
website = {
  index_document = "index.html"
}
```

{% endcode %}

### Optional Object Attributes (Terraform 1.3+)

Use optional attributes in objects to provide default values for non-required fields:

{% code title="variables.tf" %}

```hcl
variable "database_settings" {
  description = "Database configuration with optional parameters"
  type = object({
    name               = string
    engine             = string
    instance_class     = string
    backup_retention   = optional(number, 7)
    monitoring_enabled = optional(bool, true)
    tags               = optional(map(string), {})
  })
}
```

{% endcode %}

## Managing Secrets in Terraform

Secrets are sensitive data that can be anything from passwords and encryption keys to API tokens and service certificates. They are typically used to set up authentication and authorization for cloud resources. Safeguarding these sensitive resources is crucial because exposure could lead to security breaches. It’s highly recommended to avoid storing secrets in Terraform config and state, as anyone with access to version control can access them. Instead, consider using external data sources to fetch secrets from external sources at runtime. For instance, if you’re using AWS Secrets Manager, you can use the `aws_secretsmanager_secret_version` data source to access the secret value. The following example uses write-only arguments, which are supported in Terraform 1.11+, and keep the value out of Terraform state.

{% code title="main.tf" %}

```hcl
# Fetch the secret’s metadata
data "aws_secretsmanager_secret" "db_password" {
  name = "my-database-password"
}

# Get the latest secret value
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = data.aws_secretsmanager_secret.db_password.id
}

# Use the secret without persisting it to state
resource "aws_db_instance" "example" {
  engine         = "mysql"
  instance_class = "db.t3.micro"
  name           = "exampledb"
  username       = "admin"

  # write-only: Terraform sends it to AWS then forgets it
  password_wo = data.aws_secretsmanager_secret_version.db_password.secret_string
```

{% endcode %}

## Variable Validation and Input Handling

{% hint style="info" %}
Variable validation helps catch errors early, provides clear feedback, and ensures inputs meet your requirements.
{% endhint %}

### Basic Variable Validation

Use validation blocks to ensure variables meet specific criteria:

{% code title="variables.tf" %}

```hcl
variable "environment" {
  description = "Environment name for resource tagging"
  type        = string
  default     = "dev"

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be one of: dev, staging, prod."
  }
}
```

{% endcode %}

### Object and List Validation

Validate complex data structures to ensure they contain expected values:

{% code title="variables.tf" %}

```hcl
variable "database_config" {
  description = "Database configuration"
  type = object({
    engine            = string
    instance_class    = string
    allocated_storage = number
  })

  validation {
    condition     = contains(["mysql", "postgres"], var.database_config.engine)
    error_message = "Database engine must be either 'mysql' or 'postgres'."
  }
}

variable "allowed_cidr_blocks" {
  description = "List of CIDR blocks allowed to access resources"
  type        = list(string)

  validation {
    condition = alltrue([
      for cidr in var.allowed_cidr_blocks : can(cidrhost(cidr, 0))
    ])
    error_message = "All CIDR blocks must be valid IPv4 CIDR notation."
  }
}
```

{% endcode %}


# Workshop

There is also a workshop for people who want to practice some of the things described in this guide.

The content is here - <https://github.com/antonbabenko/terraform-best-practices-workshop>


# Bienvenue

Ce document a pour but de décrire systématiquement les bonnes pratiques dans l’utilisation de Terraform et de fournir des recommandations par rapport aux problèmatiques fréquemment rencontrées.

[Terraform](https://www.terraform.io), un projet relativement nouveau (comme la plus part des outils Devops actuellement), a été lancé en 2014.

Terraform est un outil puissant (si ce n'est le plus puissant actuellement disponible) et le plus utilisé pour le gestion de l'infrastructure comme code. Il permet aux developpeurs de créer plusieurs codes dont le support et l'intégration seront faciles

Certaines informations décrit dans ce livre pourraient ne pas ressembler aux bonnes pratiques. J'en suis conscient, et pour aider les lecteurs à séparer ce qui établit comme bonnes pratiques et ce que je considère être d'autres méthodes équivalentes, j'utiliserai par moment des indications pour fournir un certain contexte et des icônes pour spécifier le niveau de maturité de chaque sous-section reliée aux bonnes pratiques

Ce livre a été commencé dans une ville de Madrid ensoleillée en 2018 et est disponible gratuitement ici [https://www.terraform-best-practices.com/](https://www.terraform-best-practices.com)

Quelques années plus tard il a été mis à jour grâce à plusieurs récentes bonnes pratiques disponibles avec Terraform 1.0. Éventuellement ce livre devrait contenir la plupart des bonnes pratiques et recommandations indiscutables pour les utilisateurs de Terraform.

## Sponsors

Please [contact me](https://github.com/antonbabenko/terraform-aws-devops#social-links) if you want to become a sponsor.

| [![](/files/7oevEkIm8LVxbn6BfTf6)](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) | [Compliance.tf](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) — Terraform Compliance Simplified. Make your Terraform modules compliance-ready. |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![](https://github.com/antonbabenko/terraform-best-practices/blob/fr/.gitbook/assets)](/fr)                    | —                                                                                                                                                                             |

## Translations

{% content-ref url="/spaces/u3iITRIHQx97ro2PkfdC" %}
[العربية (Arabic)](https://www.terraform-best-practices.com/ar/)
{% endcontent-ref %}

{% content-ref url="/spaces/PJbgKPAX0ohEMLpETpg7" %}
[Bosanski (Bosnian)](https://www.terraform-best-practices.com/ba/)
{% endcontent-ref %}

{% content-ref url="/spaces/B48qUSNPO2XmkIySLzfr" %}
[Português (Brazilian Portuguese)](https://www.terraform-best-practices.com/ptbr/)
{% endcontent-ref %}

{% content-ref url="/spaces/e1Mp2scOX6OnQbifCen3" %}
[English](https://www.terraform-best-practices.com/)
{% endcontent-ref %}

{% content-ref url="/spaces/DyguS0uZfMW7X7m9BWx1" %}
[ქართული (Georgian)](https://www.terraform-best-practices.com/ka/)
{% endcontent-ref %}

{% content-ref url="/spaces/PKopCWJZbhpQ9FT0W8tL" %}
[Deutsch (German)](https://www.terraform-best-practices.com/de/)
{% endcontent-ref %}

{% content-ref url="/spaces/5c1kFpqxaDZC2g9e6rtT" %}
[ελληνικά (Greek)](https://www.terraform-best-practices.com/el/)
{% endcontent-ref %}

{% content-ref url="/spaces/4bq6CyY8vYiEHkjN63rT" %}
[עברית (Hebrew)](https://www.terraform-best-practices.com/he/)
{% endcontent-ref %}

{% content-ref url="/spaces/Mgong4S6IjtibE055zUM" %}
[हिंदी (Hindi)](https://www.terraform-best-practices.com/hi/)
{% endcontent-ref %}

{% content-ref url="/spaces/ZLCz7lNWbSJxDGuNOI44" %}
[Bahasa Indonesia (Indonesian)](https://www.terraform-best-practices.com/id/)
{% endcontent-ref %}

{% content-ref url="/spaces/8VlMHbHDbW6qRWdgN5oU" %}
[Italiano (Italian)](https://www.terraform-best-practices.com/it/)
{% endcontent-ref %}

{% content-ref url="/spaces/3vykLOWgdQLPLgHtxqQH" %}
[日本語 (Japanese)](https://www.terraform-best-practices.com/ja/)
{% endcontent-ref %}

{% content-ref url="/spaces/BoZVs6O2OJFQLNV1utmm" %}
[ಕನ್ನಡ (Kannada)](https://www.terraform-best-practices.com/kn/)
{% endcontent-ref %}

{% content-ref url="/spaces/bJnDvAqIyVgo7LDHgxYJ" %}
[한국어 (Korean)](https://www.terraform-best-practices.com/ko/)
{% endcontent-ref %}

{% content-ref url="/spaces/9yChMGbFo2G47Wiow1yY" %}
[Polski (Polish)](https://www.terraform-best-practices.com/pl/)
{% endcontent-ref %}

{% content-ref url="/spaces/sFM1GW5TPCGsskQ03mTm" %}
[Română (Romanian)](https://www.terraform-best-practices.com/ro/)
{% endcontent-ref %}

{% content-ref url="/spaces/5VD4NK4mHOY8SWjC9N5e" %}
[简体中文 (Simplified Chinese)](https://www.terraform-best-practices.com/zh/)
{% endcontent-ref %}

{% content-ref url="/spaces/fTxekzr50pIuGmrPkXUD" %}
[Español (Spanish)](https://www.terraform-best-practices.com/es/)
{% endcontent-ref %}

{% content-ref url="/spaces/Fedpbc5NbKjynXI8xTeF" %}
[Türkçe (Turkish)](https://www.terraform-best-practices.com/tr/)
{% endcontent-ref %}

{% content-ref url="/spaces/tXRvMPILxeJaJTM2CsSq" %}
[Українська (Ukrainian)](https://www.terraform-best-practices.com/uk/)
{% endcontent-ref %}

{% content-ref url="/spaces/dcjhau04KQIKHUJA90iN" %}
[اردو (Urdu)](https://www.terraform-best-practices.com/ur/)
{% endcontent-ref %}

Contactez-moi si vous voulez aider à traduire ce livre dans d'autres langues.

## Contributions

Je souhaite toujours obtenir des commentaires et mettre à jour ce livre au fur et à mesure que la communauté mûrit et que de nouvelles idées sont mises en œuvre et vérifiées au fil du temps. Si vous êtes intéressé par certains sujets, veuillez ouvrir un problème ou en indiquer un que vous souhaitez être traiter plus en détail. Si vous sentez que vous avez du contenu et que vous souhaitez y contribuer, rédigez un brouillon et soumettez un pull request (ne vous souciez pas d'écrire un bon texte à ce stade !)

## Authors

Ce livre est maintenu par Anton Babenko avec l'aide de différents contributeurs et traducteurs. Nicanor Foping l'a traduit en français.

## License

Ce travail est sous licence Apache 2. Voir LICENCE pour plus de détails.

Les auteurs et contributeurs de ce contenu ne peuvent garantir la validité des informations trouvées ici. Veuillez vous assurer que vous comprenez que les informations fournies ici sont fournies librement et qu'aucun type d'accord ou de contrat n'est créé entre vous et toute personne associée à ce contenu ou projet. Les auteurs et les contributeurs n'assument pas et déclinent par la présente toute responsabilité envers toute partie pour toute perte, dommage ou perturbation causé par des erreurs ou des omissions dans les informations contenues dans, associées ou liées à ce contenu, que ces erreurs ou omissions résultent de négligence, accident ou toute autre cause.

Copyright © 2018-2023 Anton Babenko.


# Concepts clés

La documentation officielle de Terraform décrit [tous les aspects de la configuration en détail](https://www.terraform.io/docs/configuration/index.html). Il faudrait la lire attentivement pour comprendre le reste de cette section

Cette section décrit les concepts clés qui seront utilisés dans le livre.

## Ressource

Une ressource est un objet comme`aws_vpc`, `aws_db_instance`, etc. Une ressource appartient à un fournisseur, accepte des arguments, génère des attributs et possède des cycles de vie. Une ressource peut être créée, récupérée, mise à jour et supprimée.

## Module de ressources

Un module de ressources est un ensemble de ressources connectées qui exécutent mutuellement l'action commune (par exemple, le module AWS VPC Terraform crée un VPC, des sous-réseaux, une passerelle NAT, etc.). Il dépend de la configuration du fournisseur, qui peut être définie dans celui-ci, ou dans des structures de niveau supérieur (par exemple, dans le module d'infrastructure).

## Module d'infrastructure

Un module d'infrastructure est un ensemble de modules de ressources, qui peuvent logiquement ne pas être connectés, mais dans la situation/projet/configuration actuels, ils ont le même objectif. Il définit la configuration des fournisseurs, qui est transmise aux modules de ressources en aval et aux ressources. Il est normalement limité au travail dans une entité par un séparateur logique (par exemple, AWS Region, Google Project).

Par exemple, le module [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis/) utilise des modules de ressources comme [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc/) et [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group/) pour gérer l'infrastructure requise afin d'opérationneliser [Atlantis](https://www.runatlantis.io) sur [AWS Fargate](https://aws.amazon.com/fargate/).

Un autre exemple est le module [terraform-aws-cloudquery](https://github.com/cloudquery/terraform-aws-cloudquery) qui emploie plusieurs modules de [terraform-aws-modules](https://github.com/terraform-aws-modules/) ensemble afin de gérer l'infrastructure et utilisent les ressources Docker pour créer, pousser et déployer des images Docker. Tout en un ensemble.

## Composition

La composition est une collection de modules d'infrastructure, qui peuvent s'étendre sur plusieurs zones logiquement séparées (par exemple, des régions AWS, plusieurs comptes AWS). La composition est utilisée pour décrire l'infrastructure complète requise pour l'ensemble de l'organisation ou du projet.

Une composition est constituée de modules d'infrastructure, qui comprennent des modules de ressources implémentant des ressources individuelles.

![Simple infrastructure composition](/files/gRXjhRxnTLLcdax83v0g)

## Source de données

La source de données effectue une opération en lecture seule et dépend de la configuration du fournisseur. Elle est utilisée dans un module de ressources et un module d'infrastructure.

La source de données `terraform_remote_state`agit comme une colle (lien) pour les modules et les compositions de niveau supérieur.

La source de données [externe ](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/data_source)permet à un programme externe d'agir en tant que source de données, exposant des données arbitraires à utiliser ailleurs dans la configuration Terraform. En voici un exemple à partir du module [terraform-aws-lambda](https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/258e82b50adc451f51544a2b57fd1f6f8f4a61e4/package.tf#L5-L7) où le nom de fichier est obtenu en appelant un script Python externe.

La source de données [http ](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http)envoie une requête HTTP GET à l'URL donnée et exporte des informations liées à la réponse. Ces dernières sont souvent utiles pour obtenir des informations à partir de points de terminaison où un fournisseur Terraform natif n'existe pas.

## État distant

Les modules et les compositions d'infrastructure doivent conserver leur [état Terraform](https://www.terraform.io/docs/language/state/index.html) dans un emplacement distant où il peut être récupéré par d'autres de manière contrôlable (par exemple, l'accès spécifique à l'ACL, la gestion des versions, la journalisation).

## Fournisseur, commission etc

Les fournisseurs, les commission (provisioner) et quelques autres termes sont très bien décrits dans la documentation officielle et il est inutile de le répéter ici. À mon avis, ils ont peu à voir avec l'écriture de bons modules Terraform.

## *Pourquoi est ce si difficile*?

Alors que les ressources individuelles sont comme des atomes dans l'infrastructure, les modules de ressources sont des molécules. Un module est la plus petite unité versionnable et partageable. Il a une liste exacte d'arguments, implémente une logique de base pour qu'une telle unité remplisse la fonction requise. Par exemple, le module [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group) crée des ressources `aws_security_group` et`aws_security_group_rule` en fonction de l'entrée. Ce module de ressources en lui-même peut être utilisé avec d'autres modules pour créer le module d'infrastructure.

L'accès aux données à travers les molécules (modules de ressources et modules d'infrastructure) est effectué à l'aide des sorties et des sources de données des modules.

L'accès entre les compositions est souvent effectué à l'aide de sources de données à distance. Il existe [plusieurs façons de partager des données entre les configurations](https://www.terraform.io/docs/language/state/remote-state-data.html#alternative-ways-to-share-data-between-configurations).

Lorsque vous mettez les concepts décrits ci-dessus dans des pseudo-relations, cela peut ressembler à ceci :

```
composition-1 {
  infrastructure-module-1 {
    data-source-1 => d1

    resource-module-1 {
      data-source-2 => d2
      resource-1 (d1, d2)
      resource-2 (d2)
    }

    resource-module-2 {
      data-source-3 => d3
      resource-3 (d1, d3)
      resource-4 (d3)
    }
  }

}
```


# Structure du code

Les questions liées à la structure du code Terraform sont de loin les plus fréquentes dans la communauté. Tout le monde a également pensé à la meilleure structure de code pour le projet à un moment donné.

## Comment devrais-je structurer mes configurations Terraform?

C'est l'une des questions pour lesquelles de nombreuses solutions existent, mais il est très difficile de donner des conseils universels, alors commençons par comprendre à quoi nous avons affaire.

* Quelle est la complexité de votre projet?
  * Nombre de ressources associées
  * Nombre de fournisseurs Terraform (voir la remarque ci-dessous sur les "fournisseurs logiques")
* À quelle fréquence votre infrastructure change-t-elle ?
  * À partir d'une fois par mois/semaine/jour
  * À continuellement (à chaque fois qu'il y a un nouveau commit)
* Quelles sont les initiateurs de changement de code? Laissez-vous le serveur CI mettre à jour le référentiel lorsqu'un nouvel artefact est créé ?
  * Seuls les développeurs peuvent pousser vers le référentiel d'infrastructure?
  * Tout le monde peut proposer un changement à n'importe quoi en ouvrant un PR (y compris les tâches automatisées exécutées sur le serveur CI)
* Quelle plate-forme de déploiement ou service de déploiement utilisez-vous ?
  * AWS CodeDeploy, Kubernetes ou OpenShift nécessitent une approche légèrement différente
* Comment les environnements sont-ils regroupés ?
  * Par environnement, région, projet

{% hint style="info" %}
Les fournisseurs logiques fonctionnent entièrement dans la logique de Terraform et très souvent n'interagissent avec aucun autre service, nous pouvons donc considérer leur complexité comme O(1). Les fournisseurs logiques les plus courants incluent [random](https://registry.terraform.io/providers/hashicorp/random/latest/docs), [local](https://registry.terraform.io/providers/hashicorp/local/latest/docs), [terraform](https://www.terraform.io/docs/providers/terraform/index.html), [null](https://registry.terraform.io/providers/hashicorp/null/latest/docs), [time](https://registry.terraform.io/providers/hashicorp/time/latest).
{% endhint %}

## Initiation à la structuration des configurations Terraform

Mettre tout le code dans main.tf est une bonne idée lorsque vous débutez ou que vous écrivez un exemple de code. Dans tous les autres cas, il sera préférable d'avoir plusieurs fichiers répartis logiquement comme ceci :

* `main.tf` - appelle les modules, les variables locals et les sources de données pour créer toutes les ressources
* `variables.tf` - contient les variables qui seront utilisées dans `main.tf`
* `outputs.tf` - contient les sorties des ressources créées dans `main.tf`
* `versions.tf` - contient les exigences de version pour Terraform et les fournisseurs

`terraform.tfvars` ne doit être utilisé nulle part sauf [composition](/fr/key-concepts#composition).

## Comment structurer les configurations Terraform?

{% hint style="info" %}
Veuillez vous assurer que vous comprenez les concepts clés - [resource module](/fr/key-concepts#resource-module), [infrastructure module](/fr/key-concepts#infrastructure-module), et [composition](/fr/key-concepts#composition), tels qu'ils sont utilisés dans les exemples suivants.
{% endhint %}

### Recommandations courantes pour structurer le code

* Il est plus facile et plus rapide de travailler avec un plus petit nombre de ressources
  * `terraform plan` et`terraform apply` effectuent tous deux des appels d'API cloud pour vérifier l'état des ressources
  * Si vous avez toute votre infrastructure dans une seule composition, cela peut prendre un certain temps
* Le surface d'exposition est plus petit avec moins de ressources
  * Isoler les ressources non liées les unes des autres en les plaçant dans des compositions séparées réduit le risque en cas de problème
* Démarrez votre projet en utilisant l'état distant car :
  * Votre ordinateur portable n'est pas une source fiable pour votre infrastructure
  * Gérer un fichier `tfstate` file dans un git est cauchemar
  * Plus tard, lorsque les couches d'infrastructure commenceront à se développer dans plusieurs directions (nombre de dépendances ou de ressources), il sera plus facile de garder les choses sous contrôle
* Adoptez une structure et une convention de [dénomination ](/fr/naming)cohérentes :
  * Comme tout code procédural, le code Terraform doit être écrit pour permettre d'abord aux gens de le lire. Sa cohérence aidera lorsque des changements se produiront dans une période de six mois
  * Il est possible de déplacer des ressources dans le fichier d'état Terraform, mais cela peut être plus difficile à faire si vous avez une structure et un nom incohérents
* Gardez les modules de ressources aussi clairs que possible
* Ne codez pas en dur les valeurs qui peuvent être transmises en tant que variables ou découvertes à l'aide de sources de données
* Utilisez les sources de données et `terraform_remote_state` spécifiquement comme colle (liaison) entre les modules d'infrastructure au sein de la composition.

Dans ce livre, des exemples de projets sont regroupés par complexité - des petites aux très grandes infrastructures. Cette séparation n'est pas stricte, vérifiez donc également les autres structures.

### Orchestration des modules d'infrastructure et compositions

Avoir une petite infrastructure signifie qu'il y a un petit nombre de dépendances et peu de ressources. Au fur et à mesure que le projet se développe, la nécessité d'enchaîner l'exécution des configurations Terraform, de connecter différents modules d'infrastructure et de transmettre des valeurs au sein d'une composition devient évidente.

On dénombre au moins 5 groupes distincts de solutions d'orchestration utilisées par les développeurs :

1. Terraform uniquement. Très simple, les développeurs ne doivent connaître que Terraform pour faire le travail.
2. Terragrunt. Un pur outil d'orchestration qui peut être utilisé pour orchestrer l'ensemble de l'infrastructure ainsi que pour gérer les dépendances. Terragrunt fonctionne nativement avec des modules d'infrastructure et des compositions, ce qui réduit la duplication de code.
3. Scripts maison (personnel). Ils sont souvent utilisés comme point de départ vers l'orchestration et avant de découvrir Terragrunt.
4. Ansible ou les outils d'automatisation généraux similaires. Généralement utilisé lorsque Terraform est adopté après Ansible, ou lorsque l'UI Ansible est activement utilisée.
5. [Crossplane](https://crossplane.io) et autres solutions inspirées de Kubernetes. Parfois, il est logique d'utiliser l'écosystème Kubernetes et d'employer une fonction de boucle de réconciliation pour atteindre l'état souhaité de vos configurations Terraform. Voir la vidéo [Crossplane vs Terraform](https://www.youtube.com/watch?v=ELhVbSdcqSY) pour plus d'information.

Avec cela en tête, ce livre passe en revue les deux premières structures de projet ci-dessus, [Terraform](/fr/examples/terraform) uniquement ou [Terragrunt](/fr/examples/terragrunt).

Voir des exemples de structure de code pour [Terraform](/fr/examples/terraform) et [Terragrunt](/fr/examples/terragrunt) dans le prochain chapitre.


# Exemples de structure de code

## Structures de code Terraform

{% hint style="info" %}
Ces exemples montrent un fournisseur AWS, mais la majorité des principes présentés dans les exemples peuvent être appliqués à d'autres fournisseurs de cloud public ainsi qu'à d'autres types de fournisseurs (DNS, DB, Monitoring, etc.)
{% endhint %}

| Type                                                                     | Description                                                                                                                                                             | Préparation           |
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| [petit](/fr/examples/terraform/small-size-infrastructure)                | Peu de ressources, pas de dépendances externes. Compte AWS unique. Région unique. Environnement unique                                                                  | Oui                   |
| [moyen](/fr/examples/terraform/medium-size-infrastructure)               | Plusieurs comptes et environnements AWS, modules d'infrastructure prêts à l'emploi utilisant Terraform.                                                                 | Oui                   |
| [grand](/fr/examples/terraform/large-size-infrastructure-with-terraform) | Plusieurs régions, besoin urgent de réduire le copier-coller, modules d'infrastructure personnalisés, utilisation intensive des compositions. Utilisation de Terraform. | TeC(Travail en Cours) |
| Très grand                                                               | Plusieurs fournisseurs (AWS, GCP, Azure). Déploiements multi-cloud. Utilisation de Terraform.                                                                           | Non                   |

## Structures de code Terragrunt

| Type       | Description                                                                                                                                                              | Préparation |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- |
| moyen      | Plusieurs comptes et environnements AWS, modules d'infrastructure prêts à l'emploi utilisant Terragrunt.                                                                 | No          |
| grand      | Plusieurs régions, besoin urgent de réduire le copier-coller, modules d'infrastructure personnalisés, utilisation intensive des compositions. Utilisation de Terragrunt. | No          |
| très grand | Plusieurs fournisseurs (AWS, GCP, Azure). Déploiements multi-cloud. Utilisation de Terragrunt.                                                                           | Non         |


# Terragrunt


# Terraform


# Infrastructure de petite taille avec Terraform

Source: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/small-terraform>

Cet exemple contient du code comme exemple de structuration des configurations Terraform pour une infrastructure de petite taille, où aucune dépendance externe n'est utilisée.

{% hint style="success" %}

* Parfait pour commencer et refactoriser au fur et à mesure
* Parfait pour les petits modules de ressources
* Bon pour les petits modules d'infrastructure linéaires (par exemple, [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis))
* Bon pour un petit nombre de ressources (moins de 20-30)
  {% endhint %}

{% hint style="warning" %}
Un fichier d'état unique pour toutes les ressources peut ralentir le processus de travail avec Terraform si le nombre de ressources augmente (envisagez d'utiliser un argument -target pour limiter le nombre de ressources)
{% endhint %}


# Infrastructure de taille moyenne avec Terraform

Source: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/medium-terraform>

Cet exemple contient du code comme exemple de structuration des configurations Terraform pour une infrastructure de taille moyenne qui utilise :

* 2 comptes AWS
* 2 environnements séparés (`prod` et `stage` qui ne partagent rien). Chaque environnement réside dans un compte AWS distinct
* Chaque environnement utilise une version différente du module d'infrastructure standard (alb) provenant de [Terraform Registry](https://registry.terraform.io)
* Chaque environnement utilise la même version d'un module interne `modules/network` puisqu'il provient d'un répertoire local.

{% hint style="success" %}

* Parfait pour les projets où l'infrastructure est logiquement séparée (comptes AWS séparés)
* Bon lorsqu'il n'est pas nécessaire de modifier les ressources partagées entre les comptes AWS (un environnement = un compte AWS = un fichier d'état)
* Bon quand il n'y a pas besoin d'orchestration des changements entre les environnements
* Bon lorsque les ressources d'infrastructure sont différentes par environnement à dessein et ne peuvent pas être généralisées (par exemple, certaines ressources sont absentes dans un environnement ou dans certaines régions)
  {% endhint %}

{% hint style="warning" %}
Au fur et à mesure que le projet grandit, il sera plus difficile de maintenir ces environnements à jour les uns avec les autres. Il faudrait envisagez d'utiliser des modules d'infrastructure (prêts à l'emploi ou internes) pour les tâches répétables.
{% endhint %}

##


# Infrastructure de grande taille avec Terraform

Source: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/large-terraform>

Cet exemple contient du code comme exemple de structuration des configurations Terraform pour une infrastructure de grande taille qui utilise :

* 2 comptes AWS
* 2 régions
* 2 environnements séparés (`prod` et `stage` qui ne partagent rien). Chaque environnement réside dans un compte AWS distinct et répartit les ressources entre 2 régions
* Chaque environnement utilise une version différente du module d'infrastructure standard (`alb`) provenant de [Terraform Registry](https://registry.terraform.io)
* Chaque environnement utilise la même version d'un module interne `modules/network` puisqu'il provient d'un répertoire local.

{% hint style="info" %}
Dans un grand projet comme décrit ici, les avantages de l'utilisation de Terragrunt deviennent très visibles. Voir [Code Structures examples with Terragrunt](/fr/examples/terragrunt).
{% endhint %}

{% hint style="success" %}

* Parfait pour les projets où l'infrastructure est logiquement séparée (comptes AWS séparés)
* Bon lorsqu'il n'est pas nécessaire de modifier les ressources partagées entre les comptes AWS (un environnement = un compte AWS = un fichier d'état)
* Bon quand il n'y a pas besoin d'orchestration des changements entre les environnements
* Bon lorsque les ressources d'infrastructure sont différentes par environnement à dessein et ne peuvent pas être généralisées (par exemple, certaines ressources sont absentes dans un environnement ou dans certaines régions)
  {% endhint %}

{% hint style="warning" %}
Au fur et à mesure que le projet grandit, il sera plus difficile de maintenir ces environnements à jour les uns avec les autres. Il faudrait envisagez d'utiliser des modules d'infrastructure (prêts à l'emploi ou internes) pour les tâches répétables.
{% endhint %}

##


# Convention des noms

## Conventions générales

{% hint style="info" %}
Il ne devrait y avoir aucune raison de ne pas suivre au moins ces conventions :)
{% endhint %}

{% hint style="info" %}
Prenez notes que les ressources réelles dans le cloud ont souvent des restrictions dans les noms autorisés. Certaines ressources, par exemple, ne peuvent pas contenir de tirets, certaines doivent être en camel-case. Les conventions de ce livre font référence aux noms Terraform eux-mêmes.
{% endhint %}

1. Utilisez `_` (surligné) au lieu de `-` (tiret) partout (noms des ressources, noms des sources de données, noms des variables, sorties, etc.).
2. Préférez les lettres minuscules et les chiffres (même si UTF-8 est pris en charge).

## Arguments de ressource et de source de données

1. Ne répétez pas le type de ressource dans le nom de la ressource (ni partiellement, ni complètement) :

{% hint style="success" %}

```
`resource "aws_route_table" "public" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_route_table" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_aws_route_table" {}`
```

{% endhint %}

2. Le nom de la ressource doit être ainsi donné s'il n'y a plus de nom descriptif et général disponible, ou si le module de ressources crée une seule ressource de ce type (par exemple, dans [AWS VPC module](https://github.com/terraform-aws-modules/terraform-aws-vpc) il y a une seule ressource de type `aws_nat_gateway` et plusieurs ressources de type`aws_route_table`, donc `aws_nat_gateway` pourrait être nommé `this` et`aws_route_table` devrait avoir des noms plus descriptifs - comme `private`, `public`, `database`).
3. Toujours utilisez des noms au singulier.
4. Utiliser - à l'intérieur des valeurs des arguments et aux endroits où la valeur sera exposée à un humain (par exemple, à l'intérieur du nom DNS de l'instance RDS).
5. Inclure l'argument `count` / `for_each` à l'intérieur du bloc de ressource ou de source de données comme premier argument en haut et séparé par une nouvelle ligne après celui-ci.
6. Inclure l'argument `tags,`si pris en charge par ressource, comme dernier argument réel, suivi de `depends_on` et`lifecycle`, si necessaire. Tous ces éléments doivent être séparés par une seule ligne vide.
7. Lorsque vous utilisez des conditions dans un argument`count` / `for_each,` il est préférable d'employer les valeurs booléennes au lieu de`length` ou d'autres expressions.

## Exemples de code de `ressource`

### Utilisation de `count` / `for_each`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  count = 2

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}

resource "aws_route_table" "private" {
  for_each = toset(["one", "two"])

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  vpc_id = "vpc-12345678"
  count  = 2

  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

### Emplacement de `tags`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  allocation_id = "..."
  subnet_id     = "..."

  tags = {
    Name = "..."
  }

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }
}   
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  tags = "..."

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }

  allocation_id = "..."
  subnet_id     = "..."
}
```

{% endcode %}
{% endhint %}

### Conditions dans `count`

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
resource "aws_nat_gateway" "that" {    # Best
  count = var.create_public_subnets ? 1 : 0
}

resource "aws_nat_gateway" "this" {    # Good
  count = length(var.public_subnets) > 0 ? 1 : 0
}
```

{% endcode %}
{% endhint %}

## Variables

1. Ne réinventez pas la roue dans les modules de ressources : utilisez le nom, la description et la valeur par défaut des variables telles que définies dans la section "Référence des arguments" pour la ressource avec laquelle vous travaillez.
2. La prise en charge de la validation dans les variables est plutôt limitée (par exemple, impossible d'accéder à d'autres variables ou de faire des recherches). Planifiez en conséquence car dans de nombreux cas, cette fonctionnalité est inutile.
3. Utilisez la forme plurielle dans un nom de variable lorsque type est `list(...)` ou `map(...)`.
4. Ordonner les clés dans un bloc variable comme ceci: `description` , `type`, `default`, `validation`.
5. Toujours inclure `description` sur toutes les variables même si vous pensez que c'est évident (vous en aurez besoin à l'avenir).
6. Préférez l'utilisation de types simples (`number`, `string`, `list(...)`, `map(...)`, `any`) plutôt qu'un type spécifique comme `object()` sauf si vous avez besoin d'avoir des contraintes strictes sur chaque clé.
7. Utilisez des types spécifiques comme `map(map(string))`si tous les éléments de la carte ont le même type (par exemple, `string`) ou peuvent être convertis en celui-ci (par exemple, le type de nombre peut être converti en `string`).
8. Utilisez type any pour désactiver la validation de type à partir d'une certaine profondeur ou lorsque plusieurs types doivent être pris en charge.
9. La Valeur `{}` est parfois un map mais quelques fois object. Utiliser `tomap(...)` pour créer une carte car il n'y a aucun moyen de créer un objet.

## Sorties

Faire les sorties cohérentes et compréhensibles en dehors de son champ d'application (lorsqu'un utilisateur utilise un module, le type et l'attribut de la valeur renvoyée doivent être évidents).

1. Le nom de la sortie doit décrire la propriété qu'il contient et être moins libre que vous ne le souhaiteriez normalement.
2. Une bonne structure pour le nom de la sortie ressemble à `{name}_{type}_{attribute}` , où:
   1. `{name} est le nom de la ressource ou de la source de données` sans le préfixe du fournisseur. `{name}` pour `aws_subnet` est sous-réseau, pour`aws_vpc` ce sera `vpc`.
   2. `{type}` est le type de ressource
   3. `{attribute}` est l'attribut retourné par la sortie
   4. [Voir exemples](/fr/naming).
3. Si la sortie renvoie une valeur avec des fonctions d'interpolation et plusieurs ressources, `{name}` et `{type}` il devrait être aussi générique que possible (`this` comme préfixe être omis). [Voir exemple](#code-examples-of-output).
4. Si la valeur renvoyée est une liste, elle doit avoir un nom au pluriel. [Voir exemple](#use-plural-name-if-the-returning-value-is-a-list).
5. Incluez toujours une description pour toutes les sorties, même si vous pensez que c'est évident.
6. Évitez de définir un argument `sensible` à moins que vous ne contrôliez entièrement l'utilisation de cette sortie à tous les endroits de tous les modules
7. Préférez `try()` (disponible depuis Terraform 0.13) à `element(concat(...))` (approche héritée pour la version antérieure à 0.13)

### Exemples de Code de sortie (output)

Renvoie au plus un ID de groupe de sécurité :

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "security_group_id" {
  description = "The ID of the security group"
  value       = try(aws_security_group.this[0].id, aws_security_group.name_prefix[0].id, "")
}
```

{% endcode %}
{% endhint %}

Lorsque vous avez plusieurs ressources du même type, cela doit être omis dans le nom de la sortie:

{% hint style="danger" %}
{% code title="outputs.tf" %}

```hcl
output "this_security_group_id" {
  description = "The ID of the security group"
  value       = element(concat(coalescelist(aws_security_group.this.*.id, aws_security_group.web.*.id), [""]), 0)
}
```

{% endcode %}
{% endhint %}

### Utilisez un nom pluriel si la valeur retournée est une liste

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "rds_cluster_instance_endpoints" {
  description = "A list of all cluster instance endpoints"
  value       = aws_rds_cluster_instance.this.*.endpoint
}
```

{% endcode %}
{% endhint %}


# Style de code

{% hint style="info" %}

* Les exemples et les modules Terraform doivent contenir une documentation expliquant les fonctionnalités et comment les utiliser.
* Tous les liens dans les fichiers README.md doivent être absolus pour que le site Web Terraform Registry les affiche correctement.
* La documentation peut inclure des diagrammes créés avec [mermaid](https://github.com/mermaid-js/mermaid) et des plans créés avec [cloudcraft.co](https://cloudcraft.co).
* Utilisez [Terraform pre-commit hooks](https://github.com/antonbabenko/pre-commit-terraform) pour vous assurer que le code est valide, correctement formaté et automatiquement documenté avant qu'il ne soit transmis à git et examiné par des humains
  {% endhint %}

## Documentation

### Documentation génèrée automatiquement

[pre-commit](https://pre-commit.com) est un cadre de gestion et de maintenance des hooks de pré-commit multilingues. Écrit en Python, il est un outil puissant pour faire quelque chose automatiquement sur la machine d'un développeur avant que le code ne soit validé dans un référentiel git. Normalement, il est utilisé pour exécuter des linters et formater du code (voir [supported hooks](https://pre-commit.com/hooks.html)).

Avec les configurations Terraform `pre-commit` peut être utilisé pour formater et valider le code, ainsi que pour mettre à jour la documentation.

Vérifiez le [pre-commit-terraform repository](https://github.com/antonbabenko/pre-commit-terraform/blob/master/README.md) pour vous familiariser avec lui, et les référentiels existants (par exemple, [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc)) où cela est déjà utilisé.

### terraform-docs

[terraform-docs](https://github.com/segmentio/terraform-docs) est un outil qui génère la documentation des modules Terraform dans différents formats de sortie. Vous pouvez l'exécuter manuellement (sans crochets de pré-commit), ou utiliser [pre-commit-terraform hooks](https://github.com/antonbabenko/pre-commit-terraform) pour obtenir la documentation mise à jour automatiquement.

@ToDo: Document module versions, release, GH actions

## Resources

1. [pre-commit framework homepage](https://pre-commit.com)
2. [Collection of git hooks for Terraform to be used with pre-commit framework](https://github.com/antonbabenko/pre-commit-terraform)
3. Blog posté par [Dean Wilson](https://github.com/deanwilson): [pre-commit hooks and terraform - a safety net for your repositories](https://www.unixdaemon.net/tools/terraform-precommit-hooks/)


# FAQ

FTP (Frequent Terraform Problems)

## Quels sont les outils que je devrais connaître et envisager d'utiliser?

* [**Terragrunt**](https://terragrunt.gruntwork.io) - Outil d'orchestration
* [**tflint**](https://github.com/terraform-linters/tflint) - Code linter
* [**tfenv**](https://github.com/tfutils/tfenv) - Gestionnaire de versions
* [**Atlantis**](https://www.runatlantis.io) - Automation des demandes d'extraction (Pull Request)
* [**pre-commit-terraform**](https://github.com/antonbabenko/pre-commit-terraform) - Collection de git hooks pour Terraform à utiliser avec [pre-commit framework](https://pre-commit.com)
* [**Infracost**](https://www.infracost.io/) - Estimation des coûts du cloud pour Terraform dans les demandes de pull. Fonctionne aussi avec Terragrunt, Atlantis et pre-commit-terraform

## Quelles sont les solutions à l'enfer des dépendances avec les modules ?

Les versions des modules de ressources et d'infrastructure doivent être spécifiées. Les fournisseurs doivent être configurés en dehors des modules, mais uniquement en composition. La version des fournisseurs et de Terraform peut également être verrouillée.

Il n'y a pas d'outil maître de gestion des dépendances, mais il existe quelques astuces pour rendre l'enfer des dépendances moins problématique. Par exemple, [Dependabot](https://dependabot.com) peut être utilisé pour automatiser les mises à jour des dépendances. Dependabot crée des demandes d'extraction pour garder vos dépendances sécurisées et à jour. Dependabot prend en charge les configurations Terraform.


# Références

{% hint style="info" %}
Il y a beaucoup de gens qui créent un excellent contenu et gèrent des projets open source pertinents pour la communauté Terraform, mais je ne peux pas penser à la meilleure structure pour obtenir ces liens répertoriés ici sans copier des listes comme [awesome-terraform](https://github.com/shuaibiyy/awesome-terraform).
{% endhint %}

<https://twitter.com/antonbabenko/lists/terraform-experts> - Liste des personnes qui travaillent très activement avec Terraform et qui peuvent vous en dire beaucoup (si vous leur demandez).

<https://github.com/shuaibiyy/awesome-terraform> - Liste organisée de ressources sur Terraform de HashiCorp.

<http://bit.ly/terraform-youtube> - "Your Weekly Dose of Terraform" chaine YouTube par Anton Babenko. Live avec des critiques, des interviews, des questions-réponses, du codage en direct et du hacking avec Terraform.

<https://weekly.tf> - Infolettre hebdomadaire avec Terraform. Diverses actualités dans le monde Terraform (projets, annonces, discussions) par Anton Babenko.


# Ecrire des configurations Terraform

## Utilisez `locals` pour spécifier des dépendances explicites entre les ressources

Moyen utile d'indiquer à Terraform que certaines ressources doivent être supprimées au préalable lorsqu'il n'y a pas de dépendance directe dans les configurations Terraform.

<https://raw.githubusercontent.com/antonbabenko/terraform-best-practices/master/snippets/locals.tf>

## Terraform 0.12 - Arguments réquis ou optionnels

1. L'argument obligatoire `index_document`doit être défini, si `var.website` n'est pas une map vide.
2. L'argument optionnel `error_document` peut être omis.

{% code title="main.tf" %}

```hcl
variable "website" {
  type    = map(string)
  default = {}
}

resource "aws_s3_bucket" "this" {
  # omitted...

  dynamic "website" {
    for_each = length(keys(var.website)) == 0 ? [] : [var.website]

    content {
      index_document = website.value.index_document
      error_document = lookup(website.value, "error_document", null)
    }
  }
}
```

{% endcode %}

{% code title="terraform.tfvars" %}

```hcl
website = {
  index_document = "index.html"
}
```

{% endcode %}


# Atélier

Il existe également un atelier pour les personnes qui souhaitent mettre en pratique certaines des choses décrites dans ce guide.

Le contenu est ici - <https://github.com/antonbabenko/terraform-best-practices-workshop>


# მოგესალმებით!

ეს დოკუმენტი არის მცდელობა სისტემატიურად აღიწეროს Terraform-ის გამოყენების საუკეთესო პრაქტიკები და მოგაწოდოთ მითითებები ყველაზე გავრცელებულ პრობლემებზე, რომლებსაც აწყდებიან Terraform-ის მომხმარებლები.

[Terraform](https://www.terraform.io) არის ერთერთი ძლიერი (თუ არა დღესდღეისობით ყველაზე ძლიერი) და ერთერთი ყველაზე ხშირად გამოყენებადი ხელსაწყო რომელიც გაძლევთ საშუალებას მართოთ ინფრასტრუქტურა როგორც კოდი (IaC). ის საშუალებას გაძლევთ შექმნათ სხვადასხვა რესურსები და არ ზღუდავს მათ ინტეგრაციასა და მხარდაჭერაში.

ამ წიგნში აღწერილი ზოგიერთი ინფორმაცია შეიძლება არ ჩანდეს საუკეთესო პრაქტიკად. მე ვიცი ეს, ვეხმარები მკითხველს გამიჯნოს თუ რა არის საუკეთესო პრაქტიკა და რა არის კიდევ ერთი გზა ამის გამეორებისა.

ამ წიგნის შექმნა დაიწყო მზიან მადრიდში 2018 წელს და ის ხელმისაწვდომია შემდეგ მისამართზე: [https://www.terraform-best-practices.com/](https://www.terraform-best-practices.com).

წლების შემდეგ ის განახლდა უფრო აქტუალური პრაქტიკები რომლებიც ხელმისაწვდომი გახდა Terraform 1.0-ის შემდეგ. საბოლოოდ, ეს წიგნი შეიცავს უდაოდ ყველაზე საუკეთესო პრაქტიკებსა და რეკომენდაციებს Terraform-ის მომხმარებლებისათვის.

## სპონსორები

Please [contact me](https://github.com/antonbabenko/terraform-aws-devops#social-links) if you want to become a sponsor.

| [![](/files/hNXHybraZu7xfTMK4x5B)](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) | [Compliance.tf](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) — Terraform Compliance Simplified. Make your Terraform modules compliance-ready. |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![](https://github.com/antonbabenko/terraform-best-practices/blob/ka/.gitbook/assets)](/ka)                    | —                                                                                                                                                                             |

## თარგმანები

{% content-ref url="/spaces/u3iITRIHQx97ro2PkfdC" %}
[العربية (Arabic)](https://www.terraform-best-practices.com/ar/)
{% endcontent-ref %}

{% content-ref url="/spaces/PJbgKPAX0ohEMLpETpg7" %}
[Bosanski (Bosnian)](https://www.terraform-best-practices.com/ba/)
{% endcontent-ref %}

{% content-ref url="/spaces/B48qUSNPO2XmkIySLzfr" %}
[Português (Brazilian Portuguese)](https://www.terraform-best-practices.com/ptbr/)
{% endcontent-ref %}

{% content-ref url="/spaces/e1Mp2scOX6OnQbifCen3" %}
[English](https://www.terraform-best-practices.com/)
{% endcontent-ref %}

{% content-ref url="/spaces/6shyPtr2KrqW4ANbFXYg" %}
[Français (French)](https://www.terraform-best-practices.com/fr/)
{% endcontent-ref %}

{% content-ref url="/spaces/PKopCWJZbhpQ9FT0W8tL" %}
[Deutsch (German)](https://www.terraform-best-practices.com/de/)
{% endcontent-ref %}

{% content-ref url="/spaces/5c1kFpqxaDZC2g9e6rtT" %}
[ελληνικά (Greek)](https://www.terraform-best-practices.com/el/)
{% endcontent-ref %}

{% content-ref url="/spaces/4bq6CyY8vYiEHkjN63rT" %}
[עברית (Hebrew)](https://www.terraform-best-practices.com/he/)
{% endcontent-ref %}

{% content-ref url="/spaces/Mgong4S6IjtibE055zUM" %}
[हिंदी (Hindi)](https://www.terraform-best-practices.com/hi/)
{% endcontent-ref %}

{% content-ref url="/spaces/ZLCz7lNWbSJxDGuNOI44" %}
[Bahasa Indonesia (Indonesian)](https://www.terraform-best-practices.com/id/)
{% endcontent-ref %}

{% content-ref url="/spaces/8VlMHbHDbW6qRWdgN5oU" %}
[Italiano (Italian)](https://www.terraform-best-practices.com/it/)
{% endcontent-ref %}

{% content-ref url="/spaces/3vykLOWgdQLPLgHtxqQH" %}
[日本語 (Japanese)](https://www.terraform-best-practices.com/ja/)
{% endcontent-ref %}

{% content-ref url="/spaces/BoZVs6O2OJFQLNV1utmm" %}
[ಕನ್ನಡ (Kannada)](https://www.terraform-best-practices.com/kn/)
{% endcontent-ref %}

{% content-ref url="/spaces/bJnDvAqIyVgo7LDHgxYJ" %}
[한국어 (Korean)](https://www.terraform-best-practices.com/ko/)
{% endcontent-ref %}

{% content-ref url="/spaces/9yChMGbFo2G47Wiow1yY" %}
[Polski (Polish)](https://www.terraform-best-practices.com/pl/)
{% endcontent-ref %}

{% content-ref url="/spaces/sFM1GW5TPCGsskQ03mTm" %}
[Română (Romanian)](https://www.terraform-best-practices.com/ro/)
{% endcontent-ref %}

{% content-ref url="/spaces/5VD4NK4mHOY8SWjC9N5e" %}
[简体中文 (Simplified Chinese)](https://www.terraform-best-practices.com/zh/)
{% endcontent-ref %}

{% content-ref url="/spaces/fTxekzr50pIuGmrPkXUD" %}
[Español (Spanish)](https://www.terraform-best-practices.com/es/)
{% endcontent-ref %}

{% content-ref url="/spaces/Fedpbc5NbKjynXI8xTeF" %}
[Türkçe (Turkish)](https://www.terraform-best-practices.com/tr/)
{% endcontent-ref %}

{% content-ref url="/spaces/tXRvMPILxeJaJTM2CsSq" %}
[Українська (Ukrainian)](https://www.terraform-best-practices.com/uk/)
{% endcontent-ref %}

{% content-ref url="/spaces/dcjhau04KQIKHUJA90iN" %}
[اردو (Urdu)](https://www.terraform-best-practices.com/ur/)
{% endcontent-ref %}

დამეკონტაქტეთ თუ გსურთ დახმარება გამიწიოთ ამ წიგნის თარგმნაში სხვა ენაზე.

## კონტრიბუცია

მსურს ყოველთვის მივიღო უკუკავშირი და ახალი იდეები საზოგადოებისგან რაც მომცემს საშუალებას განვაახლო წიგნი დროდადრო.

თუ დაინტერესებული ხართ კონკრეტული საკითხებით, გახსენით ან მონიშნეთ [issue](https://github.com/antonbabenko/terraform-best-practices/issues), რომელიც გსურთ რომ უფრო დეტალურად იქნას განხილული. თუ გაქვთ კონტექნი კონკრეტული მიმართულებით რომელიც გსურთ რომ გააზიაროთ, დაწერეთ და გააკეთეთ pull-request (ამ ეტაპზე არ არის აუცილებელი იდეალურად დაწერილი ტექსტი გქონდეთ).

## ავტორები

ეს წიგნი შექმნილია[ ანტონ ბაბენკოს](https://github.com/antonbabenko), სხვადასხვა კონტრიბუტორებისა და მთარგმნელების მიერ.

## ლიცენზია

ეს ნამუშევარი გახლავთ Apache 2 License-ის ქვეშ. იხილეთ LICENSE მეტი ინფორმაციისთვის.

ამ წიგნში მოცემულ ინფორმაციის ვალიდურობაზე მისი ავტორები და კონტრიბუტორები ვერ მოგცემენ გარანტიას. გთხოვთ გაითვალისწინოთ რომ, ამ წიგნში მოყვანილი ინფორმაციას ვრცელდება უფასოდ, არანაირი ხელშეკრულება ან კონტრაქტი არ არსებობს თქვენსა და ამ კონტენტთან/პროექტთან ნებისმიერ ასოცირებულ პირს შორის.ავტორები და კონტრიბუტორები არ იღებენ და ამით უარს აცხადებენ პასუხისმგებლობაზე რომელიმე მხარის წინაშე ნებისმიერი დანაკარგის, დაზიანების ან შეფერხების გამო, რომელიც გამოწვეულია ამ კონტენტში შემავალ, ასოცირებულ ან დაკავშირებულ ინფორმაციაში შეცდომით ან უმოქმედობით, მიუხედავად იმისა, ასეთი შეცდომები ან გამოტოვებები გამოწვეულია დაუდევრობისა, უბედური შემთხვების ან სხვა მიზეზით.

Copyright © 2018-2022 ანტონ ბაბენკო.


# ძირითადი ცნებები

ოფიციალური Terraform დოკუმენტაცია აღწერს [კონფიგურაციის ყველა ასპექტს დეტალებში](https://www.terraform.io/docs/configuration/index.html). გაეცანით ყურადღებით რათა ადვილად გაიგოთ შემდეგი თემები.

ეს სექცია შეიცავს ძირითად ცნებებს რომლებიც გამოიყენება წიგნში.

## რესურსი (Resource)

Resource(რესურსი) არის `aws_vpc`, `aws_db_instance`და ა.შ. ყოველი რესურსი მიეკუთვნება განსაზღვრულ პროვაიდერს, ენიჭება არგუმენტები, აბრუნებს ატრიბუტებს (შემდეგში outputs) და გააჩნია სასიცოცხლო ციკლი(lifecycle). რესურსი შეიძლება შეიქმნას, მოძიებულ იქნას, განახლდეს ან წაიშალოს.

## მოდულის რესურსი (Resource module)

Resource module არის ერთმანეთთანფ დაკავშირებული რესურსების ერთობლიობა რომელიც ჯამურად ქმნის მოქმედებას (მაგ.: [AWS VPC Terrafor](https://github.com/terraform-aws-modules/terraform-aws-vpc/) მოდული ქმნის VPC, subnets, NAT gateway და ა.შ.). ეს დამოკიდებულია პროვაიდერის კონფიფურაციაზე, შესაძლოა თუ არა ეს რესურსები განსაზღვრულ იქნას მოდულის ფარგლებში ან უფრო მაღალი დონის სტრუქტურაში (მაგ.: ინფრასტრუქტურის მოდულში).

## ინფრასტრქუტურის მოდული (Infrastructure module)

ინფრასტუქტურის მოდული არის რესურსების ერთობლიობა, რომლებიც შეიძლება ლოგიკურად ერთმანეთთად კავშირში არ იყვნენ, თუმცა კონკრეტული შემთხვევაში/პროქტში/კონფიგურაციაში ერთსა და იმავე საქმეს ემსახურებოდნენ. ის აღწერს კონფიგურაციას პროვაიდერისთვის, რომელიც შემდეგ გადაეცემა ქვედა დონის მოდულის რესურსებსა და რესურსებს. როგორც წესი ის შემოიფარგლება მხოლოდ ერთ ერთეულში თითოეული ლოგიკურ პროცესორზე (მაგ: AWS Region, Google Project).

მაგალითად, [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis/) მოდული იყენებას ისეთ რესურსებს როგორებიცაა [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc/) და [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group/) რათა მართოს ინფრასტრუქტურა რომელიც საჭიროა [Atlantis](https://www.runatlantis.io)-ის გასაშვებად [AWS Fargate](https://aws.amazon.com/fargate/)-ზე.

კიდევ ერთი მაგალითი გახლავთ [terraform-aws-cloudquery](https://github.com/cloudquery/terraform-aws-cloudquery) მოდული, სადაც რამოდენიმე  [terraform-aws-modules](https://github.com/terraform-aws-modules/) მოდული გამოიყენება ერთდროულად რომ მართონ ინფრასტრუქტურა ისეთივე წარმატებით როგორც მაგალითად Docker-ის რესურსები გამოიყენება build, push, და  deploy ოპერაციებისთვის.

## კომპოზიცია (Composition)

კომპოზიცია არის ინფრასტრუქტურის მოდულების ერთობლიობა, რომლებიც შეიძლება მოიცავდეს ლოგიკურად გაყოფილ ზონებს (მაგ.: AWS Regions, several AWS accounts). კომპოზიცია გამოიყენება სრული ინფრასტრუქტურის აღსაწერად მთელი ორგინაზაციისთვის ან პროქტისთვის.

კომპოზიცია შედგება ინფრასტრუქტურის მოდულებისგან, რომელიც შედგება რესურსის მოდულებისგან, რომელიც შედგება ინდივიდუალური რესურსებისგან.

![Simple infrastructure composition](/files/gRXjhRxnTLLcdax83v0g)

## მონაცემთა წყარო (Data source)

მონაცემთა წყარო ასრულებს read-only ოპერაციას და დამოკიდებულია პროვაიდერის კონფიგურაციაზე, ის გამოიყენება რესურსის და ინფრასტრუქტურის მოდულებში.

მონაცემთა წყარო `terraform_remote_state` ასრულებს დამაკავშირებელ ფუნქციას ზედა დონის მოდულებსა და კომპოზიციებს შორის.

[External](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/data_source) მონაცემთა წყარო გარე პროგრამებს აძლევს საშუალებას შეასრულონ მონაცემთა წყაროს როლი, მოწოდებული ინფორმაცია კი გამოიყენოთ Terraform კონფიგურაციაში. მაგალითი [terraform-aws-lambda module](https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/258e82b50adc451f51544a2b57fd1f6f8f4a61e4/package.tf#L5-L7)-დან სადაც ფაილის სახელი (filename) გენერირდება გამოძახებული Python script-ის გამოყენებით.

[http](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) მონაცემთა წყარო აკეთებს HTTP GET მოთხოვნას მოცემულ URL-ზე და ახორციელებს ინფორმაციის ექსპორტს პასუხის შესახებ, რომელიც ხშირად სასარგებლოა ინფორმაციის მისაღებად ბოლო წერტილებიდან, სადაც Terraform-ის ადგილობრივი პროვაიდერი არ არსებობს.

## Remote state&#x20;

ინფრასტრუქტურული მოდულები და კომპიზიციები უნდა ინახავდნენ [Terraform state](https://www.terraform.io/docs/language/state/index.html)-ს მოშორებულ(Remote) ადგილას რაც სხვა Terraform-ის მომხმარებლებს მისცემს საშუალებას ჰქონდეთ წვდომა და შეძლონ გუნდური/კონტროლირებადი მუშაობა (მაგ. მიუთითე ACL, versioning, logging).

## Provider, provisioner, etc

Providers, provisioners, და სხვა ტერმინები არის ძალიან კარგად აღწერილი ოფიციალურად დოკუმენტაციაში. ჩემი აზრით არ არის აუცილებლობა აქვს ამ აღწერის აქ გამეორებას და მათ ნაკლები საერთო აქვთ Terraform-ის კარგ მოდულების წერასთან.

## რატომ ასე რთულად?

როდესაც ცაკლეული რესურსები ჰგვანან ატომებს ინფრასტრუქტურაში, რესურსის მოდულები არიან მოლეკულები (ატომებისგან შემდგარი). მოდული არის ყველაზე პატარა ვერსიონირებადი და გაზიარებადი ერთეული. მას გააჩნია არგუმენტების ზუსტი სია, მას აქვს არგუმენტების ზუსტი სია, განახორციელოს ძირითადი ლოგიკა ასეთი ერთეულისთვის საჭირო ფუნქციის შესასრულებლად. მაგალითად [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group) მოდული ქმნის  `aws_security_group` და `aws_security_group_rule` რესურსებს input-ზე დაფუძნებით. ეს რესურს მოდული თავისთავად შესაძლოა გამოიყენოთ სხვა მოდულთან ერთობლიობაში ახალი ინფრასტრუქტურის მოდულის შესაქმნელად.&#x20;

მოლეკულებს (რესურს და ინფრასტრუქტურის) შორის მონაცემთა გაცვლა ხორციელდება მოდულების, Outputs-ების და მონაცემთა წყაროების მეშვეობით.

კომპოზიციებს შორის წვდომა ხშირად ხორციელდება remote state მონაცემთა წყაროების მეშვეობით. არსებობს [მონაცემთა გაზიარების სხვადასხვა გზები](https://www.terraform.io/docs/language/state/remote-state-data.html#alternative-ways-to-share-data-between-configurations).&#x20;

ზემოაღწერილი ცნებების ფსევდო-კავშირებში ჩასმისას შეიძლება ასე გამოიყურებოდეს:&#x20;

```
composition-1 {
  infrastructure-module-1 {
    data-source-1 => d1

    resource-module-1 {
      data-source-2 => d2
      resource-1 (d1, d2)
      resource-2 (d2)
    }

    resource-module-2 {
      data-source-3 => d3
      resource-3 (d1, d3)
      resource-4 (d3)
    }
  }
}
```


# კოდის სტრუქტურა

საზოგადოებაში ყველაზე ხშირად დასმული კითხვები როგორც წესი დაკავშირებულია Terraform-ის კოდის სტრუქტურასთან. ყველას ერთხელ მაინც დროის რომელიმე მონაკვეთში უფიქრია კოდის საუკეთესო სტრუქტურაზე.

## როგორ დავასტრუქტურო Terraform-ის პროექტი?

ეს ერთერთი იმ კითხვათაგანია სადაც რთულია უნივერსალური გადაწყვეტილება ან რჩევა გასცე. ამიტომ უმჯობესია იმის გარკვევა თუ რასთან გვაქვს საქმე.

* რა სირთულის პროექტია?
  * დაკავშირებული რესურსების რაოდენობა
  * Terraform-ის პროვაიდერების რაოდენობა (იხილეთ შენიშვნა "ლოგიკური პროვაიდერი")
* რამდენად ხშირად იცვლება თქვენი ინფრასტრუქტურა?
  * **დაწყებული** ერთხელ თვეში/კვირაში/დღეში
  * **დამთავრებული** მუდმივად (ყოველი commit-ის დროს)
* ვინ არის კოდის ცვლილების ინიციატორი? აძლევთ თუ არა უფლებას თქვენს *CI სერვერს მოახდინოს რეპოზიტორიის განახლება ახალი არტეფაქტის შექმნისას?*
  * მხოლოდ დეველოპერებს აქვთ უფლება შეიტანონ ცვლილება ინფრასტრუქტურის კოდში &#x20;
  * ყველას შეუძლია შეიტანოს ცვლილებაზე განაცხადი, PR-ის გახსნის მეშვეობით (CI სერვერზე ავტომატურად გაშვებულ დავალებების ჩათვლით)
* რომელ პლატფორმას ან სერვისს იყენებთ დეფლოისთვის?&#x20;
  * AWS CodeDeploy, Kubernetes, ან OpenShift ითხოვენ სხვადასხვა მიდგომას
* როგორ აჯგუფებთ გარემოებს?
  * გარემოთი, რეგიონით, პროექტით

{% hint style="info" %}
ლოგიკური პროვაიდერები მთლიანად მუშაობენ Terraform-ის ლოგიკის ფარგლებში და ძალიან ხშირად არ ურთიერთობენ სხვა სერვისებთან, ასე რომ, ჩვენ შეგვიძლია ვიფიქროთ მათ სირთულეზე, როგორც O(1). ყველაზე გავრცელებული ლოგიკური პროვაიდერები მოიცავს [random](https://registry.terraform.io/providers/hashicorp/random/latest/docs), [local](https://registry.terraform.io/providers/hashicorp/local/latest/docs), [terraform](https://www.terraform.io/docs/providers/terraform/index.html), [null](https://registry.terraform.io/providers/hashicorp/null/latest/docs), [time](https://registry.terraform.io/providers/hashicorp/time/latest).
{% endhint %}

## Terraform-ის კონფიგურაციების სტრუქტურირება

მთლიანი კოდის ჩასმა `main.tf`-ში არის კარგი იდეა როდესაც წერთ მაგალითს. ყველა სხვა დანარჩენ შემთხვევაში უნდა დაყოთ კოდი ფაილებად ლოგიკის მიხედვით:

* `main.tf` - აღწერს მოდულებს, locals და მონაცემთა წყაროებს რესურსების შესაქმნელად
* `variables.tf` - შეიცავს `main.tf`-ში აღწერილი ცვლადების ეკლარაციებს(declarations)&#x20;
* `outputs.tf` - შეიცავს outputs -ში `main.tf` აღწერილი რესურსებისთვის&#x20;
* `versions.tf` - შეიცავს ვერსიის მოთხოვნებს Terraform-ისთვის და პროვაიდერებისთვის

`terraform.tfvars` უნდა გამოიყენებოდეს მხოლოდ [composition](/ka/key-concepts#composition)-ში.

## როგორ ვიფიქროთ Terraform-ის კონფიგურაციის სტრუქტურაზე?

{% hint style="info" %}
დარწმუნდით, რომ გესმით ძირითადი ცნებები - [resource module](/ka/key-concepts#resource-module), [infrastructure module](/ka/key-concepts#infrastructure-module), და [composition](/ka/key-concepts#composition), რადგან ისინი გამოიყენება შემდეგ მაგალითებში.
{% endhint %}

### საერთო რეკომენდაციები კოდის სტრუქტურირებისთვის

* უფრო ადვილი და სწრაფია მუშაობა მცირე რაოდენობის რესურსებთან
  * `terraform plan` და `terraform apply` ორივე აკეთებს cloud API გამოძახებებს(calls) რესურსების მდგომარეობის შესამოწმებლად
  * თუ მთელი ინფრასტრუქტურა გაქვთ ერთ კომპოზიციაში, ამას შეიძლება გარკვეული დრო დასჭირდეს
* აფეთქების რადიუსი/Blast Radius  (უსაფრთხოების დარღვევის შემთხვევაში) უფრო მცირეა ნაკლები რესურსებით
  * ერთმანეთისგან შეუსაბამო რესურსების იზოლირება მათი ცალკეულ კომპოზიციებში მოთავსებით ამცირებს რისკს, თუ რამე არასწორედ მოხდება
* დაიწყეთ თქვენი პროექტი დისტანციური მდგომარეობის გამოყენებით, რადგან:
  * თქვენი ლეპტოპი არ არის ადგილი თქვენი ინფრასტრუქტურის წყაროსთვის
  * `tfstate`-ის მართვა git-ში არის ღამის კოშმარი
  * მოგვიანებით, როდესაც ინფრასტრუქტურის ფენები დაიწყებენ ზრდას რამდენიმე მიმართულებით (დამოკიდებულებების ან რესურსების რაოდენობა), უფრო ადვილი იქნება ნივთების კონტროლის ქვეშ შენარჩუნება
* ივარჯიშეთ თანმიმდევრულ სტრუქტურასა და [დასახელების](/ka/naming) კონვენციაში:
  * პროცედურული კოდექსის მსგავსად, Terraform კოდი უნდა დაიწეროს იმისთვის, რომ ადამიანებმა პირველ რიგში წაიკითხონ, თანმიმდევრულობა დაგეხმარებათ, როდესაც ცვლილებები მოხდება ექვსი თვის შემდეგ
  * შესაძლებელია რესურსების გადატანა Terraform State ფაილში, მაგრამ ეს შეიძლება იყოს უფრო რთული, თუ თქვენ გაქვთ არათანმიმდევრული სტრუქტურა და დასახელება
* შეეცადეთ რესურსის მოდულები რაც შეიძლება სადად&#x20;
* ნუ დააკოდირებთ მნიშვნელობებს, რომლებიც შეიძლება გადავიდეს ცვლადებად ან შეიძლება მიეთითონ მონაცემთა წყაროების გამოყენებით
* გამოიყენებთ მონაცემთა წყაროები და `terraform_remote_state` როგორც წებო ინფრასტრუქტურის მოდულებას და კომპოზიციას შორის.&#x20;

ამ წიგნში პროექტების მაგალითები დაჯგუფებულია სირთულის მიხედვით - მცირე ინფრასტრუქტურიდან ძალიან დიდამდე. ეს განცალკევება არ არის მკაცრი, ასე რომ შეამოწმეთ სხვა სტრუქტურებიც.

### ინფრასტრუქტურის მოდულების და კომპოზიციების ორკესტრირება

მცირე ინფრასტრუქტურის ქონა ნიშნავს, რომ არის მცირე რაოდენობის დამოკიდებულებები და მცირე რესურსი. პროექტის ზრდასთან, აშკარა ხდება Terraform-ის კონფიგურაციების შესრულების ჯაჭვის მოთხოვნილება, სხვადასხვა ინფრასტრუქტურული მოდულების დაკავშირება და კომპოზიციის შიგნით მნიშვნელობების გადაცემა.

არსებობს ორკესტრაციული გადაწყვეტილებების სულ მცირე 5 განსხვავებული ჯგუფი, რომელსაც დეველოპერები იყენებენ::

1. მხოლოდ Terraform. ძალიან მარტივია, დეველოპერებმა უნდა იცოდნენ მხოლოდ Terraform სამუშაოს შესასრულებლად.
2. Terragrunt. სუფთა საორკესტრო ინსტრუმენტი, რომელიც შეიძლება გამოყენებულ იქნას მთელი ინფრასტრუქტურის ორკესტრირებისთვის, ასევე დამოკიდებულებების დასამუშავებლად. Terragrunt მუშაობს ინფრასტრუქტურის მოდულებით და კომპოზიციებით, ამიტომ ამცირებს კოდის დუბლირებას.
3. In-house სკრიპტები. ხშირად ეს ხდება როგორც ამოსავალი წერტილი ორკესტრირებისკენ და Terragrunt-ის აღმოჩენამდე.
4. Ansible ან მსგავსი ზოგადი დანიშნულების ავტომატიზაციის ინსტრუმენტი. ჩვეულებრივ გამოიყენება, როდესაც Terraform მიიღება Ansible-ის შემდეგ, ან როდესაც Ansible UI აქტიურად გამოიყენება.
5. [Crossplane](https://crossplane.io) და კუბერნეტესისგან შთაგონებული სხვა გადაწყვეტილებები. ზოგჯერ აზრი აქვს Kubernetes-ის ეკოსისტემის გამოყენებას და  შერიგების მარყუჟის(reconciliation loop) ფუნქციის გამოყენებას თქვენი Terraform კონფიგურაციის სასურველი მდგომარეობის მისაღწევად. იხილეთ ვიდეო[Crossplane vs Terraform](https://www.youtube.com/watch?v=ELhVbSdcqSY) მეტი ინფორმაციისთვის.

ამის გათვალისწინებით, ეს წიგნი მიმოიხილავს ამ პროექტის პირველ ორ სტრუქტურას, მხოლოდ Terraform-სა და Terragrunt-ს.

იხილეთ კოდის სტრუქტურების მაგალითები [Terraform](/ka/examples/terraform)-ის ან [Terragrunt](/ka/examples/terragrunt)-ისთვის მომდევნო თავში.


# კოდის სტრუქტურის მაგალითები

## Terraform კოდის სტრუქტურები

{% hint style="info" %}
These examples are showing AWS provider but the majority of principles shown in the examples can be applied to other public cloud providers as well as other kinds of providers (DNS, DB, Monitoring, etc)
{% endhint %}

| ტიპი                                                                     | აღწერა                                                                                                                                                                      | მზაობა |
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
| [small](/ka/examples/terraform/small-size-infrastructure)                | ცოტა რესურსი, არანაირი გარე დამოკიდებულება. ერთი AWS ანგარიში. ერთი რეგიონი. ერთი გარემო.                                                                                   | კი     |
| [medium](/ka/examples/terraform/medium-size-infrastructure)              | რამდენიმე AWS ანგარიში და გარემო, თაროზე მოთავსებული ინფრასტრუქტურის მოდული Terraform-ის გამოყენებით.                                                                       | კი     |
| [large](/ka/examples/terraform/large-size-infrastructure-with-terraform) | ბევრი AWS ანგარიში, ბევრი რეგიონი, გადაუდებელი აუცილებლობა შემცირდეს კოპირება, მორგებული ინფრასტრუქტურის მოდულები, კომპოზიციების მძიმე გამოყენება. Terraform-ის გამოყენება. | WIP    |
| very-large                                                               | რამდენიმე პროვაიდერი (AWS, GCP, Azure). მრავალ ღრუბლოვანი განლაგება. Terraform-ის გამოყენება.                                                                               | არა    |

## Terragrunt კოდის სტრუქტურა

| ტიპი       | აღწერა                                                                                                                                                                       | მზაობა |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
| medium     | რამდენიმე AWS ანგარიში და გარემო, მზა ინფრასტრუქტურის მოდული, კომპოზიციის ნიმუში Terragrunt-ის გამოყენებით.                                                                  | არა    |
| large      | ბევრი AWS ანგარიში, ბევრი რეგიონი, გადაუდებელი აუცილებლობა შემცირდეს კოპირება, მორგებული ინფრასტრუქტურის მოდულები, კომპოზიციების მძიმე გამოყენება. Terragrunt-ის გამოყენება. | არა    |
| very-large | რამდენიმე პროვაიდერი (AWS, GCP, Azure). მრავალ ღრუბლოვანი განლაგება. Terragrunt-ის გამოყენება.                                                                               | არა    |


# Terragrunt


# Terraform


# მცირე ზომის ინფრასტრუქტურა Terraform-ით

წყარო: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/small-terraform>

ეს Terraform კოდის სტრუქტურის მაგალითი განკუთვნილია მცირე ზომის ინფრასტრუქტურისთვის რომელიც იყენებს შემდეგ კომპონენტებს:

მაგალითი შეიცავს კოდს, როგორც Terraform-ის კონფიგურაციის სტრუქტურირების მაგალითს მცირე ზომის ინფრასტრუქტურისთვის, სადაც არ არის გამოყენებული გარე დამოკიდებულებები(Dependencies).

{% hint style="success" %}

* შესანიშნავია დასაწყებად და რეფაქტირებისთვის
* შესანიშნავია მცირე რესურსის მოდულებისთვის
* კარგია მცირე და ხაზოვანი ინფრასტრუქტურის მოდულებისთვის (მაგალითად [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis))
* კარგია მცირე რაოდენობის რესურსებისთვის (20-30-ზე ნაკლები)
  {% endhint %}

{% hint style="warning" %}
ყველა რესურსისთვის საერთო მდგომარეობის(State) ფაილს შეუძლია Terraform-თან მუშაობის პროცესი შეანელოს, თუ რესურსების რაოდენობა იზრდება (განიხილეთ `-target`არგუმენტის გამოყენება სამიზნე რესურსების რაოდენობის შესაზღუდად)
{% endhint %}


# საშუალო ზომის ინფრასტრუქტურა Terraform-ით

წყარო: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/medium-terraform>

ეს Terraform კოდის სტრუქტურის მაგალითი განკუთვნილია საშუალო ზომის ინფრასტრუქტურისთვის რომელიც იყენებს შემდეგ კომპონენტებს:

* 2 AWS ანგარიშს
* 2 გამოყოფილი გარემო (`prod` და `stage` რესურსების გაზიარების გარეშე). თითოეული გარემო მუშაობს გამოყოფილ AWS ანგარიშში და მოიცავს რესურსებს ორივე რეგიონში
* თითოეული გარემო იყენებს მზა ინფრასტრუქტურის მოდულის (`alb`) განსხვავებულ ვერსიას რომლის კოდის წყაროც არის [Terraform Registry](https://registry.terraform.io/)
* თითოეული გარემო იყენებს თვითნაწერ მოდულს `modules/network` რომელიც ლოკალურად დირექტორიაში ინახება.&#x20;

{% hint style="success" %}

* იდეალურია პროექტებისთვის, სადაც ინფრასტრუქტურა ლოგიკურად არის გამოყოფილი (ცალკე AWS ანგარიშები)
* კარგია, როდესაც არ არის საჭირო AWS ანგარიშებს შორის გაზიარებული რესურსების ცვლილება (ერთი გარემო = ერთი AWS ანგარიში = ერთი მდგომარეობის ფაილი)
* კარგია, როდესაც არ არის საჭირო გარემოს შორის ცვლილებების ორკესტრირება
* კარგია, როდესაც ინფრასტრუქტურის რესურსები განსხვავებულია თითო გარემოზე დანიშნულებისამებრ და არ შეიძლება განზოგადდეს (მაგ., ზოგიერთი რესურსი არ არის ერთ გარემოში ან ზოგიერთ რეგიონში)
  {% endhint %}

{% hint style="warning" %}
რაც უფრო იზრდება პროექტი, უფრო რთული იქნება ამ გარემოს ერთმანეთთან განახლების შენარჩუნება. განიხილეთ ინფრასტრუქტურის მოდულების გამოყენება განმეორებადი ამოცანებისთვის.
{% endhint %}

##


# დიდი ზომის ინფრასტრუქტურა Terraform-ით

წყარო: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/large-terraform>

ეს Terraform კოდის სტრუქტურის მაგალითი განკუთვნილია დიდი ზომის ინფრასტრუქტურისთვის რომელიც იყენებს შემდეგ კომპონენტებს:

* 2 AWS ანგარიში
* 2 რეგიონი
* 2 გამოყოფილი გარემო (`prod` და `stage` რესურსების გაზიარების გარეშე). თითოეული გარემო მუშაობს გამოყოფილ AWS ანგარიშში და მოიცავს რესურსებს ორივე რეგიონში
* თითოეული გარემო იყენებს მზა ინფრასტრუქტურის მოდულის (`alb`) განსხვავებულ ვერსიას რომლის კოდის წყაროც არის [Terraform Registry](https://registry.terraform.io/)
* თითოეული გარემო იყენებს თვითნაწერ მოდულს `modules/network` რომელიც ლოკალურად დირექტორიაში ინახება.&#x20;

{% hint style="info" %}
დიდ პროექტში, როგორიც აქ არის აღწერილი, Terragrunt-ის გამოყენების სარგებელი ძალიან თვალსაჩინო ხდება. იხილეთ [კოდის სტრუქტურის მაგალითები Terragrunt](/ka/examples/terragrunt).
{% endhint %}

{% hint style="success" %}

* იდეალურია პროექტებისთვის, სადაც ინფრასტრუქტურა ლოგიკურად არის გამოყოფილი (ცალკე AWS ანგარიშები)
* კარგია, როდესაც არ არის საჭირო AWS ანგარიშებს შორის გაზიარებული რესურსების ცვლილება (ერთი გარემო = ერთი AWS ანგარიში = ერთი სახელმწიფო ფაილი)
* კარგია, როდესაც არ არის საჭირო გარემოს შორის ცვლილებების ორკესტრირება
* კარგია, როდესაც ინფრასტრუქტურის რესურსები განსხვავებულია თითო გარემოში და მიზანმიმართულად და მათი განზოგადება შეუძლებელია (მაგ., ზოგიერთი რესურსი არ არის ერთ გარემოში ან ზოგიერთ რეგიონში)
  {% endhint %}

{% hint style="warning" %}
რაც უფრო იზრდება პროექტი, უფრო რთული იქნება ამ გარემოს ერთმანეთთან განახლების შენარჩუნება. განიხილეთ ინფრასტრუქტურის მოდულების გამოყენება (თაროზე მოთავსებული ან შიდა) განმეორებადი ამოცანებისთვის.
{% endhint %}

##


# დასახელების კონვენცია

## ზოგადი კონვენცია

{% hint style="info" %}
არ არსებობს მიზეზი იმისა თუ რატომ არ უნდა გაყვეთ ამ დასახელების კონვენციას :)
{% endhint %}

{% hint style="info" %}
გაითვალისწინეთ რომ ღრუბლოვან რესურსებს (cloud resources) ხშირად აქვთ აკრძალვები დაშვებულ სახელებში. ზოგიერთი რესურსს მაგალითად არ შეუძლია შეიცავდეს - ს. ამ წიგნში მოყვანილი კონვენციები თავისთავად ეხება Terraform სახელებს.&#x20;
{% endhint %}

1. გამოიყენეთ `_` (underscore) `-` (dash) მაგიერ ყველგან (რესურსების დასახელებაში, მონაცემთა წყაროს სახელებში, ცვლადების სახელეში და ა.შ.).
2. უკეთესია გამოიყენოთ lowercase ასოები და ციფრები (მიუხედავად UTF-8 მხარდაჭერისა).

## რესურსისა და მონახემთა წყაროების არგუმენტები

1. ნუ გამოიყენებთ რესურსის ტიპს რესურსის სახელში (ნაწილობრივ თუ სრულად):

{% hint style="success" %}

```
`resource "aws_route_table" "public" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_route_table" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_aws_route_table" {}`
```

{% endhint %}

1. რესურსის სახელს უნდა ერქვას `this` იმ შემთხვევაში თუ არ არის მეტი აღწერითი და ზოგადი სახელი ხელმისაწვდომი, ან თუ რესურსის მოდული ქმნის მხოლოდ ერთ ასეთი ტიპის რესურსს (მაგალითად, [AWS VPC module](https://github.com/terraform-aws-modules/terraform-aws-vpc) -ში არის მხოლოდ ერთი  `aws_nat_gateway` ტიპის რესურსი და რამოდენიმე  type`aws_route_table` ტიპის რესურსი, ამიტომ `aws_nat_gateway` -ს უნდა დაერქვას `this` და `aws_route_table` -ს უნდა ჰქონდეს მეტი აღწერითი სახელები - როგორიცაა `private`, `public`, `database`).
2. დასახელებებში ყოველთვის გამოიყენეთ მხოლობითი არსებითი სახელები.
3. გამოიყენეთ `-` არგუმენტების მნიშვნელობებსა და იმ ადგილებში სადაც ეს მნიშვნელობები იქნება გამოტანილი საჯაროდ (მაგალითად, RDS instance-ის  DNS სახელში).
4. გამოიყენე არგუმენტი nclude argument `count` / `for_each` რესურსში ან მონაცემთა წყაროს ბლოკში პირველ არგუმენტად დასაწყისში და გამოყავით ახალი ხაზით.&#x20;
5. გამოიყენეთ არგუმენტი `tags,` თუ რესურსს აქვს ამის მხარდაჭერა, `depends_on` და `lifecycle` ბლოკებამდე, თუ არსებობს ამის საჭიროება. ყველა ბლოკი უნდა გამოიყოს ცარიელი ხაზით.&#x20;
6. `count` / `for_each` არგუმენტებში კონდიციების გამოყენებისას, უპირატესობა მიანიჭეთ boolean მნიშვნელობს ვიდრე `length` ან სხვა ტიპის expression-ებს.

## რესურსის(`resource`) კოდის მაგალითები

### `count` / `for_each`U-ის გამოყენება

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  count = 2

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}

resource "aws_route_table" "private" {
  for_each = toset(["one", "two"])

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  vpc_id = "vpc-12345678"
  count  = 2

  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

### ტეგების (`tags`) განთავსება

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  allocation_id = "..."
  subnet_id     = "..."

  tags = {
    Name = "..."
  }

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }
}   
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  tags = "..."

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }

  allocation_id = "..."
  subnet_id     = "..."
}
```

{% endcode %}
{% endhint %}

### კონდიციები `count`-ში

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
resource "aws_nat_gateway" "that" {    # Best
  count = var.create_public_subnets ? 1 : 0
}

resource "aws_nat_gateway" "this" {    # Good
  count = length(var.public_subnets) > 0 ? 1 : 0
}
```

{% endcode %}
{% endhint %}

## ცვლადები

1. ნუ გამოიგონებთ ველოსიპედს რესურსის მოდულებში, გამოიყენეთ:  `name`, `description`, და `default` მნიშვნელობები ცვლადებისთვის ისე როგორც  "Argument Reference" სექციაში არის განსაზღვრული.
2. ცვლადებში ვალიდაციის მხარდაჭერა არის საკმაოდ შეზღუდული (მაგალითად არ აქვს წვდომა სხვა ცვლადებზე). ძირითად შემთხვევბში მიზანშეწონილია  გამოიყენოთ Plan-ი, რადგაც ვალიდაციის ოფცია ხშირ შემთხვევაში არის უშედეგო.
3. ცვლადის სახელებში გამოიყენეთ მრავლობითი ფორმა როდესაც მისი ტიპი არის `list(...)` ან `map(...)`.
4. ცვლადის ბლოკში დაიცავით ელემენტების შემდეგი მიმდევრობა: `description` , `type`, `default`, `validation`.
5. ყოველთვის გამოიყენეთ `description` ყველა ცვლადზე (სამომავლოდ აუცილებლად გამოგადგებათ)
6. უმჯობესია გამოიყენოთ მარტივი ცვლადის ტიპები  (`number`, `string`, `list(...)`, `map(...)`, `any`) ვიდრე სპეციფიური ტიპები როგორიცაა `object()` იმ შემთხვევაში თუ არ გჭირდებათ მკაცრი შეზღუდვები key-ებზე.
7. გამოიყენეთ სპეციფიური ცვლადის ტიპები როგორიცაა `map(map(string))` იმ შემთხვევაში თუ ყველა ელემენტს map-ზე ერთი და იგივე ცვლადის ტიპი (მაგალითად `string`) ან შესაძლოა დაკონვერტირდეს მასში (მაგალითად  `number` შესაძლოა დაკონვერტირდეს `string`-ში).
8. გამოიყენეთ ცვლადის ტიპი `any` რათა გამოტოვოთ ვალიდაცია როდესაც კონკრეტულ შრეზე ან როდესაც რამოდენიმე ცვლადის ტიპი უნდა იქნას მხარდაჭერილი.
9. მნიშვნელობა(Value) `{}` ზოგჯერ არის map ტიპისა მაგრამ ხანდახან არის  object ტიპის. გამოიყენეთ `tomap(...)` რათა შექმნა map იმ შემთხვევაში თუ შეზღუდულია object-ის შექმნა.

## Outputs

Make outputs consistent and understandable outside of its scope (when a user is using a module it should be obvious what type and attribute of the value it returns).

1. The name of output should describe the property it contains and be less free-form than you would normally want.
2. Good structure for the name of output looks like `{name}_{type}_{attribute}` , where:
   1. `{name}` is a resource or data source name without a provider prefix. `{name}` for `aws_subnet` is `subnet`, for`aws_vpc` it is `vpc`.
   2. `{type}` is a type of a resource sources
   3. `{attribute}` is an attribute returned by the output
   4. [See examples](#code-examples-of-output).
3. If the output is returning a value with interpolation functions and multiple resources, `{name}` and `{type}` there should be as generic as possible (`this` as prefix should be omitted). [See example](#code-examples-of-output).
4. If the returned value is a list it should have a plural name. [See example](#use-plural-name-if-the-returning-value-is-a-list).
5. Always include `description` for all outputs even if you think it is obvious.
6. Avoid setting `sensitive` argument unless you fully control usage of this output in all places in all modules.
7. Prefer `try()` (available since Terraform 0.13) over `element(concat(...))` (legacy approach for the version before 0.13)

### `output`კოდის მაგალითი

დააბრუნეთ security group მხოლოდ ერთი ID:

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "security_group_id" {
  description = "The ID of the security group"
  value       = try(aws_security_group.this[0].id, aws_security_group.name_prefix[0].id, "")
}
```

{% endcode %}
{% endhint %}

ერთი და იმავე ტიპის მრავალი რესურსის არსებობისას, `this` უნდა იყოს გამოტოვებული Output სახელით:

{% hint style="danger" %}
{% code title="outputs.tf" %}

```hcl
output "this_security_group_id" {
  description = "The ID of the security group"
  value       = element(concat(coalescelist(aws_security_group.this.*.id, aws_security_group.web.*.id), [""]), 0)
}
```

{% endcode %}
{% endhint %}

### გამოიყენეთ მრავლობითი სახელი, თუ დაბრუნებული მნიშვნელობა არის სია

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "rds_cluster_instance_endpoints" {
  description = "A list of all cluster instance endpoints"
  value       = aws_rds_cluster_instance.this.*.endpoint
}
```

{% endcode %}
{% endhint %}


# კოდის სტილი

{% hint style="info" %}

* ტერაფორმის მოდულები და მაგალითები უნდა შეიცავდეს დოკუმენტაციას, ფუნქციონალების გამოყენების შესახებ.
* ყველა ბმული README.md ფაილებში უნდა იყოს სრული, რათა Terraform Registry ვებსაიტმა ისინი აჩვენოს სწორად.
* დოკუმენტაცია შესაძლოა მოიცავდეს mermaid-ით შექმნილ დიაგრამებს და [cloudcraft.co](http://cloudcraft.co/)-ით შექმნილ blueprint-ებს.
* იმისათვის რომ დავრწმუნდეთ კოდის არის ვალიდური, სათანადო ფორმატში და ავტომატურად დოკუმენტირებული, უნდა გამოვიყენოთ Terraform pre-commit hooks, სანამ კოდი გადავა Git-ზე და გახდება საჯარო.
  {% endhint %}

## დოკუმენტაცია

### ავტომატურად გენერირებული დოკუმენტაცია&#x20;

[pre-commit](https://pre-commit.com/) არის ფრეიმვორქი მრავალენოვანი pre-commit hooks-ების სამართავად და შესანარჩუნებლად. ის დაწერილია python-ზე და არის ძლიერი ხელსაწყო დეველოპერების კომპიუტერებზე კოდის git რეპოზიტორიაში გადატანამდე მოხდეს შემოწმება typo-ებზე. ჩვეულებრივ, ის გამოიყენება linter-ების გასაშვებად და კოდის ფორმატირებისთვის (იხილეთ [supported hooks](https://pre-commit.com/hooks.html)).

ტერაფორმის კონფიგურაციით `pre-commit` შეიძლება გამოყენებულ იქნას კოდის ფორმატირებისა და ვალიდაციისთვის, ასევე დოკუმენტაციის განახლებისთვის.

შეამოწმეთ [pre-commit-terraform repository](https://github.com/antonbabenko/pre-commit-terraform/blob/master/README.md) და უკვე არსებული საცავები, რათა უკეთესად გაიგოთ მისი არსი.

### terraform-docs

[terraform-docs](https://github.com/segmentio/terraform-docs) არის ხელსაწყო, რომლის მეშვეობითაც ტერაფორმის მოდულებიდან გენერირდება დოკუმენტაცია სხვადასხვა ფორმატში. შეგიძლიათ ის გაუშვათ მანუალურად (pre-commit hooks-ის გამოყენების გარეშე) ან გამოიყენოთ [pre-commit-terraform hooks](https://github.com/antonbabenko/pre-commit-terraform) თუ გსურთ რომ დოკუმენტაცია განახლდეს ავტომატურად.

@todo: Document module versions, release, GH actions

## რესურსები

1. [pre-commit framework homepage](https://pre-commit.com/)
2. [Collection of git hooks for Terraform to be used with pre-commit framework](https://github.com/antonbabenko/pre-commit-terraform)
3. Blog post by [Dean Wilson](https://github.com/deanwilson): [pre-commit hooks and terraform - a safety net for your repositories](https://www.unixdaemon.net/tools/terraform-precommit-hooks/)


# ხშირად დასმული კითხვები

FTP (Frequent Terraform Problems)

## რომელი ხელსაწყოები უნდა ვიცოდეთ და გამოვიყენოთ?&#x20;

* [**Terragrunt**](https://terragrunt.gruntwork.io/) - ორკესტრაციის ხელსაწყო
* [**tflint**](https://github.com/terraform-linters/tflint) - Code linter
* [**tfenv**](https://github.com/tfutils/tfenv) - ვერსიების მენეჯერი
* [**Atlantis**](https://www.runatlantis.io/) - Pull Request-ების ავტომატიზაცია
* [**pre-commit-terraform**](https://github.com/antonbabenko/pre-commit-terraform) - git hook-ების კოლექცია Terraform-ისთვის
* [**Infracost**](https://www.infracost.io) - ქლაუდის ხარჯის განსაზღვრა Terraform-ის pull request-ში. მუშაობს Terragrunt, Atlantis და pre-commit-terraform-თან.

## რა გამოსავალია მოდულების [dependency hell](https://en.wikipedia.org/wiki/Dependency_hell) -თან მიმართებით?

რესურსების და ინფრასტრუქტურის მოდულების ვერსიები უნდა იყოს განსაზღვრული. პროვაიდერების კონფიგურაცია უნდა განისაზღვროს მოდულებს გარეთ, მაგრამ სტრუქტურულად. პროვაიდერების და Terraform-ის ვერსიები ასევე შეიძლება განისაზღვროს მკაცრად.

არ არსებობს დამოკიდებულების(dependency) სამართავი ხელსაწყო, მაგრამ გარკვეული რჩევები როგორ ავირიდოთ თავიდან მსგავსი პრობლემები. მაგალითად, [Dependabot](https://dependabot.com/) შეგიძლიათ გამოიყენოთ დამოკიდებულებების განახლების ავტომატიზაციისთვის. Dependabot ქმნის pull requests რათა შეინარჩუნოს დამოკიდებულებები უსაფრთხოს და განახლებულ მდგომარეობაში. Dependabot -ს გააჩნია Terraform კონფიგურაციების მხარდაჭერა.


# მითითებები

{% hint style="info" %}
არის უამრავი ადამიანი ვინც ქმნის და მართავს Terraform-თან დაკავშირებულ open-source პროექტებს, თუმცა რთულია ვიფიქრო საუკეთესო სტრუქტურაზე. ამიტომ უცვლელად გთავაზობთ ჩამონათვალს [awesome-terraform](https://github.com/shuaibiyy/awesome-terraform).
{% endhint %}

<https://twitter.com/antonbabenko/lists/terraform-experts> - List of people who work with Terraform very actively and can tell you a lot (if you ask them).

<https://github.com/shuaibiyy/awesome-terraform> - Curated list of resources on HashiCorp's Terraform.

<http://bit.ly/terraform-youtube> - "Your Weekly Dose of Terraform" YouTube channel by Anton Babenko. Live streams with reviews, interviews, Q\&A, live coding, and some hacking with Terraform.

<https://weekly.tf> - Terraform Weekly newsletter. Various news in the Terraform world (projects, announcements, discussions) by Anton Babenko.


# Terraform კონფიგურაციის წერა

## გამოიყენეთ `locals` რესურსებს შორის აშკარა დამოკიდებულების დასაზუსტებლად

სასარგებლო გზა Terraform-ისთვის მინიშნებას მისაცემად, რომ ზოგიერთი რესურსი მანამდე უნდა წაიშალოს მაშინაც კი, როცა Terraform-ის კონფიგურაციებში პირდაპირი დამოკიდებულება არ არსებობს.

<https://raw.githubusercontent.com/antonbabenko/terraform-best-practices/master/snippets/locals.tf>

## Terraform 0.12 - საჭირო vs არჩევითი არგუმენტები

1. საჭირო არგუმენტი `index_document` უნდა იყოს არჩეული თუ `var.website` არ არის ცარიელი map.
2. არჩევითი არგუმენტი `error_document` შეიძლება გამოტოვოთ.

{% code title="main.tf" %}

```hcl
variable "website" {
  type    = map(string)
  default = {}
}

resource "aws_s3_bucket" "this" {
  # omitted...

  dynamic "website" {
    for_each = length(keys(var.website)) == 0 ? [] : [var.website]

    content {
      index_document = website.value.index_document
      error_document = lookup(website.value, "error_document", null)
    }
  }
}
```

{% endcode %}

{% code title="terraform.tfvars" %}

```hcl
website = {
  index_document = "index.html"
}
```

{% endcode %}


# ვორქშოპი

ვორქშოპი განკუთვნილია მათთვის ვინც ცდილობს პრაქტიკა მიიღოს ამ წიგნში მოცემულ მასალაზე.&#x20;

ვორქშოპის კონტენტი - <https://github.com/antonbabenko/terraform-best-practices-workshop>


# Willkommen

Dieses Dokument ist ein Versuch, die besten Praktiken bei der Verwendung von Terraform systematisch zu beschreiben und Empfehlungen für die häufigsten Probleme zu geben.

[Terraform](https://www.terraform.io/) ist ein relativ neues Projekt (wie die meisten DevOps-Tools), das 2014 gestartet wurde.

Terraform ist leistungsfähig (wenn nicht sogar das leistungsfähigste, das es derzeit gibt) und eines der am häufigsten verwendeten Tools, das die Verwaltung der Infrastruktur als Code ermöglicht. Es erlaubt Entwicklern eine Menge Dinge zu tun und schränkt sie nicht ein, Dinge zu tun, die schwer zu unterstützen oder zu integrieren sind.

Einige der in diesem Buch beschriebenen Informationen mögen nicht als die besten Praktiken erscheinen. Ich weiß das, und um den Lesern zu helfen, zu unterscheiden, was bewährte Praktiken sind und was nur eine andere Art ist, Dinge zu tun, verwende ich manchmal Hinweise, um etwas Kontext zu liefern, und Symbole, um den Reifegrad jedes Unterabschnitts in Bezug auf bewährte Praktiken anzugeben.

Das Buch ist 2018 im sonnigen Madrid entstanden und ist hier kostenlos erhältlich - <https://www.terraform-best-practices.com/> .

Ein paar Jahre später wurde es mit mehr aktuellen Best Practices aktualisiert, die mit Terraform 1.0 verfügbar waren. Letztendlich sollte dieses Buch die meisten der unbestrittenen besten Praktiken und Empfehlungen für Terraform-Nutzer enthalten.

## Sponsoren

Please [contact me](https://github.com/antonbabenko/terraform-aws-devops#social-links) if you want to become a sponsor.

| [![](/files/Se02buB6qKi1AkOR2nCW)](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) | [Compliance.tf](https://compliance.tf/?utm_source=tf_best_practices\&utm_medium=sponsorship) — Terraform Compliance Simplified. Make your Terraform modules compliance-ready. |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![](https://github.com/antonbabenko/terraform-best-practices/blob/de/.gitbook/assets)](/de)                    | —                                                                                                                                                                             |

## Übersetzungen

{% content-ref url="/spaces/u3iITRIHQx97ro2PkfdC" %}
[العربية (Arabic)](https://www.terraform-best-practices.com/ar/)
{% endcontent-ref %}

{% content-ref url="/spaces/PJbgKPAX0ohEMLpETpg7" %}
[Bosanski (Bosnian)](https://www.terraform-best-practices.com/ba/)
{% endcontent-ref %}

{% content-ref url="/spaces/B48qUSNPO2XmkIySLzfr" %}
[Português (Brazilian Portuguese)](https://www.terraform-best-practices.com/ptbr/)
{% endcontent-ref %}

{% content-ref url="/spaces/e1Mp2scOX6OnQbifCen3" %}
[English](https://www.terraform-best-practices.com/)
{% endcontent-ref %}

{% content-ref url="/spaces/6shyPtr2KrqW4ANbFXYg" %}
[Français (French)](https://www.terraform-best-practices.com/fr/)
{% endcontent-ref %}

{% content-ref url="/spaces/DyguS0uZfMW7X7m9BWx1" %}
[ქართული (Georgian)](https://www.terraform-best-practices.com/ka/)
{% endcontent-ref %}

{% content-ref url="/spaces/5c1kFpqxaDZC2g9e6rtT" %}
[ελληνικά (Greek)](https://www.terraform-best-practices.com/el/)
{% endcontent-ref %}

{% content-ref url="/spaces/4bq6CyY8vYiEHkjN63rT" %}
[עברית (Hebrew)](https://www.terraform-best-practices.com/he/)
{% endcontent-ref %}

{% content-ref url="/spaces/Mgong4S6IjtibE055zUM" %}
[हिंदी (Hindi)](https://www.terraform-best-practices.com/hi/)
{% endcontent-ref %}

{% content-ref url="/spaces/ZLCz7lNWbSJxDGuNOI44" %}
[Bahasa Indonesia (Indonesian)](https://www.terraform-best-practices.com/id/)
{% endcontent-ref %}

{% content-ref url="/spaces/8VlMHbHDbW6qRWdgN5oU" %}
[Italiano (Italian)](https://www.terraform-best-practices.com/it/)
{% endcontent-ref %}

{% content-ref url="/spaces/3vykLOWgdQLPLgHtxqQH" %}
[日本語 (Japanese)](https://www.terraform-best-practices.com/ja/)
{% endcontent-ref %}

{% content-ref url="/spaces/BoZVs6O2OJFQLNV1utmm" %}
[ಕನ್ನಡ (Kannada)](https://www.terraform-best-practices.com/kn/)
{% endcontent-ref %}

{% content-ref url="/spaces/bJnDvAqIyVgo7LDHgxYJ" %}
[한국어 (Korean)](https://www.terraform-best-practices.com/ko/)
{% endcontent-ref %}

{% content-ref url="/spaces/9yChMGbFo2G47Wiow1yY" %}
[Polski (Polish)](https://www.terraform-best-practices.com/pl/)
{% endcontent-ref %}

{% content-ref url="/spaces/sFM1GW5TPCGsskQ03mTm" %}
[Română (Romanian)](https://www.terraform-best-practices.com/ro/)
{% endcontent-ref %}

{% content-ref url="/spaces/5VD4NK4mHOY8SWjC9N5e" %}
[简体中文 (Simplified Chinese)](https://www.terraform-best-practices.com/zh/)
{% endcontent-ref %}

{% content-ref url="/spaces/fTxekzr50pIuGmrPkXUD" %}
[Español (Spanish)](https://www.terraform-best-practices.com/es/)
{% endcontent-ref %}

{% content-ref url="/spaces/Fedpbc5NbKjynXI8xTeF" %}
[Türkçe (Turkish)](https://www.terraform-best-practices.com/tr/)
{% endcontent-ref %}

{% content-ref url="/spaces/tXRvMPILxeJaJTM2CsSq" %}
[Українська (Ukrainian)](https://www.terraform-best-practices.com/uk/)
{% endcontent-ref %}

{% content-ref url="/spaces/dcjhau04KQIKHUJA90iN" %}
[اردو (Urdu)](https://www.terraform-best-practices.com/ur/)
{% endcontent-ref %}

Kontaktieren Sie mich, wenn Sie bei der Übersetzung dieses Buches in andere Sprachen helfen wollen.

## Beiträge

Ich möchte immer Rückmeldungen erhalten und dieses Buch aktualisieren, wenn die Gemeinschaft reift und neue Ideen umgesetzt und überprüft werden.

Wenn Sie an bestimmten Themen interessiert sind, [eröffnen Sie bitte ein Issue](https://github.com/antonbabenko/terraform-best-practices/issues) oder bewerten Sie ein Issue mit einem Daumen-hoch, von dem Sie möchten, dass es am meisten behandelt werden soll. Wenn Sie das Gefühl haben, dass Sie Inhalte haben und einen Beitrag leisten wollen, schreiben Sie einen Entwurf und reichen Sie einen Pull Request ein (machen Sie sich zu diesem Zeitpunkt keine Sorgen über einen guten Textstil!)

## Autoren

Dieses Buch wird von [Anton Babenko](https://github.com/antonbabenko) mit Hilfe verschiedener Mitwirkender und Übersetzer gepflegt.

## Lizenzen

Dieses Projekt steht unter der Apache-2-Lizenz. Siehe LICENSE für weitere Details.

Die Autoren und Mitwirkenden an diesem Inhalt können die Gültigkeit der hier gefundenen Informationen nicht garantieren. Bitte vergewissern Sie sich, dass Sie verstehen, dass die hier zur Verfügung gestellten Informationen frei zur Verfügung gestellt werden und dass keine Art von Vereinbarung oder Vertrag zwischen Ihnen und Personen, die mit diesem Inhalt oder Projekt in Verbindung stehen, entsteht. Die Autoren und Mitwirkenden übernehmen keine Haftung für Verluste, Schäden oder Störungen, die durch Fehler oder Auslassungen in den Informationen verursacht werden, die in diesem Inhalt enthalten sind, mit ihm in Verbindung stehen oder mit ihm verlinkt sind, unabhängig davon, ob solche Fehler oder Auslassungen auf Fahrlässigkeit, Unfälle oder andere Ursachen zurückzuführen sind.

Copyright © 2018-2023 Anton Babenko.


# Grundlegende Konzepte

Die offizielle Terraform-Dokumentation beschreibt [alle Aspekte der Konfiguration im Detail](https://www.terraform.io/docs/configuration/index.html). Lesen Sie sie sorgfältig, um den Rest dieses Abschnitts zu verstehen.

In diesem Abschnitt werden die wichtigsten Konzepte beschrieben, die in diesem Buch verwendet werden.

## Ressource

`aws_vpc`, `aws_db_instance` und andere sind Beispiele für Ressourcen. Eine Ressource gehört zu einem Provider, akzeptiert Argumente, gibt Attribute aus und hat Lebenszyklen. Eine Ressource kann erstellt, abgerufen, aktualisiert und gelöscht werden.

## Ressourcenmodule

Ein Ressourcenmodul ist eine Sammlung zusammenhängender Ressourcen, die gemeinsam eine  Aktion durchführen (z. B. erstellt das [AWS VPC Terraform-Modul](https://github.com/terraform-aws-modules/terraform-aws-vpc/) VPC, Subnetze, NAT-Gateway usw.). Es hängt von der Konfiguration des Providers ab, die darin oder in übergeordneten Strukturen (z. B. im Infrastrukturmodul) definiert werden kann.

## Infrastrukturmodule

Ein Infrastrukturmodul ist eine Sammlung von Ressourcenmodulen, die nicht zwingend logisch miteinander verbunden sein müssen, aber in der aktuellen Situation/im aktuellen Projekt/im aktuellen Setup demselben Zweck dienen. Es definiert die Konfiguration für Provider, die an die nachgelagerten Ressourcenmodule und an Ressourcen weitergegeben wird. Normalerweise ist die Arbeit auf eine Einheit pro logischer Begrenzung beschränkt (z. B. AWS Region, Google Project).

Das Modul [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis/) verwendet beispielsweise Ressourcenmodule wie [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc/) und [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group/), um die für den Betrieb von [Atlantis](https://www.runatlantis.io/) auf [AWS Fargate](https://aws.amazon.com/fargate/) erforderliche Infrastruktur zu verwalten.

Ein weiteres Beispiel ist das [terraform-aws-cloudquery](https://github.com/cloudquery/terraform-aws-cloudquery) Modul, bei dem mehrere Module von [terraform-aws-modules](https://github.com/terraform-aws-modules/) zusammen verwendet werden, um die Infrastruktur zu verwalten und Docker-Ressourcen zu nutzen, um Docker-Images zu erstellen, zu pushen und zu verteilen. Alles in einem Modul.

## Komposition <a href="#komposition" id="komposition"></a>

Eine Komposition ist eine Sammlung von Infrastrukturmodulen, die sich über mehrere logisch getrennte Bereiche erstrecken kann (z. B. AWS-Regionen, mehrere AWS-Konten). Eine Komposition wird verwendet, um die komplette Infrastruktur zu beschreiben, die für das gesamte Unternehmen oder Projekt erforderlich ist.

Eine Komposition besteht aus Infrastrukturmodulen, die aus Ressourcenmodulen bestehen, die einzelne Ressourcen implementieren.

![Einfache Infrastruktur Komposition](/files/gRXjhRxnTLLcdax83v0g)

## Datenquellen

Eine Datenquelle führt einen Lese-Vorgang durch und ist abhängig von der Konfiguration des Providers; sie wird in einem Ressourcenmodul und einem Infrastrukturmodul verwendet.

Die Datenquelle `terraform_remote_state` dient als Bindeglied für übergeordnete Module und Kompositionen.

Die [externe Datenquelle](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/data_source) ermöglicht es einem externen Programm, als Datenquelle zu fungieren und beliebige Daten zur Verwendung in der Terraform-Konfiguration freizugeben. Hier ist ein Beispiel aus dem [terraform-aws-lambda Modul](https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/258e82b50adc451f51544a2b57fd1f6f8f4a61e4/package.tf#L5-L7), bei dem der Dateiname durch den Aufruf eines externen Python-Skripts berechnet wird.

Die [http-Datenquelle](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) führt eine HTTP-GET-Anfrage an die angegebene URL durch und exportiert Informationen über die Antwort, was oft nützlich ist, um Informationen von Endpunkten zu erhalten, für die kein eigener Terraform-Provider existiert.

## Remote-Status

Infrastrukturmodule und Kompositionen sollten ihren [Terraform-Status](https://www.terraform.io/docs/language/state/index.html) an einem entfernten Ort aufbewahren, wo er von anderen kontrolliert (z.B. mittels ACL, Versionierung und Logging) abgerufen werden kann.

## Provider, Provisioner, ...&#x20;

Provider, Provisioner und einige andere Begriffe sind in der offiziellen Dokumentation sehr gut beschrieben und es macht keinen Sinn, sie hier zu wiederholen. Meiner Meinung nach haben sie wenig mit dem Schreiben guter Terraform-Module zu tun.

## Warum so *kompliziert*?

Während einzelne Ressourcen wie Atome in der Infrastruktur sind, sind Ressourcenmodule Moleküle. Ein Modul ist die kleinste versionierte und gemeinsam nutzbare Einheit. Es hat eine genaue Liste von Argumenten und implementiert die grundlegende Logik für eine solche Einheit, um die erforderliche Funktion auszuführen. Beispielsweise erstellt das  [terraform-aws-security-group](https://github.com/terraform-aws-modules/terraform-aws-security-group) Modul  `aws_security_group` und `aws_security_group_rule` Ressourcen basierend auf der Eingabe. Dieses Ressourcenmodul selbst kann zusammen mit anderen Modulen verwendet werden, um das Infrastrukturmodul zu erstellen.

Der übergreifende Zugriff auf die Daten einzelner Moleküle (Ressourcenmodule und Infrastrukturmodule) erfolgt über die Ausgaben und Datenquellen der Module.

Der Zugriff zwischen Kompositionen erfolgt häufig über Remote state Datenquellen. Für die [gemeinsame Nutzung von Daten zwischen Konfigurationen](https://www.terraform.io/docs/language/state/remote-state-data.html#alternative-ways-to-share-data-between-configurations) gibt es mehrere Möglichkeiten.

Wenn man die oben beschriebenen Konzepte in Pseudo-Beziehungen zueinander setzt, kann das so aussehen:

```
composition-1 {
  infrastructure-module-1 {
    data-source-1 => d1

    resource-module-1 {
      data-source-2 => d2
      resource-1 (d1, d2)
      resource-2 (d2)
    }

    resource-module-2 {
      data-source-3 => d3
      resource-3 (d1, d3)
      resource-4 (d3)
    }
  }

}
```


# Aufbau des Codes

Fragen, die sich auf die Terraform-Code-Struktur beziehen, sind mit Abstand die häufigsten in der Community. Jeder hat sich irgendwann einmal Gedanken über die beste Codestruktur für das Projekt gemacht.

## Wie sollte ich meine Terraform-Konfigurationen strukturieren?

Dies ist eine der Fragen, für die es viele Lösungen gibt, und es ist sehr schwer, allgemeingültige Ratschläge zu erteilen, also sollten wir zunächst einmal verstehen, womit wir es zu tun haben.

* Wie komplex ist Ihr Projekt?
  * Anzahl der zugehörigen Ressourcen
  * Anzahl der Terraform-Provider (siehe Hinweis unten über "logische Provider")
* Wie oft ändert sich Ihre Infrastruktur?
  * **Von** einmal pro Monat/Woche/Tag
  * **Bis** kontinuierlich (jedes Mal, wenn es einen neuen Commit gibt)
* Initiatoren von Codeänderungen? Lassen Sie den CI-Server das Repository aktualisieren, wenn ein neues Artefakt erstellt wird?
  * Nur Entwickler können Änderungen am Infrastruktur-Repository vornehmen.
  * Jeder kann eine Änderung vorschlagen, indem er einen PR öffnet (einschließlich automatisierter Aufgaben, die auf dem CI-Server laufen)
* Welche Einsatzplattform oder welchen Einsatzdienst verwenden Sie?
  * AWS CodeDeploy, Kubernetes oder OpenShift erfordern einen etwas anderen Ansatz
* Wie sind die Umgebungen gruppiert?
  * Nach Umgebung, Region, Projekt

{% hint style="info" %}
Logische Provider arbeiten vollständig innerhalb der Terraform-Logik und interagieren sehr oft nicht mit anderen Diensten, so dass wir ihre Komplexität als O(1) betrachten können. Zu den häufigsten logischen Providern gehören [random](https://registry.terraform.io/providers/hashicorp/random/latest/docs), [local](https://registry.terraform.io/providers/hashicorp/local/latest/docs), [terraform](https://www.terraform.io/docs/providers/terraform/index.html), [null](https://registry.terraform.io/providers/hashicorp/null/latest/docs) und [time](https://registry.terraform.io/providers/hashicorp/time/latest).
{% endhint %}

## Erste Schritte bei der Strukturierung von Terraform-Konfigurationen

Es ist eine gute Idee, den gesamten Code in die Datei `main.tf` zu packen, wenn Sie gerade erst anfangen oder einen Beispielcode schreiben. In allen anderen Fällen ist es besser, mehrere Dateien zu haben, die logisch aufgeteilt sind, wie hier:

* `main.tf` - ruft Module, lokale Variablen und Datenquellen auf, um alle Ressourcen zu erstellen
* `variables.tf` - enthält die Deklarationen der in `main.tf` verwendeten Variablen
* `outputs.tf` - enthält die Ausgaben der in `main.tf` erstellten Ressourcen
* `versions.tf` - enthält die Versionsanforderungen für Terraform und Provider

`terraform.tfvars` sollte nur in der [Komposition](/de/key-concepts#komposition) verwendet werden.

## Wie sollte man über die Struktur von Terraform-Konfigurationen nachdenken?

{% hint style="info" %}
Vergewissern Sie sich, dass Sie die grundlegenden Konzepte - [Ressourcenmodul](/de/key-concepts#ressourcenmodule), [Infrastrukturmodul](/de/key-concepts#infrastrukturmodule) und [Komposition](/de/key-concepts#komposition) - verstehen, da sie in den folgenden Beispielen verwendet werden.
{% endhint %}

### Allgemeine Empfehlungen für die Strukturierung von Code

* Es ist einfacher und schneller, mit einer kleineren Anzahl von Ressourcen zu arbeiten
  * `terraform plan` und `terraform apply` machen beide Cloud-API-Aufrufe, um den Status der Ressourcen zu überprüfen
  * Wenn Sie Ihre gesamte Infrastruktur in einer einzigen Komposition haben, kann dies einige Zeit dauern
* Der Blast-Radius ("Explosionsradius") ist kleiner mit weniger Ressourcen
  * Die Isolierung nicht zusammenhängender Ressourcen voneinander, indem sie in getrennten Kompositionen untergebracht werden, verringert das Risiko, wenn etwas schief geht.
* Beginnen Sie Ihr Projekt mit einem Remote-Status, denn:
  * Ihr Laptop ist kein Ort für Ihre Infrastruktur als "Quelle der Wahrheit"
  * Die Verwaltung einer `tfstate`-Datei in Git ist ein Alptraum
  * Später, wenn die Infrastrukturebenen in verschiedene Richtungen wachsen (Anzahl der Abhängigkeiten oder Ressourcen), wird es einfacher sein, die Dinge unter Kontrolle zu halten
* Achten Sie auf eine einheitliche Struktur und [Namenskonvention](/de/naming):
  * Wie prozeduraler Code sollte auch Terraform-Code so geschrieben werden, dass ihn Entwickler gut lesen können; Konsistenz ist hilfreich, wenn sechs Monaten später Änderungen vorgenommen werden müssen
  * Es ist möglich, Ressourcen innerhalb der Terraform-Statusdatei zu verschieben, aber es kann schwieriger sein, wenn Sie eine inkonsistente Struktur und Namenskonvention haben
* Halten Sie die Ressourcenmodule so einfach wie möglich
* Geben Sie keine Werte fest ein, die als Variablen übergeben oder über Datenquellen ermittelt werden können
* Verwenden Sie Datenquellen und `terraform_remote_state` speziell als Verbindung zwischen Infrastrukturmodulen innerhalb der Komposition

In diesem Buch sind die Beispielprojekte nach *Komplexität* gruppiert - von kleinen bis zu sehr großen Infrastrukturen. Diese Trennung ist nicht strikt, probieren Sie daher auch Strukturen.

### Orchestrierung von Infrastrukturmodulen und Kompositionen

Eine kleine Infrastruktur bedeutet, dass es nur eine geringe Anzahl von Abhängigkeiten und wenige Ressourcen gibt. Wenn das Projekt wächst, wird die Notwendigkeit deutlich, die Ausführung von Terraform-Konfigurationen zu verketten, verschiedene Infrastrukturmodule zu verbinden und Werte innerhalb einer Komposition zu übergeben.

Es gibt mindestens 5 verschiedene Gruppen von Orchestrierungslösungen, die Entwickler verwenden:

1. Nur Terraform. Sehr einfach, Entwickler müssen nur Terraform kennen, um die Arbeit zu erledigen.
2. Terragrunt. Ein reines Orchestrierungstool, mit dem die gesamte Infrastruktur orchestriert und Abhängigkeiten gehandhabt werden können. Terragrunt arbeitet nativ mit Infrastrukturmodulen und Kompositionen und reduziert so die Duplizierung von Code.
3. Interne Skripte. Dies geschieht oft als Ausgangspunkt für die Orchestrierung und bevor Terragrunt entdeckt wird.
4. Ansible oder ein ähnliches Allzweck-Automatisierungswerkzeug. Wird normalerweise verwendet, wenn Terraform nach Ansible eingeführt wird oder wenn die Ansible UI aktiv genutzt wird.
5. [Crossplane](https://crossplane.io/) und andere Kubernetes-inspirierte Lösungen. Manchmal ist es sinnvoll, das Kubernetes-Ökosystem zu nutzen und eine Reconciliation-Schleife einzusetzen, um den gewünschten Zustand Ihrer Terraform-Konfigurationen zu erreichen. Sehen Sie sich das Video [Crossplane vs. Terraform](https://www.youtube.com/watch?v=ELhVbSdcqSY) für weitere Informationen an.

Vor diesem Hintergrund werden in diesem Buch die ersten beiden dieser Projektstrukturen, Terraform only und Terragrunt, vorgestellt.

Beispiele für Codestrukturen für [Terraform](/de/examples/terraform) oder [Terragrunt](/de/examples/terragrunt) finden Sie im nächsten Kapitel.


# Beispiele für Code-Strukturen

## Terraform-Code Strukturen

{% hint style="info" %}
Diese Beispiele zeigen den AWS-Anbieter, aber die meisten der in den Beispielen gezeigten Prinzipien können auch auf andere öffentliche Cloud-Anbieter und andere Arten von Anbietern (DNS, Datenbanken, Monitoring usw.) angewendet werden.
{% endhint %}

| Typ                                                                     | Beschreibung                                                                                                                                                                      | Einsatzbereitschaft |
| ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| [klein](/de/examples/terraform/small-size-infrastructure)               | Wenige Ressourcen, keine externen Abhängigkeiten. Ein AWS-Konto. Eine Region. Eine Umgebung.                                                                                      | Ja                  |
| [mittel](/de/examples/terraform/medium-size-infrastructure)             | Mehrere AWS-Konten und Umgebungen, handelsübliche Infrastrukturmodule mit Terraform.                                                                                              | Ja                  |
| [groß](/de/examples/terraform/large-size-infrastructure-with-terraform) | Viele AWS-Konten, viele Regionen. Dringender Bedarf, Copy-Paste zu reduzieren, benutzerdefinierte Infrastrukturmodule, starke Nutzung von Compositions. Verwendung von Terraform. | In Arbeit           |
| sehr groß                                                               | Mehrere Anbieter (AWS, GCP, Azure). Multi-Cloud-Einsatz. Verwendung von Terraform.                                                                                                | Nein                |

## Terragrunt-Code Strukturen

| Typ       | Beschreibung                                                                                                                                                                                                                                                                                                                                   | Einsatzbereitschaft |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| mittel    | Mehrere AWS-Konten und Umgebungen, handelsübliche Infrastrukturmodule, Kompositionsmuster mit Terragrunt.                                                                                                                                                                                                                                      | Nein                |
| groß      | <p>Many AWS accounts, many regions, urgent need to reduce copy-paste, custom infrastructure modules, heavy usage of compositions. Using Terragrunt.<br>Viele AWS-Konten, viele Regionen. Dringender Bedarf, Copy-Paste zu reduzieren, benutzerdefinierte Infrastrukturmodule, starke Nutzung von Kompositionen. Verwendung von Terragrunt.</p> | Nein                |
| sehr groß | Mehrere Anbieter (AWS, GCP, Azure). Multi-Cloud-Einsätze. Verwendung von Terragrunt.                                                                                                                                                                                                                                                           | Nein                |


# Terragrunt


# Terraform


# Kleinere Infrastruktur mit Terraform

Quellcode: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/small-terraform>

Dieses Beispiel enthält Code als Beispiel für die Strukturierung von Terraform-Konfigurationen für eine kleine Infrastruktur, in der keine externen Abhängigkeiten verwendet werden.

{% hint style="success" %}

* Perfekt für den Einstieg und zum Refactoring im laufenden Betrieb
* Perfekt für kleine Ressourcenmodule
* Gut für kleine und lineare Infrastrukturmodule (z.B. [terraform-aws-atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis))
* Gut geeignet für eine kleine Anzahl von Ressourcen (weniger als 20-30)
  {% endhint %}

{% hint style="warning" %}
Eine einzige Statusdatei für alle Ressourcen kann den Arbeitsprozess mit Terraform verlangsamen, wenn die Anzahl der Ressourcen wächst (erwägen Sie die Verwendung des Arguments `-target`, um die Anzahl der Ressourcen zu begrenzen)
{% endhint %}


# Mittlere Infrastruktur mit Terraform

Quellcode: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/medium-terraform>

Dieses Beispiel enthält Code als Beispiel für die Strukturierung von Terraform-Konfigurationen für eine mittelgroße Infrastruktur, dazu werden verwendet:

* 2 AWS-Konten
* 2 separate Umgebungen (`prod` und `stage`, die sich nichts teilen). Jede Umgebung befindet sich in einem separaten AWS-Konto.
* Jede Umgebung verwendet eine andere Version des Standard-Infrastrukturmoduls (`alb`), das aus der [Terraform-Registry](https://registry.terraform.io/) stammt.
* Jede Umgebung verwendet die gleiche Version eines internen Moduls `modules/network`, da es aus einem lokalen Verzeichnis stammt.

{% hint style="success" %}

* Perfekt für Projekte, bei denen die Infrastruktur logisch getrennt ist (separate AWS-Konten)
* Gut, wenn keine Notwendigkeit besteht, zwischen AWS-Konten geteilte Ressourcen zu ändern (eine Umgebung = ein AWS-Konto = eine Statusdatei)
* Gut, wenn kein Bedarf an der Orchestrierung von Änderungen zwischen den Umgebungen besteht
* Gut, wenn die Infrastrukturressourcen pro Umgebung absichtlich unterschiedlich sind und nicht verallgemeinert werden können (z. B. sind einige Ressourcen in einer Umgebung oder in einigen Regionen nicht vorhanden)
  {% endhint %}

{% hint style="warning" %}
Je größer das Projekt wird, desto schwieriger wird es, diese Umgebungen untereinander auf dem neuesten Stand zu halten. Erwägen Sie den Einsatz von Infrastrukturmodulen (von der Stange oder intern) für wiederholbare Aufgaben.
{% endhint %}

##


# Größere Infrastruktur mit Terraform

Quellcode: <https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/large-terraform>

Dieses Beispiel enthält Code als Beispiel für die Strukturierung von Terraform-Konfigurationen für eine groß angelegte Infrastruktur, dazu werden verwendet:

* 2 AWS-Konten
* 2 Regionen
* 2 separate Umgebungen (`prod` und `stage`, die sich nichts teilen). Jede Umgebung befindet sich in einem separaten AWS-Konto und verteilt die Ressourcen auf 2 Regionen.
* Jede Umgebung verwendet eine andere Version des Standard-Infrastrukturmoduls (`alb`), das aus der [Terraform-Registry](https://registry.terraform.io/) stammt.
* Jede Umgebung verwendet die gleiche Version eines internen Moduls `modules/network`, da es aus einem lokalen Verzeichnis stammt.

{% hint style="info" %}
In einem großen Projekt wie dem hier beschriebenen werden die Vorteile der Verwendung von Terragrunt sehr deutlich. Siehe [Beispiele für Code-Strukturen mit Terragrunt](/de/examples/terragrunt).
{% endhint %}

{% hint style="success" %}

* Perfekt für Projekte, bei denen die Infrastruktur logisch getrennt ist (separate AWS-Konten)
* Gut, wenn keine Notwendigkeit besteht, zwischen AWS-Konten geteilte Ressourcen zu ändern (eine Umgebung = ein AWS-Konto = eine Statusdatei)
* Gut, wenn kein Bedarf an der Orchestrierung von Änderungen zwischen den Umgebungen besteht
* Gut, wenn die Infrastrukturressourcen pro Umgebung absichtlich unterschiedlich sind und nicht verallgemeinert werden können (z. B. sind einige Ressourcen in einer Umgebung oder in einigen Regionen nicht vorhanden)
  {% endhint %}

{% hint style="warning" %}
Je größer das Projekt wird, desto schwieriger wird es, diese Umgebungen untereinander auf dem neuesten Stand zu halten. Erwägen Sie den Einsatz von Infrastrukturmodulen (von der Stange oder intern) für wiederholbare Aufgaben.
{% endhint %}

##


# Namenskonventionen

## Allgemeine Konventionen

{% hint style="info" %}
Es sollte keinen Grund geben, nicht wenigstens diese Konventionen zu befolgen :)
{% endhint %}

{% hint style="info" %}
Beachten Sie, dass die tatsächlichen Cloud-Ressourcen oft Einschränkungen bei den zulässigen Namen haben. Einige Ressourcen dürfen zum Beispiel keine Bindestriche enthalten, andere müssen in Kamelschreibweise geschrieben werden. Die Konventionen in diesem Buch beziehen sich auf die Terraform-Namen selbst.
{% endhint %}

1. Verwenden Sie überall `_` (Unterstrich) anstelle von `-` (Bindestrich) (Ressourcennamen, Datenquellennamen, Variablennamen, Ausgaben usw.).
2. Verwenden Sie vorzugsweise Kleinbuchstaben und Zahlen (auch wenn UTF-8 unterstützt wird).

## Argumente für Ressourcen und Datenquellen

1. Wiederholen Sie den Ressourcentyp nicht im Ressourcennamen (weder teilweise noch vollständig):

   <div data-gb-custom-block data-tag="hint" data-style="success" class="hint hint-success"><p><code>resource "aws_route_table" "public" {}</code></p></div>

   <div data-gb-custom-block data-tag="hint" data-style="danger" class="hint hint-danger"><p><code>resource "aws_route_table" "public_route_table" {}</code></p></div>

   <div data-gb-custom-block data-tag="hint" data-style="danger" class="hint hint-danger"><p><code>resource "aws_route_table" "public_aws_route_table" {}</code></p></div>
2. Der Ressourcenname sollte `this` benannt werden, wenn kein beschreibender und allgemeiner Name verfügbar ist, oder wenn das Ressourcenmodul eine einzelne Ressource dieses Typs erstellt (z. B. gibt es im [AWS VPC-Modul](https://github.com/terraform-aws-modules/terraform-aws-vpc) eine einzelne Ressource des Typs `aws_nat_gateway` und mehrere Ressourcen des Typs `aws_route_table`, daher sollte `aws_nat_gateway` `this` benannt werden und `aws_route_table` sollte beschreibendere Namen haben - wie `private`, `public` oder `database`).
3. Verwenden Sie bei Namen immer die Einzahl.
4. Verwenden Sie `-` innerhalb von Argumenten und an Stellen, an denen der Wert für einen Menschen sichtbar ist (z. B. innerhalb des DNS-Namens der RDS-Instanz).
5. Fügen Sie das Argument `count` / `for_each` innerhalb des Ressourcen- oder Datenquellenblocks als erstes Argument oben ein und trennen Sie es danach durch einen Zeilenumbruch.
6. Fügen Sie das Argument `tags`, falls von der Ressource unterstützt, als letztes echtes Argument ein, gefolgt von `depends_on` und `lifecycle`, falls erforderlich. Alle diese Argumente sollten durch eine einzelne Leerzeile getrennt werden.
7. Bei der Verwendung von Bedingungen in einem `count` / `for_each` Argument sind boolesche Werte zu bevorzugen, anstatt `length` oder andere Ausdrücke zu verwenden.

## Code-Beispiele für `resource`

### Verwendung von `count` / `for_each`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  count = 2

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}

resource "aws_route_table" "private" {
  for_each = toset(["one", "two"])

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  vpc_id = "vpc-12345678"
  count  = 2

  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

### Platzierung von `tags`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  allocation_id = "..."
  subnet_id     = "..."

  tags = {
    Name = "..."
  }

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }
}   
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  tags = "..."

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }

  allocation_id = "..."
  subnet_id     = "..."
}
```

{% endcode %}
{% endhint %}

### Bedingungen in `count`

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
resource "aws_nat_gateway" "that" {    # Best
  count = var.create_public_subnets ? 1 : 0
}

resource "aws_nat_gateway" "this" {    # Good
  count = length(var.public_subnets) > 0 ? 1 : 0
}
```

{% endcode %}
{% endhint %}

## Variablen

1. Erfinden Sie das Rad in Ressourcenmodulen nicht neu: Verwenden Sie den Namen, die Beschreibung und den Standardwert für Variablen, wie sie im Abschnitt "Argumentreferenz" für die Ressource, mit der Sie arbeiten, definiert sind.
2. Die Unterstützung für die Validierung von Variablen ist ziemlich begrenzt (z.B. kann nicht auf andere Variablen zugegriffen werden oder Lookups durchgeführt werden). Planen Sie entsprechend, denn in vielen Fällen ist diese Funktion nutzlos.
3. Verwenden Sie die Pluralform in einem Variablennamen, wenn der Typ `list(...)` oder `map(...)` ist.
4. Ordnen Sie die Schlüssel in einem Variablenblock wie folgt an: `description` , `type`, `default`, `validation`
5. Fügen Sie immer eine `description` allen Variablen hinzu, auch wenn Sie denken, dass es offensichtlich ist (Sie werden es in Zukunft brauchen).
6. Verwenden Sie lieber einfache Typen (`number`, `string`, `list(...)`, `map(...),` `any`) als spezifische Typen wie `object()`, es sei denn, Sie müssen strenge Einschränkungen für jeden Schlüssel haben.
7. Verwenden Sie spezifische Typen wie `map(map(string))`, wenn alle Elemente der Map denselben Typ haben (z. B. `string`) oder in diesen umgewandelt werden können (z. B. kann der Typ `number` in `string` umgewandelt werden).
8. Verwenden Sie type `any`, um die Typüberprüfung ab einer bestimmten Tiefe zu deaktivieren oder wenn mehrere Typen unterstützt werden sollen.
9. Der Wert `{}` ist manchmal eine `map()` und manchmal ein `object()`. Verwenden Sie `tomap(...)`, um eine `map()` zu erstellen, da es keine Möglichkeit gibt, ein `object()` zu erstellen.

## Ausgaben

Machen Sie Ausgaben konsistent und verständlich außerhalb des Moduls (wenn ein Benutzer ein Modul verwendet, sollte es offensichtlich sein, welchen Typ und welches Attribut der Wert hat, den es zurückgibt).

1. Der Name der Ausgabe sollte die darin enthaltene Eigenschaft beschreiben und weniger frei formuliert sein, als Sie es normalerweise wünschen würden.
2. Eine gute Struktur für den Namen der Ausgabe sieht aus wie `{name}_{type}_{attribute}` , wobei:&#x20;
   1. `{name}` ein Ressourcen- oder Datenquellenname ohne Provider-Präfix ist. `{name}` für `aws_subnet` ist `subnet`, für `aws_vpc` ist es `vpc`.&#x20;
   2. `{type}` ist ein Typ einer Ressourcenquelle
   3. `{attribute}` ist ein Attribut, das von der Ausgabe zurückgegeben wird.
   4. [Siehe Beispiele](#code-examples-of-output).
3. Wenn die Ausgabe einen Wert mit Interpolationsfunktionen und mehreren Ressourcen zurückgibt, sollten `{name}` und `{type}` dort so allgemein wie möglich sein (`this` sollte als Präfix weggelassen werden). [Siehe Beispiel](#code-examples-of-output).
4. Wenn der zurückgegebene Wert eine Liste ist, sollte er einen Pluralnamen haben. [Siehe Beispiel](#code-examples-of-output).
5. Geben Sie immer eine `description` für alle Ausgaben an, auch wenn Sie denken, dass es offensichtlich ist.
6. Vermeiden Sie es, `sensitive` Argumente zu setzen, es sei denn, Sie kontrollieren die Verwendung dieser Ausgabe an allen Stellen in allen Modulen vollständig.
7. Bevorzugen Sie `try()` (verfügbar seit Terraform 0.13) gegenüber `element(concat(...))` (Legacy-Ansatz für die Version vor 0.13)

### Code-Beispiele für `output`

Höchstens eine ID einer Security-Gruppe ausgeben:

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "security_group_id" {
  description = "The ID of the security group"
  value       = try(aws_security_group.this[0].id, aws_security_group.name_prefix[0].id, "")
}
```

{% endcode %}
{% endhint %}

Wenn mehrere Ressourcen desselben Typs vorhanden sind, sollte `this` im Namen der Ausgabe weggelassen werden:

{% hint style="danger" %}
{% code title="outputs.tf" %}

```hcl
output "this_security_group_id" {
  description = "The ID of the security group"
  value       = element(concat(coalescelist(aws_security_group.this.*.id, aws_security_group.web.*.id), [""]), 0)
}
```

{% endcode %}
{% endhint %}

Pluralname verwenden, wenn der Rückgabewert eine Liste ist:

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "rds_cluster_instance_endpoints" {
  description = "A list of all cluster instance endpoints"
  value       = aws_rds_cluster_instance.this.*.endpoint
}
```

{% endcode %}
{% endhint %}




---

[Next Page](/llms-full.txt/1)

