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_urlson an adlanguagesandlocationson a campaign (so every campaign shares one geo/language list)texts(andmatch_types) inside a compactkeywordsblock
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
variableblocks are for. A local can reference a variable (local.daily = var.budget_micros), so the two compose naturally. - Reusable resource bundles. That’s what
moduleblocks 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.