What the Terraform regex function returns and what to know before using it
The regex function is simple to call, but its return type changes shape with your capture groups. It fails loudly the moment a pattern doesn't match, and its RE2 engine will flatly refuse a lookahead-heavy password pattern that looks perfectly valid in most other languages.
regex actually returns, a string, a list, or a map, set entirely by the capture groups in your pattern.can() and try() patterns that guard against it.Terraform's regex function looks like a small utility: one line, two arguments, a match. Underneath, the shape of the return value and what happens the instant nothing matches are two separate behaviors you should know.
A third factor decides whether your pattern even compiles: the exact set of features the underlying engine will accept. Get any of the three wrong, and you'll find out mid-plan, with an error that doesn't explain itself.
This article works through what regex returns, based on your capture groups, why a non-match is a hard failure rather than a quiet no-op, how regexall and replace extend the same syntax, and an RE2 limitation that stops common validation patterns from working in Terraform.
What the terraform regex function returns
The syntax is regex(pattern, string). What it returns is determined by how you use capture groups in your pattern. The return-type rule holds regardless of which specific pattern you write:
- When there are no capture groups at all,
regexreturns a single string, the substring the whole pattern matched. - One or more unnamed capture groups, and it returns a list, one entry per group, in the order they appear in the pattern.
- One or more named capture groups (
(?P<name>...)), and it returns a map keyed by group name.
Mixing named and unnamed groups in the same pattern isn't valid, so pick one style per pattern and stick to it.
For example, take a resource-naming convention like prod-usw2-api-07 (environment, region, service, and instance number). Three patterns against the same string show all three return types side by side:
The same technique parses Terraform workspace names into an environment and a suffix. Once a pattern has more than one group, use named groups: positional indexing like name_parts[0] breaks if someone reorders the naming convention, while a map keyed by env or service keeps working.
Why Terraform regex raises a no-match error
The instant a regex pattern doesn't match anywhere in the string, Terraform raises an error and stops the plan then and there. It's an intentional design choice, not a bug, as silently returning an empty string would hide a real issue downstream.
That's in contrast with regexall, which always returns a list and simply returns an empty one when nothing matches. If you genuinely don't know in advance whether a value will match, testing regexall's length is a safer move than calling regex directly. It's also worth testing a pattern against real values in the Terraform CLI's terraform console before wiring it into a validation block, since console evaluates regex calls the same way plan will.
When you do need regex itself, whether inside a locals block, a for expression, or a validation condition, wrap it so a non-match becomes a value instead of a halted plan:
Design Principle
can() turns the whole expression into a boolean (true if the pattern matched, false if regex errored), which is exactly what a validation condition needs.
try() keeps the error from surfacing at all and hands back whatever fallback value makes sense for your configuration.
Use can() when you only need a yes or a no answer, and try() when you need the extracted value itself with a sensible default on hand for when there isn't one.
Finding every match with regexall and replace
regex only ever gives you the first match. When a string can contain more than one, reach for regexall instead: it returns every match as a list, using the same capture-group rules as regex for what each list entry looks like.
Say a resource description carries several key=value tags packed into a single string:
replace handles a different job: substitution rather than extraction.
By default, replace(string, substring, replacement) swaps a literal substring, but the moment you want Terraform regex replace behavior rather than just a swap, wrap the second argument in forward slashes so that Terraform treats it as an RE2 pattern, the same syntax regex uses. The replacement string can then reference capture groups with $1, $2, or $name:
Implementation Detail
That $n support exists entirely in the replacement string. That doesn't mean RE2 supports backreferences generally. RE2 still can't reference an earlier part of the same match from within the pattern itself; only replace's output string can.
The RE2 constraint that breaks lookahead-style password validation
Outside Google's RE2 engine, where Terraform's regex functions run, most regex engines support lookahead assertions (?=...), which check a condition without consuming characters. They're the standard way to write password-complexity rules, requiring a digit, a symbol, an uppercase letter, and a minimum length in a single pattern.
Engineers porting that habit into Terraform will hit a wall once it becomes clear that RE2 doesn't implement lookaround at all (a deliberate design choice rather than an oversight). Guaranteeing linear-time matching (meaning no pattern can blow up execution time, regardless of the input) means giving up backreferences and lookaround, the two features most likely to make matching exponential.
A pattern like this looks entirely reasonable and still won't compile:
Instead, use fewer conditions per pattern. A single variable can carry several independent validation blocks, so decompose the one lookahead-heavy rule into several plain ones, each checking a single condition with character classes RE2 already handles:
Observation
Five small patterns, each of which RE2 handles fine, and five separate error messages that tell the caller exactly which rule failed, instead of one opaque failure for the whole policy, is a genuine improvement, even when ignoring the RE2 limitation that forced it.
The same decomposition applies in OpenTofu, which shares Terraform's RE2-based regex functions rather than a different engine.
Regex in variable validation and check blocks
Variable validation is the obvious home for regex, and it's worth stating the general shape once, distinct from the password example above.
Here is a common naming-convention check on an S3-style bucket name:
check blocks, which have been available since Terraform 1.5, extend the same can(regex(...)) pattern past a single variable's value and into invariants about the plan or applied infrastructure as a whole, including checks that reference more than one resource.
The syntax mirrors validation closely enough that the same regex habits carry over directly:
Pattern Recognition
Bear in mind that a failed validation block or precondition stops the plan outright, but a failed check block reports a warning and lets the run continue, making it useful for invariants you want visibility into without blocking every apply over them. In this light, check blocks are a natural fit for naming and tagging conventions enforced across an entire Terraform CI/CD pipeline, without turning every violation into a hard stop.
Pattern matching like this is ideal when you're confirming a string has the shape you expect. It tells you nothing about what happens next, whether changing a bucket name or an instance tag ripples into other resources that reference it.
To answer that question, you need blast radius analysis to map what actually relies on a value before you commit to changing it.
Conclusion
regex rewards precision. Know the return-type rule (string with no groups, list with unnamed groups, map with named groups) and you stop guessing at indices.
Guard every call with can() or try() and a non-match stops being a broken plan.
Know where RE2 draws the line: no backreferences, no lookaround, and a password-complexity pattern that would work fine in Python or JavaScript stops being a trap.
Try Stategraph free to see the dependency graph behind your Terraform and OpenTofu state, not just the pattern match in front of it.
Terraform regex FAQs
What does regex mean in Terraform?
Regex is short for regular expressions: special text patterns used for matching substrings in a given string. Like many programming languages, Terraform uses regular expressions as a powerful tool for input validation and manipulating text data: you can check that a string matches a specific format, pull specific data points out of text data, or enforce a validation rule on user input before a terraform plan runs.
A regular expression pattern combines literal characters with special syntax. For example, [a-z]+ matches one or more lowercase letters. You might use a pattern like this to validate resource names or extract specific words from a longer value. In Terraform code, the regex and regexall functions do the work: regex takes a pattern and a string and returns a single match, while regexall returns every match it finds.
Terraform's regular expression language is RE2 (a library developed at Google) rather than the built-in libraries found in other programming languages or text editors. It's fast and predictable, but it doesn't support some advanced features, which could be important if you're porting regex expressions from elsewhere.
Common uses include validating phone numbers, matching dates, extracting tags from resource names, and confirming values follow a specific format, all directly in the Terraform language.
Does terraform regex support backreferences?
No. Terraform's regex, regexall, and replace functions all run on Google's RE2 engine, which is built to guarantee linear-time matching and deliberately excludes both backreferences within a pattern and lookaround assertions. The one place backreference-style syntax does show up is the replacement string in replace, where $1 or $name can reference a capture group from the pattern. That's a property of the replacement, not of RE2 matching itself.
What's the difference between regex and regexall in Terraform?
regexreturns only the first match and raises an error the moment its pattern doesn't match anywhere in the string.regexallreturns every match as a list, and simply returns an empty list when there's nothing to find.
Use regex when a single expected value is enough (a version string, a tag, or an instance suffix); use regexall the moment a string might legitimately contain zero, one, or several matches.