DevOps Interview Questions: Declarative vs Imperative IaC

Reviewed by Mark Dickie · Last updated

14 questions on this page you can answer and see marked on the spot. Go to the first one

Declarative and imperative are two paradigms for managing infrastructure as code (IaC), and the difference between them is one of the most common interview topics in a DevOps round. In a declarative approach you describe the desired end state of your infrastructure and let the tool figure out how to get there. In an imperative approach you give explicit, step-by-step instructions that tell the tool what to do and in what order. Terraform and CloudFormation are declarative; Ansible and Bash scripts are imperative. Interviewers want to see that you can name tools on each side, explain when one fits better than the other, and reason about idempotency, state drift, and reconciliation.

What is the difference between declarative and imperative IaC?

AspectDeclarativeImperative
You describeThe desired end stateThe sequence of actions
Tool decidesHow to reach that stateNothing — you script every step
IdempotencyBuilt in (re-running converges to state)You must code it yourself
Drift detectionSupported via state filesNot automatic
ExamplesTerraform, CloudFormation, Pulumi (with desired-state APIs)Ansible (task mode), Bash, Cloud-init scripts

What should I know for a declarative-vs-imperative interview question?

  1. Be able to define both paradigms in one sentence each and give a concrete tool example for each.
  2. Explain idempotency: why re-running a declarative config produces the same result, and why that matters for pipelines.
  3. Know how state files work in Terraform — they store the last-known infrastructure state so the tool can diff and reconcile.
  4. Discuss when imperative still wins: ad-hoc tasks, configuration that changes per run, or environments where a step-by-step order is required (e.g. a rolling restart sequence).
  5. Be ready to explain drift: what happens when someone changes infrastructure outside of IaC, and how a declarative tool can detect or correct it.

Can a tool be both declarative and imperative?

Yes. Ansible lets you write imperative playbooks with command and shell tasks, but its core modules like apt, file, and service are declarative — you specify the desired package state and Ansible handles the steps. Pulumi supports an imperative programming model in TypeScript, Python, and Go, yet the infrastructure it provisions is described as a desired state. The interview point is that the paradigms describe how you express intent, not a hard partition between tools.

Key facts

  • Tarmac's DevOps interview questions cover 25 questions at difficulty 1–5 of 5.
  • Tarmac tracked 2,209 job postings asking for DevOps in September 2026.
  • Roles asking for DevOps advertise a median base salary of £80,000, across 763 job postings as of September 2026.
  • Tarmac last reviewed these DevOps interview questions on 13 October 2026.

At a glance

Questions25
Difficulty1–5 of 5
FormatsTrue / false, Multiple choice, Ordering, Fill in the blank, Short answer, Multiple answer

What you'll review

  1. declarative vs imperative

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

A declarative Infrastructure-as-Code tool like Terraform specifies the desired end state of infrastructure (e.g., "ensure 3 web servers exist") rather than the step-by-step commands needed to reach that state.#

Options

Show answer

True. Declarative Infrastructure-as-Code tools like Terraform express the desired end state of infrastructure — for example, "ensure 3 web servers exist" — and the tool itself computes and applies the steps to reach that state. Imperative approaches, such as shell scripts issuing explicit API calls, spell out each step individually.

Why:

Declarative IaC describes the desired end state — what the infrastructure should look like — and leaves it to the tool to figure out how to get there. Imperative IaC, by contrast, lists the explicit sequence of actions (create this, then configure that). Terraform, CloudFormation, and Pulumi's declarative DSLs are declarative; a shell script that runs aws ec2 run-instances three times is imperative.

Which statement correctly describes the declarative approach to infrastructure-as-code?#

Options

Show answer

The declarative approach to infrastructure-as-code describes the desired end state of the infrastructure, and the tool works out the steps needed to reach that state and correct any drift. Imperative IaC, by contrast, specifies the exact commands to run in sequence.

Why:

Declarative IaC states the desired final state of the infrastructure (for example, three web servers behind a load balancer) and leaves the sequence of actions to the tool, which also reconciles drift when the real state diverges. The option describing exact ordered commands is the imperative approach, and the option requiring the operator to specify creation order step by step is also imperative in nature. The option claiming declarative IaC cannot be expressed as code files is false, since declarative IaC is written as code files such as manifests or templates.

True or false: In a declarative IaC tool such as Terraform, you must specify the exact sequence of commands needed to bring the infrastructure to its desired state.#

Options

Show answer

False. A declarative IaC tool such as Terraform requires you to describe the desired end state of the infrastructure, not the exact sequence of commands. The tool works out the necessary actions and their order itself; specifying the exact sequence of actions is the imperative approach.

Why:

This is false because a declarative IaC tool like Terraform only requires you to declare the desired end state, for example three web servers behind a load balancer. The tool itself computes the necessary actions, their order, and any reconciliation of drift. Specifying the exact sequence of commands is a hallmark of the imperative approach, not the declarative one.

True or false: in a declarative infrastructure-as-code tool such as Terraform, you describe the desired end state of the infrastructure (for example, '3 EC2 instances behind a load balancer') rather than the individual steps or commands needed to reach that state.#

Options

Show answer

True. In declarative infrastructure-as-code tools such as Terraform, you describe the desired end state, for example three EC2 instances behind a load balancer, and the tool works out the actions needed to reach it. Imperative approaches, such as shell scripts calling the AWS CLI, instead require you to spell out each step in order.

Why:

Declarative infrastructure-as-code tools let you specify the desired outcome, and the tool works out the sequence of actions needed to reach that state. Imperative IaC, such as shell scripts calling the AWS CLI, requires you to write out each step explicitly: create the security group, launch the instances, register them with the load balancer, and so on. Terraform is a canonical example of the declarative approach, so the statement is true.

In infrastructure-as-code, what distinguishes a declarative tool (e.g., Terraform, CloudFormation) from an imperative tool (e.g., a Bash script calling cloud APIs)?#

Options

Show answer

A declarative IaC tool lets you describe the desired end state of infrastructure and computes the steps to get there, whereas an imperative tool requires you to write the explicit sequence of actions yourself. Terraform and CloudFormation are declarative; a Bash script calling cloud APIs is imperative. The core difference is 'what' versus 'how.'

Why:

Declarative IaC tools like Terraform and CloudFormation let you describe the desired end state of your infrastructure; the tool's provider/executor computes the diff and applies the necessary changes to converge reality to that state. Imperative tools require you to spell out each step (e.g., create this, then update that). The option that says "A declarative tool requires you to write the exact sequence of API calls to reach the desired state, while an imperative tool lets you specify only the end state" reverses the definitions. The option that says "A declarative tool can only provision resources once and cannot detect drift on subsequent runs, whereas an imperative tool continuously reconciles state" is incorrect because declarative tools typically DO support drift detection on re-apply. The option that says "A declarative tool is always idempotent, while an imperative tool can never be made idempotent" overstates the claim — imperative scripts can be written to be idempotent, and declarative tools are not 'always' idempotent in every edge case, but the key distinction is the level of abstraction, not a guarantee of idempotency.

True or false: in declarative infrastructure-as-code, reapplying an unchanged configuration produces the same result, making the operation naturally idempotent, because the tool compares the current state to the desired state and makes no changes where they already match.#

Options

Show answer

Declarative infrastructure-as-code is idempotent. It describes the desired state, so the tool compares current state to it and applies only the differences. Reapplying an unchanged configuration is therefore a no-op that produces the same result. Imperative scripts often re-run every step unless explicitly guarded.

Why:

Declarative IaC describes the desired state rather than the sequence of operations. Because the tool compares the current state to the desired state and only applies changes where they differ, reapplying an unchanged configuration is a no-op, which is the hallmark of idempotency. The statement is therefore true. Imperative scripts, by contrast, may re-execute every step regardless of current state unless explicitly guarded.

Which statement correctly describes the fundamental difference between declarative and imperative Infrastructure as Code (IaC)?#

Options

Show answer

Declarative IaC describes the desired end state and lets the tool work out and apply the steps to reach it. Imperative IaC gives explicit, step-by-step commands to reach that state. Tools like Terraform and CloudFormation reconcile the actual infrastructure with the declared state on each run.

Why:

Declarative IaC (Terraform, CloudFormation, ARM, Pulumi in declarative mode) defines the desired end state, such as three VMs with these tags, and the tool computes and applies the steps to reach it, reconciling on each run. Imperative IaC (shell scripts, aws cli sequences, manual docker run chains) gives explicit step-by-step instructions to reach the state. The option that swaps the two definitions is wrong, the option claiming a location restriction for each style is false because both work on-premises and in cloud, and the option claiming declarative is always faster per run is false because per-run speed is not inherently better for either approach.

True or False: When an unchanged declarative IaC configuration is re-applied to infrastructure the tool already manages, the tool compares the desired state with its recorded or observed state, finds no difference, and makes no changes, so the run is idempotent.#

Options

Show answer

Re-applying an unchanged declarative configuration is idempotent: the tool compares desired state with its recorded or observed state, finds nothing to change, and makes no modifications. Imperative scripts lack this built-in comparison, so re-running them can create duplicates unless the commands are explicitly guarded.

Why:

Declarative IaC tools such as Terraform, CloudFormation, and Kubernetes manifests store or observe the current state and compare it with the desired configuration on each run. If the two already match, the tool plans no changes and the end state stays the same, which is the definition of idempotency. Imperative scripts do not perform this comparison by default, so re-running them can create duplicate resources or fail unless the commands are explicitly guarded.

A declarative IaC tool such as Terraform converges infrastructure to a desired configuration. Put these three steps in the order the tool performs them.#

Put these in order

Show answer

The order is: load the desired state from configuration files, compare it with actual infrastructure to compute a plan, then apply the computed changes. Declarative tools such as Terraform cannot compute a diff until the desired state is known, and cannot apply changes until the diff exists.

Why:

A declarative tool must first load the desired state from the authored configuration, since there is nothing to compare until that state exists. It then compares desired state against the actual infrastructure (Terraform's plan step includes refreshing actual state) to compute the diff. Only after the diff is computed can it apply the changes that move the real infrastructure toward the desired state. Each step depends on the output of the one before it, so the order is strict.

In a _____ approach to infrastructure as code, you describe the desired end state and let the tool compute the steps to reach it; in an _____ approach, you write the explicit sequence of commands that change the system step by step.#

Show answer

In a declarative approach to infrastructure as code, you describe the desired end state and let the tool compute the steps to reach it; in an imperative approach, you write the explicit sequence of commands that change the system step by step.

Why:

This is the foundational IaC distinction. A declarative approach states what you want — 'three web servers behind a load balancer with this config' — and the tool diffs that desired state against reality and figures out how to converge (create what's missing, change what differs, leave correct things alone). Because the spec is the end state, re-applying it is naturally idempotent and the tool can detect and correct drift. An imperative (or procedural) approach states how: an ordered list of commands ('create server, then install package, then edit file') that you must make safe to re-run yourself, since the script doesn't inherently know the current state. Most modern IaC and orchestration tools are declarative for exactly these reasons — idempotency, drift detection, and reproducibility — while shell-style provisioning scripts are imperative. Knowing which model a tool uses tells you how it will behave on a second run.

A DevOps team is debating whether to manage their cloud infrastructure with Terraform (a declarative IaC tool) or with a collection of imperative Bash scripts that call the cloud provider's CLI. Which statement accurately captures a fundamental difference between these two approaches?#

Options

Show answer

Declarative tools like Terraform compare recorded state against the desired configuration and apply only the differences, so re-running an already-converged configuration makes no changes. Imperative Bash scripts run a fixed command sequence with no memory of prior runs, so idempotency must be hand-coded with explicit existence checks.

Why:

The declarative approach is defined by the tool holding a model of desired state and recorded state (Terraform's state file) and computing a diff, so it only applies the delta; when the real infrastructure already matches the declaration, a rerun produces no changes, which is idempotency. Imperative Bash scripts issue a fixed sequence of commands with no memory of prior runs, so they are not idempotent by default and must add explicit existence checks to approximate the same behaviour. The statement about retrying CLI calls is wrong because retries do not make a create command safe to repeat, the statement about file order is wrong because Terraform builds a dependency graph rather than following file order, and the statement about scripts keeping a state file is wrong because plain Bash scripts have no built-in state.

In one or two sentences, describe a key difference between declarative and imperative approaches to infrastructure as code. Your answer should address how each approach relates to idempotency and to specifying the desired state versus the steps to reach it.#

Show answer

Declarative infrastructure as code lets you specify the desired end state and the tool converges to it idempotently, so applying the same configuration repeatedly produces the same result without manual guards. Imperative approaches specify the step-by-step commands needed to reach that state, so ensuring idempotency is left to the author and may require explicit checks.

Why:

The central difference is that declarative tools (e.g., Terraform, CloudFormation) describe the desired end state and reconcile toward it idempotently. Reapplying the same declaration yields the same end state because the tool diffs current state against desired state and only makes the changes needed. Imperative tools (e.g., shell scripts calling cloud APIs) encode the exact sequence of commands to reach the state; nothing inherent guarantees idempotency, so the author must add guards or conditional checks to avoid duplicating resources or re-running destructive steps. A strong answer therefore names idempotency and contrasts desired-state specification with step-by-step specification.

A DevOps team manages cloud infrastructure using two approaches under evaluation: Approach A – a bash script that calls cloud-provider CLI commands in a fixed order (create-vpc, create-subnet, create-security-group, authorize-security-group-ingress) with no existence checks before each call. Approach B – a Terraform configuration in HCL that declares the same VPC, subnets, and security-group rules. After both approaches successfully provision the infrastructure once, an engineer manually adds an extra ingress rule to the security group directly in the cloud console. When each approach is run again, what is the key behavioral difference?#

Show answer

Terraform compares the real infrastructure against its configuration and stored state file; it detects the extra ingress rule as drift and produces a plan to reconcile it (e.g., remove the unmanaged rule). The imperative bash script simply re-executes its commands in sequence with no awareness of the manual change; it has no state to compare against and therefore cannot detect or reconcile the drift.

Why:

Terraform is declarative: it maintains a state file and compares the actual cloud resources against the desired configuration declared in HCL. On re-application it computes a diff and produces a plan showing what must change to converge reality toward the declared state, including detecting the manually added ingress rule as drift. The imperative bash script has no such state model; it re-runs each CLI command in order regardless of what already exists. Without explicit existence checks (which the scenario rules out), the script cannot detect the manual change, may error on resources that already exist, or may run commands that succeed or fail without any awareness of the extra rule. The core distinction is that a declarative tool with state enables drift detection and idempotent reconciliation, while an imperative script without existence checks cannot detect or undo out-of-band changes.

In Terraform's declarative model, a state file is maintained alongside the configuration files (.tf). Which statement most accurately describes how Terraform uses the state file during a default terraform plan operation (Terraform 1.x)?#

Options

Show answer

Terraform's plan first refreshes the state file by querying the real infrastructure, then computes a diff between that refreshed state and the desired configuration declared in the .tf files to produce an execution plan. The state file is not the desired state itself—the configuration files are—nor is it a disposable cache, because without it Terraform cannot map resource blocks to existing cloud resources. Real infrastructure is queried during plan, not only during apply.

Why:

During a default terraform plan, Terraform performs three steps: (1) it refreshes the state file by querying the real infrastructure to capture the current actual state of each managed resource, (2) it reads the .tf configuration files to determine the desired state, and (3) it computes a diff between the refreshed state and the desired configuration, presenting the result as an execution plan. The claim that the state file defines desired state is wrong because the state file does not define desired state (the configuration files do), and plan never modifies resources—that requires apply. The claim that Terraform only queries real infrastructure during apply is wrong because Terraform queries real infrastructure during plan via the refresh step, not only during apply. The claim that the state file is merely a performance cache is wrong because the state file is not merely a cache; without it, Terraform has no mapping between resource blocks in the configuration and existing real resources, and would attempt to recreate everything, likely failing on name conflicts.

You are provisioning infrastructure for a three-tier web application. A colleague proposes replacing your team's existing imperative bash scripts (which call aws ec2 run-instances, aws ec2 create-security-group, and similar AWS CLI commands) with a Terraform configuration that declares the same resources. Consider a provisioning run that is interrupted midway: some resources were created, others were not, and the process was killed before completion. Which statement accurately describes a fundamental behavioral difference between the two approaches when you re-run the provisioning?#

Options

Show answer

On a re-run after an interrupted run, Terraform checks declared configuration against its state and real infrastructure and creates only what is missing. A plain bash script re-executes its commands from the top, which can create duplicate resources unless each step is guarded by an existence check.

Why:

Terraform's declarative model means each invocation refreshes the recorded state against the real resources, diffs that against the declared configuration, and produces a plan containing only the changes needed to converge, so an interrupted run can be re-applied. An imperative bash script executes commands sequentially, so a re-run repeats completed commands and can create duplicates or fail with 'already exists' errors unless each command has an explicit idempotency check. The other statements are wrong: neither approach provides automatic rollback or atomic all-or-nothing provisioning, and it is Terraform, not the bash script, that maintains a state file.

In a declarative IaC system like Terraform, the tool maintains a state file (terraform.tfstate) alongside the declared configuration. Explain two critical roles this state file plays that an imperative provisioning script using cloud-provider CLIs does not have, and explain how state enables drift detection.#

Show answer

The state file serves two key roles. First, it maps declared resource addresses (e.g., aws_instance.web) to real-world resource IDs (e.g., i-0abcd1234), so Terraform knows which cloud object each config block manages across runs, and it can plan updates or destroys for resources that were removed from configuration. Second, it records the last-known attribute values and dependency metadata for each managed resource, giving Terraform a baseline to diff against the desired configuration. Drift detection works because, on a normal plan, Terraform refreshes state by querying the provider for each managed resource's current attributes (this refresh can be skipped with -refresh=false), then compares the refreshed reality against the recorded state and the desired configuration. Any divergence, whether from manual console changes, out-of-band CLI edits, or other automation, appears as a planned change to bring the resource back to the desired state. An imperative script has no such address-to-ID mapping or recorded baseline; it runs discrete commands and cannot tell that a resource's current attributes differ from what an earlier run set.

Why:

The state file is what makes declarative IaC fundamentally different from imperative scripting. First, it binds declared resource addresses to real-world cloud resource IDs; without this mapping Terraform cannot know which aws_instance.web refers to which EC2 instance, nor which resources to destroy when a block is removed from configuration. Second, it records last-known attribute values and dependency metadata, giving Terraform a baseline to diff against the desired configuration. Drift detection then relies on refresh: on a normal plan Terraform queries the provider for each managed resource's current attributes and compares them with the recorded state and the desired configuration, so out-of-band changes surface as planned changes. An imperative script using the AWS CLI keeps no persistent record of what it created or what attributes those resources should have, so it cannot detect that a resource has been modified out-of-band.

Your organization manages 300+ AWS resources (VPCs, EC2 instances, RDS databases, IAM roles, S3 buckets, and load balancers) through a collection of imperative bash scripts that wrap the AWS CLI. The scripts have grown unmaintainable, lack idempotency guarantees, and frequently produce inconsistent environments. You have been tasked with designing a migration to Terraform as a declarative IaC tool. Describe your end-to-end migration strategy, covering: how existing resources are brought under Terraform management without downtime or recreation, how state is stored and protected for team collaboration, how the transition period is managed when some resources are still provisioned by scripts and others by Terraform, and what ongoing guardrails prevent configuration drift from manual changes after migration.#

Show answer

I would run a phased, incremental migration rather than a big-bang cutover. For each resource group, I would write Terraform configuration that describes the existing infrastructure, then bring each resource under management with terraform import (or declarative import blocks in Terraform 1.5+) so the declared address maps to the existing AWS ID. Nothing is created or replaced during import. I would run terraform plan and adjust the code until it shows no changes, never changing the live infrastructure to fit the code. For state, I would use a remote S3 backend with versioning and encryption, and a DynamoDB table for state locking so concurrent runs cannot corrupt state; access to the bucket and table would be restricted by IAM. I would use separate state files per environment (dev, staging, prod) to limit blast radius. For the transition, I would migrate by resource group in dependency order: networking first (VPCs, subnets, route tables), then compute (EC2, Auto Scaling groups, load balancers), then data stores (RDS, S3), then IAM. For each group I would freeze the matching bash scripts, import the resources, confirm a clean plan, and only then retire the scripts for that group, so no resource is managed by both systems at once. After migration, guardrails would include: a CI pipeline that runs terraform plan on every pull request and blocks merges on unexpected changes; IAM policies that deny direct console and CLI modifications to production resources so changes flow through Terraform; and a scheduled job running terraform plan -detailed-exitcode daily that alerts the team when drift is detected. Together these keep the declarative configuration as the single source of truth.

Why:

This is an open design question with no single key, so it is graded by keyword matching on the four required aspects: import mechanics (terraform import or import blocks, with no recreation and a clean plan as verification), remote state with locking (S3 plus DynamoDB or an equivalent backend), coexistence during transition (group-by-group migration with no dual management), and drift prevention (CI plan gating, restricted manual access, and scheduled terraform plan -detailed-exitcode checks). A strong answer names concrete mechanisms for each of these rather than saying only that Terraform will be used. The keyword rule requires the import, plan, and locking terms, plus at least one drift or pipeline term.

You lead the platform engineering team for a company running 200+ microservices across three AWS accounts (dev, staging, production). Infrastructure is currently provisioned by a mix of CloudFormation templates (declarative) and bespoke Python scripts using boto3 (imperative) that call AWS APIs in sequence with explicit wait conditions and conditional branches. Leadership has asked you to consolidate onto a unified IaC strategy. Several provisioning workflows require runtime-dependent ordering: e.g., VPC creation → wait for BGP route propagation on a Transit Gateway attachment → create a VPC peering connection → inject the peered CIDR ranges into security group rules, where the CIDR values are only known after the peered VPC is queried at runtime. Design an IaC strategy that appropriately leverages both declarative and imperative patterns. Address: (1) when to use declarative vs imperative tooling, (2) how to manage state, (3) how to structure the codebase, and (4) how to handle the runtime-dependent ordering scenarios above.#

Show answer

Use a declarative engine (Terraform or Pulumi) as the primary tool for steady-state infrastructure such as VPCs, subnets, IAM, Transit Gateway attachments, ECS clusters, RDS, and load balancers, because plan/diff, idempotent applies, and drift detection through state comparison matter at this fleet size. Reserve imperative logic for steps that are inherently sequenced or event-driven, such as waiting for TGW route propagation, polling the peered VPC's routes at runtime, and retry/branch logic. Handle that in a thin orchestration layer (Pulumi Automation API, a Makefile, or an Ansible playbook) that runs the wait-and-query loop with backoff, writes the observed CIDRs into a declared input, and then invokes Terraform to apply the peering connection and security group rules. A depends_on on a data source only defers the read until its dependencies are applied; it does not guarantee that route propagation has converged, so the wait must live in the orchestrator. Provisioners such as local-exec are a last resort because they run outside state and cannot be reconciled on later plans. State lives in a remote backend (S3 with DynamoDB locking for Terraform, or Terraform Cloud or Pulumi SaaS), with a separate state file per account and environment pair to prevent cross-account contamination. A nightly plan in CI detects drift; each drift is either fixed by re-applying the configuration or deliberately imported into state. The codebase is layered: a foundational layer (accounts, VPCs, Transit Gateway, IAM) exports outputs such as VPC IDs, subnet CIDRs, and TGW IDs, and a service layer (ECS, RDS, ALBs) consumes them via Terragrunt or terraform_remote_state, so each layer applies independently and the service layer never queries live AWS state directly.

Why:

This is an open senior-level design question with no single key, so it stays SHORT_ANSWER and is graded by keywords plus the sample answer. A strong answer draws a principled boundary: declarative tooling for idempotent steady-state resources with plan and drift detection, and imperative logic confined to runtime-dependent ordering and external API gaps. It manages state remotely with per-account and per-environment isolation and scheduled drift checks, and it layers the codebase so foundational outputs feed service-layer inputs. For the ordering scenario, the sample answer correctly places the propagation wait in an orchestration layer rather than assuming a data source's depends_on will wait for routes to converge.

In Terraform's declarative IaC model, a managed resource's state file entry matches the configuration. Between two apply runs, an out-of-band process changes a mutable (non-force-new) attribute of the corresponding cloud resource — the resource still exists, just with drifted attribute values. You run terraform plan with default settings and no configuration changes. Briefly explain the two internal phases Terraform performs before producing the plan output, and why the resulting plan shows an in-place update rather than 'No changes.'#

Show answer

Terraform first performs a refresh: it calls the provider's Read operation to update the state file with the actual current state of the cloud resource. Then it computes a diff between the refreshed state and the desired configuration. Because the out-of-band change introduced drift, the refreshed state no longer matches the configuration, so the diff is non-empty and the plan proposes an in-place update to reconverge the resource back to the declared desired state. This reconvergence-on-drift behavior is a hallmark of declarative IaC: the tool continuously drives actual state toward declared state, whereas an imperative approach would have no automatic mechanism to detect or correct the drift.

Why:

In Terraform's declarative model, plan always begins with a refresh phase that queries each provider's Read to sync the state file with real-world cloud state. After refresh, Terraform diffs the refreshed state against the configuration to determine what actions are needed. When drift has occurred (an attribute changed out-of-band), the refreshed state diverges from the config, producing a non-empty diff. For a mutable, non-force-new attribute, that diff resolves to an in-place update — Terraform will modify the existing resource to match the declared desired state. If the attribute were force-new, the plan would instead show a destroy-and-recreate. The key insight is that declarative IaC tools converge actual state toward declared state on every run; they do not treat 'configuration unchanged since last apply' as a reason for a no-op when the real world has drifted. This is the core distinction from imperative IaC, where there is no persistent state mapping and no automatic drift detection or reconciliation.

You are the platform architect for a company running 40+ microservices on Kubernetes across dev, staging, and prod. The platform team manages cluster infrastructure with Terraform (declarative). Application teams deploy with a mix of Helm charts (declarative) and imperative kubectl scripts for operational tasks such as canary rollouts, emergency scaling, and manual rollbacks. Imperative actions cause drift that Helm or the GitOps controller reconciles away, and application teams resist 'pure GitOps' because it slows emergency response.#

Show answer

Separate desired state (the 'what') from runtime actions (the 'how and when') and make the boundary explicit. Declarative layer: Terraform owns cloud and cluster infrastructure (VPCs, node pools, ingress controllers, CRDs). A GitOps controller (Argo CD or Flux) owns application manifests (Helm charts, Kustomize overlays, release versions) for every environment; all desired state lives in Git, and a hand-run kubectl apply or helm install is never the source of truth. Imperative actions are allowed only when they are expressed as desired-state changes that the controller converges on: a rollback commits the previous image tag or chart revision; a canary shift updates a weight in a progressive-delivery resource; an emergency scale-up updates HPA min/max or replica count in the chart. Drift detection: the GitOps controller continuously reconciles live state against Git (for example every few minutes) and alerts on any diff; a scheduled terraform plan job (for example nightly) detects infrastructure drift from console changes and opens a PR or ticket. Emergency guardrails: time-boxed, audited break-glass RBAC lets on-call engineers mutate resources directly during an incident; every break-glass action is logged, and a post-incident Git commit encoding the permanent change is mandatory. Reconciliation can be paused per workload (pause annotation or scaling the controller down for a namespace) so it does not fight a deliberate rollout or manual fix, and the pause is itself time-limited and recorded. Ownership: the platform team owns Terraform, the GitOps controller configuration, and convergence policy; application teams own their Helm charts and release-manifest paths in Git; the SRE on-call owns the reconciliation decision during an active incident, including pausing reconciliation and owning the follow-up commit that makes the change declarative. Imperative actions are therefore temporary by construction: they buy time, and the Git commit is the durable record.

Why:

This is an open system-design question with no single correct implementation, so it stays a short answer graded against its rubric rather than a fixed key. A strong answer must (1) separate a declarative desired-state layer (Terraform for infrastructure, a GitOps controller for application manifests, all desired state in Git) from the runtime layer, and show that rollbacks, canary shifts, and emergency scaling are expressed as desired-state fields the controller converges on rather than raw mutations it fights; (2) specify automated drift detection with a defined cadence, namely continuous GitOps reconciliation plus a scheduled Terraform plan for infrastructure drift; (3) define emergency guardrails that are time-boxed, audited break-glass access, a way to pause reconciliation per workload, and a mandatory post-incident Git commit; and (4) give an ownership model that distinguishes who owns desired state (platform team for infrastructure and GitOps configuration, application teams for charts and release manifests) from who makes the runtime reconciliation decision during an incident (SRE on-call).

Terraform uses a declarative HCL DSL and applies changes through a one-shot plan-and-apply cycle that depends on an external state file tracking the resources it manages. A Kubernetes GitOps controller such as Argo CD or Flux also uses declarative manifests, but runs a continuous reconciliation loop that converges observed cluster state toward declared desired state, with Git as the source of truth. Which statement correctly describes the fundamental architectural distinction between these two paradigms?#

Options

Show answer

Terraform runs a one-shot plan-and-apply cycle that depends on an external state file, while a Kubernetes GitOps controller such as Argo CD runs a continuous reconciliation loop that converges live cluster state toward Git-declared desired state without a Terraform-style state file. Both tools remain declarative; the push or pull delivery model is orthogonal to this distinction.

Why:

Both Terraform and a GitOps controller are declarative: the engine derives the steps from a declared end state, so the option claiming Terraform is imperative is wrong, and the option saying the user writes ordered commands in both tools is wrong for the same reason. The real architectural split is execution model and state handling. Terraform performs a one-shot plan-and-apply that diffs against an external state file it depends on. A GitOps controller runs a continuous loop that re-derives drift on every iteration by comparing Git-declared desired state with the live cluster API, and it does not need a separate Terraform-style state file. The option saying a GitOps controller stores no state at all is wrong because controllers persist status inside Kubernetes (for example Argo CD Application status or Flux resource state in etcd), and Terraform does not keep its state in etcd. The push-versus-pull delivery model is orthogonal to this distinction.

Consider a mature declarative IaC tool such as Terraform or AWS CloudFormation that maintains an explicit state file (or stack representation). Which of the following statements about its declarative reconciliation semantics are true? Select all that apply.#

Options

Pick every one that applies.

Show answer

Three statements are true: removing a resource block in a stateful declarative IaC tool schedules that resource for destruction on the next apply; imperative provisioners break the convergence guarantee because their effects are not tracked in state and may not be idempotent; and the tool can only reconcile state its provider schema can model — external mutations it cannot represent go uncorrected even after a refresh. Drift detection is still needed because the tool does not continuously reconcile, and resource creation order is determined by the dependency graph, not the file order.

Why:

In a declarative IaC system that maintains state (Terraform, CloudFormation), removing a resource block from the configuration signals that the resource is no longer desired, so the next apply schedules its destruction — this is the defining lifecycle behavior of stateful declarative tools. Imperative provisioners (such as Terraform's local-exec/remote-exec) run on creation or destruction triggers, but their effects are not tracked in the tool's state, so repeated applies may not converge to the same result, breaking the idempotency guarantee that makes declarative IaC valuable. The tool can only reconcile what its provider schema models: if a resource attribute is mutated externally in a way the schema cannot represent (e.g., database schemas inside an RDS instance, in-cluster Kubernetes objects), the tool will not detect or correct it even after a refresh. Drift detection is therefore still necessary, contradicting the claim that reconciliation is automatic. Resource creation order is determined by the dependency graph derived from implicit references and explicit depends_on, not by the order of blocks in the configuration file.

A platform team provisions a managed PostgreSQL RDS instance through a declarative IaC tool such as Terraform. The provider models the database instance itself, but not the schemas, extensions, and roles inside it. Describe the two most common architectural patterns for managing this internal database state. For each pattern, give its key trade-off. Then name the specific guarantee the declarative tool loses when either pattern introduces imperative lifecycle steps.#

Show answer

Pattern 1 (external migration tool): schemas, extensions and roles are managed by an imperative migration tool such as Flyway, Liquibase or Ansible, run by the CI/CD pipeline after Terraform reports the RDS instance healthy. Trade-off: the schema lifecycle sits outside Terraform's state, so terraform plan cannot detect schema drift, and the pipeline must enforce ordering between the two tools. Pattern 2 (in-band provisioners): run SQL through local-exec or remote-exec provisioners attached to the instance or to a null_resource. Trade-off: provisioner effects are not recorded in Terraform state, the SQL is not idempotent unless the author makes it so, and nothing is refreshed, so the SQL runs only on create, destroy, or a change to a trigger. In both cases the guarantee lost is declarative convergence: repeated terraform apply runs cannot detect or correct drift in schema, role, or extension state, because that state lives outside the tool's state model and reconciliation loop.

Why:

This question tests the boundary of declarative IaC: the tool reconciles only what its provider models. Schemas, extensions and roles are not in the RDS provider's API surface, so they need either an external migration tool run by the pipeline or in-band provisioners. Both patterns move state outside Terraform's state file and refresh loop, so plan cannot see schema drift and repeated applies cannot converge that state. The lost guarantee is convergence (idempotent drift detection and correction) for the internal database state.

You are designing a Terraform-based IaC strategy for a multi-tenant SaaS platform moving from manually provisioned infrastructure in one AWS account to multi-account, code-managed infrastructure. The platform contains: (1) VPC networking and transit gateway topology across accounts, (2) EKS clusters with autoscaling node groups per environment, (3) per-tenant managed PostgreSQL RDS instances with tenant-specific schemas, roles, and extensions, (4) IAM roles and permission boundaries, and (5) CI/CD pipelines that deploy application code. Explain: (a) where the declarative Terraform model stops and imperative lifecycle management begins for stateful pieces (RDS schemas, roles and extensions; EKS add-ons such as ingress controllers and CSI drivers), and which reconciliation guarantees are kept or lost at that boundary; (b) how you would structure remote state and modules for account and tenant isolation, including blast radius; (c) how drift will be detected and reconciled across 50+ tenant environments, and what drift your detection cannot see.#

Show answer

Terraform owns the infrastructure layer: VPCs, transit gateway attachments, IAM roles and boundaries, EKS clusters and node groups, and RDS instances. That is the declarative boundary, where plan/apply give a diff-then-converge guarantee and terraform plan detects drift in these resources. In-database objects (schemas, roles, extensions) and cluster add-ons are handled imperatively or by their own reconcilers: versioned SQL migrations applied by Flyway from CI/CD after the RDS instance is healthy (Flyway records applied migrations in its schema history table, so reruns are idempotent), and Helm releases for ingress controllers and CSI drivers reconciled continuously by ArgoCD. The guarantee lost at this boundary is that Terraform's refresh and plan cannot see or correct drift inside the database or inside Helm-managed objects; we accept that explicitly and delegate it to Flyway and ArgoCD. State is split into tiers: a global foundation state (transit gateway, IAM baseline, DNS), per-account platform states (VPC, EKS, RDS), and per-tenant application states, stored in an S3 backend with DynamoDB locking, with cross-tier data flowing downward via terraform_remote_state. Separate state per account and per tenant limits blast radius so a bad apply for one tenant cannot destroy another tenant's resources. Drift is detected by a nightly terraform plan -detailed-exitcode job per state; exit code 2 opens a ticket with the diff, and a human decides whether to codify a legitimate hotfix or revert it with apply. Drift inside Flyway-managed schemas and Helm-managed add-ons, and any resource Terraform does not model, is invisible to this layer and must be covered by those tools' own reconciliation.

Why:

This is an open design question with no single correct answer, so it is graded against the sample answer's content rather than a keyed option. A strong answer must name the exact boundary between Terraform-managed infrastructure (networking, IAM, EKS clusters, RDS instances) and imperative or self-reconciling layers (in-database schemas, roles and extensions via a migration tool; cluster add-ons via Helm and ArgoCD), state explicitly what guarantees are lost there, and separate state by account and tenant to bound blast radius. It must also give a concrete drift workflow (scheduled plan with detailed exit codes, ticketing, codify-or-revert) and honestly acknowledge what the Terraform-based detection cannot observe.

Your organization runs ~2,000 cloud resources across AWS and GCP. Roughly 60% were created imperatively over five years — CloudFormation custom resources, bash scripts wrapping the AWS CLI, and manual console clicks. The remaining 40% are Terraform-managed. Several production RDS clusters were created manually and receive in-place schema migrations from a separate CI job; their underlying infrastructure (instance class, parameter groups, security groups) is drifted from any config file. A set of VPC peering routes have a strict provisioning order that, if violated, causes a brief cross-account outage. The platform team has been asked to consolidate everything under a single IaC strategy within two quarters while keeping all production workloads online. Design that strategy, paying specific attention to where the declarative model breaks down and how you bridge the gap with imperative escape hatches, drift management, and stateful-resource safety.#

Show answer

I would adopt Terraform (or OpenTofu) as the single declarative source of truth for all long-lived resources, wrapped in an imperative orchestration layer (a CI-driven workflow such as Atlantis, or a Makefile) for the cases where pure declarative is insufficient. (1) State import: for the imperatively created 60%, I would run a resource-by-resource inventory, write the matching HCL, and use terraform import to adopt each resource into state, then run terraform plan to surface and reconcile drift. Resources whose drift cannot be safely reconciled in place, such as the manually created RDS clusters, would be imported with lifecycle { ignore_changes } on fields like instance class, then moved to full management in a later scheduled cutover. (2) Imperative escape hatches: the VPC peering routes with strict ordering get explicit depends_on chains, and where the DAG cannot express the constraint, a local-exec provisioner or null_resource trigger waits for route propagation before continuing. The RDS schema migrations stay in the existing CI job; a Terraform output such as the RDS endpoint feeds a null_resource triggers value so the declarative layer controls when migration runs but not how. (3) Drift management: a scheduled nightly terraform plan -detailed-exitcode across all workspaces, with non-zero exit codes routed to alerting with the diff attached. Manual hot-fixes must be reconciled by the owning team, either by updating config to match or reverting the change, with no silent reconciliation. (4) Stateful resource safety: every stateful resource (RDS, ElastiCache, EFS) gets lifecycle { prevent_destroy = true } and ignore_changes on fields that are dangerous to replace. The RDS cutover uses blue-green: provision a new instance via Terraform, cut over with read-replica promotion or logical replication, and only then remove the old instance through a separately gated destroy that requires manual approval, so a config edit can never silently destroy a production database.

Why:

This is an open staff-level design question with no single fixed answer, so it stays a short-answer item graded against the sample answer and keywords. A strong response must name a concrete import mechanism (terraform import plus plan-driven reconciliation) rather than hand-waving migration, identify where pure declarative fails (ordering-dependent peering routes, stateful RDS with out-of-band schema migrations) and prescribe real escape hatches such as depends_on, local-exec or null_resource triggers, and CI-driven migrations. It must also include an ongoing drift process (scheduled plans with alerting and ownership) rather than a one-time import, and protect stateful resources from destructive replacement via prevent_destroy, ignore_changes, and gated blue-green cutovers. A answer that simply says to rewrite everything in Terraform fails the stateful-resource and escape-hatch criteria.

Related interview questions

Job market

See devops salaries and hiring demand from live job postings.

Practise these until they stick

That's every question we hold on this topic, and the page marks what you pick. What it can't do is remember. A free account keeps every answer, and what you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

Interview booked? Practise the job ad instead

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.