Courseiva

CCNA Dynamic Hcl Configuration Questions

44 questions · Dynamic Hcl Configuration topic · All types, answers revealed

1
MCQeasy

You are defining an input variable for a Terraform module that will accept a list of AWS subnet IDs. You want to ensure that the variable only accepts a list of strings and that it is required. Which variable declaration correctly achieves this?

A.variable "subnet_ids" { type = set(string) }
B.variable "subnet_ids" { type = list(string) default = [] }
C.variable "subnet_ids" { type = string }
D.variable "subnet_ids" { type = list(string) }
AnswerD

This declaration defines a required variable of type list(string). Since no default is provided, it is required. The type constraint ensures that only a list of strings is accepted, which is exactly what is needed for subnet IDs.

Why this answer

The correct variable declaration uses type = list(string) and does not provide a default, making it a required variable. This ensures that callers must supply a list of strings. The other options either make the variable optional, use a different collection type, or use a scalar type, all of which fail to meet the specified requirements.

Exam trap

The trap here is overlooking that providing a default value, even an empty list, makes the variable optional rather than required.

2
MCQmedium

You are authoring a Terraform module that must accept a map of environment names to instance counts. The map is provided by callers and may contain up to 20 entries. You need to derive a flat list of instance names in the form "<env>-<index>" (e.g., "prod-0", "prod-1") so that a downstream resource can use `for_each` with stable keys. Which expression correctly produces this list?

A.concat([for env, count in var.instance_counts : "${env}-${count}"])
B.[for env, count in var.instance_counts : "${env}-${count}"]
C.flatten([for env, count in var.instance_counts : [for i in range(count) : "${env}-${i}"]])
D.[for env, count in var.instance_counts : { name = "${env}-${count}" }]
AnswerC

This nested for expression first builds, for each environment, a list of names from 0 to count-1, then flattens the resulting list of lists into a single flat list. It correctly expands each environment's count into individual instance names, matching the required "<env>-<index>" format for use with for_each.

Why this answer

The requirement is to expand each environment's count into individual instance names and then combine them into one flat list. A nested for expression with flatten achieves this by generating a list of names per environment and then merging them. The other options either produce the wrong data shape or fail to expand the count into multiple entries.

Exam trap

The trap here is assuming that a simple for expression over a map can expand a numeric count into multiple elements without an inner loop or flatten.

3
MCQhard

What happens if you use a dynamic block with a 'for_each' expression that evaluates to an empty set?

A.It pauses execution and waits for input
B.It throws a configuration error
C.It renders no blocks
D.It generates a single block with null values
AnswerC

A dynamic block iterates its for_each collection to generate nested blocks. When that collection is an empty set, the iterator produces zero iterations, so no nested blocks are rendered at all and the resource is created without them.

Why this answer

Terraform's dynamic blocks iterate over the collection given to 'for_each'; if that collection is empty (empty set, list, or map), the iterator produces zero iterations and therefore zero nested blocks are rendered. This is the documented and intended behaviour, allowing conditional block generation without 'count' hacks.

Exam trap

The trap is assuming Terraform treats an empty 'for_each' as an error or as a null block — candidates who have not tested this often pick 'throws a configuration error' or 'generates a single block with null values' instead of the correct 'renders no blocks'.

How to eliminate wrong answers

Option A is wrong because Terraform never pauses for interactive input during plan/apply — it is non-interactive by design and would error or proceed, not wait. Option B is wrong because an empty 'for_each' is a valid, supported case; Terraform only errors if 'for_each' is given a value it cannot iterate (e.g., null in some versions) or a non-collection type. Option D is wrong because Terraform does not synthesize a placeholder block with nulls — it simply emits no block at all, which is why downstream references to the block must be guarded.

4
MCQmedium

In a dynamic block, you need to access the current iteration item. What is the default label assigned to the iterator object?

A.count.index
B.The name of the block
C.each.value
D.item
AnswerB

When no explicit iterator is declared, Terraform names the iterator object after the dynamic block's label itself, so a block labelled 'ingress' exposes each element as 'ingress.value'. This satisfies the default-label constraint without requiring an iterator argument.

Why this answer

In a dynamic block, the iterator object defaults to the name of the block being generated. For `dynamic "ingress" { ... }`, the iterator is `ingress`, so you reference `ingress.value` and `ingress.key`. You can override this with the `iterator` argument, but the default is the block label.

This is why `each.value` and `count.index` are not used inside dynamic blocks.

Exam trap

The trap is reaching for `each.value` or `count.index` out of habit — those belong to `for_each`/`count` on resources, while dynamic blocks use the block label as the default iterator.

How to eliminate wrong answers

Option A is wrong because `count.index` belongs to the `count` meta-argument on resources/modules, not to dynamic blocks. Option C is wrong because `each.value` is used with `for_each` on resources/modules, not inside dynamic blocks — dynamic blocks use the block-name iterator. Option D is wrong because `item` is not a default label; it would only apply if you explicitly set `iterator = item`, which is not the default.

5
MCQhard

You are writing a Terraform configuration that reads a list of environment names from a variable and creates one aws_s3_bucket for each environment. The order of the list may change over time, and you want to avoid unnecessary bucket replacement. Which configuration snippet achieves this with stable resource addressing?

A.resource "aws_s3_bucket" "env" { for_each = toset(var.environments) bucket = each.value }
B.resource "aws_s3_bucket" "env" { count = length(var.environments) bucket = element(var.environments, count.index) }
C.resource "aws_s3_bucket" "env" { for_each = var.environments bucket = each.value }
D.resource "aws_s3_bucket" "env" { count = length(var.environments) bucket = var.environments[count.index] }
AnswerA

Converting the list to a set with toset and using for_each creates one bucket per unique environment, keyed by the environment name itself. Because the keys are the values, reordering the original list does not change the resource addresses, so buckets are not replaced. This provides the stable addressing required.

Why this answer

To achieve stable resource addressing when order may change, the resource instances must be keyed by a value that does not depend on list position. Using for_each with toset(var.environments) creates instances keyed by the environment names themselves, so reordering the input list does not alter the resource addresses. The count-based approaches all depend on index positions and therefore do not provide stability.

Exam trap

The trap here is believing that count with element or index provides stable addressing, when any index-based approach remains sensitive to list order.

6
MCQeasy

You want to optionally create an S3 bucket only if a specific variable 'create_bucket' is set to true. Which HCL feature is best for this?

A.conditional expression (ternary)
B.try()
C.dynamic block
D.for_each
AnswerA

Setting count = var.enabled ? 1 : 0 is the common pattern for optional resource creation.

Why this answer

The conditional expression (ternary) in HCL uses the syntax `condition ? true_val : false_val` and can be applied to a resource's `count` argument, e.g., `count = var.create_bucket ? 1 : 0`. This is the idiomatic way to conditionally create a resource based on a boolean variable. When `create_bucket` is true, one instance is created; when false, zero instances are created.

Exam trap

AWS provider exams often test the distinction between conditional expressions and other HCL features like `dynamic` blocks or `for_each`, tricking candidates into choosing `for_each` for conditional creation when it cannot produce zero instances.

How to eliminate wrong answers

Option B is wrong because `try()` is used to gracefully handle errors when accessing attributes that may not exist, not to conditionally create resources. Option C is wrong because a `dynamic` block generates repeated nested blocks within a resource based on a collection, not conditional resource creation. Option D is wrong because `for_each` iterates over a map or set to create multiple resource instances, but it cannot conditionally create zero instances based on a boolean.

7
Multi-Selectmedium

Which TWO of the following are valid uses of the 'for' expression?

Select 2 answers
A.Defining a resource
B.Generating a list
C.Managing provider blocks
D.Creating dynamic blocks
E.Generating a map
AnswersB, E

A for expression iterating over a collection and wrapping the result in square brackets produces a new list, optionally filtered by an if clause. This matches the stem's requirement, whereas the curly-brace form yields a map instead.

Why this answer

In Terraform, the 'for' expression is used to transform and produce collection values: option B is correct because a 'for' expression over a list or set returns a tuple/list, e.g. [for s in var.subnets : upper(s)], which generates a list. Option E is correct because using the curly-brace form, e.g. {for k, v in var.tags : k => upper(v)}, produces a map (object) of key-value pairs. Option A is not a use of 'for' because resources are declared with a 'resource' block, not an expression.

Option C is wrong because provider configuration is handled by 'provider' blocks and required_providers, not 'for'. Option D is wrong because dynamic blocks are created with the 'dynamic' block and its 'for_each' argument, not with a 'for' expression.

Exam trap

The trap here is confusing value-producing expressions (for) with configuration blocks (resource, provider, dynamic) — remember that for expressions only generate lists or maps, never infrastructure.

8
MCQeasy

You are writing a Terraform configuration that uses a conditional expression to set the instance type for an AWS EC2 instance. The condition checks if the environment is production. The true branch returns a string, but the false branch returns a number. What will happen when you run terraform plan?

A.Terraform will raise an error because the true and false expressions must have the same type.
B.Terraform will convert both to a common type, such as string, and then proceed.
C.Terraform will automatically convert the number to a string and proceed without error.
D.Terraform will use the type of the true expression and ignore the false expression's type.
AnswerA

Conditional expressions in Terraform require both the true and false results to be of the same type. If they are not, Terraform will produce an error during plan, indicating that the types do not match. This ensures that the resulting value has a consistent type regardless of the condition.

Why this answer

Terraform's conditional expression requires that the true and false results have the same type. If they differ, Terraform cannot determine a consistent type for the expression and will raise an error during plan. This is a common pitfall when mixing types in conditionals.

Exam trap

The trap here is assuming Terraform will automatically convert types, when it actually requires both branches to be the same type.

9
Multi-Selectmedium

Which THREE of the following are valid ways to transform data for Terraform configuration?

Select 3 answers
A.flatten()
B.merge()
C.split_map()
D.loop()
E.for expressions
AnswersA, B, E

`flatten()` collapses nested lists into a single flat list, which satisfies the stem's requirement for a valid data transformation function. Terraform's built-in collection functions, including `flatten()`, restructure values during evaluation, so nested structures from variables or outputs become directly usable in resource arguments.

Why this answer

Option A, flatten(), is correct because it is a built-in Terraform function that takes a list of lists and transforms it into a single flat list, which is a valid data transformation for configuration. Option B, merge(), is correct because it is a built-in Terraform function that combines multiple maps or objects into one map, a common way to transform and consolidate data in Terraform. Option E, for expressions, is correct because Terraform's for expressions iterate over lists, sets, maps, or objects to produce a new transformed list or map, making them a core transformation mechanism.

Option C, split_map(), is not a valid Terraform function, so it cannot transform data. Option D, loop(), is not a Terraform construct; iteration is handled by for expressions, count, or for_each, not a loop() function.

Exam trap

The trap here is confusing Terraform's built-in functions with non-existent ones like split_map() or loop(), which sound plausible but are not part of the Terraform language.

10
MCQmedium

You have a Terraform variable defined as a list of objects, each with a name and a list of ports. You need to create AWS security group rules for each port across all objects. Which expression correctly generates a flattened list of objects containing the name and port for each combination?

A.flatten([for obj in var.services : [for p in obj.ports : { name = obj.name, port = p }]])
B.concat([for obj in var.services : { name = obj.name, port = obj.ports }])
C.merge([for obj in var.services : { (obj.name) = obj.ports }])
D.[for obj in var.services : { name = obj.name, port = obj.ports }]
AnswerA

This uses a nested for expression to create a list of lists, then flatten combines them into a single list. Each inner list contains one object per port for that service, and the outer flatten merges all services into a single list. This correctly produces one object per name-port combination.

Why this answer

The nested for expression creates an inner list for each service, where each element is an object with the service name and a single port. The flatten function then merges all these inner lists into one flat list. This is the standard pattern for transforming a list of objects with nested lists into a flat list for use with for_each or other iteration.

Exam trap

The trap here is using concat or merge instead of flatten, which do not recursively flatten nested lists of objects.

11
MCQhard

You are writing a Terraform configuration that creates multiple AWS S3 buckets. The bucket names are derived from a list of project names, but you need to ensure that the names are unique and that the configuration can be safely modified without affecting existing buckets. Which approach should you use?

A.Use for_each with a map where the keys are the project names and the values are the desired bucket configurations.
B.Use a for expression to create a list of bucket names and then pass that list to the bucket resource's name argument.
C.Use count with the length of the project list and reference each bucket by index.
D.Use a dynamic block to generate multiple bucket resources within a single resource block.
AnswerA

Using for_each with a map creates one resource instance per map key, and the key becomes part of the resource address. This provides stable identity: adding or removing a key only affects that instance, and existing buckets remain untouched. It is the recommended way to manage multiple similar resources with unique names.

Why this answer

Using for_each with a map ensures that each resource instance is uniquely identified by its map key, so changes to the map only affect the corresponding instances. This provides stable resource addresses and avoids the pitfalls of count, where reordering or removing elements can cause unintended destruction and recreation of resources.

Exam trap

The trap here is thinking that count or dynamic blocks can provide stable identity, when only for_each with a map does so.

12
MCQmedium

You are authoring a Terraform module that creates an AWS IAM policy. The policy's Statement blocks must be generated from a list of objects, but each Statement must also conditionally include a Condition block only when the object's condition attribute is non-null. Which approach correctly generates the Statement blocks while conditionally including the nested Condition block?

A.Use a dynamic "statement" block with for_each over the list, and inside each statement block include a Condition block unconditionally, setting its values to null when not needed.
B.Use a dynamic "statement" block with for_each over the list, and inside each statement block use a nested dynamic "condition" block with for_each set to a one-element list when the condition attribute is non-null, or an empty list otherwise.
C.Use a for expression to build a list of statement objects, then pass that list directly to the statement argument of the aws_iam_policy_document data source.
D.Use a count meta-argument on a null_resource to loop over the list, and use provisioners to construct the policy JSON, then pass it to aws_iam_policy.
AnswerB

This is correct because dynamic blocks can be nested, and conditionally including a nested block is best achieved by setting the inner dynamic block's for_each to a one-element list or an empty list based on the condition. This pattern is idiomatic HCL for optional nested blocks and preserves the correct structure.

Why this answer

The correct approach uses nested dynamic blocks with conditional for_each to generate optional nested blocks. This is the standard HCL pattern for conditionally including blocks based on input data. It ensures that the Condition block is only present when needed, avoiding invalid policy documents.

The other options either misuse data sources, create empty blocks, or rely on provisioners, which are not appropriate for this declarative configuration.

Exam trap

The trap here is assuming that setting nested block arguments to null is equivalent to omitting the block entirely, which is not true for many providers including AWS IAM policies.

13
Multi-Selecteasy

Which TWO of the following functions are most useful for manipulating lists?

Select 2 answers
A.keys()
B.concat()
C.flatten()
D.lookup()
E.merge()
AnswersB, C

The `concat()` function joins two or more lists into a single combined list, directly satisfying the requirement to manipulate lists. Unlike `join()`, which merges list elements into a string, `concat()` preserves list structure and element order, making it the appropriate choice for combining list values without altering their underlying data type.

Why this answer

In this scenario, concat() (B) is correct because it joins two lists end-to-end into a single list, which is a core list-manipulation operation for combining sequences. flatten() (C) is also correct because it collapses nested lists into a single-level list, directly transforming list structure. The other options do not belong: keys() (A) is used to retrieve the key names of a map/object, not to manipulate lists; lookup() (D) retrieves a value associated with a key in a map or table, not a list operation; and merge() (E) typically combines maps/objects by key, not lists.

Exam trap

The trap is confusing map functions (keys, lookup, merge) with list functions — candidates see 'keys()' and assume it returns list elements, but it operates on maps.

14
MCQmedium

You are authoring a Terraform configuration that provisions an aws_autoscaling_group. The desired instance count is stored in a variable named instance_count, and you need to create exactly that many aws_instance resources. Which approach correctly creates the required number of instances while keeping the configuration valid?

A.Use the for_each meta-argument with for_each = var.instance_count on the aws_instance resource.
B.Use the depends_on meta-argument with depends_on = [var.instance_count] on the aws_instance resource.
C.Use the count meta-argument with count = var.instance_count on the aws_instance resource.
D.Use a dynamic block inside the aws_instance resource with for_each = range(var.instance_count).
AnswerC

The count meta-argument accepts a whole number and creates that many instances of the resource. Setting count = var.instance_count directly satisfies the requirement of creating exactly the desired number of aws_instance resources, and each instance is addressable by index. This is the idiomatic Terraform way to provision a fixed quantity of identical resources.

Why this answer

The count meta-argument is designed to create a specified number of identical resource instances. Because the desired quantity is a numeric variable, count = var.instance_count directly produces exactly that many aws_instance resources. The other options either misuse meta-arguments that expect different input types or confuse block-level repetition with resource-level repetition.

Exam trap

The trap here is assuming that any repetition meta-argument can accept a number, when for_each specifically requires a map or set of strings.

15
Multi-Selecthard

Which THREE of the following represent potential pitfalls when using 'count'?

Select 3 answers
A.Inability to use string keys for identification
B.Complex logic for conditional creation
C.Index shifts when removing list items
D.Performance issues with large collections
E.Type mismatch errors
AnswersA, B, C

count indexes instances numerically, so resources are identified by position rather than meaningful string keys. This satisfies the stem's pitfall about identification: you cannot reference an instance by a stable name, making lookups and references brittle compared with for_each.

Why this answer

Option A is correct because count-based resources are identified by numeric index in the state file, so you cannot use meaningful string keys to reference or target individual instances, making identification and targeting cumbersome. Option B is correct because conditionally creating or excluding specific instances with count requires awkward inline conditional expressions (for example, count = var.enabled ? 1 : 0) rather than clean per-item logic, which complicates configuration. Option C is correct because count indexes are positional, so removing an item from the middle of a list shifts the indexes of all subsequent items, causing Terraform to plan destruction and recreation of resources that should have remained unchanged.

Option D is not correct because count itself does not inherently cause performance problems with large collections; performance depends on provider API calls and plan graph size, not the count meta-argument specifically. Option E is not correct because count is an integer meta-argument and does not introduce type mismatch errors; type mismatches arise from incorrect variable or attribute types, not from using count.

Exam trap

The trap is including plausible-sounding but non-specific issues like 'performance' or 'type mismatch' — the exam tests whether you know the three canonical `count` pitfalls (no string keys, awkward conditionals, index shifts).

16
MCQhard

You are writing a Terraform configuration that uses a for expression to transform a map of objects into a new map. The original map has keys that are strings, and each object contains an attribute 'enabled' of type bool. You need to create a new map that includes only the entries where 'enabled' is true, and the new map's keys should be the original keys prefixed with 'prod-'. Which for expression achieves this?

A.[ for k, v in var.input_map : "prod-${k}" => v if v.enabled ]
B.{ for k, v in var.input_map : "prod-${k}" => v.enabled }
C.{ for k, v in var.input_map : "prod-${k}" => v if v.enabled }
D.{ for k, v in var.input_map : k => v if v.enabled }
AnswerC

This for expression correctly iterates over the map, applies a condition to filter only enabled entries, and constructs a new map with keys prefixed by 'prod-'. The syntax is valid: it uses the map form with key and value, includes an if clause for filtering, and the key expression generates the new key.

Why this answer

The correct for expression uses curly braces to produce a map, iterates over the input map, applies an if clause to filter only enabled entries, and transforms the key by prefixing 'prod-'. This meets all requirements: filtering, key transformation, and value preservation. The other options either use incorrect delimiters, omit the key transformation, or alter the value incorrectly.

Exam trap

The trap here is confusing the syntax for map and list for expressions, particularly the use of curly braces versus square brackets and the placement of the if clause.

17
MCQmedium

Which of these is a valid use case for a dynamic block?

A.Defining nested repeatable blocks
B.Conditional module inclusion
C.Changing the provider configuration dynamically
D.Defining multiple separate resources
AnswerA

Dynamic blocks generate repeated nested configuration blocks from a collection, such as multiple ingress rules inside an aws_security_group. This directly satisfies the requirement for nested repeatable blocks, which count and for_each cannot produce inside a resource argument.

Why this answer

A dynamic block in Terraform generates nested repeatable blocks within a resource, data source, provider, or module — for example, multiple `ingress` blocks in an `aws_security_group` or multiple `tag` blocks. It iterates over a collection and produces one nested block per element, using an iterator to reference each item. This is its sole purpose.

Exam trap

The trap is confusing dynamic blocks with `count`/`for_each` on resources or modules — dynamic blocks only generate nested blocks, never top-level resources or conditional modules.

How to eliminate wrong answers

Option B is wrong because conditional module inclusion is handled with `count` or `for_each` on the `module` block, not dynamic blocks. Option C is wrong because provider configuration is set via `provider` blocks and `alias`, and cannot be dynamically generated per-iteration by a dynamic block. Option D is wrong because defining multiple separate resources is done with `count` or `for_each` on the resource itself, not with a dynamic block, which only produces nested blocks inside a single resource.

18
MCQmedium

You are writing a Terraform configuration that uses a `dynamic` block to generate multiple `ingress` blocks inside an `aws_security_group` resource. The `var.ingress_rules` variable is a list of objects with `from_port`, `to_port`, and `cidr_blocks`. You need to reference the current rule inside the dynamic block's content. Which iterator configuration and reference is correct?

A.dynamic "ingress" { for_each = var.ingress_rules iterator = rule content { from_port = rule.value.from_port } }
B.dynamic "ingress" { for_each = var.ingress_rules iterator = ingress content { from_port = ingress.key.from_port } }
C.dynamic "ingress" { for_each = var.ingress_rules content { from_port = ingress.value.from_port } }
D.dynamic "ingress" { for_each = var.ingress_rules content { from_port = each.value.from_port } }
AnswerA

This explicitly sets the iterator name to rule and references rule.value inside the content block. This is the correct and unambiguous way to access the current element when the default iterator name might conflict or when you want clarity. It correctly iterates over the list of objects and uses the iterator's value attribute.

Why this answer

In a dynamic block, the iterator argument lets you name the temporary variable that represents the current element. The default is the block label, but explicitly setting iterator avoids ambiguity. Inside the content block, you access the current element via <iterator>.value.

Using each.value is incorrect because each is only for resource-level for_each. Referencing key instead of value also fails to access the object's attributes.

Exam trap

The trap here is confusing the dynamic block iterator with the resource-level each object, or mistakenly using key to access the element's attributes.

19
Multi-Selecthard

Which TWO of the following statements are correct regarding 'for_each' and state?

Select 2 answers
A.for_each creates stable identifiers based on keys
B.count is preferred for all resource types
C.count creates stable identifiers based on keys
D.for_each is preferred over count for map-based configurations
E.for_each is only available for cloud resources
AnswersA, D

for_each keys each instance by its map or set key rather than a positional index, so Terraform tracks instances by stable identifiers in state. Adding or removing entries therefore leaves unrelated instances untouched, satisfying the stem's requirement regarding state behaviour.

Why this answer

Option A is correct because for_each keys each instance by a map key or set member, so the resulting resource addresses (for example aws_instance.web["web1"]) remain stable across reordering or insertion of other elements, which keeps state entries tied to the same object. Option D is correct because for_each is the recommended construct for map-based (and set-based) configurations, since it iterates over key/value pairs and avoids the index-shifting problems that count causes when list elements are added or removed. Option B is wrong because count is not preferred for all resource types; it is only suitable when instances are truly identical and index-based addressing is acceptable.

Option C is wrong because count uses numeric indexes (resource[0], resource[1]), which are not key-based and therefore not stable when the list changes. Option E is wrong because for_each is a core Terraform meta-argument available to resources, data sources, and modules, not limited to cloud resources.

Exam trap

The trap is assuming count and for_each are interchangeable — candidates miss that count's positional indices cause cascading destroy/recreate operations, while for_each's key-based addressing is what makes it stable.

20
MCQhard

You are using a for expression to create a map from a list of objects. Each object has an attribute 'name' that is a string. You want the resulting map's keys to be the 'name' values and the values to be the entire objects. However, you encounter an error because some objects have duplicate 'name' values. How can you modify the for expression to handle duplicates by grouping objects with the same name into a list, so that the map values become lists of objects?

A.{ for obj in var.objects : obj.name => obj }
B.[ for obj in var.objects : obj.name => obj ]
C.{ for obj in var.objects : obj.name => obj... }
D.{ for obj in var.objects : obj.name => [obj] }
AnswerC

This for expression uses the grouping ellipsis (...) after the value expression. The ellipsis groups all values that share the same key into a list. So the resulting map will have keys from obj.name and values as lists of objects. This is the correct way to handle duplicate keys by grouping.

Why this answer

The correct for expression includes the grouping ellipsis (...) after the value expression. This tells Terraform to collect all values that share the same key into a list, thus handling duplicates by grouping. Without the ellipsis, duplicate keys cause an error.

The other options either omit the ellipsis, use the wrong delimiter, or incorrectly attempt to wrap values without grouping.

Exam trap

The trap here is not knowing about the grouping ellipsis and attempting to manually handle duplicates with nested loops or conditional logic.

21
MCQeasy

Which meta-argument is best for deploying resources that are strictly dependent on the number of items in a configuration?

A.count
B.for_each
C.provider
D.dynamic
AnswerA

The count meta-argument creates a fixed number of resource instances derived from a whole number, so scaling is tied strictly to the count of items in a configuration. This satisfies the stem's dependency on item quantity, unlike for_each, which iterates over map or set keys.

Why this answer

The `count` meta-argument is best for deploying resources that are strictly dependent on the number of items in a configuration. It takes an integer and creates that many instances of the resource. This is ideal when you need a specific number of identical resources, such as creating three EC2 instances.

Exam trap

AWS provider exams often test the choice between `count` and `for_each`, tricking candidates into selecting `for_each` for simple numeric counts when `count` is more appropriate.

How to eliminate wrong answers

Option B is wrong because `for_each` is used when you need to create resources based on a map or set of items, not strictly on a count. Option C is wrong because `provider` is a meta-argument used to specify which provider configuration to use, not to manage resource instances. Option D is wrong because `dynamic` is a block type for generating nested blocks, not a meta-argument for creating multiple resource instances.

22
MCQmedium

When using for_each on a resource, how do you access the value of the current iteration inside the resource configuration?

A.each.value
B.each.key
C.item.value
D.count.index
AnswerA

With for_each over a map or set, Terraform exposes each.key and each.value; each.value yields the current element's value, letting the resource configuration reference per-iteration data. This satisfies the need to access the current iteration's value directly within the resource block.

Why this answer

When a resource uses for_each, Terraform iterates over a map or set and exposes the current element through the each object. each.value returns the value of the current map entry (or the set member itself when iterating a set), which is what you reference inside the resource block to configure per-instance attributes. This is the standard mechanism for deriving unique names, tags, or settings for each instance created by the loop.

Exam trap

The trap here is confusing the count meta-argument with for_each — candidates who habitually use count.index may select option D, forgetting that for_each exposes each.key and each.value instead of a numeric index.

How to eliminate wrong answers

Option B is wrong because each.key returns the map key (or set member used as the key), not the value, so it would give you the identifier rather than the associated configuration data. Option C is wrong because item.value is not a Terraform construct — there is no item object in the for_each iteration scope; the correct object is each. Option D is wrong because count.index only exists when using the count meta-argument, not for_each, and it returns a numeric index rather than a map value.

23
MCQeasy

You are creating a security group in AWS and need to define multiple ingress rules based on a list of port numbers. Which meta-argument is most appropriate to use to create one resource instance per port?

A.instance_count
B.count
C.dynamic
D.for_each
AnswerD

for_each iterates over a map or set of strings, creating one distinct resource instance per element with a stable key. Feeding the port list as a set produces one ingress rule per port, unlike count, which lacks per-item identity.

Why this answer

The for_each meta-argument accepts a map or set of strings and creates one resource instance per element, making it the correct choice when iterating over a list of port numbers (converted to a set or map). Unlike count, for_each produces stable, keyed instance addresses (e.g., aws_security_group_rule.ingress["22"]) so adding or removing a port doesn't cause unrelated rules to be destroyed and recreated. This is the idiomatic Terraform pattern for defining multiple ingress rules from a collection of ports.

Exam trap

The trap here is confusing count with for_each: candidates see 'one instance per element' and reflexively pick count, missing that for_each is the correct meta-argument when stable keyed addressing matters and that instance_count is a fabricated distractor.

How to eliminate wrong answers

Option A is wrong because instance_count is not a valid Terraform meta-argument — the actual meta-argument is count, and this name does not exist in the language. Option B is wrong because count uses numeric indexes (resource[0], resource[1]), so removing a port from the middle of the list shifts all subsequent indexes and forces Terraform to destroy and recreate unrelated rules. Option C is wrong because dynamic is a block-generation construct used inside a resource to produce repeated nested blocks (e.g., multiple ingress blocks), not a meta-argument that creates separate resource instances per port.

24
MCQmedium

You are defining an input variable for a Terraform module that will accept a map of environment names to configuration objects. The object should have an instance_type string and a min_size number. You want Terraform to validate that any provided value matches this structure. Which type constraint should you use for the variable?

A.type = map(string)
B.type = object({ instance_type = string, min_size = number })
C.type = list(object({ instance_type = string, min_size = number }))
D.type = map(object({ instance_type = string, min_size = number }))
AnswerD

This type constraint defines a map whose values are objects with the specified attributes and types. Terraform will validate that each value in the map is an object containing an instance_type string and a min_size number. This precisely matches the requirement and provides strong type checking for the module input.

Why this answer

The requirement is a map where each key is an environment name and each value is an object with specific attributes. The type constraint map(object({ instance_type = string, min_size = number })) expresses exactly that. Terraform will enforce that the variable is a map and that each value conforms to the object schema, catching configuration errors early.

Exam trap

The trap here is choosing a simple map(string) because it is easy, overlooking that the values must be structured objects with multiple typed attributes.

25
MCQmedium

You are iterating over a map using for_each. You need to access the key of the current map element. Which object do you use?

A.var.key
B.each.key
C.count.key
D.each.value
AnswerB

Within a `for_each` block over a map, Terraform exposes `each.key` as the map key and `each.value` as its corresponding value, satisfying the requirement to reference the current element's key. Using `each.value` would return only the value, so `each.key` is the precise object needed here.

Why this answer

When using for_each with a map, Terraform automatically creates an each object that contains two attributes: each.key and each.value. each.key gives you the map key for the current iteration, while each.value gives the corresponding value. Therefore, to access the key of the current map element, you use each.key.

Exam trap

The trap here is confusing the each object with other iteration constructs like count or var, and mixing up each.key with each.value. Candidates often mistakenly choose each.value when asked for the key, or try to use count.index in a for_each context.

How to eliminate wrong answers

Option A is wrong because var.key is not a valid reference; var is used to access input variables, not iteration metadata. Option C is wrong because count.key is invalid; count is used with count-based loops and only provides count.index, not a key. Option D is wrong because each.value provides the value associated with the current key, not the key itself.

26
MCQeasy

A team defines a variable `allowed_ports` as `list(number)` and wants to validate that every value is between 1 and 65535 before any resources are planned. They add a `validation` block to the variable. Which configuration correctly enforces this constraint?

A.validation { condition = alltrue([for p in var.allowed_ports : p >= 1 && p <= 65535]) error_message = "Ports must be between 1 and 65535." }
B.validation { condition = anytrue([for p in var.allowed_ports : p >= 1 && p <= 65535]) error_message = "Ports must be between 1 and 65535." }
C.validation { condition = contains(var.allowed_ports, 80) error_message = "Ports must be between 1 and 65535." }
D.validation { condition = length(var.allowed_ports) > 0 error_message = "Ports must be between 1 and 65535." }
AnswerA

The `alltrue` function returns true only when every element of the list satisfies the predicate, and the `for` expression maps each port to a boolean. This condition correctly fails if any port is out of range. The `error_message` provides a clear diagnostic, and validation runs before planning, preventing invalid input from reaching resources.

Why this answer

Variable validation conditions must evaluate to a boolean that is true when the input is acceptable. To enforce a per-element range, the condition should reduce the list of per-port checks with `alltrue`, ensuring every element meets the bounds. This catches invalid ports before planning and produces the configured error message.

Exam trap

The trap here is using `anytrue`, which passes when just one port is valid, instead of `alltrue`, which is required to enforce a constraint on every element.

27
MCQmedium

You maintain a Terraform module that deploys AWS resources into several environments. Rather than declaring a separate variable per environment, you want a single input variable that accepts a map of environment names to configuration objects, where each object contains a region string and an instance count number, and your configuration should reject any input that does not match this shape at plan time. Which variable type constraint should you declare?

A.variable "environments" { type = list(object({ region = string, count = number })) }
B.variable "environments" { type = map(string) }
C.variable "environments" { type = map(object({ region = string, count = number })) }
D.variable "environments" { type = any }
AnswerC

A map(object({...})) constraint requires every value in the map to be an object whose attributes match the declared schema, so region must be a string and count a number. Terraform converts compatible values and raises a type error for mismatches during validation, which is exactly the plan-time rejection the scenario requires.

Why this answer

A map of objects is the only constraint that simultaneously enforces named environment keys and a fixed attribute schema with region as a string and count as a number. Terraform validates caller input against that schema during planning and converts compatible primitive values automatically, so malformed input fails early rather than producing broken resources later.

Exam trap

The trap here is assuming that any makes a variable flexible and safe, when in reality it disables the schema checking that would catch malformed environment objects.

28
MCQeasy

You are writing a Terraform configuration that uses a `for` expression to transform a list of strings into a map where each string is both the key and the value. Which of the following expressions achieves this?

A.`[ for s in var.list : s => s ]`
B.`{ for s in var.list : s => s }`
C.`{ for s in var.list : s = s }`
D.`{ for s in var.list : key => value }`
AnswerB

This expression uses a `for` expression with the syntax `{ for s in var.list : s => s }`. It iterates over each element `s` in `var.list` and creates a map entry with key `s` and value `s`. This is the correct syntax for a map comprehension where both key and value are derived from the same element.

Why this answer

The correct expression is `{ for s in var.list : s => s }`. It uses a map comprehension with the `=>` separator, creating a map where each string is both key and value. The other options either use incorrect brackets, wrong separator, or undefined variables.

Exam trap

The trap here is mixing up list and map comprehension syntax. List comprehensions use `[ ]` without `=>`, while map comprehensions use `{ }` with `=>`.

29
MCQmedium

Your team manages AWS infrastructure with Terraform. A module needs to accept a map of environment names to instance types, where each instance type must be one of a fixed set of allowed values. You want Terraform to reject invalid configurations during plan with a clear error message. Which approach best validates the variable input?

A.Define the variable with type = map(string) and use a lifecycle precondition on each AWS instance resource that checks the instance type.
B.Define the variable with type = map(string) and add a validation block with a condition that checks each value against the allowed list using the contains function and alltrue.
C.Define the variable with type = map(string) and add a custom validation using the file function to read a list of allowed values from an external file.
D.Define the variable with type = map(string) and rely on the AWS provider to reject invalid instance types when the plan is applied.
AnswerB

This is correct because a validation block in a variable definition can enforce constraints on the variable's value. Using alltrue over a for expression that checks each map value against the allowed list ensures every entry is valid, and Terraform will produce a custom error message when the condition fails, stopping the plan early.

Why this answer

Variable validation blocks allow you to enforce custom constraints on input variables. By using a condition that checks all values in the map against a list of allowed instance types, you ensure that invalid configurations are caught during plan with a helpful error message. This is the intended way to validate complex variable inputs in Terraform.

Exam trap

The trap here is assuming that provider-level validation is sufficient, when the exam expects you to use variable validation for early, clear feedback.

30
MCQmedium

How can you debug the output of a complex 'for' expression in Terraform?

A.Check the state file
B.Use terraform plan
C.Use terraform console
D.Use terraform debug
AnswerC

terraform console evaluates expressions interactively against current state and variables, so you can inspect the intermediate result of a complex for expression without applying. This isolates whether the comprehension itself is malformed, satisfying the need to debug its output directly.

Why this answer

terraform console launches an interactive REPL where you can evaluate HCL expressions — including complex for expressions, functions, and variable references — against the current configuration and state, printing the result immediately. This makes it the fastest way to inspect what a for expression actually produces without running a full plan. It's the standard debugging tool for expression logic in Terraform.

Exam trap

The trap is assuming a general-purpose command like terraform plan or the state file can reveal expression output; candidates overlook that terraform console is the dedicated REPL for evaluating and debugging HCL expressions interactively.

How to eliminate wrong answers

Option A is wrong because the state file records resource attributes and metadata, not the evaluated output of arbitrary for expressions, so it cannot show you what an expression returns. Option B is wrong because terraform plan shows the resulting resource changes, not the intermediate value of a specific expression, making it impractical for isolating expression bugs. Option D is wrong because terraform debug is not a valid Terraform command — there is no such subcommand in the CLI.

31
MCQeasy

When using 'for_each' on a list of strings, what must you do to ensure the configuration is stable?

A.Use count instead
B.Convert the list to a set
C.Use the index
D.Nothing, it's always stable
AnswerB

for_each requires a map or set of unique keys; a list of strings can contain duplicates, producing unstable addresses and plan errors. Converting the list to a set guarantees unique keys, keeping resource instance addresses stable across runs.

Why this answer

for_each requires a map or set of strings, not a list, because list indices are positional and unstable — inserting or removing an element shifts every subsequent index, causing Terraform to destroy and recreate resources. Converting the list to a set (e.g., toset(var.list)) gives each element a stable key based on its value, so resource addresses remain consistent across changes.

Exam trap

The trap is believing for_each works directly on lists — candidates forget that Terraform requires a map or set, and that converting with toset() is the canonical fix for list inputs.

How to eliminate wrong answers

Option A is wrong because count is the less stable alternative — it uses numeric indices that shift when list order changes, which is exactly the instability for_each is designed to avoid. Option C is wrong because using the index (via count.index) reintroduces positional instability; for_each deliberately avoids index-based addressing. Option D is wrong because a raw list is never stable with for_each — Terraform rejects lists outright for for_each and requires a map or set, so 'nothing' is technically impossible as well as incorrect.

32
MCQeasy

Which function is most useful when you have a nested list of objects and want to create a single flat list to iterate over with for_each?

A.concat()
B.flatten()
C.lookup()
D.merge()
AnswerB

flatten() collapses nested lists into a single-level list, so a list of lists of objects becomes one flat list of objects. This satisfies the stem's requirement to iterate the result with for_each, which rejects nested collections.

Why this answer

The flatten() function takes a nested list (list of lists) and returns a single flat list containing all elements. When you have a nested list of objects and want to iterate with for_each, flatten() collapses the nesting so each object becomes a top-level element that for_each can key on. This is the idiomatic Terraform pattern for iterating over nested collections.

Exam trap

The trap is confusing concat() with flatten() — concat() merges lists at one level, while flatten() recursively collapses nested lists; candidates often pick concat() when the question mentions 'nested'.

How to eliminate wrong answers

Option A is wrong because concat() concatenates two or more lists into one list but does not recursively flatten nested lists — it only merges the top level, leaving inner lists intact. Option C is wrong because lookup() retrieves a value from a map by key; it has nothing to do with flattening nested structures. Option D is wrong because merge() combines multiple maps into a single map, which is useful for maps but not for flattening a list of lists of objects.

33
MCQeasy

Which function allows you to safely access a map value even if the key might not exist?

A.try()
B.element()
C.lookup()
D.coalesce()
AnswerC

lookup() retrieves a value from a map for a given key and returns null when the key is absent, rather than erroring. This makes it the safe accessor for potentially missing map keys in Terraform configurations.

Why this answer

The lookup() function is specifically designed to retrieve a value from a map by key while providing a fallback default if the key is absent, preventing errors. Its signature is lookup(map, key, default), so it safely handles missing keys without throwing an error. This makes it the idiomatic choice for optional map attributes in Terraform configurations.

Exam trap

The trap here is confusing lookup() with try() — both can handle missing data, but lookup() is the map-specific function with an explicit default argument, while try() is a general error-suppressing expression evaluator.

How to eliminate wrong answers

Option A is wrong because try() evaluates expressions and catches errors, but it is not the canonical map-key accessor and is typically used for nested attribute access on objects, not simple map lookups. Option B is wrong because element() retrieves an item from a list by index (wrapping around), not a value from a map by key. Option D is wrong because coalesce() returns the first non-null/non-empty argument from a list of values, not a map lookup by key.

34
MCQhard

You are attempting to use a conditional expression to set a variable, but the types returned by the true and false results differ. What happens?

A.The result is coerced to a string
B.A type mismatch error is returned
C.It defaults to the true result type
D.Terraform automatically casts to the most flexible type
AnswerB

Terraform requires both conditional results to share a compatible type. When the true and false expressions return different types, evaluation fails with a type mismatch error rather than coercing one value, satisfying the stem's scenario.

Why this answer

In Terraform, a conditional expression requires both the true and false results to be of the same type. If they differ, Terraform returns a type mismatch error during evaluation. This is because Terraform's type system is strict and does not perform implicit type coercion in conditional expressions.

Exam trap

AWS provider exams often test the strictness of Terraform's type system in conditional expressions, tricking candidates into thinking Terraform will automatically coerce types when it actually errors out.

How to eliminate wrong answers

Option A is wrong because Terraform does not automatically coerce the result to a string; it raises an error. Option C is wrong because it does not default to the true result type; it requires both to be the same. Option D is wrong because Terraform does not automatically cast to the most flexible type; it enforces type consistency.

35
MCQhard

A module needs to render a JSON policy document where the list of allowed actions comes from a variable that may contain duplicates and mixed case, and the final output must be deterministic and free of duplicates. The engineer writes jsonencode({ Version = "2012-10-17", Statement = [{ Effect = "Allow", Action = local.actions, Resource = "*" }] }). Which local definition for local.actions best guarantees determinism and uniqueness?

A.actions = distinct(var.actions)
B.actions = sort(distinct(var.actions))
C.actions = toset(var.actions)
D.actions = sort(distinct([for a in var.actions : lower(a)]))
AnswerD

The for expression lowercases every action, distinct() removes duplicates including those that differed only by case, and sort() imposes a stable lexicographic order. The combination yields a deterministic, case-normalized, duplicate-free list regardless of input order, which is exactly what the policy document needs for reproducible JSON output across callers.

Why this answer

Wrapping the values in a for expression that applies lower() normalizes case, distinct() then collapses duplicates that previously differed only by capitalization, and sort() fixes a stable lexicographic order. The resulting list serializes identically for any caller passing the same logical set of actions, satisfying both determinism and uniqueness for the JSON policy.

Exam trap

The trap here is treating toset() as a deterministic container, when set ordering is undefined and jsonencode() may serialize its elements in an unstable sequence.

36
MCQeasy

What is the primary difference between 'count' and 'for_each' regarding state management?

A.for_each is only for maps
B.for_each does not support complex objects
C.count is faster than for_each
D.count uses index-based indexing, causing potential recreation when items are removed
AnswerD

count addresses instances by numeric index, so removing an item shifts subsequent indices and forces recreation of those resources. for_each keys instances by map or set value, so removing one item only affects that instance, avoiding unrelated recreation.

Why this answer

The primary difference is that `count` uses index-based indexing, so removing an item from the middle of a list causes all subsequent resources to be re-indexed, potentially leading to unnecessary recreation. `for_each` uses key-based indexing, so removing an item only affects that specific resource, avoiding recreation of others.

Exam trap

The trap is thinking that `count` and `for_each` are interchangeable, but the index-based nature of `count` can cause unintended resource recreation when items are removed from the middle of a list.

How to eliminate wrong answers

Option A is wrong because `for_each` can accept both maps and sets of strings, not only maps. Option B is wrong because `for_each` supports complex objects as values in the map. Option C is wrong because there is no inherent speed difference between `count` and `for_each`; performance depends on the provider and resource.

37
MCQhard

You want to pass a complex object type to a module, but you need to ensure specific attributes are present. How do you define this in the variable block?

A.type = map(any)
B.type = list(object)
C.type = object({...})
D.type = tuple([...])
AnswerC

An object type constraint declares named attributes with their individual types, so Terraform validates that every required attribute is present and correctly typed. This enforces the specific attributes the stem demands, unlike map or any, which permit arbitrary keys.

Why this answer

The object({...}) type constraint lets you define a schema with specific named attributes and their types, ensuring required attributes are present and correctly typed. This is the correct way to pass a complex object with enforced structure to a module. It validates at plan time and rejects missing or extra attributes as configured.

Exam trap

The trap is choosing map(any) for structured data — map(any) does not enforce specific attribute names, so required attributes can be missing without a plan-time error.

How to eliminate wrong answers

Option A is wrong because map(any) only enforces that all values are of the same type and does not define specific required attribute names. Option B is wrong because list(object) is not valid syntax — object requires a schema, and list(object) without attribute definitions is incomplete. Option D is wrong because tuple([...]) defines a fixed-length sequence of typed values by position, not a named-attribute object, so it cannot enforce specific attribute names.

38
MCQeasy

A team wants to pass a variable "allowed_ports" to a module and guarantee at apply time that every value is an integer between 1024 and 65535, rejecting invalid plans with a clear message. They prefer the validation to live in the variable block so callers get immediate feedback. Which declaration accomplishes this?

A.variable "allowed_ports" { type = list(number) validation { condition = alltrue([for p in var.allowed_ports : p >= 1024 && p <= 65535]) error_message = "Ports must be between 1024 and 65535." } }
B.variable "allowed_ports" { type = list(number) validation { condition = can(regex("^[0-9]+$", join(",", var.allowed_ports))) error_message = "Ports must be between 1024 and 65535." } }
C.variable "allowed_ports" { type = list(number) validation { condition = length(var.allowed_ports) > 0 error_message = "Ports must be between 1024 and 65535." } }
D.variable "allowed_ports" { type = list(number) validation { condition = contains(var.allowed_ports, 1024) && contains(var.allowed_ports, 65535) error_message = "Ports must be between 1024 and 65535." } }
AnswerA

The condition applies alltrue() over a for expression that evaluates the range check for every element, so any out-of-range port makes the condition false and produces the custom error message during plan. Because validation lives in the variable block, callers receive the failure immediately, and list(number) enforces the element type before the range logic runs.

Why this answer

A variable validation block with alltrue() over a per-element for expression evaluates the range predicate for every port and fails the plan when any element falls outside 1024–65535. Placing it in the variable block gives callers immediate, clearly worded feedback, and the list(number) type constraint handles element typing before the range check executes.

Exam trap

The trap here is using contains() to test list membership against boundary values, which checks for presence of specific ports rather than validating every element's range.

39
Multi-Selectmedium

Which THREE of the following are valid ways to pass data to a module dynamically?

Select 3 answers
A.Using variables
B.Directly editing the state
C.Using a dynamic block for module calling
D.Using for_each on a module block
E.Using complex object types
AnswersA, D, E

Input variables declared in the child module accept values supplied by the calling module's arguments, making data injection dynamic per invocation. This satisfies the requirement because the same module can receive different values on each call.

Why this answer

Option A (Using variables) is correct because Terraform modules accept input values through declared input variables, and callers pass data dynamically via the module block's arguments (e.g., variable_name = value), which is the standard mechanism for parameterizing a module. Option D (Using for_each on a module block) is correct because Terraform supports for_each on module blocks, allowing multiple module instances to be created dynamically from a map or set, with each instance receiving distinct input values derived from the iteration. Option E (Using complex object types) is correct because input variables can be typed as complex types such as object({...}), map, list, or tuple, enabling structured, dynamic data to be passed into a module rather than only simple primitives.

Option B (Directly editing the state) is not a valid way to pass data to a module, since the state file is a record of managed infrastructure and manually editing it is unsupported and does not feed inputs into module configuration. Option C (Using a dynamic block for module calling) is not valid because dynamic blocks only generate repeated nested configuration blocks within a resource, data, or module argument, not the module call itself, so they cannot be used to invoke modules dynamically.

Exam trap

The trap is thinking dynamic blocks can be used to call modules — dynamic blocks only generate nested configuration blocks within resources, not module invocations.

40
MCQhard

A configuration defines a dynamic block that generates ingress rules for an aws_security_group from a list of objects. Each object has a from_port, to_port, and protocol attribute, and one object also carries an extra cidr_blocks list. You want every generated rule to include the from_port, to_port, and protocol values, and to include cidr_blocks only when it is present, without writing a separate block for the odd entry. Which approach satisfies this?

A.Declare the variable as a list of maps and rely on Terraform to supply empty defaults for any map keys that are absent.
B.Use content with from_port = rule.value.from_port and reference cidr_blocks = rule.value.cidr_blocks inside the same content block.
C.Add a nested dynamic "cidr_blocks" block inside the ingress content, iterating over rule.value.cidr_blocks with for_each and guarding the iteration with a conditional that yields an empty list when the attribute is null.
D.Set the iterator argument to a custom label and reference the custom label inside content for all attributes, including cidr_blocks.
AnswerC

Nested dynamic blocks let you conditionally generate inner arguments, and wrapping the source in a conditional that returns an empty list when the attribute is null means zero inner blocks are produced for entries lacking cidr_blocks. The required scalar arguments remain in the outer content, so every rule is valid.

Why this answer

A nested dynamic block whose for_each source is guarded by a conditional that returns an empty list when the attribute is null produces inner blocks only for entries that actually carry cidr_blocks, while the outer content always emits the required scalar arguments. This keeps one uniform ingress block definition that adapts to heterogeneous input.

Exam trap

The trap here is assuming Terraform silently tolerates references to attributes that some objects lack, when such references actually fail evaluation for the missing entries.

41
MCQmedium

A team defines an aws_instance resource whose instance_type should normally come from a variable, but for one specific workspace named "dr" the value must be forced to t3.micro regardless of the variable. The team wants a single resource block that behaves correctly in every workspace without duplicating the resource. Which expression assigns instance_type appropriately?

A.instance_type = terraform.workspace != "dr" ? "t3.micro" : var.instance_type
B.instance_type = terraform.workspace == "dr" ? var.instance_type : "t3.micro"
C.instance_type = terraform.workspace == "dr" ? "t3.micro" : var.instance_type
D.instance_type = var.instance_type == "dr" ? "t3.micro" : var.instance_type
AnswerC

The conditional operator evaluates the workspace name at plan time and selects the hard-coded type only in the dr workspace, falling back to the variable everywhere else. Both branches produce strings, so the result type is consistent and the single resource block behaves correctly across all workspaces.

Why this answer

The conditional operator compares the current workspace name to the target and chooses the hard-coded instance type only when they match, preserving the variable-driven value elsewhere. Both result branches are strings, so no type inconsistency arises and the single resource block adapts per workspace at plan time.

Exam trap

The trap here is comparing the wrong operand to the workspace name, which silently produces a condition that never matches instead of an obvious error.

42
MCQeasy

You are writing a Terraform configuration that uses a dynamic block to generate multiple ingress rules within an aws_security_group resource. The dynamic block iterates over a variable named ingress_rules. Inside the content block, you need to reference the current rule. Which expression correctly accesses the current element?

A.ingress_rules.value
B.rule.value
C.each.value
D.dynamic.value
AnswerB

By default, a dynamic block uses the label of the block as the iterator name. Since the dynamic block is named rule, the iterator object is rule, and rule.value accesses the current element from the collection being iterated. This is the correct way to reference the current rule inside the content block.

Why this answer

Inside a dynamic block, the iterator object is named after the block's label unless an iterator argument is explicitly provided. With a dynamic block labeled rule, the current element is accessed via rule.value. This allows the content block to reference attributes of the current item.

The other options confuse the iterator with other meta-argument contexts or with the variable name.

Exam trap

The trap here is assuming that the iterator name matches the variable being iterated or that each.value is universally available, when dynamic blocks use a label-based iterator.

43
MCQmedium

An engineer is authoring a reusable module that accepts variable "subnets", typed as map(object({ cidr_block = string, public = bool })). The module must create one aws_subnet per entry, attaching an aws_route_table_association only when that entry's public attribute is true. The association resource must not appear at all for private subnets, and its key must match the subnet's map key. Which approach satisfies these requirements with the least waste?

A.Iterate aws_subnet with for_each = var.subnets, and iterate aws_route_table_association with for_each = { for k, v in var.subnets : k => v if v.public }.
B.Use count = length(var.subnets) on both aws_subnet and aws_route_table_association, and set the association's count to the number of entries where public is true.
C.Iterate aws_subnet with count over keys(var.subnets), and use element() lookups inside aws_route_table_association with a dynamic "public" block to skip private entries.
D.Iterate aws_subnet with for_each = var.subnets, then give aws_route_table_association a single instance whose subnet_id is a list built from all public entries via a for expression.
AnswerA

Both resources use map keys as instance keys. The filtered for expression produces a new map containing only entries whose public attribute is true, so the association exists solely for public subnets and its key equals the subnet key, giving clean one-to-one correlation. Adding or removing a private entry never disturbs association addresses, and no separate index bookkeeping is required.

Why this answer

Filtering a map with a for expression that includes an if clause yields a new map containing only the entries that satisfy the predicate, and using that map with for_each gives each association the same stable key as its subnet. This avoids index drift, keeps resource addresses stable across changes, and naturally expresses the conditional existence of the association without count arithmetic.

Exam trap

The trap here is reaching for count plus element() index math to express conditional resource creation, when filtering the map and feeding it to for_each expresses both the condition and the key correlation directly.

44
MCQmedium

You have a list of objects representing AWS tags. You need to transform this list into a map for use with a for_each argument. Which function should you use?

A.tolist()
B.flatten()
C.merge()
D.for expression
AnswerD

A for expression iterates the tag list and builds a map keyed by each element, which for_each requires. It satisfies the transformation constraint directly, unlike a for_each meta-argument or lookup, since it produces the map value rather than consuming one.

Why this answer

A for expression is the correct tool to transform a list of objects into a map, using the syntax { for item in list : key => value }, which is exactly what for_each requires (a map or set of strings). Functions like merge() combine existing maps but cannot reshape a list of objects into a map keyed by an attribute. The for expression is the idiomatic Terraform construct for this transformation.

Exam trap

The trap is reaching for a familiar function like merge() or flatten() to reshape data; candidates forget that only a for expression can construct a new map from a list of objects, which is the specific transformation for_each demands.

How to eliminate wrong answers

Option A is wrong because tolist() converts a set or tuple to a list — it does not produce a map and does not restructure objects into key/value pairs. Option B is wrong because flatten() collapses nested lists into a single flat list, which is the opposite of what's needed to build a map. Option C is wrong because merge() combines two or more existing maps into one; it cannot derive a map from a list of objects because it requires maps as inputs.

Ready to test yourself?

Try a timed practice session using only Dynamic Hcl Configuration questions.