Skip to content

google_ads_ad_group

An ad group is a bucket inside a campaign. It holds the keywords people search for and the ads that show in response. Every campaign needs at least one ad group; well-structured campaigns have several, each focused on a single theme.

Callouts and snippets for this ad group only

An ad group can own text assets the same way a campaign can, for extensions that should show with one theme’s ads and not the rest:

resource "google_ads_ad_group" "chrome" {
name = "Chrome"
campaign = google_ads_campaign.gh_cookies.id
callouts = ["Works in Chrome", "One-click install"]
structured_snippet {
header = "Brands"
values = ["Chrome", "Firefox", "Edge"]
}
}

bidsmith creates the assets and attaches them to the ad group. Declaring the same text on a campaign and on one of its ad groups creates two assets — which is what the account gets either way, since neither level can borrow the other’s attachment.

Use google_ads_callout_asset plus google_ads_ad_group_asset when the asset is shared between ad groups or campaigns. refresh folds only the ones a single owner uses, so a shared asset keeps its resource.

Example

resource "google_ads_ad_group" "summer_default" {
name = "Summer — Default"
campaign = google_ads_campaign.summer_search.id
status = "ENABLED"
type = "SEARCH_STANDARD"
cpc_bid_micros = 2000000 # €2.00 max CPC
}

Which bid field?

The campaign picks how it bids; the ad group says how much. Those are two different files’ worth of settings, and the field that carries the amount depends on the strategy the campaign chose:

Campaign bidding blockAd group bid field
manual_cpc {}cpc_bid_micros
manual_cpm {}cpm_bid_micros
manual_cpv {}cpv_bid_micros
target_cpm {}target_cpm_micros
target_cpv {}target_cpv_micros

target_cpa_micros, percent_cpc_bid_micros and fixed_cpm_micros cover strategies set outside those blocks (a portfolio strategy, or one picked in the Google Ads UI).

Getting this wrong is quiet rather than loud. A YouTube ad group running Target CPV keeps cpc_bid_micros at zero and bids through target_cpv_micros — so a file that sets cpc_bid_micros on it looks like it set a bid, matches what Google reports back, and changes nothing. Set the field that matches the strategy.

Targeting, or just watching

An audience, an age range, or a gender attached to an ad group can mean one of two very different things:

  • Targeting — only people who match are eligible to see the ad.
  • Observation — everyone stays eligible; the match only tells Google how to bid.

Same criteria, very different reach, and nothing in the criterion itself says which one is in force. targeting_setting is where you say it, one dimension at a time:

resource "google_ads_ad_group" "remarketing" {
name = "Remarketing — cart abandoners"
campaign = google_ads_campaign.summer_search.id
cpc_bid_micros = 2000000
targeting_setting {
target_restriction {
targeting_dimension = "AUDIENCE"
bid_only = true
}
}
}

That reads as “the audience on this ad group is an observation — bid differently for cart abandoners, but keep showing ads to everyone else.” Change bid_only to false and the same file says only cart abandoners ever see the ad. It’s a one-word edit with a large change in reach, which is exactly the kind of thing worth having in review.

Three things worth knowing:

  • The restrictions are managed as a set, and only once you declare the block. An ad group with no targeting_setting leaves the setting to whoever set it in the Google Ads UI; write the block and the whole set becomes yours, because replacing the list wholesale is the only update the Google Ads API offers. A dimension you drop from the file goes back to targeting.
  • bid_only = false is what Google assumes for a dimension nobody mentions, so bidsmith doesn’t write those entries out. An ad group whose live setting is all defaults reads back with no block at all rather than seven lines that say nothing.
  • Declare it on the ad group or the campaign, not both. Google Ads refuses to write an ad group’s setting while its campaign has one, and validate warns when a file does that — a refused operation takes the whole apply down with it.

Schema

Required

  • name ( String )
  • campaign ( Reference to google_ads_campaign )

Optional

  • status ( String ) One of: ENABLED, PAUSED, REMOVED . Default "ENABLED"; omit to manage at the default.
  • type ( String ) One of: SEARCH_STANDARD, DISPLAY_STANDARD, SHOPPING_PRODUCT_ADS, VIDEO_BUMPER, VIDEO_TRUE_VIEW_IN_STREAM, VIDEO_TRUE_VIEW_IN_DISPLAY, VIDEO_NON_SKIPPABLE_IN_STREAM, VIDEO_RESPONSIVE .
  • cpc_bid_micros ( Integer )
  • cpv_bid_micros ( Integer )
  • cpm_bid_micros ( Integer )
  • target_cpa_micros ( Integer )
  • target_cpm_micros ( Integer )
  • target_cpv_micros ( Integer )
  • percent_cpc_bid_micros ( Integer )
  • fixed_cpm_micros ( Integer )
  • final_url_suffix ( String )
  • custom_parameters ( string_map )
  • callouts ( List of String )

Nested blocks documented below: targeting_setting, structured_snippet, ai_max_ad_group_setting, audience_setting, demand_gen_ad_group_settings.

Nested blocks

targeting_setting

The targeting_setting block carries one target_restriction block per dimension you want to pin down — see Targeting, or just watching above. It takes no attributes of its own.

target_restriction

Required
  • targeting_dimension ( String ) One of: AUDIENCE, TOPIC, GENDER, AGE_RANGE, PLACEMENT, PARENTAL_STATUS, INCOME_RANGE .
  • bid_only ( Boolean )

KEYWORD is deliberately absent from targeting_dimension: keywords always restrict, so Google Ads has no observation mode for them.

ai_max_ad_group_setting

AI Max is switched on at the campaign — see ai_max_setting — but which queries it is allowed to broaden into is decided per ad group. This block is where an ad group opts out of that while the rest of the campaign keeps it:

resource "google_ads_ad_group" "brand" {
name = "Brand terms"
campaign = google_ads_campaign.brand_search.id
type = "SEARCH_STANDARD"
ai_max_ad_group_setting {
disable_search_term_matching = true
}
}

Optional

  • disable_search_term_matching ( Boolean )

Leave the block out and the ad group follows whatever the account is doing. Write it, and someone changing it in the Google Ads UI shows up as drift on the next plan.

audience_setting

Whether the ad group is targeted through a google_ads_audience — one reusable definition holding segments and demographics together — rather than through segment criteria of its own. Demand Gen ad groups work this way:

resource "google_ads_ad_group" "home_battery" {
name = "Home battery"
campaign = google_ads_campaign.demand_gen.id
audience_setting {
use_audience_grouped = true
}
}

Optional

  • use_audience_grouped ( Boolean )

demand_gen_ad_group_settings

Where a Demand Gen ad group’s ads may serve. By default Demand Gen delivers across YouTube, Gmail, Discover and the Display Network; this block is how the file narrows that — most often to “YouTube only, please”:

resource "google_ads_ad_group" "youtube_only" {
name = "Spring launch — YouTube only"
campaign = google_ads_campaign.demand_gen.id
demand_gen_ad_group_settings {
channel_controls {
selected_channels {
youtube_in_stream = true
youtube_in_feed = true
youtube_shorts = true
}
}
}
}

channel_controls takes exactly one of two forms:

  • channel_strategy — a named preset. "ALL_CHANNELS" is everywhere Demand Gen can serve; "ALL_OWNED_AND_OPERATED_CHANNELS" is everything Google-owned (YouTube, Gmail, Discover, Maps) with the Display Network off.
  • selected_channels — an explicit list. Turn on the channels you want; a channel you leave out (or set to false) is off. At least one has to be on.

validate rejects a block that declares both, one that declares neither, and a channel list with nothing switched on — Google would refuse all three. It also rejects the block on an ad group whose campaign is not DEMAND_GEN, because no other campaign type has anywhere to put it.

channel_controls

Optional
  • channel_strategy ( String ) One of: ALL_CHANNELS, ALL_OWNED_AND_OPERATED_CHANNELS .

Nested blocks documented below: selected_channels.

selected_channels
Optional
  • youtube_in_stream ( Boolean )
  • youtube_in_feed ( Boolean )
  • youtube_shorts ( Boolean )
  • gmail ( Boolean )
  • discover ( Boolean )
  • display ( Boolean )
  • maps ( Boolean )

Unlike audience_setting, this one is an ordinary update: declare it on an existing ad group and the next apply narrows it in place, no recreation needed. Someone widening the channels in the Google Ads UI shows up as drift on the next plan.

See also