Skip to content

Reuse one ad across every ad group

Search campaigns usually run one ad in every ad group — one ad group per keyword theme, the same creative everywhere. So the same 12 headlines and 4 descriptions get pasted into ad after ad. Change one headline and you’re editing every copy by hand; miss one and the ads quietly drift apart.

Locals fix a repeated headline list, but here the whole ad body repeats — final URLs, headlines, descriptions, paths, all of it. An ad_template names that body once.

Declare the body once

A top-level ad_template "name" { … } block holds exactly an ad {} body:

ad_template "ublock_rsa" {
final_urls = ["https://ghostery.info/ublock?utm_source=google&utm_medium=cpc&utm_term={keyword}"]
responsive_search_ad {
headlines = [
"uBlock Gone? Try Ghostery",
"uBlock Replacement Found",
"Switch from uBlock Now",
]
descriptions = [
"Blocks ads, trackers & popups. Manifest V3 ready.",
"The privacy suite millions of people trust.",
]
path1 = "ublock"
path2 = "ghostery"
}
}

Attach it to each ad group

Each ad is still its own google_ads_ad_group_ad. Instead of an inline ad {} block, point it at the template:

resource "google_ads_ad_group_ad" "replacement_rsa" {
ad_group = google_ads_ad_group.replacement.id
status = "ENABLED"
template = ad_template.ublock_rsa
}
resource "google_ads_ad_group_ad" "chrome_rsa" {
ad_group = google_ads_ad_group.chrome.id
status = "ENABLED"
template = ad_template.ublock_rsa
}
resource "google_ads_ad_group_ad" "safari_rsa" {
ad_group = google_ads_ad_group.safari.id
status = "ENABLED"
template = ad_template.ublock_rsa
}

Three five-line stubs instead of three 30-line bodies. Edit the headline in ad_template.ublock_rsa once and bidsmith plan shows it landing on all three ads at the same time.

Adopting it on a live account is safe

Each ad keeps its own resource address, so folding existing ads onto a template is a pure source refactor: the body bidsmith sends is identical, and plan reports no changes. Nothing is recreated — your ads keep their history and review status.

Share one template across files

An ad_template declared in one file can be referenced from any other — the same resolution rules as resources. Put the bodies that several campaigns share in one file (say templates.bid) and reference them from each campaign file:

campaigns/search/ublock.bid
resource "google_ads_ad_group_ad" "replacement_rsa" {
ad_group = google_ads_ad_group.replacement.id
template = ad_template.ublock_rsa # declared in templates.bid
}

Vary the landing page per ad group

Often the creative is shared but each ad group points at a different landing page — homepage here, a comparison page there. You don’t need a separate template for each URL. Drop final_urls from the template and let every ad supply its own; path1 / path2 work the same way:

ad_template "ublock_rsa" {
responsive_search_ad {
headlines = ["uBlock Gone? Try Ghostery", "uBlock Replacement Found", "Switch from uBlock Now"]
descriptions = ["Blocks ads, trackers & popups. Manifest V3 ready.", "The privacy suite millions trust."]
path2 = "ghostery"
}
}
resource "google_ads_ad_group_ad" "chrome_rsa" {
ad_group = google_ads_ad_group.chrome.id
template = ad_template.ublock_rsa
final_urls = ["https://ghostery.info/chrome?utm_term={keyword}"]
path1 = "chrome"
}
resource "google_ads_ad_group_ad" "safari_rsa" {
ad_group = google_ads_ad_group.safari.id
template = ad_template.ublock_rsa
final_urls = ["https://ghostery.info/safari?utm_term={keyword}"]
path1 = "safari"
}

Anything you set on the resource (final_urls, path1, path2, final_url_suffix, custom_parameters) wins over the template; anything you leave out is inherited. The headlines and descriptions still live in one place. If a template omits final_urls, every reference has to provide one — validate catches any that forget.

Vary a headline or a tracking slug

final_urls and the paths are the common case, but an A/B pair usually differs in one headline too — and duplicating the whole body to change one string is exactly what a template is meant to prevent.

Anywhere in a template body, write input.<name> for a value the ad supplies:

ad_template "fb_rsa" {
final_urls = ["https://ghostery.info/facebook?utm_content=${input.slug}"]
responsive_search_ad {
headline {
text = input.headline_1
pin = "HEADLINE_1"
}
headline { text = "Block Facebook Ads" }
headline { text = "Free & Open Source" }
description { text = "Stop the feed ads and the tracking behind them." }
description { text = "Free forever. No account needed." }
}
}
resource "google_ads_ad_group_ad" "fbads_block_rsa" {
ad_group = google_ads_ad_group.fbads.id
template = ad_template.fb_rsa
inputs = {
headline_1 = "Block Facebook Ads"
slug = "rsa_a"
}
}
resource "google_ads_ad_group_ad" "fbads_getrid_rsa" {
ad_group = google_ads_ad_group.fbads.id
template = ad_template.fb_rsa
inputs = {
headline_1 = "Get Rid of Facebook Ads"
slug = "rsa_b"
}
}

An A/B pair that used to be two full bodies is now two four-line resources, and the shared copy has one home.

A template’s parameters are simply the input. names its body uses — there is no separate list to declare, so the two can never disagree. bidsmith checks both directions:

  • Leave one out and validate says which template needs it and what to add.
  • Pass one the template never mentions and it says so too, with the names it does take. A silently-ignored headline2 is the kind of typo that only shows up in a performance report weeks later.

Values are checked where they land, not where they’re written: bind pin = input.slot to "TOPLEFT" and you get the same expected one of [HEADLINE_1, …] error as if you’d typed it inline.

See also