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:
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
validatesays 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
headline2is 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
google_ads_ad_group_ad— thetemplateattribute andadblock.- Reuse a headline set across many ads — the locals version, for when only the headline list repeats.