Skip to main content
guide20 min read

What Actually Stops Cloud Spend: The Hard-Cap Inventory for AWS, GCP and Azure

CloudCostChefs TeamCloudCostChefs Team
Blaze
Blaze says:Go through every budget you own and write down two things: whether an action is attached, and whether that action fires automatically or waits for a human. A budget with no action is a notification. Label it as one in whatever document finance is reading.

A number was entered into a form, and the form accepted it.

Someone in finance has told you the cloud spend is capped. They believe it because of that form. Your job this week is to find out what the number actually does.

Usually it does one of three things. It sends an email. It sends an email and applies a policy that blocks some subset of new actions. Or it is a credit balance on a non-production account and it genuinely stops everything, which is why the person who set it thinks caps work.

The useful question is not whether cloud spending limits exist. They do. The useful question is much narrower, and once you ask it in the narrow form the answer across all three providers comes out the same way: none of the three documents an account-wide, customer-chosen, dollar-denominated hard stop on pay-as-you-go usage. What they document instead is service-scoped, inconsistently denominated, and in several cases described by the vendor itself as leaky.

That is not a conspiracy. It is an architectural consequence of billing telemetry arriving after the resource has already run, and both AWS and Google Cloud say so on the page where they describe the control.

Four conditions, and a control has to meet all four

To be the thing finance thinks it bought, a control has to clear every one of these:

1. Account-wide

It covers the whole account or subscription, not one service.

2. Customer-chosen

You pick the number.

3. Dollar-denominated

The number is money, not gigabytes or bytes processed.

4. Enforcing

Reaching it stops the spend rather than describing it.

Read the rest of this with those four in hand. Most controls score two or three. Nothing in this post scores four, and the reason is always the same one: the controls that genuinely stop something are scoped to a single service, which fails the first condition before you get to the other three.

AWS: notifications by default, enforcement only if you wire it

Start with the thing everybody has already turned on. AWS Budgets tracks cost and usage against a threshold you set and notifies you. That is the default posture, and the default posture does not stop anything.

The refresh cadence is the first number worth writing down

AWS Budgets information updates up to three times a day, and updates typically land 8 to 12 hours after the previous one. On the same page, AWS adds the note that matters more than the cadence does: there is a delay between incurring a charge and receiving the notification, because resource usage is billed after it happens. You can blow past your notification threshold before Budgets can tell you, and your actual cost can keep climbing after the alert lands.

Take that seriously as an operational number rather than a footnote. A runaway launched at 02:00 against a budget refreshed at 04:00 and again at 14:00 is a runaway you learn about on the second refresh at the earliest. Nothing in the alerting path shortens that, because the lag is in the billing pipeline and not in the alarm.

To be precise about what Budgets is and is not: AWS describes Budgets as a way to track and take action, and its own examples include automatically applying a custom IAM deny policy at a threshold. So the correct statement is not that Budgets does not stop spend. Budgets notifications do not stop spend. Enforcement is a separate thing called Budget Actions, and it does not exist until you configure it.

What Budget Actions can actually do

Three action types, and the list is short enough to memorise:

Apply an IAM policy.
Attach a service control policy. Only a management account can do this.
Target specific EC2 or RDS instances.

Cite that list from Configuring a budget action rather than from the Budget Actions overview. The overview frames the same three as actions that are available to you, which reads like a sample. The configuration page presents them as the set you choose from, which is what you need if you are going to tell finance the list is closed.

The most common false pass

You can attach more than one action to a single alert. Each action either runs automatically when the threshold is crossed or waits for you to run it by hand from the alert details page, and which of those you get is a yes-or-no question you answer per action at configuration time. Automatic is not the default state of the world. An action that exists is not necessarily an action that fires, which is the single biggest gap between what a budget looks like in a screenshot and what it does at 02:00.

From a management account you can apply an SCP to another account, but you cannot target EC2 or RDS instances in another account.

That is the documented action list. AWS documents no account-wide halt among them, and the shape of the list tells you why the ones that exist do not add up to one. An IAM or SCP deny is a permissions control, so it blocks the creation of new things while leaving everything already running exactly as expensive as it was a minute ago. Stopping instances is a compute control, so it touches EC2 and RDS and nothing else. Neither one touches the categories that generate spend without anybody creating a resource: data transfer, request charges, storage that keeps existing, managed-service consumption already in flight.

Reversal is a manual operation

The guide documents undoing a completed action from the action history table, with a reversed state meaning Budgets stops evaluating that action for the rest of the budgeted period, and a reset control to make Budgets evaluate it again inside the same period. What the documentation describes is a human going in and clicking. Plan the un-break as a runbook step, not as something that happens on the first of the month.

Enforcement costs money and alerting does not

Monitoring and notifications carry no charge. Your first two action-enabled budgets per month are free regardless of how many actions you configure inside them. Each additional action-enabled budget costs $0.10 per day, and budget reports delivered by email cost $0.01 each.

Assume a mature estate with ten action-enabled budgets in a 30-day month: eight chargeable budgets at $0.10 per day is $24 for the month. That is a rounding error against most cloud bills, and it is also the reason a lot of estates have forty alert-only budgets and two enforcing ones. The free path and the safe path are not the same path.

Google Cloud: the cap is a pipeline you build and own

Google Cloud does not ship a spend cap you switch on. It ships a tutorial for building one, and the tutorial states the consequences clearly enough that most of this section is just its own warnings.

The architecture: a Cloud Billing budget scoped to a single project publishes programmatic notifications to a Pub/Sub topic. A function subscribed to that topic compares the reported cost against the budget amount, and if cost has exceeded budget it calls updateProjectBillingInfo with the billing account name set to an empty string. That unlinks the project from its billing account, which shuts down the project.

Note the two words that do the work: project and shuts down. This is not a subscription-wide dollar cap. It is a project-scoped kill switch, and its granularity is binary. There is no setting between running and detached.

Google puts the consequences on the same page. Every one belongs in your risk register before anybody deploys this.

  • Removing billing shuts down all resources in the project, and resources might be irretrievably deleted.
  • You can turn billing back on, but it takes manual configuration and there is no guarantee of service recovery.
  • Because costs are incurred before budget notifications arrive, following the tutorial does not guarantee you stay under your budget. Google's own mitigation is to set the maximum budget below your available funds to absorb the billing delay.
  • You cannot disable billing on a project locked to a billing account.
  • After a successful run, the project's resources are disabled including the function itself if it lives in the same project. The automation destroys its own runtime.

One detail worth having in front of you before your first deploy: the Python sample ships with a simulation flag set to true, with a developer note to flip it off only after validating against a test budget. The Node.js sample has no such guard and disables billing on the spot. If someone on your team copied the Node sample into a production project and wired it to a real budget, the first genuine threshold crossing is also the first live test.

That flag is the tell. The vendor shipping the sample built a safety catch into it because the sample is dangerous, and the safety catch means the thing you deployed may not do anything at all. Check which sample you are running and which state that flag is in. Both failure modes are real and they point in opposite directions.

This citation is also the one on the fastest drift in this post. The documentation host moved to docs.cloud.google.com and the function runtime already moved onto Cloud Run. Pin your access date and re-read the limitations section before you rely on any of it.

Azure: a real built-in limit, unavailable on the subscriptions you probably hold

Azure is the only one of the three with a built-in, default-on subscription spending limit, and the honest version of that sentence has a second half.

From Microsoft's Azure spending limit doc: the limit is on by default for the Azure free account and for subscription types that include credits across multiple months. When usage exhausts it, the services you deployed are disabled for the rest of the billing period. Where the subscription includes credits across multiple months, it re-enables automatically at the start of the next billing period.

Now the constraints, all from the same page.

It is not available for subscriptions with commitment plans or with pay-as-you-go pricing

On those, the spending limit does not appear in the portal and you cannot enable one. That is the enterprise estate. The control exists precisely where the stakes are lowest.

You cannot choose the number

The spending limit equals the credit amount. The page's own example is a free account at USD 200 that you cannot change to USD 500, and a separate section states plainly that custom spending limits are not available. So condition two of the four fails outright, and condition three passes only by coincidence, because the currency is inherited from the credit rather than chosen by you.

The page hedges its own scope in the opening paragraph

It says the limit prevents most kinds of spending rather than all of it. Further down, under a heading stating that a spending limit does not prevent all charges, Azure lists the exceptions: some external services published in the Azure Marketplace cannot draw on subscription credits and bill separately even with the limit set. The examples given include Visual Studio licences, Microsoft Entra ID P1 and P2, support plans, and most third-party branded services.

On what breaking looks like: the page's illustration is the free account, where exhausting the included credit removes deployed resources from production, stops and deallocates virtual machines, and leaves storage account data readable but not writable. It repeats the deallocation warning in its guidance on why you might want to remove the limit, along with a note that settings such as virtual IP addresses are lost when resources are deallocated. Treat that as the shape of the outage, and treat the specific behaviour on your own subscription type as something to confirm rather than assume.

The subscription-type question is delegated. The spending-limit page points to Azure's offer details page for the authoritative per-offer availability list, and that is where you check your own offer number rather than reasoning from the free-account example.

The configurable Azure path is a runbook you build and own

The built-in limit is not the whole Azure story, and stopping there would leave a false asymmetry in place: AWS gets Budget Actions, Google Cloud gets a disable-billing pipeline, and Azure appears to offer nothing you can configure on the subscriptions that matter. It does. Microsoft documents the analogue in a tutorial called Azure billing and cost management budget scenario, and it is the closest thing Azure has to enforcement on a subscription with no built-in limit available.

The pipeline: a Cost Management budget scoped to the subscription notifies an Azure Monitor action group, the action group triggers a Logic App, and the Logic App parses the threshold amount out of the notification payload and calls one of two webhooks on an Azure Automation runbook imported from the gallery. At the 80 percent threshold the webhook carries a resource group name, so it stops the virtual machines in that one group. At 100 percent the second webhook carries no parameters and stops all remaining virtual machines in the subscription. The tutorial creates the budget itself through the Consumption REST API.

Score that against the four conditions and it lands almost exactly where AWS Budget Actions land. Customer-chosen and dollar-denominated, yes. Account-wide, no: what it stops is virtual machines, so storage, databases, egress and every managed service go on billing at the same rate they did a minute earlier. Enforcing, only in the sense that stopping compute reduces future spend rather than capping the period. And it inherits the latency of whatever fires it, because the trigger is a budget notification with the same reporting lag as everything else in this post.

Two practical notes before anybody builds from that page. The whole thing is four Azure resources deep, an Automation account, a runbook, a Logic App and an action group, all of which you then own, patch and pay for. And its setup step still walks you through creating an Automation Run As account, so check that against your current Automation account guidance rather than following it literally.

The exceptions that actually exist

Here is where the sweeping version of this argument falls apart, and it is worth being precise because the sweeping version is what most write-ups on this topic publish. Customer-set enforcing limits do exist. They all fail the same condition, the first one, because every one of them is scoped to a single service. Where they differ is which of the other three they also miss.

Treat what follows as the set worth knowing rather than a closed list. One of these shipped in November 2025 and another lives on a page almost nobody opens, which is the practical reason not to publish a count and then have to defend it.

AWS End User Messaging SMS enforced spend limit

This is the closest thing to the control everyone assumes exists. Each messaging channel has an account limit, the maximum in US dollars you can spend per month on that channel, and separately an optional enforced limit you set anywhere from $1 up to the account limit. Reach the enforced limit and AWS stops sending your messages until you raise it. The documented example: account limit $100, enforced limit $50, sending stops at $50. You can change it from the console or the CLI without contacting Support. Account-wide, no. Customer-chosen, dollar-denominated and enforcing, yes on all three. Anyone claiming that no AWS spend control lets you pick a dollar figure and stop at it has not read that page.

AWS End User Messaging Notify enforced spend limit

Same mechanic, separate meter, its own page, and easy to miss precisely because it looks like the one above. Notify has a dedicated monthly spend limit that is separate from your SMS, MMS and voice limits, and messages sent through Notify do not count against those. The structure repeats: an account limit in US dollars, plus an optional enforced limit you set anywhere from $1 up to it. Reach the enforced limit and SendNotifyTextMessage and SendNotifyVoiceMessage return a ServiceQuotaExceededException. One detail that changes the number you should set: the Notify limit counts the messaging channel fee and the Notify service fee together, so it is not the figure you would derive from a per-message rate card. Worth listing separately because a team that set an SMS enforced limit and assumed it covered their notification traffic has two meters and one control.

BigQuery custom quotas

An administrator sets a daily ceiling on data processed. Exceed it and queries return a usage-quota-exceeded error, and when the project-level quota goes, BigQuery stops working for everyone in that project for the rest of the day, individual headroom notwithstanding. Quotas reset at midnight Pacific. The project default is 200 TiB per project per day and the per-user default is unlimited. Two failures here beyond the scope one, both from Google's own custom-quotas page. It is denominated in bytes rather than dollars, and Google states that custom quotas are approximate and not designed to strictly limit bytes processed, conceding that BigQuery might occasionally run a query that exceeds a quota. It also applies only to the on-demand query pricing model, so it does nothing on a reservation.

Azure Monitor Log Analytics daily cap

You set a volume in GB per day, and reaching it stops collection of billable data for tables in the Analytics and Basic table plans for the rest of a 24-hour period. Auxiliary-plan tables are not subject to any daily cap. The reset hour differs per workspace and cannot be configured.

Read the daily-cap page itself rather than a summary of it, because it is the vendor documenting that its own hard cap leaks. The note under the mechanics states that the cap cannot stop data collection precisely at the level you set, that some excess data is expected, that the overshoot can be particularly large when the workspace is taking data at high rates, and that data collected above the cap is still billed. Microsoft also warns against using the daily cap as a routine cost-control tool at all, on the grounds that once collection stops you have no monitoring, no alerts, and no record of events you may need later. That is the whole argument of this post in one vendor note: a real cap, a real overshoot, a real bill for the overshoot, and a real operational cost for using it.

Amazon Braket per-device spending limits

Announced in November 2025, so newer than most write-ups on this topic. You define maximum spending thresholds per device, and tasks that would exceed the remaining budget are rejected before creation, which is a genuinely preventive control rather than a reactive one. It applies only to on-demand tasks on quantum processing units, and explicitly not to simulators, notebook instances, hybrid jobs, or tasks created during a Braket Direct reservation. The feature itself carries no additional cost. The announcement names no currency, so do not describe this one as dollar-denominated until you have confirmed that in the console.

The pattern across all of them

The enforcing controls live where the vendor can evaluate cost per unit of work before doing the work. Per message. Per query. Per ingestion batch. Per task submission. Nothing evaluates an account total in real time, because the account total is assembled downstream of the thing you want to prevent.

Credit walls are hard stops. Pay-as-you-go gets alerts.

The clean way to see the boundary is to compare the two account postures side by side, because both AWS and Azure enforce hard on credits and alert soft on pay-as-you-go.

Since the July 2025 Free Tier change, AWS accounts choose between a free account plan and a paid account plan at sign-up. As of August 2026, new customers receive USD 100 in credits on sign-up regardless of plan, and can earn up to another USD 100 by completing activities, for up to USD 200 total. The free account plan runs for up to six months and AWS documents that you will not incur charges during that period until you upgrade to a paid plan. The plan ends after six months or when credits are fully used, whichever comes first. Free account plans also exclude access to services that could rapidly deplete the credits, and they convert automatically to paid if you do things like join AWS Organizations or set up a Control Tower landing zone.

Those credit figures and that duration are promotional, so treat them as the most perishable numbers in this post.

That is a genuine hard stop. It is also unavailable to you, because the mechanism is a credit balance running out rather than a limit you configured, and the enforcement AWS can offer on it is closing the account. Azure's built-in limit works the same way: the limit is the credit, and the credit is the limit. Where money is prepaid, the vendor can stop at zero because there is nothing to bill against. Where the payment method is a card with no ceiling of its own, the meter runs and the controls describe it.

Run the enforcement inventory this week

Do not go into the next vendor review with an assumption. Go in with a one-page list of what keeps billing past every control you have wired.

1. Separate your alerts from your enforcement

For every budget in every account, write down whether it has an action attached, what that action is, and whether it runs automatically or waits for a human. A budget with no action is a notification, and should be labelled as one in whatever document finance is reading.

2. Read the policy, do not assume it

An attached IAM policy or SCP tells you an action exists. It does not tell you what the action denies. Fetch the policy document and read the denied action strings. A deny that covers ec2:RunInstances and nothing else is a very different control from what its name implies, and the gap between the two is where the surprise bill lives.

3. Derive your own leak list from your own bill

The categories that keep billing past an IAM deny are specific to your estate, so pull your top billable usage types from Cost Explorer instead of copying a generic list. Whatever sits at the top and is not gated by resource creation is your real exposure. If you want this as a run rather than a spreadsheet afternoon, the companion tool for this post does exactly this pass read-only and outputs the gap list: AWS Spend Guardrail Line Check. Note that Cost Explorer API requests are billed at $0.01 each against your primary billing view, so the inventory is cheap but not free.

4. Confirm availability per subscription, not per vendor

For Azure, look up your actual offer number on the offer details page and confirm whether a spending limit is available on it. For Google Cloud, check whether the project you care about is locked to a billing account, because a locked project cannot have billing disabled and the entire tutorial pipeline is inert against it.

5. Time the response, not just the threshold

Given three Budgets refreshes a day at 8 to 12 hour spacing, work out the worst-case window between a runaway starting and your first signal. That number, not the threshold, is what determines your maximum billing-signal exposure, and it is the number finance has never been shown.

6. Find the automation that kills itself

If you run the Google Cloud disable-billing pattern, check whether the function lives in the project it is meant to protect, and check the state of the simulation flag. Then decide which failure you would rather have.

When this does not apply, and what it costs

If you are on committed spend, the cap conversation is the wrong conversation.

Enterprise Agreements, commitment plans and Savings Plans all mean money is already spent. Governance there is about consumption against a commitment you have made, not about a ceiling you can lower. Trying to bolt a hard stop onto a commitment structure produces the worst outcome available: a stopped service and a bill that arrives anyway.

Every enforcing control in this post breaks something on purpose.

That is what enforcement means. An IAM deny at 90 percent of forecast will block a legitimate deployment during an incident. A Log Analytics daily cap blinds your monitoring on the day you most need it, which is why Microsoft warns against routine use of it. Detaching a Google Cloud project's billing account can irretrievably delete resources and offers no guarantee of recovery when you reattach. If your control has never broken anything, either it has never fired or it does not enforce, and both of those are findings.

Alerting is the right default for production, and it has its own timing problem.

Say the first part plainly rather than treating it as a compromise: for most production estates the correct posture is anomaly detection plus a human with authority to act, because the failure mode of a false-positive kill switch on revenue-generating infrastructure costs more than the overspend it prevents.

Then be honest about the second part. AWS documents that after you create your monitors you might start receiving alerts within 24 hours, and only the individual-alert subscription notifies as soon as an anomaly is detected, which requires an SNS topic. The daily summary is generated at 00:00 UTC for the previous day, so AWS's own worked example has an anomaly detected at 04:30 landing in an inbox roughly twenty hours later. Anomaly detection wired to the email summary is a better signal than a budget threshold, not a faster one. Enforcement belongs where the blast radius is bounded: sandbox accounts, training environments, per-service limits like the SMS enforced limit where stopping the service is exactly the desired outcome.

A vendor-native control is usually the right answer, and it is not free of cost.

Budget Actions, the Azure spending limit and the daily cap are all first-party, well documented, and cheap. They also encode your cost policy in each provider's own primitives, which means a multi-cloud estate maintains the same policy three times in three shapes with three sets of exemptions, and none of it moves if you move. That is the trade, and it is worth making for most teams. Just make it knowingly.

The ceiling logic generalises past infrastructure.

The same enforcement-versus-notification gap runs through AI spend, where the unit is tokens instead of instance-hours and the ceiling has to sit in the application rather than the billing console. We worked that through in The Agent Tax. And if you are considering handing enforcement to automation rather than a human approver, the tradeoffs of policy-enforced action are in AI-Native FinOps.

Recheck this in six months.

The architectural claim is the durable part of this post. Three things underneath it are moving: the AWS Free Tier credit amounts and duration are promotional, the Google Cloud documentation just changed host and its function runtime already moved to Cloud Run, and the Budget Actions list could gain a fourth entry without anybody announcing it loudly. Every citation below carries an access date for that reason.

Chef's Pro Tip

The question to bring to the next vendor review is not what the cap is set to. It is which of your budgets has an action attached, and whether that action fires without a human. Those two facts separate a control from a screenshot, and only one of them is in the form finance filled in.

The Bottom Line

Across AWS, Google Cloud and Azure, no documented control clears all four conditions on pay-as-you-go. The enforcing ones are scoped to a single service, because that is the only place the vendor can price a unit of work before doing it. Everything account-wide is telemetry, and telemetry arrives after the money is spent.

So stop asking whether the spend is capped. Ask which budgets have an action, whether it fires on its own, what the policy actually denies, and what keeps billing after it fires. Four questions, one afternoon, and a list finance has never seen.

The cap finance thinks it bought is a notification with a number on it. Find out which of yours is more than that.

CloudCostChefs Team

CloudCostChefs Team

Democratizing FinOps and cloud cost optimization for everyone

#finops#aws#azure#google-cloud#aws-budgets#budget-actions#spending-limit#cost-governance#guardrails#cloud-cost-optimization