"Your AKS platform is modularized, state is remote, and environments are separated. Now let's master advanced HCL patterns, automate deployments with CI/CD, and lock down security for production."
Dynamic Blocks
Generate repeated nested blocks from a collection, keeping your code DRY.
Without Dynamic (repetitive)
resource"azurerm_network_security_group""main" {
# ...security_rule {
name = "AllowHTTPS"priority = 100# ... 6 more attributes
}
security_rule {
name = "AllowHTTP"priority = 200# ... 6 more attributes
}
security_rule {
name = "AllowSSH"priority = 300# ... 6 more attributes
}
}
HashiCorp recommends provisioners as a last resort.
Problems
Not tracked in state
Can't be planned or previewed
Failure handling is limited
Creates hidden dependencies
Not idempotent by default
Better Alternatives
cloud-init for VM configuration
Ansible for configuration management
Kubernetes provider for K8s resources
Helm provider for Helm charts
null_resource + triggers if you must
terraform import
Bring existing Azure resources under Terraform management.
# Step 1: Write the resource block in your .tf fileresource"azurerm_resource_group""legacy" {
name = "rg-legacy-app"location = "uksouth"
}
# Step 2: Import the existing resource into state
$ terraform import azurerm_resource_group.legacy \
/subscriptions/xxx/resourceGroups/rg-legacy-app
# Step 3: Run plan to verify - adjust config until plan shows no changes
$ terraform plan
No changes. Your infrastructure matches the configuration.
Import Block (Terraform 1.5+)
Declarative imports - no CLI command needed.
# import.tfimport {
to = azurerm_resource_group.legacy
id = "/subscriptions/xxx/resourceGroups/rg-legacy-app"
}
# Generate config automatically (Terraform 1.5+)
$ terraform plan -generate-config-out=generated.tf
Import blocks are processed during terraform plan
-generate-config-out creates the HCL for you (review it!)
Once imported successfully, remove the import block
Much better than the old CLI-based import workflow
Moved Blocks: Refactoring Safely
# Scenario: Extract networking into a module# Before: resource was at root level# resource "azurerm_virtual_network" "main" { ... }# After: resource is inside a module# module "networking" { source = "./modules/networking" }# Tell Terraform it moved (don't destroy/recreate!)moved {
from = azurerm_virtual_network.main
to = module.networking.azurerm_virtual_network.main
}
moved {
from = azurerm_subnet.aks_nodes
to = module.networking.azurerm_subnet.main["snet-aks-nodes"]
}
Moved blocks let you refactor without downtime
Version-controlled - team members see the change in code review
Safe to remove after the move is applied everywhere
Knowledge Check 2
1. Why does HashiCorp recommend avoiding provisioners?
2. What does terraform plan -generate-config-out=generated.tf do?
3. What happens to the actual Azure resource during a moved block operation?
CI/CD for Terraform
Automate the plan-review-apply cycle in your pipeline.
Post the terraform plan as a PR comment so reviewers see what will change.
# GitHub Actions step to post plan as PR comment
- name: Terraform Plan
id: plan
run: terraform plan -no-color -out=tfplan
continue-on-error: true
- name: Post Plan to PR
uses: actions/github-script@v7
if: github.event_name == 'pull_request'
with:
script: |
const plan = `${{ steps.plan.outputs.stdout }}`;
const body = `#### Terraform Plan
\`\`\`
${plan.substring(0, 65000)}
\`\`\`
*Triggered by @${{ github.actor }}*`;
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});