Most people can stand up their first canary in an afternoon. That’s not the hard part. For example, you can create an AWS S3 bucket named something that looks worth stealing, drop a realistic file in it, and wire an alert to any GetObject or ListObjects call against it. There is one step the short version of this walkthrough usually leaves out, and it is the step that decides whether the canary works at all: both of those calls are CloudTrail data events, and trails do not log data events by default. You have to add a data event selector covering the canary bucket before anything reaches your trail. Do that and you have a working tripwire. Skip it and you have a decoy nobody is watching.
The bucket itself costs almost nothing to hold, but the detection is not free. CloudTrail bills data events at $0.10 per 100,000 delivered, and how much you pay depends entirely on how tightly you scope the selector. Scoped to the canary bucket, you pay for the handful of events the canary produces. Scoped to the whole account to save yourself the per-bucket work, you pay for every object read in it, forever. That trade-off comes back when we get to designing believable canaries, because it is also a design decision about how easy your decoys are to enumerate. No employee will touch the canary because it is not meaningfully part of any project, and the first time it fires you will get a genuinely useful signal. Even the occasional employee opening it, in extreme cases could indicate an insider threat. A tiny bit of work for a truly high value signal, even if it comes with an occasional false positive.
But that’s a single canary, in a single location, that requires someone who’s compromised your system to navigate directly to it. It really requires the absolute perfect storm against the attacker, so in practice for modern cloud environments, it’s just not going to be good enough. It’s better than nothing, yes… but barely.
So while that afternoon of setting up your first canary is not the hard part, there is a significant gap between one good canary and a deception strategy that covers a real modern cloud environment. That cloud estate like has hundreds of accounts, more than one cloud provider, a CI/CD system that touches all of it, a Kubernetes fleet, an identity provider that fans out to a hundred SaaS apps, and an engineering org that ships changes to varying part of the above every hour of every day. Designing deception for that environment is a very different kind of problem.
Before building your own deception technology system or looking for a new vendor, we recommend you take some time to develop your deception technology strategy. While our aim is for this to be quite encompassing, we encourage you to use this as a starting point. Use the parts that work for your company, without over engineering the plan. By the end of this you should be able to name your crown jewels, map the front doors to them, sketch the perimeter, decide what a believable decoy looks like for each resource type you hold, and describe how you would keep all of it fresh and owned over time. If you finish this section and think you can run this yourself, that is a great outcome. Or if you conclude you would rather someone else carry the ongoing weight, that is also a great outcome. Either way you will know what the work is.
Designing believable canaries
In deception technology, believability is the entire game. A decoy exists to be intentionally found, examined, and acted upon by an intruder who has already gained some foothold and is now looking around to decide what is worth taking. That intruder is not stupid and your environment is likely not the first real production environment they’ve seen. They know what a genuine access key looks like when it sits next to a genuine deployment script, and they are looking for anything that smells like a trap. A canary that is obviously fake gets skipped, and a skipped canary is worse than no canary because it consumed effort and produced nothing. Even worse, it could give you a false sense of security if you’re relying on a single canary being tripped.
So the first design principle is that a believable canary has to be indistinguishable from the real thing at the moment of discovery, and it has to be discoverable through the same paths an attacker would actually walk. A secret buried where nobody, including an attacker, would ever look is not a canary. It’s just noise. The second principle is that believability is contextual. The same access key is convincing in one place and absurd in another. A long-term IAM access key sitting in a .env file on a developer laptop is entirely plausible. The same key pasted into a Slack channel called #random is not, because nobody leaks their real production keys there either, and an attacker knows it.
Work through your environment resource type by resource type, using the following guide as a starting path for consideration. This list is not exhaustive but instead illustrative on how to think about different resource types. For each one there is a short list of things that make a decoy convincing and a shorter, sharper list of things that give it away.
Before that, one piece of plumbing that decides whether any of it works. Believability determines whether an attacker touches your canary. Logging determines whether you find out. In AWS those are separate problems, and they are not consistent across resource types. Reads against object and item data are data events, which no trail logs unless you turn them on and pay per event. Reads against secrets and parameters are management events, which every trail logs for free. Kubernetes is not CloudTrail at all. Work out which column each of your canary types sits in before you build it:
Two consequences worth sitting with. First, the credential and secret canaries are the cheap ones, because their touches are management events you are already paying nothing for. The S3 and DynamoDB canaries, the two most people build first, are the ones that carry a recurring bill. Second, the scope of your data event selectors is itself a disclosure. If you enable data events only on your canary buckets to keep the cost down, then the trail configuration is a list of your decoys, readable by anyone who can call GetEventSelectors or GetTrail. Anyone who has already compromised an identity with read access to your logging configuration has read access to that list. You get to pick which one you would rather live with: account-wide data events at real cost and no bait map, or selective logging that is cheap and enumerable. There is no third option, and no vendor removes the choice. It is worth deciding deliberately rather than discovering it later.
S3 buckets
A believable bucket looks like it belongs to a team and holds something worth taking. The name follows your real naming convention, whatever that is: acme-prod-billing-exports, acme-analytics-pii-us-east-1. It carries the tags your other buckets carry, sits in a region you actually use, and has a creation date that is not last Tuesday. It sits next to your real production buckets and at a glance would contextually make sense to be there. The bucket itself contains objects with realistic names and of non-trivial size, so a ListObjects returns something that reads like a real data store rather than a single flag.txt. Ideally the objects themselves are plausible on inspection as well: a CSV with realistic headers and synthetic rows, not a 3-byte placeholder.
What gives it away: a bucket named honeypot-do-not-touch, a policy that is either wide open or comically locked down relative to your norm, a single object, a creation timestamp minutes old, zero access history so it looks untouched, and tags that none of your real buckets carry. Attackers read metadata, so make the metadata boring and consistent.
IAM roles and access keys
A convincing IAM canary derives its credibility from the identity around it. The role or user has a name that fits your convention (svc-billing-reconciler, not test-role-1), a permissions boundary or policy set that looks like a real service identity, and a plausible age. Access keys are most believable where credentials actually leak in the real world: in a config file on an instance, in a CI variable, in a container image layer, or in a script in a repository. A key found in one of those places, attached to a user whose name implies it can do something useful, is exactly what an attacker hopes to find. In fact, many of the most infamous compromises came from credentials laying around in the open.
What gives it away: an access key attached to a user with no other trace of existence, a role assumable by everyone or by nobody, a key that has literally never been used sitting in a place where real keys get rotated, and naming that screams test or demo. Also watch the story that the permissions tell. For example, a canary identity that appears to grant full AdministratorAccess and lives somewhere trivially reachable is the credential equivalent of a suitcase of cash left on a park bench. Sure some attackers take it, but the more dangerous ones are far more careful. Calibrate the apparent privilege to be attractive but not surreal, you want the canary to look like a human error.
There is also a real design decision here between long-term and short-term canary credentials. Short-term credentials that are created on demand (just in time privilege) and expire quickly, close their own window of misuse and blend better with modern practice. Meanwhile long-term keys are the more familiar shape of a leaked credential. The choice depends on what your real environment looks like, because the canary has to match its neighbourhood. If your environment operates with short-lived credentials, have the canary match instead of a single long-term credential standing out.
Secrets Manager and Parameter Store secrets
A believable secret is named for something that clearly matters and looks like it unlocks it: prod/stripe/api_key, rds/analytics/master_pwd. The stored value has the right shape for what it claims to be, so a GetSecretValue returns a string that passes a glance: a token with the right prefix and length and a connection string that parses, with rotation metadata and tags matching your real secrets.
What gives it away: a secret whose value is changeme or test123, a name that does not map to any service you appear to run, no rotation configuration when every real secret has one, and a resource policy inconsistent with your others. The tell here is usually the value. An attacker who retrieves the secret will look at it, so it has to survive being looked at. However, there is something to be said about a secret that does stick out as long as it, again, looks like it was left in human error.
DynamoDB tables
A convincing table has a name that implies valuable contents (user_sessions, payment_tokens, customer_accounts), a schema that matches that implication, and enough seeded items with realistic-looking attributes that a Scan or Query returns something worth exfiltrating. Check the table above before you build this one. Scan, Query, GetItem and BatchGetItem are DynamoDB data plane operations. Your control plane calls, CreateTable and DescribeTable, are logged by default, so the table's existence is visible in your trail from the moment you create it, which makes it easy to assume the reads are covered too. They are not. Until you add a data event selector for the table, the exact interaction you designed this canary to catch is the one interaction that leaves no trace. Capacity mode, tags, and stream configuration should match your real tables.
What gives it away: an empty table, a table with one item, attribute names that do not correspond to the promised contents, and a name that fits no product you appear to run. A Scan that returns nothing tells an attacker either that this is dead or that it is bait. With a seeded table you may also get the added benefit that an attacker believes it to be real, exfiltrates the canary database, and leaves your environment hoping it's undetected and has something truly valuable. Anything you can do to buy your security team time is valuable.
API tokens and repository tokens
These live in source and your repos. A believable one is a token with the correct provider format (a real-looking GitHub, GitLab, Stripe, or internal API token shape) committed into a repository in a place tokens genuinely leak: a config file, a test fixture, a CI definition, or an old commit. The surrounding code should make its presence plausible, so the token sits in something that looks like it once needed it.
What gives it away: a token in the wrong format for what it claims to be, a token in a README under a heading that might as well say “trust me this is totally real”, a repository that exists only to hold the token, and a commit history one entry deep. Attackers scan git history, so the token should look like an accident someone made two years ago, not a prop placed yesterday.
Kubernetes secrets
A believable Kubernetes canary is a Secret object in a namespace that looks operational, named for a real-sounding dependency (db-credentials, payments-api-key), mounted or referenced by something so it does not look orphaned, with a value that has the right shape. It should sit in the cluster the way real secrets do, reachable by the service accounts and RBAC paths an attacker would traverse after compromising a pod.
What gives it away: a secret in the default namespace with no owner, referenced by nothing, holding an obviously fake value, in a cluster where every other secret is managed by an external secrets operator. If your real secrets are all synced from Secrets Manager or Vault, a hand-created one stands out to anyone who knows what to look for.
The detection path for this one is different from every other canary in this section, and it is worth flagging before you build it. A kubectl get secret never reaches CloudTrail. It is recorded in the Kubernetes API server audit log, which on EKS is one of five control plane log types and is off by default: you enable audit per cluster, it delivers to CloudWatch Logs, and you pay CloudWatch ingestion and storage on the volume. That is a second detection pipeline with its own enablement, its own latency, its own retention and its own cost, running alongside your CloudTrail one. Worth building, but budget for it as a separate piece of work rather than assuming it falls out of what you already have. Credentials pulled from a container or its orchestrator are a named technique (T1552.007), so the canary is well placed. It just needs its own wiring.
By now, you should have noticed a few patterns as examples for our earlier statement. Believability of a canary is achieved when it's indistinguishable within the context of your unique environment. The decoy has to match your conventions, your metadata, your value shapes, and your operational norms, and it has to sit on a path an attacker would actually walk. Get either half wrong and the canary either gets skipped or gets spotted. Modern cloud environments do make this difficult, because your environment is often complex and changing. A canary that doesn’t change with your environment, sticks out like a sore thumb.
When it comes time to deploy your canaries, use this as your own “believability matrix.” Score each resource type as you deploy it. Anything you cannot tick is a tell you are shipping to the attacker. Aim to tick every applicable row before you deploy that type.
Exercise: The over-the-shoulder test
Take one canary design for each resource type you hold. For each, write down the exact API response an attacker would see at the moment of discovery: the ListObjects output, the GetSecretValue string, the Scan result, and the token as it appears in the file. Now read each response as if you were the intruder who just found it. Would you take it, skip it, or get suspicious? Anything that reads as skip or suspicious goes back to the drawing board before it ships.
Coverage across the surface
Once you can build a believable canary, the next question is where they go and how many. Now is when we think of deception as a strategy, instead of a one off clever trick for catching a careless attacker. The value of the whole system comes from putting the right decoys in the right places at the right density. Too sparse and an attacker walks straight past the tripwires, but too dense and you drown in maintenance and start tripping your own alerts. The last thing we want is a “canary who cried wolf” situation.
The model that works is to think in three rings, working from the inside out. Start with what you most need to protect, then the paths into it, then the outer edge where intrusions begin.
Ring 1: Crown jewels - Your most sensitive assets
Begin with where your most sensitive information actually lives. A sometimes less than fun note: actually go document where it lives, not where you think it should live (pressure testing that reality might have you thanking us later). You want your most sensitive assets like credentials, customer data, financial records, HR data, employee records (so social engineering doesn’t become the next step), source code, encryption keys, and the production database just to name a few. These are the places where you would need to know immediately and without ambiguity if someone reached them. They are detrimental to your business moving forward if compromised. Crown-jewel canaries go directly alongside the real assets, so that any interaction with the decoy is a near-certain sign that someone has reached a place they should never be. Density here is high and tolerance for false negatives is near zero. If an attacker gets to your billing data store, there should be a decoy in that store, in that account, that they cannot avoid tripping on the way to the real thing. Don’t forget it must be believable.
Ring 2: Front doors - the pivot points and attacker paths
A layer out from the crown jewels are the front doors: the places from which privilege would escalate toward the crown jewels. These are the places once your environment has already been compromised and attackers are trying to get to your most sensitive assets, such as the CI/CD pipeline that can deploy to production, the bastion or jump host, the admin roles and the identity provider, the service accounts with broad standing access, or the secrets stores that hold the keys to everything else. An attacker who has gained a foothold in your environment rarely lands directly on the data they’d like to exfiltrate. They land somewhere adjacent and then pivot deeper. Front-door canaries are placed on those pivot paths: a decoy admin credential in the CI system, a fake privileged role reachable from a compromised workstation, or a planted secret in the vault that looks like it unlocks production. The goal is to catch the attacker in the act of escalating.
This is a good time to remind you that Deception Technology is approached from an assume breach standpoint. Some of the best companies with the most well funded security teams have been compromised, many of them simply due to human error. Most security systems were not designed to catch human errors, like leaving a hardcoded password in the repo. So we want to give ourselves as much opportunity as possible to catch attackers at every step of their journey.
Ring 3: Perimeter - the first entry point and earliest signal
The outer ring is where attackers gain initial access. This is often where human error shows up the most, such as developer workstations and laptops, public-facing repositories, or SaaS applications reachable with a phished credential. Canaries here are the early-warning layer and give your teams the biggest breadth of time to stop an attack. The perimeter is where you might place a decoy credential on a laptop, a planted token in a public repo, or a fake set of API keys in a SaaS app that an attacker would find while establishing their foothold. Perimeter canaries fire earliest in the intrusion, when the attacker has the least context and is most likely to grab whatever looks useful. They are also the noisiest ring, because the perimeter is where legitimate scanning and automation live, so placement here demands care about self-inflicted noise.
The perimeter is also where attackers are likely to be the sloppiest, doing broad scanning of machines or open systems they discover connected to the public domain. They have nothing to lose yet, so they’re less careful about the noise they’re making.
The three rings are a prioritization order. You fill Ring 1 first because a missed crown-jewel intrusion is the worst outcome. You fill Ring 2 next because it buys you time and catches the pivot. You fill Ring 3 to get the earliest possible warning. The output of the exercise is an inventory map of your environment categorised by criticality, which becomes the backbone of your deployment plan and the thing you review every quarter.
Notice, as you fill it in, how large the surface actually is. Crown jewels are spread across many accounts. Front doors run through CI/CD, identity, and every privileged role. The perimeter includes every laptop and every SaaS tenant. Coverage that is meaningful spans resource types, hundreds of accounts, more than one cloud, and reaches onto endpoints and into SaaS. As a starting point, it’s okay to list things in bulk like “Engineering laptops x 200” just remember that your actual Deception Technology rollout must account for all 200.
This is your core downloadable artifact for the section. Fill one row per asset or asset group, working outward from Ring 1. The final columns turn the map into a deployment backlog.
How to read the density column: Ring 1 wants near-total coverage of the real crown-jewel locations, because you cannot afford a gap where the real asset sits but no decoy does. Ring 2 wants coverage of every distinct pivot path rather than every host. Ring 3 wants breadth over depth, since the point is early warning and a light, wide scatter catches the opportunistic grab.
Exercise: Name your five crown jewels
Before you fill in the whole worksheet, do the fast version. List your five highest-value data stores. For each, list the IAM principals and human roles with standing access to it. Then, for each principal, ask how an attacker would come to control it. That chain, from asset back to principal back to entry point, is your first three rows of Ring 1 and the front doors that feed them. Most teams find that the same two or three principals show up against every crown jewel, which tells you exactly where your first front-door canaries belong.
Exercise: Walk the intrusion
Pick one realistic starting point: a phished developer laptop. Trace the path an attacker would take from that laptop to your single most sensitive asset, naming each hop: laptop, to stored AWS credential, to assumed role, to CI access, to deploy role, to production data. At every hop, ask whether a decoy sits on that path such that the attacker would trip it before completing the hop. Every hop without a canary is a blind spot. This exercise usually finds two or three, and those become P1 rows on the map.
Deploying at scale
Building a single canary is extremely easy, and we’ll actually show you how to do that later in this guide. However, a thousand canaries, placed consistently across four hundred accounts and two cloud providers, kept identical in shape but individually plausible, is an infrastructure engineering problem.
The old model of deception was hardware and hand placement, the era of the network oriented honeypots. Someone stood up a decoy host, configured it by hand, and physically or manually situated it on the network. That approach does not survive contact with a modern cloud estate. You cannot hand-place a decoy in four hundred accounts. You cannot manually keep them consistent. You cannot click your way to coverage that keeps pace with an environment defined in code and changed continuously. Cloud deception has to be built the way the rest of the cloud is built: API-driven, defined as code, and deployed through the same pipelines that deploy everything else.
In practice that means your canaries are Terraform or CloudFormation or Pulumi resources, parameterised by account and region, rolled out through the same multi-account tooling you use for guardrails and baseline configuration. It means a canary definition is a module you can instantiate five hundred times with per-instance variation so that each one is individually believable while the fleet is centrally managed. It means placement is driven by your coverage map, encoded so that when a new account is created it inherits its canaries automatically, the way it inherits logging and IAM baselines. A canary that has to be remembered and placed by hand in each new account will be missing from half of them within a year.
The scale problem has a second edge, which is per-instance uniqueness. If all five hundred of your S3 canaries are byte-identical, an attacker who spots one in one account can fingerprint the rest across your whole estate, and worse, so can anyone who finds your public deployment module. Believable at scale means centrally defined but individually varied: different names within a convention, different seeded contents, different plausible ages. That tension, between centralized consistency and per-instance uniqueness, is most of the engineering difficulty.
There is a subtler version of the same problem, and it comes from the deployment method itself. Infrastructure as code leaves provenance on the resources it creates. CloudFormation writes aws:cloudformation:stack-name and aws:cloudformation:logical-id onto every resource in the stack, and most Terraform conventions attach a module or workspace tag that does the same job. Vary the names, contents and ages of five hundred canaries as carefully as you like: if they all carry a stack tag naming the stack that made them, a single tag query returns every decoy in the estate, sorted. Your deployment tooling has to be as deliberate about what it stamps on a canary as it is about what it puts inside one, which usually means the canary module deploys from a stack whose name fits your ordinary infrastructure naming and whose tags are indistinguishable from the tags on everything else you deploy. It is a small detail that undoes a lot of careful work when it is missed.
Exercise: The new-account test
Ask how a canary gets into an account created tomorrow. If the answer is that someone has to remember to add it, you have a manual process that will drift over time making your overall deception posture weaker. Write down the mechanism that would place canaries automatically in a brand-new account: the module, the pipeline stage, the account-factory hook. If you cannot name it, that is the gap between where you are and coverage that holds.