צור משאב אחד עבור כל כניסה במפה של אובייקטים, עם מפתח יציב כך ששינוי סדר לעולם לא יכפה החלפה.
הגדר `for_each = var.objects` (מפה). השתמש ב-`each.key` עבור המפתח היציב וב-`each.value.<attr>` עבור שדות. הימנע מ-`count` כאן - שינויי אינדקס גורמים לתנודתיות.
למה: מפתחות מפה הם זהויות יציבות במצב; אינדקסי רשימה הם עמדות וזזים כאשר אלמנטים מתווספים/מוסרים.
החלט בין `count` לבין `for_each` עבור מספר מופעים.
השתמש ב-`for_each` כאשר למופעים יש זהויות מובחנות (סט/מפה); השתמש ב-`count` רק עבור N עותקים זהים, שאינם רגישים לסדר. העדף `for_each` לכל דבר שעשוי לגדול/להתכווץ.
למה: `for_each` מתייחס לפי מפתח (resource["key"]); `count` מתייחס לפי אינדקס (resource[0]) אשר משנה את הסדר בהוספות/מחיקות.
משתנה קלט הוא אובייקט שבו חלק מהתכונות הן אופציונליות וזקוקות לערכי ברירת מחדל.
הגדר את הטיפוס שלו כ-`object({ name = string, size = optional(number, 10) })`. `optional(type, default)` מספק את ערך ברירת המחדל כאשר הקורא משמיט את התכונה.
למה: `optional()` עם ערך ברירת מחדל שומר על קוד קצר עבור הקוראים תוך הבטחת ערך קונקרטי בהמשך הזרם - ללא טיפול ב-null בכל מקום.
אמת הנחה לגבי משאב לפני יישום, או הבטח תוצאה לאחר מכן.
השתמש ב-`lifecycle { precondition { ... } }` כדי לאמת קלטים לפני יצירה/עדכון, וב-`postcondition` כדי לאמת פלטים לאחר מכן. שניהם מקבלים `condition` + `error_message`.
למה: תנאים מותאמים אישית נכשלים במהירות עם הודעה ברורה במקום לייצר יישום שבור או שגיאה מבלבלת בהמשך הזרם.
למה: תופס קלט שגוי בזמן התכנון עם הודעה קריאה במקום להיכשל עמוק בתוך קריאת ספק.
התייחס לערך שאולי לא קיים מבלי לקרוס את התוכנית.
השתמש ב-`try(local.maybe.value, "default")` כדי לחזור לערך ברירת מחדל במקרה של שגיאות, או ב-`can(expr)` כדי לקבל ערך בוליאני אם ביטוי מצליח.
למה: טיפול חינני בנתונים אופציונליים/בעלי צורה משתנה; מונע "Error: Unsupported attribute" על מפתחות חסרים.
המר רשימה למפה, או סנן/עצב אוסף עבור ארגומנט משאב.
השתמש בביטוי `for`: `{ for u in var.users : u.name => u.role if u.active }` (מפה) או `[for x in list : upper(x)]` (רשימה).
למה: ביטויי `for` הם הדרך המקובלת לעצב מחדש נתונים; סעיף ה-`if` מסנן, וצורת `k => v` בונה מפות.
משתנה או פלט מכילים סוד שאין להדפיס בפלט של תוכנית/יישום.
סמן את המשתנה `sensitive = true` (וגם פלטים). Terraform מסתיר אותו בפלט ה-CLI, אם כי הוא עדיין נשמר במצב.
למה: מונע חשיפה מקרית ביומנים/פלט CI; המצב עצמו עדיין חייב להיות מוגן (backend מוצפן, בקרת גישה).
נהל משאבים בשני אזורים/חשבונות בתוך תצורה אחת.
הצהר על ספקים עם כינויים (`provider "aws" { alias = "west" region = "us-west-2" }`) והגדר `provider = aws.west` על משאבים או העבר למודולים.
למה: כינויים מאפשרים לתצורה אחת למקד מופעי ספק מרובים; מודולים מקבלים אותם במפורש באמצעות ארגומנט ה-`providers`א.
תלות נסתרת (שאינה מבוטאת באמצעות הפניות) גורמת לבעיות סדר.
הוסף `depends_on = [aws_iam_role_policy.x]` כדי לכפות סדר. השתמש בזה במשורה - העדף תלויות עקיפות באמצעות הפניות לתכונות.
למה: `depends_on` מפורש מטפל בתלויות שהגרף אינו יכול להסיק, אך שימוש יתר יוצר תוכניות שמרניות ואיטיות יותר.
רנדר קובץ תצורה/נתוני משתמש מתבנית עם משתנים מובנים.
השתמש ב-`templatefile("${path.module}/tpl.tftpl", { items = local.items })`; התבנית משתמשת באינטרפולציה `%{ for }` / `${}`.
למה: `templatefile` שומר על עיבוד טהור/בזמן התכנון (בניגוד לספק התבניות המיושן) ותומך בלולאות/תנאים.
בנה רשימה שטוחה של כל שילוב (subnet, rule) כדי להזין ל-`for_each` יחיד.
השתמש ב-`setproduct(var.subnets, var.rules)` עבור המכפלה הקרטזית, או ב-`flatten([for ...])` כדי לקפל רשימות מקוננות לאחת.
למה: פונקציות אלו הופכות נתונים מקוננים לאוסף השטוח ובעל המפתחות הייחודיים הנדרש על ידי `for_each`.
מודולים
מודול רישום משתנה ומשנה באופן בלתי צפוי תשתית באתחול הבא.
קבע גרסה עם `version = "~> 4.2"` (מודולי רישום בלבד). עבור מקורות Git, קבע תגית `?ref=v4.2.0`. הפעל `terraform init -upgrade` במכוון כדי להזיז קביעות גרסה.
למה: מודולים לא קבועים צפים לגרסה העדכנית ביותר; קיבוע גרסאות הופך שדרוגים למכוונים וניתנים לבדיקה.
נתיבים מקומיים (`./modules/x`) עבור מודולים בתוך המאגר, Git (`git::...?ref=tag`) עבור מודולים משותפים אך לא מפורסמים, רישום (`namespace/name/provider`) עבור מודולים עם גרסאות/מפורסמים.
למה: סוג המקור תואם להיקף השיתוף; רק מקורות רישום תומכים בארגומנט `version` ופתרון אילוצים.
פלט מודול נושא סוד הנצרך על ידי השורש.
סמן את פלט המודול כ-`sensitive = true`. צריכתו בהקשר לא רגיש תגרום לשגיאה עד שתטפל בו גם כרגיש.
למה: רגישות מתפשטת על פני גבול המודול, ומונעת דליפה מקרית בפלט השורש.
סט משאבים קיים מבוסס `count` צריך להפוך ל-`for_each` מבלי להשמיד מופעים.
הוסף בלוקי `moved` הממפים כל אינדקס `resource[0]` לכתובת `resource["key"]` החדשה, ולאחר מכן עבור ל-`for_each`.
למה: בלוקי `moved` משנים את מפתח המצב מכתובות מבוססות מיקום לכתובות מבוססות זהות, ובכך נמנעים מהשמדה/יצירה מחדש.
CLI וניהול מצב
שינית את שם המשאב בתצורה; התוכנית רוצה כעת להשמיד את הישן וליצור חדש.
העדף בלוק `moved` בתצורה. לתיקונים אד-הוק/CLI, השתמש ב-`terraform state mv aws_x.old aws_x.new` כדי להפנות מחדש את האובייקט הקיים.
למה: שניהם מעדכנים את כתובת המצב כך ש-Terraform רואה את האובייקט הקיים כמשאב ששונה שמו - ללא השמדה.
שלב בדיקה חיצונית (אומדן עלות, סריקת אבטחה) לתוך צינור ההרצה.
הגדר משימת הרצה בשלב (טרום-תכנון, לאחר-תכנון, טרום-יישום). HCP Terraform קורא לשירות החיצוני ומגביל את ההרצה על פי תוצאתה.
למה: משימות הרצה מרחיבות את הצינור עם בדיקות צד שלישי ללא צורך בהגדרת CI מותאמת אישית.
בחר כיצד מופעלות הרצות עבור מרחב עבודה.
מונע VCS (קומית/PR מפעיל תוכנית), מונע CLI (`terraform plan/apply` מול המרוחק), או מונע API (תצורה שהועלתה). בחר לפי זרימת עבודה של הצוות.
למה: מונע VCS מתאים ל-GitOps; מונע CLI מתאים לאיטרציה מקומית; מונע API מתאים לצינורות מותאמים אישית. הם מוציאים זה את זה לכל מרחב עבודה.
הענק לצוות גישת כתיבה למרחבי עבודה של staging אך גישת קריאה בלבד לייצור.
הגדר היקף הרשאות ברמת הארגון/פרויקט/מרחב עבודה: הקצה גישת צוות (קריאה/תכנון/כתיבה/אדמין) לכל פרויקט או מרחב עבודה; השתמש בקיבוץ פרויקטים לניהול בקנה מידה.
למה: הרשאות גרנולריות ומוגדרות היקף אוכפות את עקרון הפריבילגיה המינימלית; הענקות ברמת הפרויקט מפחיתות את הניהול לכל מרחב עבודה.
יישום של מרחב עבודה של רשת צריך להוסיף באופן אוטומטי הרצה לתור במרחבי עבודה תלויים של יישומים.
הגדר טריגר הרצה: מרחב העבודה במורד הזרם נרשם למרחב העבודה במעלה הזרם; יישום מוצלח מוסיף את ההרצה במורד הזרם לתור.
למה: טריגרי הרצה משרישרים מרחבי עבודה תלויים כך ששינויי תשתית משותפים מתפשטים בסדר.
אפשר למשתמשים שאינם Terraform לספק תשתית סטנדרטית באמצעות טופס.
פרסם מודול ללא קוד ברישום הפרטי; משתמשים יוצרים מופע שלו דרך ממשק המשתמש, ומספקים רק קלטים - ללא כתיבת HCL.
למה: מודולים ללא קוד ממשיכים את הדמוקרטיזציה של אספקת שירות עצמי תוך שמירה על התצורה הבסיסית מנוהלת ומסופקת בגרסאות.
שתף מודולים וספקים מאושרים ברחבי הארגון.
פרסם לרישום הפרטי של HCP Terraform; צרכנים מתייחסים ל-`app.terraform.io/org/name/provider` עם אילוצי גרסה.
למה: רישום פרטי מרכז גילוי, גרסאות וניהול של מודולים פנימיים.
ארגן עשרות מרחבי עבודה לפי צוות/יישום עבור הרשאות וסטי משתנים.
קבוץ מרחבי עבודה לפרויקטים; יישם הרשאות צוות וסטי משתנים ברמת הפרויקט.
למה: פרויקטים מרחיבים את הניהול - אתה מנהל גישה ותצורה משותפת לכל פרויקט במקום לכל מרחב עבודה.
זהה באופן רציף מתי הייצור סוטה מהמצב המוגדר.
הפעל הערכות תקינות (זיהוי סחף / אימות רציף) במרחב העבודה; HCP Terraform מרענן ומדווח מעת לעת על סחף ועל אימותים שנכשלו.
למה: הערכות אוטומטיות חושפות סחף ותנאים שלא התקיימו בין יישומים, לפני שהם גורמים לתקלות.
HCP Terraform חייב להגיע לתשתית בתוך רשת פרטית ללא כניסה ציבורית.
פרוס סוכני HCP Terraform ברשת הפרטית והקצה את מרחב העבודה למאגר סוכנים; הרצות יבוצעו דרך הסוכן.
למה: סוכנים מאפשרים ל-HCP Terraform לפעול מול סביבות פרטיות/מבודדות אוויר מבלי לחשוף אותן לציבור.
יישום שגוי השחית את המצב ואתה צריך לשחזר.
HCP Terraform שומר מצב עם גרסאות; חזור לגרסת מצב קודמת מממשק המשתמש/API של מרחב העבודה ותכנן מחדש.
למה: גירסאות מצב מובנות מספקות נקודות שחזור מבלי שתצטרך לנהל בעצמך את תצלומי ה-backend.