0
# Compilation and Hooks
1
2
System for compiling registered Terraform objects to JSON format and extending functionality through transformation hooks. This includes global object registry management, compilation control, and an extensible hook system for modifying objects during the compilation process.
3
4
## Capabilities
5
6
### Object Compilation
7
8
Functions for converting registered Terraform objects into Terraform JSON configuration format and managing the global object registry.
9
10
```python { .api }
11
def compile() -> dict:
12
"""
13
Compile all registered Terraform objects to dictionary format.
14
15
Returns:
16
dict - Complete Terraform configuration as Python dictionary
17
18
The compilation process:
19
1. Collects all registered TFObject instances
20
2. Applies transformation hooks for each object type
21
3. Organizes objects by Terraform type (resource, data, provider, etc.)
22
4. Generates properly formatted Terraform JSON
23
24
Example:
25
terraform_json = compile()
26
print(terraform_json) # Complete Terraform configuration
27
"""
28
29
def reset() -> None:
30
"""
31
Clear all registered objects from the global registry.
32
33
Use this function to start fresh configurations or between test runs.
34
After calling reset(), previously created objects will no longer be
35
included in compilation output.
36
37
Example:
38
# Create some resources
39
Resource('aws_instance', 'web')
40
compile() # Includes the instance
41
42
reset() # Clear registry
43
compile() # Empty configuration
44
"""
45
```
46
47
### Hook System
48
49
Extensible system for transforming Terraform objects during compilation, enabling custom modifications, validation, and schema transformations.
50
51
```python { .api }
52
class TFObject:
53
@classmethod
54
def add_hook(cls, object_type: str, hook_function: Callable[[dict], dict]) -> None:
55
"""
56
Add a transformation hook for objects of the specified type.
57
58
Parameters:
59
- object_type: str - Terraform object type to hook (e.g., 'resource', 'provider')
60
- hook_function: Callable - Function that transforms the complete output dictionary
61
62
Hook function signature:
63
def hook_function(output: dict) -> dict:
64
# output: Complete Terraform output dictionary for this object type
65
# Return: Modified output dictionary
66
67
Note: This is the low-level hook interface. For easier use with specific
68
resource types, use Resource.add_hook() or Provider.add_hook() instead.
69
70
Example:
71
def my_hook(output):
72
# Modify entire resource section
73
if 'resource' in output:
74
for resource_type in output['resource']:
75
for resource_name in output['resource'][resource_type]:
76
# Add common tags to all resources
77
output['resource'][resource_type][resource_name].setdefault('tags', {})
78
output['resource'][resource_type][resource_name]['tags']['ManagedBy'] = 'Terraformpy'
79
return output
80
81
TFObject.add_hook('resource', my_hook)
82
"""
83
84
# High-level hook interfaces for specific object types:
85
86
# For Resource and Data objects (TypedObject subclasses):
87
Resource.add_hook(resource_type: str, hook_function: Callable[[str, dict], dict]) -> None
88
Data.add_hook(data_type: str, hook_function: Callable[[str, dict], dict]) -> None
89
90
# For Provider, Variable, Output, Module objects (NamedObject subclasses):
91
Provider.add_hook(provider_name: str, hook_function: Callable[[dict], dict]) -> None
92
Variable.add_hook(variable_name: str, hook_function: Callable[[dict], dict]) -> None
93
Output.add_hook(output_name: str, hook_function: Callable[[dict], dict]) -> None
94
Module.add_hook(module_name: str, hook_function: Callable[[dict], dict]) -> None
95
96
@classmethod
97
def compile(cls) -> dict:
98
"""
99
Class method version of compile() function.
100
101
Returns:
102
dict - Complete Terraform configuration dictionary
103
104
This is equivalent to the standalone compile() function.
105
"""
106
107
@classmethod
108
def reset(cls) -> None:
109
"""
110
Class method version of reset() function.
111
112
Clears all registered objects from the global registry.
113
This is equivalent to the standalone reset() function.
114
"""
115
```
116
117
## Usage Examples
118
119
### Basic Compilation Workflow
120
121
```python
122
from terraformpy import Resource, Variable, Output, compile, reset
123
124
# Create Terraform objects
125
env = Variable('environment', default='dev')
126
web_server = Resource('aws_instance', 'web',
127
instance_type='t3.micro',
128
tags={'Environment': env.ref()}
129
)
130
Output('instance_id', value=web_server.id)
131
132
# Compile to JSON
133
terraform_config = compile()
134
print(terraform_config)
135
136
# Clear for next configuration
137
reset()
138
139
# Verify registry is empty
140
empty_config = compile()
141
print(empty_config) # Should be empty or minimal
142
```
143
144
### Hook-Based Transformations
145
146
```python
147
from terraformpy import Resource, TFObject
148
149
# Hook to add standard tags to all EC2 instances
150
def add_standard_tags(object_id, attrs):
151
"""Add standard tags to EC2 instances."""
152
attrs['tags'] = attrs.get('tags', {})
153
attrs['tags'].update({
154
'ManagedBy': 'Terraformpy',
155
'CreatedAt': '2023-01-01' # Could use datetime.now()
156
})
157
return attrs
158
159
# Hook to enforce naming convention
160
def enforce_naming_convention(object_id, attrs):
161
"""Ensure instance names follow convention."""
162
tags = attrs.get('tags', {})
163
if 'Name' in tags and not tags['Name'].startswith('app-'):
164
tags['Name'] = f"app-{tags['Name']}"
165
attrs['tags'] = tags
166
return attrs
167
168
# Register hooks
169
TFObject.add_hook('aws_instance', add_standard_tags)
170
TFObject.add_hook('aws_instance', enforce_naming_convention)
171
172
# Create resources - hooks will be applied automatically
173
web_server = Resource('aws_instance', 'web',
174
instance_type='t3.micro',
175
tags={'Name': 'webserver', 'Environment': 'prod'}
176
)
177
178
# Compile - hooks transform the resource
179
config = compile()
180
# The instance will have:
181
# - Name: 'app-webserver' (enforced naming)
182
# - ManagedBy: 'Terraformpy' (standard tag)
183
# - CreatedAt: '2023-01-01' (standard tag)
184
# - Environment: 'prod' (original tag preserved)
185
```
186
187
### AWS Security Group Hook Example
188
189
```python
190
from terraformpy import Resource
191
from terraformpy.hooks.aws import install_aws_security_group_attributes_as_blocks_hook
192
193
# Install the AWS security group hook
194
install_aws_security_group_attributes_as_blocks_hook()
195
196
# Create security group - hook will transform rule format
197
web_sg = Resource('aws_security_group', 'web',
198
name='web-sg',
199
description='Security group for web servers',
200
201
# These attributes will be transformed by the hook
202
ingress=[
203
{
204
'from_port': 80,
205
'to_port': 80,
206
'protocol': 'tcp',
207
'cidr_blocks': ['0.0.0.0/0']
208
},
209
{
210
'from_port': 443,
211
'to_port': 443,
212
'protocol': 'tcp',
213
'cidr_blocks': ['0.0.0.0/0']
214
}
215
]
216
)
217
218
# Compile - AWS hook transforms ingress rules to proper format
219
config = compile()
220
```
221
222
### Custom Validation Hook
223
224
```python
225
from terraformpy import Resource, TFObject
226
227
def validate_instance_type(object_id, attrs):
228
"""Validate EC2 instance types for cost control."""
229
allowed_types = ['t3.nano', 't3.micro', 't3.small']
230
instance_type = attrs.get('instance_type', '')
231
232
if instance_type not in allowed_types:
233
raise ValueError(f"Instance type {instance_type} not allowed for {object_id}. "
234
f"Allowed types: {allowed_types}")
235
236
return attrs
237
238
def add_cost_center(object_id, attrs):
239
"""Ensure all resources have cost center tag."""
240
attrs['tags'] = attrs.get('tags', {})
241
if 'CostCenter' not in attrs['tags']:
242
attrs['tags']['CostCenter'] = 'engineering'
243
244
return attrs
245
246
# Register validation and tagging hooks
247
TFObject.add_hook('aws_instance', validate_instance_type)
248
TFObject.add_hook('aws_instance', add_cost_center)
249
250
# This will pass validation
251
web_server = Resource('aws_instance', 'web',
252
instance_type='t3.micro',
253
ami='ami-12345678'
254
)
255
256
# This will raise ValueError during compilation
257
try:
258
expensive_server = Resource('aws_instance', 'expensive',
259
instance_type='c5.24xlarge', # Not in allowed list
260
ami='ami-12345678'
261
)
262
compile() # Validation hook will raise error here
263
except ValueError as e:
264
print(f"Validation failed: {e}")
265
```
266
267
### Hook for Resource Dependencies
268
269
```python
270
from terraformpy import Resource, TFObject
271
272
def add_dependency_tags(object_id, attrs):
273
"""Add dependency information to resources."""
274
# Extract resource type and name from object_id
275
# Format: "resource_type.resource_name"
276
resource_type, resource_name = object_id.split('.', 1)
277
278
attrs['tags'] = attrs.get('tags', {})
279
attrs['tags']['ResourceType'] = resource_type
280
attrs['tags']['ResourceName'] = resource_name
281
282
return attrs
283
284
# Hook to automatically add VPC tags to subnet resources
285
def add_vpc_context(object_id, attrs):
286
"""Add VPC context to subnet resources."""
287
if 'aws_subnet' in object_id:
288
attrs['tags'] = attrs.get('tags', {})
289
attrs['tags']['NetworkTier'] = 'private' if 'private' in object_id else 'public'
290
291
return attrs
292
293
# Register hooks for multiple resource types
294
for resource_type in ['aws_instance', 'aws_security_group', 'aws_subnet']:
295
TFObject.add_hook(resource_type, add_dependency_tags)
296
297
TFObject.add_hook('aws_subnet', add_vpc_context)
298
299
# Create resources
300
private_subnet = Resource('aws_subnet', 'private_web',
301
vpc_id='${aws_vpc.main.id}',
302
cidr_block='10.0.1.0/24'
303
)
304
305
web_server = Resource('aws_instance', 'web_server',
306
subnet_id=private_subnet.id,
307
instance_type='t3.micro'
308
)
309
310
# Compile with hooks applied
311
config = compile()
312
```
313
314
### Conditional Hook Application
315
316
```python
317
from terraformpy import Resource, Variable, TFObject
318
import os
319
320
def environment_specific_hook(object_id, attrs):
321
"""Apply different transformations based on environment."""
322
environment = os.getenv('ENVIRONMENT', 'dev')
323
324
attrs['tags'] = attrs.get('tags', {})
325
attrs['tags']['Environment'] = environment
326
327
if environment == 'production':
328
# Production-specific settings
329
attrs['tags']['Backup'] = 'daily'
330
attrs['tags']['Monitoring'] = 'enabled'
331
332
# Enable detailed monitoring for production instances
333
if 'aws_instance' in object_id:
334
attrs['monitoring'] = True
335
attrs['ebs_optimized'] = True
336
337
elif environment == 'development':
338
# Development-specific settings
339
attrs['tags']['Backup'] = 'none'
340
attrs['tags']['AutoShutdown'] = '6pm'
341
342
return attrs
343
344
# Register environment-aware hook
345
for resource_type in ['aws_instance', 'aws_rds_instance', 'aws_efs_file_system']:
346
TFObject.add_hook(resource_type, environment_specific_hook)
347
348
# Create resources - hooks will apply environment-specific settings
349
web_server = Resource('aws_instance', 'web',
350
instance_type='t3.micro',
351
ami='ami-12345678'
352
)
353
354
# Environment determines the final configuration
355
config = compile()
356
```
357
358
## AWS Specific Hooks
359
360
### Pre-built AWS Security Group Hook
361
362
```python
363
from terraformpy.hooks.aws import (
364
install_aws_security_group_attributes_as_blocks_hook,
365
fill_in_optional_aws_security_group_rules_attrs
366
)
367
368
# Install the built-in AWS security group transformation hook
369
install_aws_security_group_attributes_as_blocks_hook()
370
371
# The hook function can also be used directly
372
def custom_sg_hook(object_id, attrs):
373
"""Custom wrapper around AWS security group hook."""
374
# Apply the built-in transformation first
375
attrs = fill_in_optional_aws_security_group_rules_attrs(object_id, attrs)
376
377
# Add custom logic
378
attrs['tags'] = attrs.get('tags', {})
379
attrs['tags']['SecurityLevel'] = 'standard'
380
381
return attrs
382
383
# Register custom hook that includes built-in functionality
384
TFObject.add_hook('aws_security_group', custom_sg_hook)
385
```