Skip to content

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 to google_ads_ad_group )

Optional

  • status ( String ) One of: ENABLED, PAUSED, REMOVED . Default "ENABLED"; omit to manage at the default.
  • template ( Reference to ad_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

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 to google_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 to google_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:

attributepoints at
videosgoogle_ads_youtube_video_asset
logo_imagesgoogle_ads_image_asset
call_to_actionsgoogle_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