google_ads_ad_group_ad
An ad is the creative that runs inside an ad group. Bidsmith
supports responsive search ads (RSAs) — Google’s recommended search
ad format, where you supply 3–15 headlines and 2–4 descriptions and
Google permutes them at serve time — video ads for YouTube preroll
and bumper campaigns, and Demand Gen video ads for DEMAND_GEN
campaigns (all documented below). An ad carries one creative: an
RSA, a video_responsive_ad, a video_ad, or a Demand Gen video ad.
Alongside the creative, the ad block carries the URLs the click goes
to. final_urls is the landing page — and, in practice, where the UTM
parameters a campaign is measured on live, which is why it belongs in
the repo rather than in a UI field nobody can diff. final_mobile_urls
overrides it on phones, and display_url is the shortened address the
ad shows rather than navigates to.
Example
A responsive search ad with one pinned headline and one pinned description:
resource "google_ads_ad_group_ad" "summer_default_rsa" { ad_group = google_ads_ad_group.summer_default.id status = "ENABLED"
ad { name = "Summer RSA" final_urls = ["https://example.com/summer"]
responsive_search_ad { headline { text = "Summer Deals 2026" pin = "HEADLINE_1" }
headline { text = "Save On Summer Gear" }
headline { text = "Free Shipping This Season" }
description { text = "Limited-time offers across our summer range." pin = "DESCRIPTION_1" }
description { text = "Order today, get it before July." }
path1 = "summer" path2 = "deals" } }}Schema
Required
-
ad_group( Reference togoogle_ads_ad_group)
Optional
-
status( String ) One of:ENABLED,PAUSED,REMOVED. Default"ENABLED"; omit to manage at the default. -
template( Reference toad_template) -
final_urls( List of String ) -
path1( String ) -
path2( String ) -
final_url_suffix( String ) -
custom_parameters( string_map )
Nested blocks documented below: ad.
Nested blocks
ad
The wrapper around the actual creative. Bidsmith currently supports
exactly one ad block per google_ads_ad_group_ad.
Required
-
final_urls( List of String )
Optional
-
name( String ) -
final_mobile_urls( List of String ) -
display_url( String ) -
final_url_suffix( String ) -
custom_parameters( string_map )
Nested blocks documented below: responsive_search_ad, video_responsive_ad, video_ad, demand_gen_video_responsive_ad.
responsive_search_ad
A responsive search ad. Goes inside the ad block.
Optional
-
path1( String ) -
path2( String ) -
headlines( List of strings or { text, pin } objects ) -
descriptions( List of strings or { text, pin } objects )
Nested blocks documented below: headline, description.
headline
A single headline asset. Repeat the block 3–15 times. text is the
ad copy (≤ 30 characters); pin optionally locks the headline to a
specific position (HEADLINE_1, HEADLINE_2, or HEADLINE_3).
Required
-
text( String )
Optional
-
pin( String ) One of:HEADLINE_1,HEADLINE_2,HEADLINE_3,DESCRIPTION_1,DESCRIPTION_2.
description
A single description asset. Repeat the block 2–4 times. text is the
ad copy (≤ 90 characters); pin optionally locks the description to
DESCRIPTION_1 or DESCRIPTION_2.
Required
-
text( String )
Optional
-
pin( String ) One of:HEADLINE_1,HEADLINE_2,HEADLINE_3,DESCRIPTION_1,DESCRIPTION_2.
video_responsive_ad
A video ad for a YouTube campaign (preroll / in-stream / bumper /
non-skippable). Goes inside the ad block, in place of a
responsive_search_ad. The video attribute references a
google_ads_youtube_video_asset —
the video that plays.
resource "google_ads_youtube_video_asset" "brand_12s" { youtube_video_id = "dQw4w9WgXcQ"}
resource "google_ads_ad_group_ad" "preroll" { ad_group = google_ads_ad_group.instream.id status = "PAUSED"
ad { name = "Preroll 12s" final_urls = ["https://example.com/?utm_source=youtube"]
video_responsive_ad { video = google_ads_youtube_video_asset.brand_12s.id headlines = ["Block Ads & Trackers"] long_headlines = ["Ghostery blocks ads and trackers on every site"] descriptions = ["Install the free privacy extension in one click."] call_to_actions = ["Install"] breadcrumb1 = "AdBlocker" breadcrumb2 = "Browser" } }}The ad group must be a video type (VIDEO_TRUE_VIEW_IN_STREAM,
VIDEO_BUMPER, VIDEO_NON_SKIPPABLE_IN_STREAM, …) and its campaign
must be advertising_channel_type = "VIDEO".
Ad copy is create-only, exactly as it is for RSAs: editing a headline plans as a new ad plus a destroy of the old one, not an in-place edit.
Required
-
video( Reference togoogle_ads_youtube_video_asset)
Optional
-
headlines( List of String ) -
long_headlines( List of String ) -
descriptions( List of String ) -
call_to_actions( List of String ) -
breadcrumb1( String ) -
breadcrumb2( String )
video_ad
The creative a video campaign built in the Google Ads UI actually carries — one YouTube video in an in-stream, bumper, or in-feed format. Declare it to bring an ad that already exists under management:
resource "google_ads_youtube_video_asset" "brand_12s" { youtube_video_id = "dQw4w9WgXcQ"}
resource "google_ads_ad_group_ad" "us_instream" { ad_group = google_ads_ad_group.us_instream.id status = "ENABLED"
ad { final_urls = ["https://www.ghostery.com/?utm_campaign=GH_YouTubeUS_v1_0811-instream"] final_mobile_urls = ["https://m.ghostery.com/?utm_campaign=GH_YouTubeUS_v1_0811-instream"] display_url = "www.ghostery.com"
video_ad { video = google_ads_youtube_video_asset.brand_12s.id } }}You don’t have to write this by hand: bidsmith refresh renders it from
the live account, tracking URL and all. That is the point of the block —
without it the ad could not be declared, so the UTM slug a video test is
measured on lived only in the UI, unversioned and never in a diff.
Required
-
video( Reference togoogle_ads_youtube_video_asset)
demand_gen_video_responsive_ad
The creative a Demand Gen campaign runs
(advertising_channel_type = "DEMAND_GEN"). It’s a distinct ad type
from video_responsive_ad, and it is the video creative bidsmith can
create rather than only adopt. Three of its attributes are lists of
asset references:
| attribute | points at |
|---|---|
videos | google_ads_youtube_video_asset |
logo_images | google_ads_image_asset |
call_to_actions | google_ads_call_to_action_asset |
resource "google_ads_youtube_video_asset" "shorts" { youtube_video_id = "dQw4w9WgXcQ"}
resource "google_ads_image_asset" "logo" { name = "Brand logo 1200x1200"}
resource "google_ads_call_to_action_asset" "learn_more" { call_to_action = "LEARN_MORE"}
resource "google_ads_ad_group_ad" "shorts_ad" { ad_group = google_ads_ad_group.demand_gen.id status = "ENABLED"
ad { name = "Ad 1" final_urls = ["https://example.com/?utm_source=youtube"]
demand_gen_video_responsive_ad { videos = [google_ads_youtube_video_asset.shorts.id] logo_images = [google_ads_image_asset.logo.id] call_to_actions = [google_ads_call_to_action_asset.learn_more.id] headlines = ["Block Ads & Trackers Free"] long_headlines = ["Block ads, trackers and cookie popups across the web"] descriptions = ["Add the free privacy extension to your browser."] business_name = "Ghostery" breadcrumb1 = "Adblocker" breadcrumb2 = "Browser" } }}The other common use is adopting a Demand Gen campaign you built in
the Google Ads UI — see
Adopt an existing campaign.
bidsmith pull selects the Demand Gen creative and the campaign
round-trips through export.
Optional
-
videos( List of Reference ) -
logo_images( List of Reference ) -
headlines( List of String ) -
long_headlines( List of String ) -
descriptions( List of String ) -
call_to_actions( List of Reference ) -
breadcrumb1( String ) -
breadcrumb2( String ) -
business_name( String )
Reusing one body across ad groups
When the same ad runs in every ad group of a campaign, you don’t have
to paste the ad {} body into each google_ads_ad_group_ad. Declare
the body once in a top-level ad_template "name" { … } block — its body
is exactly an ad {} body — and attach it with template = ad_template.<name> in place of the inline ad {} block:
ad_template "ublock_rsa" { final_urls = ["https://ghostery.info/ublock?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 trust."] path1 = "ublock" path2 = "ghostery" }}
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}Each ad stays its own resource with its own address, so adopting a
template on an account that’s already live is a no-op plan — the
expanded body is identical to what was there before. A template declared
in one file can be referenced from any other (same resolution as
resources), so a body shared across campaigns lives in one place.
Overriding part of a template
Most templates are split apart only because the landing page differs —
the headlines and descriptions are identical. Instead of forking the
template, keep one and override the bits that change at the reference.
Set final_urls, path1, or path2 directly on the
google_ads_ad_group_ad; they take precedence over the template’s
values, and anything you leave unset is inherited:
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"}A template that’s meant to be URL-agnostic can omit final_urls
entirely, as above — but then every reference must supply its own,
or validate flags it. Overrides only apply on top of a template;
with an inline ad {} block, set these fields inside the block instead.
See the recipe Reuse one ad across every ad group for the before/after on a real campaign.
See also
google_ads_ad_group— required parent.google_ads_youtube_video_asset— the video avideo_responsive_adorvideo_adplays.- Tutorial: Launch a new search campaign — includes a full RSA example.
- Reuse one ad across every ad group — the
ad_templaterecipe.