Skip to content

Locals

A locals block lets you name a constant once and reference it everywhere else in the file. It’s the same idea as a variable in any programming language, scoped to your .bid file.

If you find yourself pasting the same daily budget, language code, or city radius into every campaign — that’s the use case.

A simple example

Without locals:

resource "google_ads_campaign_budget" "warsaw" {
amount_micros = 10000000
...
}
resource "google_ads_campaign_budget" "krakow" {
amount_micros = 10000000
...
}
resource "google_ads_campaign_budget" "gdansk" {
amount_micros = 10000000
...
}

Three campaigns, three places to remember to update if the daily budget changes.

With locals:

locals {
daily_budget_micros = 10000000
}
resource "google_ads_campaign_budget" "warsaw" {
amount_micros = local.daily_budget_micros
...
}
resource "google_ads_campaign_budget" "krakow" {
amount_micros = local.daily_budget_micros
...
}
resource "google_ads_campaign_budget" "gdansk" {
amount_micros = local.daily_budget_micros
...
}

Now there’s one place to change the budget. plan will show which campaigns picked up the new value.

Where it goes

A locals block sits at the top level of a .bid file, next to your provider and resource blocks:

locals {
daily_budget_micros = 10000000
default_cpc_micros = 3000000
city_radius_km = 15
polish = "languageConstants/1030"
}

Values can be strings, numbers, booleans, lists, or maps. Each name has to be unique within the file. (Lists and maps get their own section below.)

Using a local

Reference a local with local.<name> — the same dot-form you use for resource references, but with local instead of a resource type:

resource "google_ads_ad_group" "warsaw_default" {
cpc_bid_micros = local.default_cpc_micros
...
}
resource "google_ads_campaign_criterion" "warsaw_proximity" {
proximity {
latitude = 52.229675
longitude = 21.012228
radius = local.city_radius_km
radius_units = "KILOMETERS"
}
}

validate and plan resolve local.daily_budget_micros to its underlying value before any other check runs — so type errors, enum checks, and the validateOnly mutate all see the substituted value.

Locals can reference other locals

A local can reference another local. Chains are followed all the way to the leaf:

locals {
base_cpc = 1500000
premium_cpc = local.base_cpc
high_value_cpc = local.premium_cpc
}

Cycles (a = local.b, b = local.a) are caught at validate time.

Lists: the headline-set problem

The values above are single numbers. But the copy you repeat most is usually a list — the same dozen headlines pasted into every responsive search ad, the same descriptions, the same keyword theme. A local can hold a list, and any attribute that expects a list can take the reference instead of an inline copy:

locals {
brand_headlines = [
"Ghostery Blocks Trackers",
"Privacy That Just Works",
"Switch to Ghostery Free",
{ text = "Ghostery — Private by Default", pin = "HEADLINE_1" },
]
brand_descriptions = [
"Block ads, trackers, and popups. Manifest V3 ready.",
"The privacy extension millions of people already trust.",
]
}
resource "google_ads_ad_group_ad" "search_ad_1" {
ad {
final_urls = ["https://ghostery.com/get"]
responsive_search_ad {
headlines = local.brand_headlines
descriptions = local.brand_descriptions
}
}
}
resource "google_ads_ad_group_ad" "search_ad_2" {
ad {
final_urls = ["https://ghostery.com/extension"]
responsive_search_ad {
headlines = local.brand_headlines
descriptions = local.brand_descriptions
}
}
}

Two ads, one headline set. Change a headline in the local and plan shows the edit landing on every ad that points at it — that fan-out is the whole point.

Lists work the same way for the other list-shaped attributes:

  • final_urls on an ad
  • languages and locations on a campaign (so every campaign shares one geo/language list)
  • texts (and match_types) inside a compact keywords block

That last one is how you reuse a keyword list. A compact keywords block already expands its texts into one keyword criterion each, so pointing texts at a local fans a named list out into criteria:

locals {
competitor_terms = ["ublock", "ublock origin", "adblock alternative"]
}
resource "google_ads_ad_group_criterion" "competitors" {
ad_group = google_ads_ad_group.core.id
keywords {
texts = local.competitor_terms
match_type = "PHRASE"
}
}

A list element can be a plain string or a { text, pin } object (for pinned headlines), and the two can mix in one list — exactly like an inline headlines list.

Maps

A local can also hold a map (a set of key/value pairs). Today the main use is feeding a module for_each a table of variants:

locals {
cities = {
warsaw = { name = "Warsaw", radius_km = 15 }
krakow = { name = "Kraków", radius_km = 12 }
}
}
module "city" {
source = "./city-campaign.bid"
for_each = local.cities
}

Module scope

Locals live in the same module as the file that declares them — same scoping rules as resources. If warsaw.bid declares local.radius = 15 and krakow.bid doesn’t, references inside krakow.bid to local.radius resolve via the global fallback (one module declares it, so the lookup succeeds). If multiple modules declare the same local name, bidsmith asks you to rename one.

That global fallback is what makes a shared file work. When the same headline set is copied across ublock.bid, generic.bid, and a dozen others, move it into one well-known file — say shared.bid — that declares nothing but locals. Every other file references local.brand_headlines and resolves it through the fallback. One declaration, account-wide; edit it once and plan shows the change on every campaign at the same time.

What this replaces, and what it doesn’t

locals collapses repeated values — single numbers, whole headline sets, keyword lists — within a file or across every file in the folder. It does not do:

  • CLI- or env-driven overrides. That’s what variable blocks are for. A local can reference a variable (local.daily = var.budget_micros), so the two compose naturally.
  • Reusable resource bundles. That’s what module blocks are for — not yet supported. Today, a “Warsaw campaign” and “Kraków campaign” are still separate resource declarations, even if every value comes from a shared local.

If your use case needs module, the roadmap lists it under “Open decisions.”

Next

  • References — the same dotted syntax used to point at resources.
  • Modules — how multi-file projects share names without colliding.