SlideShare a Scribd company logo
Lead DevOps Engineer - Salesforce
Robert Blumen
The 'Data,
Transformations,
Resources' Pattern in
Terraform
Outline
● Terraform anti-patterns
● New features in versions 12 and 13
● The "Data + Transforms + Resources" Pattern
What Terraform 11 Can Not Do
● Looping
● Conditional
● Nested loops
● Data structures
Data Types in Terraform
12 & 13
structured data types
● list
● map
● set
● tuple
● object
list(string)
list(tuple[string,number,bool])
map(string)
map(object({ id=string, cidr_block=string }))
composition of data types
variable group_members {
type = list(string)
description = "list of emails of the group members"
}
example
variable people {
type = list(object({ name=string, age=number }))
default = [
{
name = "John"
age = 32
}
]
}
type checking (continued)
~/Devel/devops-tools (rblumen) $ /opt/hashi/terraform-0.12.20 
apply -var 'people=[ { name="Job", age=71 } ]'
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
~/Devel/devops-tools (rblumen) $ /opt/hashi/terraform-0.12.20 
apply -var 'people=[ { name="Job", age=true } ]'
Error: Invalid value for input variable
The argument -var="people=..." does not contain a valid value for variable
"people": element 0: attribute "name": string required.
groups = [
{
"key" : "value"
},
{
"key-2": "value-2"
}
]
all_groups = merge(local.groups...)
###
{
"key": "value",
"key-2": "value"
}
python-style splatting
Collections and
Iterations
terraform 11 - indexed lists
resource providerX web_thing {
count = var.num_servers
name = element(var.server_names,count.index)
}
#####
web_thing[0]
web_thing[1]
web_thing[2]
web_thing[3]
locals {
server_names = [ "db", "front-end", "back-end" ]
}
terraform 12+ - maps
web_srv["db"]
web_srv["front-end"]
web_srv["back-end"]
resource providerY web_srv {
for_each = toset(var.server_names)
name = each.value
}
old - (count based):
aws_iam_policy.aws_admin_access[3]
new - (for_each):
aws_iam_policy.aws_admin_access["AmazonS3FullAccess"]
indexing
locals {
administrator_policies = [
"AmazonEC2FullAccess",
"AmazonS3FullAccess",
"AWSKeyManagementServicePowerUser",
"AWSLambdaFullAccess",
]
}
data aws_iam_policy aws_admin_access {
for_each = toset(local.administrator_policies)
arn = "arn:aws:iam::aws:policy/${each.value}"
}
for_each - iterating over a list
resource heroku_app_feature app_feature {
for_each = {
for f, e in {
spaces-tls-legacy : false,
spaces-tls-modern : true
spaces-strict-tls : false
} :
f => e
}
app = heroku_app.app.name
name = each.key
enabled = each.value
}
for_each - iterating over a map
data aws_iam_policy_document access {
for_each = toset(local.groups)
statement {
effect = "Allow"
actions = ["s3:PutObject", ]
resources = data.aws_s3_bucket.devel.arn
}
}
chaining iteration
resource aws_iam_policy access {
for_each = data.aws_iam_policy_document.access
name = "${each.key}Access"
policy = each.value.json
}
resource aws_iam_group_policy_attachment group_access {
for_each = aws_iam_policy.access
group = each.key
policy_arn = each.value.arn
}
● 12: for_each does not work with modules
● 13: for_each supports modules
terraform 12 versus 13
set operations
functions on sets
● setintersection
● setproduct
● setsubtract
● setunion
locals {
all_groups = toset([ "dev", "core", "admin", "ops", "chat" ])
some_groups = toset(["dev", "voice"])
other_groups = setsubtract(all_groups, some_groups) # in 12.21
}
resource aws_iam_group groups {
for_each = local.all_groups
name = each.value
}
resource aws_thing thing_one {
for_each = local.some_groups
group = aws_iam_group.groups[each.value]
}
resource aws_thing thing_two {
for_each = local.other_groups
group = aws_iam_group.groups[each.value]
}
implementing conditionals with sets
locals {
test_envs = [ "test1", "test2" ]
}
resource saas_thing_one test_only {
foreach = setintersection(toset([var.environment]), local.test_envs)
…
}
conditionals (2)
for expressions
for expressions
users = [ "achman", "aziss", "bwong", "cshah", ]
# list
emails = [
for u in local.users : "${replace(u, "-", ".")}@pagerduty.com"
]
# map
emails_by_user = {
for u in local.users :
u => "${replace(u, "-", ".")}@pagerduty.com"
}
[for s in var.list :
upper(s)
if s != ""]
for expressions - conditionals
var user_email_assn {
type = map(string)
default = { ... }
}
local {
labels = [ for user, email in var.user_email_assn: "${user}:${email} ]
}
for expressions - map
{
for s in [ "abc", "def", "aef", "dps" ] :
substr(s, 0, 1) => s...
}
{
"a" = [
"abc",
"aef",
]
"d" = [
"def",
"dps",
]
}
for expressions - group by
putting it together
resource backend_foo left_hand_thing {
for_each = ...
}
resource backend_bar right_hand_thing {
for_each = ..
}
resource backend_foo_bar left_right_assn {
for_each = ??
left_hand_thing = each.value.left
right_hand_thing = each.value.right
}
many-to-many
# groups have multiple members
# user may belong to more than one group
resource pagerduty_user { … }
resource pagerduty_team { … }
resource pagerduty_team_membership {
for_each = ??
user_id = ??
team_id = ??
}
example: PagerDuty
for g in groups {
create group
for u in groups.users {
create user
create create user_group_membership
}
}
conventional imperative language
locals {
on_call_teams = {
tier_1 = [ "abe", "jaxel", "beldon" ]
tier_2 = [ "abe", "janpax", "fanlo" ]
escalation = [ "adam", "shefty", "fanlo" ]
}
}
terraform solution
resource pagerduty_team teams {
for_each = keys(local.on_call_teams)
name = each.value
}
resource pagerduty_user users {
for_each = distinct(flatten(values(local.on_call_teams)))
name = each.value
email = "${each.value}@companyx.com"
}
locals {
team_user_pairs = [
for team, users in local.on_call_teams: [
for for user in users: {
user: user
team: team
}
]
]
team_users = {
for pair in flatten(local.team_user_pairs):
"${pair.team}-${pair.user}" => {
team_id: pagerduty_team[pair.team].id,
user_id: pagerduty_user[pair.user].id
}
}
}
resource pagerduty_team_membership membership {
for_each = local.team_users
team_id = each.value.team_id
user_id = each.value.user_id
}
locals {
on_call_teams = {
tier_1 = [ "abe", "jaxel", "beldon" ]
tier_2 = [ "abe", "janpax", "fanlo" ]
escalation = [ "adam", "shefty", "fanlo" ]
}
}
###
resource saas_team_membership membership {
for_each = ??
user = each.key
groups = each.value
}
one:many inverting
user_group_pairs = flatten([
for group, members in local.group_memberships : [
for user in members : {
user = user
group = group
}
]
])
groups_by_user = {
for pair in local.user_group_pairs :
pair.user => pair.group...
}
}
resource saas_team_membership membership {
for_each = local.groups_by_user
user = each.key
groups = each.value
}
The
"Data
+ Transformations
+ Resources"
Pattern
objectives
● understandable
● easy to change
● economical (DRY)
● data
○ structure according to your domain
○ use data structures
○ normalize/minimize duplication
○ optimize for frequent changes
● transform
○ use for, if and set functions
○ inputs: your data
○ outputs: what your provider's resources need
● resources
○ create each resource once
○ chain resources
pattern for code organization
Thank You

More Related Content

What's hot (19)

PDF
MongoDB
Hemant Kumar Tiwary
 
PDF
Apache Spark - Key-Value RDD | Big Data Hadoop Spark Tutorial | CloudxLab
CloudxLab
 
PPTX
The Aggregation Framework
MongoDB
 
PDF
Is there a perfect data-parallel programming language? (Experiments with More...
Julian Hyde
 
PDF
Pragmatic Real-World Scala (short version)
Jonas Bonér
 
PDF
Python for Data Science and Scientific Computing
Abhijit Kar Gupta
 
PPTX
Should I Use Scalding or Scoobi or Scrunch?
DataWorks Summit
 
KEY
Jython: Python para la plataforma Java (EL2009)
Leonardo Soto
 
PDF
Spark Dataframe - Mr. Jyotiska
Sigmoid
 
PDF
Scalding - Hadoop Word Count in LESS than 70 lines of code
Konrad Malawski
 
PDF
JS OO and Closures
Jussi Pohjolainen
 
PPTX
30 分鐘學會實作 Python Feature Selection
James Huang
 
PDF
PHP and MySQL Tips and tricks, DC 2007
Damien Seguy
 
KEY
Advanced Django ORM techniques
Daniel Roseman
 
PDF
Data profiling with Apache Calcite
Julian Hyde
 
KEY
Django Pro ORM
Alex Gaynor
 
PPTX
Advanced geoprocessing with Python
Chad Cooper
 
PDF
ORM in Django
Hoang Nguyen
 
PPT
Chris Mc Glothen Sql Portfolio
clmcglothen
 
Apache Spark - Key-Value RDD | Big Data Hadoop Spark Tutorial | CloudxLab
CloudxLab
 
The Aggregation Framework
MongoDB
 
Is there a perfect data-parallel programming language? (Experiments with More...
Julian Hyde
 
Pragmatic Real-World Scala (short version)
Jonas Bonér
 
Python for Data Science and Scientific Computing
Abhijit Kar Gupta
 
Should I Use Scalding or Scoobi or Scrunch?
DataWorks Summit
 
Jython: Python para la plataforma Java (EL2009)
Leonardo Soto
 
Spark Dataframe - Mr. Jyotiska
Sigmoid
 
Scalding - Hadoop Word Count in LESS than 70 lines of code
Konrad Malawski
 
JS OO and Closures
Jussi Pohjolainen
 
30 分鐘學會實作 Python Feature Selection
James Huang
 
PHP and MySQL Tips and tricks, DC 2007
Damien Seguy
 
Advanced Django ORM techniques
Daniel Roseman
 
Data profiling with Apache Calcite
Julian Hyde
 
Django Pro ORM
Alex Gaynor
 
Advanced geoprocessing with Python
Chad Cooper
 
ORM in Django
Hoang Nguyen
 
Chris Mc Glothen Sql Portfolio
clmcglothen
 

Similar to Patterns in Terraform 12+13: Data, Transformations and Resources (20)

PDF
WordCamp Portland 2018: PHP for WordPress
Alena Holligan
 
PPTX
India software developers conference 2013 Bangalore
Satnam Singh
 
PDF
Functional es6
Natalia Zaslavskaya
 
PDF
Dynamic languages, for software craftmanship group
Reuven Lerner
 
PPTX
Scala
suraj_atreya
 
PDF
DataMapper
Yehuda Katz
 
KEY
Zend Framework Study@Tokyo #2
Shinya Ohyanagi
 
PDF
React.js Basics - ConvergeSE 2015
Robert Pearce
 
PDF
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
ICS User Group
 
PDF
Where's My SQL? Designing Databases with ActiveRecord Migrations
Eleanor McHugh
 
PDF
Groovy On Trading Desk (2010)
Jonathan Felch
 
PDF
Thinking in Functions: Functional Programming in Python
Anoop Thomas Mathew
 
PPTX
Professional-grade software design
Brian Fenton
 
ODP
Querying your database in natural language by Daniel Moisset PyData SV 2014
PyData
 
ODP
Quepy
dmoisset
 
ODP
Introduction to R
agnonchik
 
PDF
Using Scala Slick at FortyTwo
Eishay Smith
 
PDF
The journey of an (un)orthodox optimization
Sian Lerk Lau
 
PDF
Centralising Authorisation in PostgreSQL
Gary Evans
 
PDF
PGConf APAC 2018 - Lightening Talk #2 - Centralizing Authorization in PostgreSQL
PGConf APAC
 
WordCamp Portland 2018: PHP for WordPress
Alena Holligan
 
India software developers conference 2013 Bangalore
Satnam Singh
 
Functional es6
Natalia Zaslavskaya
 
Dynamic languages, for software craftmanship group
Reuven Lerner
 
DataMapper
Yehuda Katz
 
Zend Framework Study@Tokyo #2
Shinya Ohyanagi
 
React.js Basics - ConvergeSE 2015
Robert Pearce
 
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
ICS User Group
 
Where's My SQL? Designing Databases with ActiveRecord Migrations
Eleanor McHugh
 
Groovy On Trading Desk (2010)
Jonathan Felch
 
Thinking in Functions: Functional Programming in Python
Anoop Thomas Mathew
 
Professional-grade software design
Brian Fenton
 
Querying your database in natural language by Daniel Moisset PyData SV 2014
PyData
 
Quepy
dmoisset
 
Introduction to R
agnonchik
 
Using Scala Slick at FortyTwo
Eishay Smith
 
The journey of an (un)orthodox optimization
Sian Lerk Lau
 
Centralising Authorisation in PostgreSQL
Gary Evans
 
PGConf APAC 2018 - Lightening Talk #2 - Centralizing Authorization in PostgreSQL
PGConf APAC
 
Ad

Recently uploaded (20)

PPTX
Transforming Insights: How Generative AI is Revolutionizing Data Analytics
LetsAI Solutions
 
PDF
AOMEI Partition Assistant Crack 10.8.2 + WinPE Free Downlaod New Version 2025
bashirkhan333g
 
PDF
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
PDF
MiniTool Power Data Recovery 8.8 With Crack New Latest 2025
bashirkhan333g
 
PDF
TheFutureIsDynamic-BoxLang witch Luis Majano.pdf
Ortus Solutions, Corp
 
PDF
AI Prompts Cheat Code prompt engineering
Avijit Kumar Roy
 
PDF
MiniTool Partition Wizard Free Crack + Full Free Download 2025
bashirkhan333g
 
PDF
ERP Consulting Services and Solutions by Contetra Pvt Ltd
jayjani123
 
PDF
Simplify React app login with asgardeo-sdk
vaibhav289687
 
PDF
UITP Summit Meep Pitch may 2025 MaaS Rebooted
campoamor1
 
PDF
Latest Capcut Pro 5.9.0 Crack Version For PC {Fully 2025
utfefguu
 
PDF
Ready Layer One: Intro to the Model Context Protocol
mmckenna1
 
PPTX
Foundations of Marketo Engage - Powering Campaigns with Marketo Personalization
bbedford2
 
PPTX
Agentic Automation: Build & Deploy Your First UiPath Agent
klpathrudu
 
PPTX
Prompt Like a Pro. Leveraging Salesforce Data to Power AI Workflows.pptx
Dele Amefo
 
PDF
IObit Driver Booster Pro 12.4.0.585 Crack Free Download
henryc1122g
 
PPTX
From spreadsheets and delays to real-time control
SatishKumar2651
 
PDF
4K Video Downloader Plus Pro Crack for MacOS New Download 2025
bashirkhan333g
 
PDF
Dipole Tech Innovations – Global IT Solutions for Business Growth
dipoletechi3
 
PDF
Why is partnering with a SaaS development company crucial for enterprise succ...
Nextbrain Technologies
 
Transforming Insights: How Generative AI is Revolutionizing Data Analytics
LetsAI Solutions
 
AOMEI Partition Assistant Crack 10.8.2 + WinPE Free Downlaod New Version 2025
bashirkhan333g
 
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
MiniTool Power Data Recovery 8.8 With Crack New Latest 2025
bashirkhan333g
 
TheFutureIsDynamic-BoxLang witch Luis Majano.pdf
Ortus Solutions, Corp
 
AI Prompts Cheat Code prompt engineering
Avijit Kumar Roy
 
MiniTool Partition Wizard Free Crack + Full Free Download 2025
bashirkhan333g
 
ERP Consulting Services and Solutions by Contetra Pvt Ltd
jayjani123
 
Simplify React app login with asgardeo-sdk
vaibhav289687
 
UITP Summit Meep Pitch may 2025 MaaS Rebooted
campoamor1
 
Latest Capcut Pro 5.9.0 Crack Version For PC {Fully 2025
utfefguu
 
Ready Layer One: Intro to the Model Context Protocol
mmckenna1
 
Foundations of Marketo Engage - Powering Campaigns with Marketo Personalization
bbedford2
 
Agentic Automation: Build & Deploy Your First UiPath Agent
klpathrudu
 
Prompt Like a Pro. Leveraging Salesforce Data to Power AI Workflows.pptx
Dele Amefo
 
IObit Driver Booster Pro 12.4.0.585 Crack Free Download
henryc1122g
 
From spreadsheets and delays to real-time control
SatishKumar2651
 
4K Video Downloader Plus Pro Crack for MacOS New Download 2025
bashirkhan333g
 
Dipole Tech Innovations – Global IT Solutions for Business Growth
dipoletechi3
 
Why is partnering with a SaaS development company crucial for enterprise succ...
Nextbrain Technologies
 
Ad

Patterns in Terraform 12+13: Data, Transformations and Resources

  • 1. Lead DevOps Engineer - Salesforce Robert Blumen The 'Data, Transformations, Resources' Pattern in Terraform
  • 2. Outline ● Terraform anti-patterns ● New features in versions 12 and 13 ● The "Data + Transforms + Resources" Pattern
  • 3. What Terraform 11 Can Not Do ● Looping ● Conditional ● Nested loops ● Data structures
  • 4. Data Types in Terraform 12 & 13
  • 5. structured data types ● list ● map ● set ● tuple ● object
  • 7. variable group_members { type = list(string) description = "list of emails of the group members" } example
  • 8. variable people { type = list(object({ name=string, age=number })) default = [ { name = "John" age = 32 } ] } type checking (continued)
  • 9. ~/Devel/devops-tools (rblumen) $ /opt/hashi/terraform-0.12.20 apply -var 'people=[ { name="Job", age=71 } ]' Apply complete! Resources: 0 added, 0 changed, 0 destroyed. ~/Devel/devops-tools (rblumen) $ /opt/hashi/terraform-0.12.20 apply -var 'people=[ { name="Job", age=true } ]' Error: Invalid value for input variable The argument -var="people=..." does not contain a valid value for variable "people": element 0: attribute "name": string required.
  • 10. groups = [ { "key" : "value" }, { "key-2": "value-2" } ] all_groups = merge(local.groups...) ### { "key": "value", "key-2": "value" } python-style splatting
  • 12. terraform 11 - indexed lists resource providerX web_thing { count = var.num_servers name = element(var.server_names,count.index) } ##### web_thing[0] web_thing[1] web_thing[2] web_thing[3]
  • 13. locals { server_names = [ "db", "front-end", "back-end" ] } terraform 12+ - maps web_srv["db"] web_srv["front-end"] web_srv["back-end"] resource providerY web_srv { for_each = toset(var.server_names) name = each.value }
  • 14. old - (count based): aws_iam_policy.aws_admin_access[3] new - (for_each): aws_iam_policy.aws_admin_access["AmazonS3FullAccess"] indexing
  • 15. locals { administrator_policies = [ "AmazonEC2FullAccess", "AmazonS3FullAccess", "AWSKeyManagementServicePowerUser", "AWSLambdaFullAccess", ] } data aws_iam_policy aws_admin_access { for_each = toset(local.administrator_policies) arn = "arn:aws:iam::aws:policy/${each.value}" } for_each - iterating over a list
  • 16. resource heroku_app_feature app_feature { for_each = { for f, e in { spaces-tls-legacy : false, spaces-tls-modern : true spaces-strict-tls : false } : f => e } app = heroku_app.app.name name = each.key enabled = each.value } for_each - iterating over a map
  • 17. data aws_iam_policy_document access { for_each = toset(local.groups) statement { effect = "Allow" actions = ["s3:PutObject", ] resources = data.aws_s3_bucket.devel.arn } } chaining iteration resource aws_iam_policy access { for_each = data.aws_iam_policy_document.access name = "${each.key}Access" policy = each.value.json } resource aws_iam_group_policy_attachment group_access { for_each = aws_iam_policy.access group = each.key policy_arn = each.value.arn }
  • 18. ● 12: for_each does not work with modules ● 13: for_each supports modules terraform 12 versus 13
  • 20. functions on sets ● setintersection ● setproduct ● setsubtract ● setunion
  • 21. locals { all_groups = toset([ "dev", "core", "admin", "ops", "chat" ]) some_groups = toset(["dev", "voice"]) other_groups = setsubtract(all_groups, some_groups) # in 12.21 } resource aws_iam_group groups { for_each = local.all_groups name = each.value } resource aws_thing thing_one { for_each = local.some_groups group = aws_iam_group.groups[each.value] } resource aws_thing thing_two { for_each = local.other_groups group = aws_iam_group.groups[each.value] } implementing conditionals with sets
  • 22. locals { test_envs = [ "test1", "test2" ] } resource saas_thing_one test_only { foreach = setintersection(toset([var.environment]), local.test_envs) … } conditionals (2)
  • 24. for expressions users = [ "achman", "aziss", "bwong", "cshah", ] # list emails = [ for u in local.users : "${replace(u, "-", ".")}@pagerduty.com" ] # map emails_by_user = { for u in local.users : u => "${replace(u, "-", ".")}@pagerduty.com" }
  • 25. [for s in var.list : upper(s) if s != ""] for expressions - conditionals
  • 26. var user_email_assn { type = map(string) default = { ... } } local { labels = [ for user, email in var.user_email_assn: "${user}:${email} ] } for expressions - map
  • 27. { for s in [ "abc", "def", "aef", "dps" ] : substr(s, 0, 1) => s... } { "a" = [ "abc", "aef", ] "d" = [ "def", "dps", ] } for expressions - group by
  • 29. resource backend_foo left_hand_thing { for_each = ... } resource backend_bar right_hand_thing { for_each = .. } resource backend_foo_bar left_right_assn { for_each = ?? left_hand_thing = each.value.left right_hand_thing = each.value.right } many-to-many
  • 30. # groups have multiple members # user may belong to more than one group resource pagerduty_user { … } resource pagerduty_team { … } resource pagerduty_team_membership { for_each = ?? user_id = ?? team_id = ?? } example: PagerDuty
  • 31. for g in groups { create group for u in groups.users { create user create create user_group_membership } } conventional imperative language
  • 32. locals { on_call_teams = { tier_1 = [ "abe", "jaxel", "beldon" ] tier_2 = [ "abe", "janpax", "fanlo" ] escalation = [ "adam", "shefty", "fanlo" ] } } terraform solution
  • 33. resource pagerduty_team teams { for_each = keys(local.on_call_teams) name = each.value } resource pagerduty_user users { for_each = distinct(flatten(values(local.on_call_teams))) name = each.value email = "${each.value}@companyx.com" }
  • 34. locals { team_user_pairs = [ for team, users in local.on_call_teams: [ for for user in users: { user: user team: team } ] ] team_users = { for pair in flatten(local.team_user_pairs): "${pair.team}-${pair.user}" => { team_id: pagerduty_team[pair.team].id, user_id: pagerduty_user[pair.user].id } } } resource pagerduty_team_membership membership { for_each = local.team_users team_id = each.value.team_id user_id = each.value.user_id }
  • 35. locals { on_call_teams = { tier_1 = [ "abe", "jaxel", "beldon" ] tier_2 = [ "abe", "janpax", "fanlo" ] escalation = [ "adam", "shefty", "fanlo" ] } } ### resource saas_team_membership membership { for_each = ?? user = each.key groups = each.value } one:many inverting
  • 36. user_group_pairs = flatten([ for group, members in local.group_memberships : [ for user in members : { user = user group = group } ] ]) groups_by_user = { for pair in local.user_group_pairs : pair.user => pair.group... } }
  • 37. resource saas_team_membership membership { for_each = local.groups_by_user user = each.key groups = each.value }
  • 39. objectives ● understandable ● easy to change ● economical (DRY)
  • 40. ● data ○ structure according to your domain ○ use data structures ○ normalize/minimize duplication ○ optimize for frequent changes ● transform ○ use for, if and set functions ○ inputs: your data ○ outputs: what your provider's resources need ● resources ○ create each resource once ○ chain resources pattern for code organization