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 block | Ad 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_settingleaves 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 = falseis 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
validatewarns when a file does that — a refused operation takes the wholeapplydown with it.
Schema
Required
-
name( String ) -
campaign( Reference togoogle_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 tofalse) 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
google_ads_campaign— required parent.google_ads_ad_group_ad— at least one ad per group.google_ads_ad_group_criterion— keywords in this group.google_ads_audience— the grouped audienceaudience_settingpoints this ad group at.