Infrastructure as Code (IaC)
A practical developer learning guide to Terraform, Azure Bicep, ARM templates, infrastructure provisioning, environment management, security, and delivery pipelines — with real-world examples for modern .NET applications.
01. Introduction to Infrastructure as Code
Every application depends on infrastructure: compute, networking, databases, identity, storage, monitoring, and security controls. Creating these resources manually through a cloud portal can work for a first experiment, but it becomes difficult to repeat accurately across development, testing, staging, and production.
Infrastructure as Code (IaC) means describing infrastructure in files that can be reviewed, versioned, tested, and deployed through a repeatable process. Instead of documenting a sequence of portal clicks, you describe the intended infrastructure and let an IaC engine create or update it.
The same reviewed infrastructure definition can be promoted through multiple environments.
02. Why Developers Should Learn IaC
IaC is not only an infrastructure team's concern. A .NET developer may need a Web API, Azure SQL Database, App Service, Key Vault, Storage Account, private networking, managed identity, and Application Insights. Understanding how these dependencies are provisioned helps developers collaborate with platform teams and deliver applications consistently.
- Recreate development and test environments without repeating portal steps.
- Review infrastructure changes in pull requests.
- Reduce configuration drift between environments.
- Make deployments more predictable and auditable.
- Recover environments after accidental deletion or regional incidents.
- Apply security, naming, tagging, and cost standards consistently.
IaC does not remove operational responsibility. It makes infrastructure changes more visible and repeatable; design, permissions, monitoring, backup, and cost control still matter.
03. Imperative vs Declarative Infrastructure
There are two common ways to express infrastructure changes. An imperative script describes the steps to execute. A declarative definition describes the desired end state and lets the tool determine the required operations.
Terraform and Bicep are primarily declarative. Scripts still have a place for orchestration, validation, data migration, and tasks that are not naturally represented as managed resources. Avoid mixing imperative scripts into provisioning without understanding retries, idempotency, and failure recovery.
04. How IaC Fits into a .NET Application
Consider a .NET 10 Web API with a frontend, database, and background worker. The application repository contains source code and build instructions; the infrastructure repository or folder defines the cloud resources needed to run it.
Application code and infrastructure code are related deliverables, but they have different lifecycles and permissions.
A practical boundary is: application CI builds and tests the .NET artifact; infrastructure CI validates and provisions required resources; deployment then releases the artifact into the provisioned environment. Some teams combine these pipelines, but the permissions and approval boundaries should remain clear.
05. IaC Tools: Terraform, Bicep, and ARM Templates
| Tool | Scope | Language / model | Good fit |
|---|---|---|---|
| Terraform | Multi-cloud and many platforms | HCL, providers, state | Teams managing Azure, AWS, Google Cloud, SaaS, and other APIs with one workflow. |
| Bicep | Azure-native | Declarative domain-specific language compiled to ARM JSON | Azure-focused infrastructure with concise syntax and native ARM integration. |
| ARM templates | Azure-native | Declarative JSON templates | Existing deployments, generated templates, and direct ARM compatibility. |
These tools are not interchangeable in every detail. Terraform uses providers and state to track managed objects. Bicep and ARM templates use Azure Resource Manager deployment semantics. Microsoft recommends Bicep for new Azure template authoring because it offers ARM capabilities with a more concise syntax.
06. Choosing the Right IaC Tool
Choose a tool based on cloud strategy, team skills, governance, provider support, state management, and the lifetime of the infrastructure—not simply because one language looks shorter.
- Choose Bicep when the target is Azure and you want a first-party declarative authoring experience.
- Choose Terraform when infrastructure spans multiple clouds or services and a common provider-based workflow is valuable.
- Use ARM JSON when you must work with existing templates, generated output, or systems that explicitly require ARM template JSON. For new authoring, evaluate Bicep first.
A .NET team can use any of these tools. IaC is independent of the programming language used to build the application.
07. Infrastructure as Code Lifecycle
The lifecycle does not end when provisioning succeeds. Monitor deployed resources, detect manual changes, review drift, update modules and provider versions, and decommission resources safely when they are no longer needed.
08. Infrastructure, Resources, and Dependencies
A resource is an infrastructure object managed by a provider or cloud platform: a virtual network, subnet, App Service plan, storage account, SQL server, database, Key Vault, or monitoring workspace.
Resources often depend on one another. A private endpoint may depend on a subnet; an application may need a database connection value; a role assignment may depend on an identity existing. IaC tools build a dependency graph to determine safe ordering where dependencies are declared or inferred.
Make dependencies explicit only when the tool cannot infer them from resource references. Unnecessary dependency declarations can serialize deployments and make templates harder to maintain.
09. Terraform: What It Is and How It Works
Terraform is an infrastructure automation tool that uses HashiCorp Configuration Language (HCL) to describe resources. Providers translate Terraform operations into calls to cloud and service APIs.
A typical workflow is init, validate, plan, and apply. Terraform compares configuration, state, and the real infrastructure to calculate proposed changes. The plan should be reviewed before applying it, especially in shared or production environments.
10. Terraform Project Structure
Terraform projects are usually organized around a manageable infrastructure component. A small learning project can start with a few files; a team repository can split reusable modules, environment-specific roots, and operational configuration.
infrastructure/
modules/
app-service/
sql-database/
networking/
environments/
dev/
main.tf
variables.tf
outputs.tf
terraform.tfvars
test/
prod/
.gitignore
README.md
This is one possible structure, not a mandatory Terraform convention. Keep module boundaries cohesive and avoid creating a module for every single resource when it adds no reuse or abstraction value.
11. Terraform Providers and Version Constraints
A provider is a plugin that lets Terraform manage a platform or service. The Azure Resource Manager provider is commonly named azurerm. Provider configuration includes authentication and sometimes subscription, region, or feature settings.
terraform {
required_version = ">= 1.17.0, < 2.0.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {}
}
Version constraints express compatibility ranges. Commit .terraform.lock.hcl so the selected provider versions are reproducible. The sample constraint is illustrative; select and test versions compatible with the resource types and policies used by your project.
12. Terraform Resources and Data Sources
A resource block declares an object Terraform should manage. A data block reads information about an existing object without declaring ownership of its lifecycle.
resource "azurerm_resource_group" "app" {
name = "rg-catalog-dev"
location = "Central India"
tags = {
application = "catalog"
environment = "dev"
managedBy = "terraform"
}
}
output "resource_group_name" {
value = azurerm_resource_group.app.name
}
Resource addresses such as azurerm_resource_group.app are part of Terraform's identity model. Renaming a resource block can appear as a destroy/create unless you declare a state move or use an appropriate migration process.
13. Terraform Variables, Locals, and Outputs
Variables expose values that differ between deployments. Locals name expressions reused within a configuration. Outputs expose selected values after an operation and can be consumed by other configurations or automation.
variable "environment" {
description = "Deployment environment"
type = string
validation {
condition = contains(["dev", "test", "prod"], var.environment)
error_message = "Environment must be dev, test, or prod."
}
}
locals {
app_name = "catalog-${var.environment}"
}
output "app_name" {
value = local.app_name
}
Do not put passwords or tokens in ordinary variable files committed to source control. Marking a variable as sensitive redacts some CLI output, but it does not automatically remove the value from state.
14. Terraform Modules and Reuse
A Terraform module is a collection of resources and related configuration. The root module is the directory where you run Terraform; child modules encapsulate reusable infrastructure patterns.
module "catalog_api" {
source = "../../modules/app-service"
app_name = "catalog-api-dev"
location = "Central India"
resource_group = azurerm_resource_group.app.name
runtime_stack = "DOTNETCORE|10.0"
}
Design modules around a meaningful capability, expose the inputs consumers need, and return useful outputs. Avoid dozens of generic variables that mirror every provider property without adding a clear abstraction.
15. Terraform State: The Critical Concept
Terraform state maps resource addresses in configuration to real objects in a provider. It also stores metadata needed to plan updates and deletions. State is operationally sensitive: it may contain resource attributes and values that should not be exposed broadly.
State is not just a cache; it is part of Terraform's resource ownership and change model.
For teams, use a remote backend with access control, encryption, versioning, and locking or concurrency protection supported by that backend. Do not commit terraform.tfstate to Git, edit it manually, or share it through chat.
16. Remote State, Locking, and Collaboration
A local state file is acceptable for a disposable learning exercise. A team needs a shared backend so collaborators and automation use the same state safely. Backend capabilities differ; confirm locking, access controls, encryption, versioning, and recovery options.
For Azure, Terraform's azurerm backend commonly stores state in an Azure Storage blob. Use Microsoft Entra authentication or another approved identity flow, restrict access to the state container, and enable appropriate storage protections.
terraform {
backend "azurerm" {
resource_group_name = "rg-iac-state"
storage_account_name = "stiacstateexample"
container_name = "tfstate"
key = "catalog-dev.tfstate"
}
}
Backend configuration is initialized separately from ordinary provider configuration. Follow the backend's authentication guidance and do not place access keys or client secrets in committed files.
17. Terraform Workspaces and Environment Separation
Terraform CLI workspaces provide separate state instances for one configuration. They can be useful for similar short-lived copies, but they are not a complete environment-isolation or access-control strategy.
For long-lived production environments, separate root configurations, state backends/keys, permissions, and approval workflows often provide clearer boundaries. Keep development, test, and production state isolated so a command targeting one environment cannot accidentally affect another.
terraform workspace list
terraform workspace new dev
terraform workspace select dev
terraform workspace show
Choose an environment strategy deliberately. Avoid assuming that a workspace name alone prevents a user or pipeline from changing production infrastructure.
18. Terraform Plan, Apply, and Destroy
The core Terraform workflow separates initialization, validation, planning, and execution:
terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform show tfplan
terraform apply tfplan
Use a saved plan for controlled automation so the reviewed plan is the one applied. Plan files may contain sensitive values and should be protected like state. terraform destroy removes resources managed by the configuration and must be treated as a high-impact operation.
apply or destroy from an untrusted branch. Require protected environments, identity controls, and approval for high-impact changes.19. Drift Detection and Importing Existing Infrastructure
Drift occurs when real infrastructure changes outside the IaC workflow or when the real state differs from the configuration. Terraform refreshes information during planning, but teams should still establish a regular drift review process.
If resources already exist, import them into the appropriate state rather than creating duplicates. Importing connects an existing remote object to a Terraform resource address; it does not automatically generate a correct configuration for every property.
terraform plan -refresh-only
terraform import azurerm_resource_group.app /subscriptions/SUBSCRIPTION_ID/resourceGroups/rg-catalog-dev
Review the resulting plan and configuration carefully. Never import the same remote object into multiple independent state files unless the ownership model explicitly supports that arrangement.
20. Terraform Testing and Quality Checks
Infrastructure code should be reviewed and tested like application code, although its tests have different risks and costs. Begin with formatting and static validation, then add module tests, policy checks, and carefully controlled integration tests.
terraform fmt -check -recursive
terraform init -backend=false
terraform validate
terraform test
Terraform's test framework can exercise module behavior with mock providers or real test infrastructure depending on the test design. Keep cloud integration tests isolated, cost-limited, and able to clean up after themselves.
21. Bicep: Azure-Native Infrastructure as Code
Bicep is a declarative language for deploying Azure resources. It is designed as a more readable authoring experience for Azure Resource Manager (ARM). During deployment, Bicep is compiled to an ARM JSON template and submitted through Azure Resource Manager.
Bicep is Azure-specific; it is not intended as a general multi-cloud language. It provides type-aware authoring, modules, parameters, loops, conditions, and deployment at different Azure scopes.
22. Bicep File Structure and Syntax
A Bicep file commonly contains parameters, variables, resource declarations, modules, and outputs. Resource declarations identify an Azure resource type and API version. Use the current resource reference and stable API versions appropriate to the features you need.
param location string = resourceGroup().location
param appName string
var tags = {
application: 'catalog'
managedBy: 'bicep'
}
resource appPlan 'Microsoft.Web/serverfarms@2024-04-01' = {
name: '${appName}-plan'
location: location
sku: {
name: 'B1'
capacity: 1
}
tags: tags
}
output planId string = appPlan.id
Resource API versions vary by resource type and evolve over time. Treat the version above as a syntax illustration; verify the supported API version and properties in the Azure resource reference for your deployment.
23. Bicep Parameters, Variables, and Outputs
Parameters are values supplied at deployment time. Variables simplify expressions and reduce duplication. Outputs expose selected resource properties to deployment automation or a calling deployment.
@description('Environment name')
@allowed([
'dev'
'test'
'prod'
])
param environment string
param location string = resourceGroup().location
var namePrefix = 'catalog-${environment}'
output environmentName string = environment
Use parameters for genuine environment differences, not every constant. Give parameters descriptions, choose safe defaults, and avoid default values for credentials or values that could create unexpectedly expensive resources.
24. Bicep Modules and Reusable Components
Bicep modules split infrastructure into reusable files. A root file can deploy networking, identity, monitoring, and application hosting modules. A module should represent a useful unit with a clear interface rather than simply moving a few lines into another file.
module appHosting './modules/app-hosting.bicep' = {
name: 'app-hosting-deployment'
params: {
appName: 'catalog-api-dev'
location: resourceGroup().location
}
}
output appHostName string = appHosting.outputs.defaultHostName
Module scope matters. Bicep supports deployments at resource group, subscription, management group, and tenant scopes, with the appropriate target scope and permissions. Use the narrowest scope that matches the resources being managed.
25. Bicep Deployment Scopes and What-if
Bicep can deploy at different Azure scopes: resource group, subscription, management group, and tenant. Select the scope intentionally; a resource group deployment cannot create every tenant- or subscription-level object by itself.
Use the ARM What-if operation to preview changes before applying them. What-if is a valuable review step, but its output is still a prediction based on the template and current Azure state; validate permissions, resource behavior, and service-specific constraints.
az bicep build --file main.bicep
az deployment group what-if --resource-group rg-catalog-dev --template-file main.bicep --parameters @dev.bicepparam
az deployment group create --resource-group rg-catalog-dev --template-file main.bicep --parameters @dev.bicepparam
26. ARM Templates: Understanding the JSON Model
Azure Resource Manager templates are JSON documents that describe Azure resources and their configuration. They use a declarative model and are deployed through ARM. ARM templates remain important because Bicep compiles to ARM JSON and the deployment engine executes the resulting template.
For new Azure template authoring, Microsoft recommends Bicep because it offers the same core ARM capabilities with a more concise syntax. You may still encounter ARM JSON in existing projects, generated deployment artifacts, template specs, or integrations.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
}
},
"resources": [],
"outputs": {}
}
This is a minimal structural illustration, not a complete deployable application template. Use the schema and resource API versions documented for the target deployment.
27. Bicep vs ARM JSON
| Area | Bicep | ARM JSON |
|---|---|---|
| Authoring | Concise domain-specific language | Verbose JSON structure and expressions |
| Deployment engine | Compiles to ARM template and uses ARM | Submitted directly to ARM |
| Type experience | Resource-aware type validation and tooling | Schema and editor validation |
| Reuse | Native modules and language features | Linked/nested templates and template specs |
| Recommendation | Preferred for new Azure authoring | Useful for existing assets and generated artifacts |
Learning Bicep first usually makes new Azure infrastructure easier to maintain. Understanding ARM JSON is still useful for reading deployment output, troubleshooting, and integrating with older templates.
28. Environment Management: Dev, Test, Staging, and Production
Environments should share a consistent architecture while allowing controlled differences such as SKU size, scaling limits, network exposure, backup policy, and monitoring retention.
Keep environment-specific values in parameter files, variable sets, or pipeline inputs. Keep the resource architecture in reusable modules. Isolate production state and credentials, and avoid copying entire infrastructure definitions into separate folders that drift over time.
29. Naming, Tags, and Resource Organization
Consistent naming and metadata make cloud resources easier to locate, secure, automate, and charge back. Define a naming standard for application, environment, region, and resource type. Apply tags such as owner, cost centre, environment, application, and managed-by where supported.
Names have provider-specific character limits and uniqueness rules. Do not assume a naming formula is valid for every Azure resource type. Build names from a documented convention, validate them, and use deterministic suffixes when global uniqueness is required.
30. Secrets, Identity, and Least Privilege
Infrastructure pipelines need permissions to create and update resources. Prefer federated identity or managed workload identity over long-lived client secrets where the platform supports it. Give each pipeline only the permissions needed for its environment and deployment scope.
Do not store passwords, service principal secrets, certificates, or database credentials in source files, plain parameter files, Terraform state shared too broadly, or pipeline logs. Use a secret manager such as Azure Key Vault and integrate applications through managed identity when possible.
sensitive marking hides values in some output but does not guarantee that they are absent from state. Protect state as sensitive data.31. Networking and Private Connectivity
Infrastructure definitions often create virtual networks, subnets, route tables, network security groups, private endpoints, DNS zones, and firewall rules. These resources determine how your .NET API reaches databases, storage, identity endpoints, and external services.
Plan address spaces, subnet boundaries, DNS, egress, ingress, and private connectivity before provisioning. Avoid broad inbound rules and public database endpoints by default. Network design is difficult to retrofit after applications depend on IP ranges and DNS names.
32. Provisioning an Azure Environment for a .NET 10 API
A realistic Azure environment for a .NET API may include a resource group, App Service plan, Web App, Azure SQL Database, Key Vault, Application Insights, Log Analytics, and managed identity. A container-based design may use Azure Container Apps or AKS instead of App Service.
Choose hosting based on application needs. Do not provision AKS merely because the application uses containers; managed application platforms may be simpler for a small API.
Use managed identity for service-to-service access where supported. Provision the identity and role assignments through IaC, but keep application configuration and secret values out of the template whenever possible.
33. Terraform Example: Azure Resource Group and App Service
This small example demonstrates Terraform syntax for an Azure resource group and App Service plan. It is a teaching starting point; a complete deployment also needs an app resource, runtime configuration, identity, network decisions, monitoring, and security controls.
resource "azurerm_resource_group" "app" {
name = "rg-catalog-dev"
location = "Central India"
tags = {
application = "catalog"
environment = "dev"
}
}
resource "azurerm_service_plan" "app" {
name = "plan-catalog-dev"
resource_group_name = azurerm_resource_group.app.name
location = azurerm_resource_group.app.location
os_type = "Linux"
sku_name = "B1"
}
Resource names and provider schemas can change across provider versions. Validate against the pinned AzureRM provider documentation before using this as a deployable production template.
34. Bicep Example: App Service Plan and Web App
This example shows the relationship between an App Service plan and a Linux Web App. The runtime stack and resource API versions must be checked against the current Azure resource reference for the selected deployment region.
param location string = resourceGroup().location
param appName string
resource plan 'Microsoft.Web/serverfarms@2024-04-01' = {
name: '${appName}-plan'
location: location
sku: {
name: 'B1'
capacity: 1
}
kind: 'linux'
properties: {
reserved: true
}
}
resource web 'Microsoft.Web/sites@2024-04-01' = {
name: appName
location: location
properties: {
serverFarmId: plan.id
httpsOnly: true
siteConfig: {
linuxFxVersion: 'DOTNETCORE|10.0'
minTlsVersion: '1.2'
}
}
}
output defaultHostName string = web.properties.defaultHostName
For production, add managed identity, access restrictions/private networking, diagnostic settings, deployment slots if appropriate, and a secure database connectivity pattern.
35. Database Provisioning and Data Lifecycle
Provisioning a database server or database is not the same as managing its schema or application data. IaC should create the database infrastructure, networking, identity access, backup settings, and monitoring. Database migrations should be handled by a controlled database deployment process.
For EF Core applications, run migrations as an explicit deployment step or migration job with suitable permissions and rollback planning. Avoid having every application replica attempt schema migration during startup. Infrastructure deletion policies must protect production databases and backups.
36. Infrastructure Pipelines and Pull Request Reviews
An IaC pipeline should validate changes before it can modify shared infrastructure. A typical pull request pipeline runs formatting, validation, static analysis, security scanning, policy checks, and a plan or What-if preview. A protected deployment pipeline then applies an approved plan to the intended environment.
- Developer creates a branch and edits IaC.
- CI runs formatting, validation, and tests.
- CI produces a plan or What-if report.
- Reviewers inspect resource additions, replacements, deletions, permissions, and cost impact.
- Approved changes are applied using a trusted pipeline identity.
- Deployment outputs are recorded and the environment is monitored.
Never expose production credentials to pull requests from untrusted forks or branches. Separate plan permissions from apply permissions where practical.
37. Policy as Code and Governance
Policy as Code expresses infrastructure rules in machine-evaluable form. Policies can require tags, approved regions, private network access, encryption, allowed SKUs, or restrictions on public exposure.
Terraform ecosystems may use Sentinel, OPA/Conftest, or other policy tools. Azure environments can use Azure Policy for governance and compliance evaluation/remediation. Choose a policy layer that fits your platform and make failures actionable for developers.
38. Security Scanning and Supply Chain
IaC can create insecure infrastructure even when the application code is secure. Scan templates for public storage, overly broad network rules, weak TLS settings, privileged identities, unencrypted resources, and exposed secrets.
Use tools such as Checkov, Trivy configuration scanning, tfsec-compatible workflows, or platform-native checks as appropriate. Pin actions and tools in CI, review third-party modules, and protect the state and plan artifacts. A scanner is a useful control, not a substitute for architecture review.
39. Cost Management and Cleanup
Infrastructure code can provision resources that incur ongoing cost even when an application is idle. Review SKUs, storage tiers, retention periods, data transfer, public IPs, NAT gateways, managed database capacity, and non-production uptime.
Use tags and budgets to make ownership visible. Automatically clean up temporary test environments when safe, but protect production data and shared network resources. Destruction should be an explicit, authorized lifecycle operation—not an automatic side effect of a failed test.
40. Disaster Recovery and Reproducibility
IaC improves the ability to rebuild infrastructure, but it does not automatically restore application data or prove that a recovery plan works. Back up databases, storage, certificates, and other stateful services according to business recovery objectives.
Document dependencies that are not managed by IaC, such as DNS ownership, external SaaS configuration, manual approvals, private connectivity, and identity prerequisites. Run recovery exercises in a separate environment and measure the actual recovery time.
41. Common Mistakes and Troubleshooting
| Symptom / mistake | What to investigate |
|---|---|
| Terraform wants to recreate an existing resource | Resource address, state binding, name changes, provider schema changes, and import/move operations. |
| State lock error | Another active operation, stale lock, backend availability, and safe lock recovery procedure. |
| Provider authentication fails | Pipeline identity, subscription/tenant context, role assignments, federation configuration. |
| Bicep type or API error | Resource type, API version, property names, target scope, and current resource reference. |
| Deployment fails due to dependency | Implicit resource references, missing prerequisites, permissions, and deployment scope. |
| Environment differs unexpectedly | Parameter files, variable precedence, workspace/state selection, and manual drift. |
| Secret appears in state or logs | State access, output configuration, sensitive propagation, pipeline logging, and secret handling. |
Read the full error and deployment operation details before making another change. Avoid repeatedly applying a broken configuration without understanding which resource operation failed and whether partial resources were created.
42. Practical Mini Project: Provision a .NET API Environment
Build a repeatable non-production environment for a small .NET 10 Catalog API. You can complete the exercise using Terraform on Azure or Bicep. Keep the scope small enough to understand the lifecycle before adding private networking and advanced governance.
- Create a source repository with an
infrastructurefolder. - Define a resource group and a low-cost App Service plan.
- Provision an ASP.NET Core Web App configured for the .NET 10 runtime.
- Enable managed identity for the Web App.
- Provision an Azure SQL database or connect to a separately managed database environment.
- Grant the app identity only the required database or Key Vault access.
- Configure HTTPS, diagnostic settings, and application monitoring.
- Create separate dev and test parameter/variable inputs.
- Run format, validation, plan/What-if, and review the proposed changes.
- Deploy to dev, test the API, then promote the same infrastructure pattern to test.
- Practice drift detection and a controlled resource update.
- Document how to destroy the non-production environment safely.
Extension: add a private endpoint, DNS integration, Azure Policy checks, and a protected production approval gate. Do not place real credentials in the repository.
43. Terraform vs Bicep: Practical Comparison
| Decision point | Terraform | Bicep |
|---|---|---|
| Multi-cloud | Strong provider ecosystem across many platforms. | Azure-only by design. |
| State model | Explicit state maps resource addresses to remote objects. | Uses ARM deployment state and resource management semantics. |
| Azure service support | AzureRM provider and AzAPI provider. | ARM resource types and API versions are available through Bicep. |
| Reuse | Modules from local paths or registries. | Modules and registries such as Bicep registry. |
| Team workflow | Plan/apply with backend and state controls. | Build/validate/What-if/deploy with Azure scopes and RBAC. |
| Best fit | Common workflow across multiple providers or services. | Azure-first platform with native ARM integration. |
Both can be used professionally. The best choice is the one your team can secure, test, upgrade, and operate consistently.
44. Interview Questions and Answers
1. What is Infrastructure as Code?
A practice of defining and managing infrastructure through version-controlled, machine-readable configuration instead of relying on manual provisioning.
2. What is the difference between imperative and declarative IaC?
Imperative automation describes steps to execute; declarative IaC describes the desired state and lets the tool determine the required changes.
3. What is Terraform?
A declarative infrastructure automation tool that uses HCL, providers, a dependency graph, and state to manage resources.
4. What is a Terraform provider?
A plugin that allows Terraform to interact with a platform or service API.
5. What is Terraform state?
Data that maps Terraform resource addresses to real infrastructure objects and stores metadata needed to plan future changes.
6. Why use a remote backend?
It enables controlled collaboration and central state storage with backend capabilities such as access control, encryption, and locking.
7. What is the difference between a resource and a data source?
A resource declares an object Terraform manages; a data source reads information about an existing object.
8. What is a Terraform module?
A reusable collection of Terraform configuration that groups resources behind a defined interface.
9. What does terraform plan do?
It calculates and displays proposed changes by comparing configuration, state, and the remote infrastructure.
10. What is infrastructure drift?
A difference between the infrastructure described in code and the actual deployed resources, often caused by manual changes or external automation.
11. What is Bicep?
An Azure-specific declarative language that compiles to ARM JSON templates for deployment through Azure Resource Manager.
12. Is Bicep a replacement for ARM?
Bicep is a higher-level authoring language for ARM deployments. ARM remains the deployment engine and JSON template format.
13. Why is Bicep preferred over ARM JSON for new Azure authoring?
It provides the same core ARM capabilities with a more concise syntax and a stronger authoring experience.
14. What is an ARM deployment scope?
The level at which a deployment is executed, such as resource group, subscription, management group, or tenant.
15. How do you manage dev, test, and production?
Reuse modules, separate environment inputs and state, isolate production permissions, and promote reviewed changes through controlled pipelines.
16. How should secrets be handled in IaC?
Use a secret manager and identity federation where possible. Restrict access to state and plan artifacts, and never commit credentials to source control.
17. What is policy as code?
Machine-evaluable rules that enforce infrastructure standards such as approved regions, encryption, tagging, and network exposure.
18. How does IaC help a .NET developer?
It makes the cloud dependencies of an application repeatable, reviewable, and easier to provision consistently across environments.
19. Does IaC replace CI/CD?
No. IaC defines infrastructure; CI/CD validates, reviews, and deploys infrastructure and application artifacts through a controlled process.
20. Does IaC automatically back up application data?
No. Provisioning storage or databases is different from configuring and testing backups and recovery of application data.
45. Official Documentation and Further Learning
- Terraform documentation
- Terraform official releases
- Terraform state documentation
- Terraform modules
- Terraform style and workflow guidance
- Microsoft Bicep documentation
- Bicep official releases
- Bicep best practices
- ARM templates overview
- ARM template best practices
- .NET 10 overview
- Azure Well-Architected Framework
Use official documentation for the latest provider schemas, resource API versions, authentication options, support policies, and service-specific deployment requirements.
46. Final Thought
Infrastructure as Code helps developers treat cloud infrastructure as an engineered part of the application rather than a collection of undocumented portal settings. Terraform provides a provider-based workflow with explicit state; Bicep offers a concise Azure-native language; ARM JSON remains the underlying Azure deployment format.
Start with one small environment, understand the plan or What-if output, protect state and credentials, and keep production changes reviewed. As a .NET developer, knowing how your API's hosting, identity, database, networking, and monitoring are provisioned will make you a stronger contributor to the complete software delivery lifecycle.
Skills Tutorials – Learn • Build • Grow Together