Turn many cloned campaigns into one template
You have a folder full of campaigns that are basically the same. Maybe eight “Singapore” variants, six “India” ones, a handful of remarketing clones — each file 50-something lines, and the only real difference between siblings is a name, a UTM tag, and one targeting line. Every other change was just the rename copied through.
That’s a lot of files to keep in sync. Change the ad copy once and you
get to paste it into all eight. for_each collapses the whole set into
one template plus a table of what varies.
The shape
Pull the campaign into a template file with a variable for each thing
that changes between siblings:
variable "campaign_name" { type = string }variable "final_url" { type = string }variable "geo" { type = string }
resource "google_ads_campaign_budget" "budget" { name = var.campaign_name amount_micros = 5000000}
resource "google_ads_campaign" "search" { name = var.campaign_name advertising_channel_type = "SEARCH" campaign_budget = google_ads_campaign_budget.budget.id # ...ad group, ad, criteria...}
resource "google_ads_campaign_criterion" "geo" { campaign = google_ads_campaign.search.id location { geo_target_constant = var.geo }}Then list the variants in one module block:
module "ghostery_search" { source = "./templates/preroll-campaign.bid"
geo = "geoTargetConstants/2840" # shared: every variant targets the same place
for_each = { privacy = { campaign_name = "Ghostery — Privacy" final_url = "https://www.ghostery.com/?utm_campaign=search_privacy" } adblock = { campaign_name = "Ghostery — Ad Blocker" final_url = "https://www.ghostery.com/?utm_campaign=search_adblock" } trackers = { campaign_name = "Ghostery — Anti-Tracking" final_url = "https://www.ghostery.com/?utm_campaign=search_trackers" } }}bidsmith plan now shows three full campaigns — one per row in the
table. Eight files become one template and an eight-row map; the ad copy
lives in exactly one place.
How the table works
- Each key is a variant.
privacy,adblock,trackers— the key shows up in the plan asghostery_search.privacy.…, so you can tell the instances apart. - Each value fills in that variant’s blanks. The fields match the
template’s
variablenames. - Put shared values on the block.
geosits outside the table because all three campaigns share it. Anything that’s the same for every variant goes there; only the differences go in the rows.
Is this safe on a live account?
Yes. Converting hand-cloned files into a for_each template doesn’t
recreate anything in Google Ads. bidsmith matches your live campaigns by
their content — the campaign name, the keywords, the geo — not by
where they live in your files. As long as each variant still produces
the same campaign name and settings it had before, plan reports no
changes and your performance history, learning, and ad reviews stay
put.
The safe way to migrate:
- Build the template +
for_eachso each row reproduces an existing campaign exactly. - Run
bidsmith plan. You want0 to create, 0 to update. - Delete the old copy-pasted files in the same commit.
If plan wants to create or change something, a value in the table
doesn’t match the live campaign yet — fix the row until the plan is
clean.
See also
- Modules — the full
for_eachrules and addressing. - Variables — how a template’s inputs are typed.
- Organize a big account into folders — the file-layout side of taming a large account.