Installation guide
This guide is for the platform team installing Cloud Certainty TraceHound in their AWS account. The install is one CloudFormation stack (the hub). Every other account whose CloudWatch Logs TraceHound should reach gets a small role stack (the spoke). With IAM Identity Center or another SAML IdP, the hub takes two steps: deploy the stack, configure the SAML application, then update the stack. For the short version, see Getting started.
Before you start
- Region. Any Region with Amazon CloudWatch Logs, AWS Lambda (with function URLs and response
streaming), Amazon API Gateway (HTTP APIs), Amazon DynamoDB, Amplify Hosting, Amazon Cognito, AWS
License Manager, Amazon EventBridge and Amazon ECS on Fargate. The templates are always loaded from
the vendor's public bucket in us-east-1 (CloudFormation accepts a template URL from any Region),
and all product code comes from the container image. The hub's Region doesn't limit which Regions
you can read: list them in
LocalRegionsand in each connection. - Account. Deploy the hub in the account that subscribed on AWS Marketplace: the licence is delivered to that account through AWS License Manager. To run the hub in another account of your organisation, distribute the licence to it with a License Manager grant first.
- Permissions. You need permission to create the resources in the stack: IAM roles (two have fixed names), Lambda functions and a Lambda function URL, ECS, EC2 security groups, Cognito, API Gateway, Amplify, DynamoDB, S3, SNS, EventBridge, KMS and CloudWatch Logs.
- Service control policies. The live tail function is reached through a Lambda function URL with
auth type
NONE(the function checks every request itself, see What the stack runs). If an SCP or permissions boundary denies such URLs, ask for an exception for the<NamePrefix>-livetailfunction first. - Network. A VPC with at least one subnet with outbound HTTPS access, for the one-off installer task (see Networking). Nothing else runs in your VPC.
- Identity. Decide how users will sign in: IAM Identity Center, another SAML 2.0 IdP (Entra ID, Okta and others), an OIDC IdP, or Cognito-managed users. SAML IdPs other than IAM Identity Center, and OIDC, need the Team plan or above.
- Scope. Decide whether TraceHound should read every log group of an account (the default) or only those under some name prefixes, and whether it may change retention (see Log group access).
1. Subscribe
- Open TraceHound in AWS Marketplace and choose Continue to Subscribe. Choose the plan and the contract length (Team is the base plan; Business and Enterprise are add-ons to it) and accept the offer. AWS License Manager now holds your licence.
- Choose Continue to Configuration, select the fulfilment option and the Region, then open the CloudFormation launch link from the usage instructions.
2. Launch the hub stack
In the CloudFormation console, fill in the parameters (see the reference). Acknowledge "AWS CloudFormation might create IAM resources with custom names" and create the stack. Creation takes about 10 minutes.
The quick-create link, with the release version filled in:
https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?templateURL=https://cloudcertainty-tracehound-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/main.yaml&stackName=tracehoundFrom the AWS CLI (--region is the Region you deploy to):
aws cloudformation create-stack --region <your-region> --stack-name tracehound \
--template-url https://cloudcertainty-tracehound-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/main.yaml \
--parameters file://params.json \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAMparams.json lists the parameters as [{"ParameterKey": "VpcId", "ParameterValue": "vpc-..."}, ...].
What happens during creation
The product's code (the Lambda functions and the web app) ships inside the subscription-protected container image, not as public downloads. Early in stack creation:
- CloudFormation creates a private S3 bucket in your account (output
ArtifactsBucketName; all public access blocked, encrypted, TLS only). - The
ArtifactInstallcustom resource starts one Fargate task from the product image in yourInstallerSubnetIds. The task checks your AWS Marketplace licence in AWS License Manager, verifies every file of the code bundle against the SHA-256 checksums in the image, and copies it into the bucket under the release's version. It runs for about a minute at 0.25 vCPU and 512 MB, with a read-only root filesystem and a security group without inbound rules. - The Lambda functions and the web app are then created from that bucket.
If the installer task fails, stack creation fails and rolls back. The error message gives the task's
exit code and its log stream (log group /<NamePrefix>/installer); see
Troubleshooting.
What the stack runs
All of it is serverless and runs only when used, apart from two schedules:
| Resource | What it does |
|---|---|
API (<NamePrefix>-api) | The Lambda behind the HTTP API that the portal calls (29-second timeout) |
Live tail (<NamePrefix>-livetail) | Streams CloudWatch Logs Live Tail to the portal as Server-Sent Events. It is reached through a Lambda function URL with response streaming (output LiveTailUrl) |
Collector (<NamePrefix>-collector) | Records each log group's stored bytes and daily ingestion (see The collector). An EventBridge rule (<NamePrefix>-collect) starts it every hour; only one run at a time |
Update notifier (<NamePrefix>-update-notifier) | A daily read of the public release manifest (only with UpdateChecks = Enabled) |
| Portal deployer, installer | Run at install and at each upgrade only |
The API, live tail and collector functions share one role, the hub role
(<NamePrefix>-api-<Region>, output SpokeTrustedPrincipal), which reaches CloudWatch Logs only by
assuming the connection roles. The live tail function URL has no IAM authorisation, because browsers
can't sign requests to it; instead the function checks the user's TraceHound sign-in token itself
(signature, issuer, audience, token use and expiry), and applies the same licence, IP allowlist and
permission checks as the API. Cross-origin requests are answered only for the portal's own address.
See the security overview.
3. Connect your identity provider
IdentityProvider | What to do |
|---|---|
IAMIdentityCenter | Follow Identity providers. Create the app with the SamlAcsUrl and SamlAudienceUri outputs, then update the stack with SamlMetadataUrl. |
SAML | Team and above. Do the same in your IdP (Entra ID and Okta are covered in Identity providers). |
OIDC | Team and above. Put the client secret in Secrets Manager, then set OidcIssuer, OidcClientId and OidcClientSecretArn when you create the stack. Register <CognitoDomain>/oauth2/idpresponse as the redirect URI in your IdP. |
CognitoOnly | Nothing more to do. BootstrapAdminEmail receives a temporary password. Every user must register an authenticator app (TOTP MFA is required). |
Until SamlMetadataUrl is set, the portal falls back to Cognito sign-in, which has no users. Nobody
can sign in at that point, so the stack is safe in that state.
4. First sign-in
Open the PortalUrl output and sign in as BootstrapAdminEmail. The first account that signs in
with that (verified) email becomes the bootstrap administrator and stays bound to it (see the
admin guide).
The local connection to the hub's own account exists from the start. It uses the stack's local
role, which may do what LocalAccessLevel and LogGroupPrefixes allow, in the Regions of
LocalRegions. Administrators see every log group there. Other users see nothing until an
administrator grants them access (admin guide).
Choose Collect now on the Overview (or under Administration › Collector) to record the first storage and ingestion history straight away instead of within the hour.
Log group access
The local role and every spoke role are the upper limit of what TraceHound can do in an account. Every call runs with the credentials of the person who asked, narrowed to their grants, so the role must allow it and the grants decide who may use it.
| What | Parameter | Default | Notes |
|---|---|---|---|
| Access level | LocalAccessLevel (spoke: AccessLevel) | Read | Read: list log groups with their settings and sizes, read their CloudWatch metrics, list streams, tags, metric and subscription filters, anomaly detectors and the data protection policy's name, run Logs Insights queries, search events (FilterLogEvents, GetLogEvents), read field lists, and run Live Tail. Manage: Read plus PutRetentionPolicy and DeleteRetentionPolicy. Retention changes also need the Team plan and a grant with manage |
| Log groups | LogGroupPrefixes | empty | Comma-separated name prefixes, for example /aws/lambda/,/ecs/orders. When set, TraceHound may read events from, query, tail and change the retention of only the log groups whose names start with one of them. The names, sizes and metrics of every log group stay visible, so the inventory and cost pages still cover the whole account; the other log groups' events, streams, tags and filters are refused by AWS. Empty means every log group |
| Lambda functions | (automatic) | lambda:ListFunctions, names only, to find orphaned /aws/lambda/ log groups |
The roles never have logs:Unmask, so data masked by a data protection policy stays masked in
TraceHound. They have no IAM, CloudFormation, DynamoDB or S3 permissions, and their log group
permissions are limited to the role's own account. The local role is also explicitly denied
TraceHound's own artifacts bucket and retention changes of TraceHound's own log groups
(/aws/lambda/<NamePrefix>-* and /<NamePrefix>/* in the stack's Region), so TraceHound can't
change its own stack or shorten its own audit trail. That deny also covers your own Lambda functions
whose names start with <NamePrefix>-; choose another NamePrefix if you have such functions and
want to manage their retention from TraceHound.
Your SCPs, permissions boundaries and resource policies still apply on top: TraceHound adds restrictions and never removes yours.
The collector
CloudWatch reports a log group's stored bytes only as a value for now, so TraceHound keeps its own history. Every hour the collector looks at every connection and Region; for each one without a record for the current UTC day it:
- lists every log group (up to 20,000 per Region);
- reads yesterday's
IncomingBytesandIncomingLogEventsfor each log group from CloudWatch metrics (GetMetricData, 500 metrics a call). The first run in a Region reads the last 90 days instead, so ingestion history is there on day one; - writes one record for the day with each log group's stored bytes, bytes ingested and events.
Stored bytes history starts the day TraceHound is installed; backfilled days have ingestion only. History is kept for your plan's storage history days: 90 on Starter, 365 on Team, 730 on Business and 1,825 on Enterprise.
The collector runs as the hub role with the source identity tracehound-collector, with a session
that can only list log groups, read metrics and list Lambda functions. GetMetricData is billed per
metric requested ($0.01 per 1,000), so a Region with 2,000 log groups costs about $0.04 a day; the
90-day backfill costs about 90 times that, once. A failure in one Region is shown under
Administration › Collector and never stops the others; the next hourly run tries again.
5. Deploy the spoke role in other accounts
spoke-role.yaml creates one role, /tracehound/<RoleName>, that only the hub role (stack output
SpokeTrustedPrincipal) can assume, only with your external ID, and only with a source identity (a
user's email, or tracehound-collector). Its template URL is the stack output SpokeTemplateUrl:
https://cloudcertainty-tracehound-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/spoke-role.yaml| Parameter | Default | Meaning |
|---|---|---|
TrustedPrincipal | Required. The hub's SpokeTrustedPrincipal output (one role ARN) | |
ExternalId | Required. A secret of 16 to 128 characters (letters, digits and +=,.@:/-). Enter the same value when you add the connection in TraceHound | |
RoleName | tracehound-spoke | Role name; the role is created under the IAM path /tracehound/, the only path the hub may assume |
AccessLevel | Read | Read or Manage, as LocalAccessLevel |
LogGroupPrefixes | empty | As on the hub (Log group access) |
The role is the upper limit of what TraceHound can do in that account; grants narrow it per user or
group but never widen it. IAM is global and the role covers every Region of the account, so create
it once per account, in any Region. The stack's outputs are RoleArn (enter it in the
connection) and AccessLevel (choose the same level for the connection).
A single account
cat > spoke-params.json <<'EOF'
[
{"ParameterKey": "TrustedPrincipal", "ParameterValue": "<SpokeTrustedPrincipal output>"},
{"ParameterKey": "ExternalId", "ParameterValue": "<a secret of 16 to 128 characters>"},
{"ParameterKey": "AccessLevel", "ParameterValue": "Read"}
]
EOF
aws cloudformation create-stack --region eu-west-1 --stack-name tracehound-spoke \
--template-url https://cloudcertainty-tracehound-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/spoke-role.yaml \
--parameters file://spoke-params.json --capabilities CAPABILITY_NAMED_IAMEvery account of an AWS Organization (StackSets)
With service-managed StackSets, new accounts in the target OUs get the role automatically. Use one external ID for the whole StackSet, and enter that same external ID for every connection you create for those accounts.
# From the management account or a delegated StackSets administrator.
aws cloudformation create-stack-set --region us-east-1 --stack-set-name tracehound-spoke \
--template-url https://cloudcertainty-tracehound-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/spoke-role.yaml \
--parameters file://spoke-params.json \
--capabilities CAPABILITY_NAMED_IAM \
--permission-model SERVICE_MANAGED \
--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false
# One Region is enough: IAM is global and the role covers every Region.
aws cloudformation create-stack-instances --region us-east-1 --stack-set-name tracehound-spoke \
--deployment-targets OrganizationalUnitIds=ou-abcd-11111111 --regions us-east-1Service-managed StackSets don't deploy to the management account; use a normal stack there if
TraceHound should reach it. To change AccessLevel later, update the StackSet, then the access level
of the connections.
Add the connection
In Administration › Connections › Add connection, enter an id (used in links, it can't be
changed later), a name, the account ID, the access level, the spoke stack's RoleArn, the external
ID and the Regions. Choose Test: TraceHound assumes the role and lists the log groups in each
Region. See the admin guide.
Parameter reference
Hub (main.yaml)
| Parameter | Default | Description |
|---|---|---|
NamePrefix | tracehound | 2 to 20 lowercase letters, digits and hyphens, starting with a letter. It prefixes resource and role names, so it must be unique in the account and Region. Don't use aws, amazon or cognito: it forms part of the Cognito domain. |
TagOwner | platform-team | Value of the Owner tag on every taggable resource. |
TagCostCenter | none | Value of the CostCenter tag on every taggable resource. |
TagEnvironment | prod | Value of the Environment tag on every taggable resource. |
IdentityProvider | IAMIdentityCenter | IAMIdentityCenter, SAML, OIDC or CognitoOnly. SAML (other than IAM Identity Center) and OIDC need the Team plan or above. See Identity providers. |
SamlMetadataUrl | empty | The metadata URL of your SAML application (IAMIdentityCenter or SAML only). Leave it empty on the first deployment. |
OidcIssuer | empty | OIDC only, required: the issuer URL. |
OidcClientId | empty | OIDC only, required: the client ID. |
OidcClientSecretArn | empty | OIDC only, required: the ARN of a Secrets Manager secret whose value is the client secret. CloudFormation resolves it at deploy time. |
OidcEmailVerifiedClaim | email_verified | OIDC only: the IdP claim that says the email is verified. Use xms_edov for Microsoft Entra ID. |
BootstrapAdminEmail | The first administrator. With CognitoOnly the user is created and receives a temporary password by email. | |
LocalAccessLevel | Read | The most TraceHound may do in this account, for every user: Read or Manage (Log group access). |
LocalRegions | empty | Comma-separated Regions whose log groups to show for this account, for example eu-west-1,us-east-1. Empty means the stack's Region only. |
LogGroupPrefixes | empty | Comma-separated log group name prefixes TraceHound may read events from and change; empty for every log group (Log group access). |
VpcId | VPC for the one-off installer task. | |
InstallerSubnetIds | Subnets for the installer task, in VpcId (a template rule checks it), with outbound HTTPS (see Networking). | |
InstallerSubnetsArePublic | false | true only for subnets that route to an internet gateway; the task then gets a public IP for outbound access. |
LogRetentionDays | 90 | Retention of TraceHound's own CloudWatch log groups, including the audit events written there. One of CloudWatch's retention values. |
KmsKeyArn | empty | Optional customer managed KMS key (see Encryption). Empty uses AWS owned keys and SSE-S3. |
UpdateChecks | Enabled | Once a day, read the public release manifest from the vendor's bucket and announce a newer release to administrators and on the update SNS topic. Nothing about you, your users or your account is sent. |
UpdateNotificationEmail | empty | Optional email address subscribed to the update topic. |
ArtifactVersionOverride, ImageUriOverride | empty | Advanced (support use only). Leave empty unless Cloud Certainty support asks you to set them. |
Spoke (spoke-role.yaml)
TrustedPrincipal, ExternalId, RoleName, AccessLevel and LogGroupPrefixes; see
Deploy the spoke role.
Networking
Only the installer task runs in your VPC, for about a minute at install and at each upgrade. It needs outbound HTTPS (443) to:
- Amazon ECR in us-east-1, to pull the image from the AWS Marketplace registry (cross-Region, so this needs internet egress: a NAT gateway or public subnets);
- AWS License Manager in your Region, for the licence check;
- Amazon S3 in your Region, to write the code into the artifacts bucket (a gateway endpoint works);
- CloudWatch Logs in your Region, for its log stream.
Its security group has no inbound rules and one outbound rule, TCP 443. The Lambda functions don't run in your VPC; they reach STS, CloudWatch Logs, CloudWatch and Lambda in your accounts over AWS's public endpoints.
People's browsers reach the portal (Amplify Hosting), Cognito, the HTTP API and, for live tail, the
live tail function URL (https://<id>.lambda-url.<region>.on.aws, output LiveTailUrl). If a proxy
or firewall limits which sites people can open, allow that address too; the stream is a long-lived
HTTPS response (Server-Sent Events), so a proxy must not buffer it.
Encryption and KMS
Without KmsKeyArn, the DynamoDB tables, the log groups, the update SNS topic and the artifacts
bucket use AWS owned keys and SSE-S3.
With KmsKeyArn, your key encrypts all of them. The key must be a symmetric customer managed key in
the stack's Region (give its ARN, not an alias), and its key policy must:
- let the account's IAM policies grant its use (the default key policy statement for the account root does this). The hub role uses it only through DynamoDB, the update notifier only through DynamoDB and SNS, and the installer and the portal deployer only through S3;
- allow
logs.<region>.amazonaws.comto use it for the stack's log groups; - allow
sns.amazonaws.comto use it for the update topic.
The identity that creates or updates the stack also needs kms:Decrypt on the key, because Lambda
reads the function code from the encrypted artifacts bucket with the deploying identity's
permissions.
The key protects TraceHound's own data. Your log groups keep their own encryption: a log group encrypted with a customer managed key needs that key's policy to let CloudWatch Logs use it, as it does today; TraceHound reads through CloudWatch Logs and needs no KMS permission of its own.
Stack outputs
| Output | Use |
|---|---|
PortalUrl | The URL users and admins open. Also the application start URL in your IdP. |
ApiUrl | HTTP API base URL (informational). |
LiveTailUrl | The streaming live tail endpoint (a Lambda function URL). Informational; the portal is configured with it automatically. |
CognitoDomain | Cognito managed login domain. The OIDC redirect URI is <CognitoDomain>/oauth2/idpresponse. |
UserPoolId, UserPoolClientId | Cognito identifiers. |
SamlAcsUrl, SamlAudienceUri | Values for your SAML application. |
SpokeTrustedPrincipal | The hub role ARN for the spoke template's TrustedPrincipal. |
SpokeTemplateUrl | URL of this release's spoke-role.yaml. |
LocalAccessRoleArn | The role of the local connection. |
CollectorFunctionName | The Lambda that records storage and ingestion history every hour. |
UpdateTopicArn | SNS topic that announces new releases. Subscribe your own endpoints to it. |
TableName | DynamoDB table (retained when the stack is deleted). |
ArtifactsBucketName | Private bucket with the stack's code. Emptied and deleted with the stack; store nothing else in it. |
ProductVersion | The TraceHound release the stack runs. |
Updating
Each release is a new template; there is no version parameter. To upgrade, update the stack with the new release's template (Update stack › Replace current template) and keep every other parameter value. See the upgrading guide.
To change a setting such as LocalAccessLevel, LocalRegions, LogGroupPrefixes or
SamlMetadataUrl, update the stack with the current template and change only that parameter.
Deleting
Delete the hub stack. The DynamoDB table (
<NamePrefix>-table) is retained on purpose, with its settings, grants, saved queries, dashboards, history, storage history and audit log. Everything else is deleted: the cache table (with the usage counters), the Cognito user pool (Cognito-only users are lost), the artifacts bucket and TraceHound's own log groups. A new stack with the sameNamePrefixcan't be created while the retained table exists.To delete the table when you no longer need it, turn off its deletion protection, then delete it.
Delete the spoke stacks or the StackSets in the other accounts.
Delete the SAML or OIDC application in your identity provider.
Your log groups are never touched by deleting TraceHound; retention you changed through TraceHound stays as you set it.
Troubleshooting
| Symptom | Check |
|---|---|
Stack creation or update fails at ArtifactInstall | The status reason gives the installer's exit code and log stream (log group /<NamePrefix>/installer). 2: the image has no valid code bundle; contact support. 3: the account has no TraceHound licence: subscribe on AWS Marketplace, or deploy in the subscribing account (see Before you start). 4: the upload to the artifacts bucket failed; check SCPs, the S3 endpoint policy and, with KmsKeyArn, the key policy. 5: AWS License Manager stayed unavailable for the whole retry budget; retry the stack operation later. No exit code, with CannotPullContainerError or ResourceInitializationError: the subnets can't reach ECR in us-east-1, S3 or CloudWatch Logs. "The installer task did not finish in time": the same network checks. |
Stack creation fails at LiveTailUrl, LiveTailUrlPermission or LiveTailInvokePermission | An SCP or permissions boundary denies Lambda function URLs with auth type NONE, or public permissions on them. Ask your cloud team for an exception for the <NamePrefix>-livetail function (see What the stack runs). |
Stack creation fails at Table with "already exists" | A table from an earlier stack with the same NamePrefix in this account and Region was kept. Delete it (turn off deletion protection first), or choose another prefix. |
| Every page says a licence is required | Administration › Licence and about shows what License Manager returned. The subscription may have expired, or the stack runs in an account without the licence. Choose Check licence now after renewing. |
A connection test fails at AssumeRole | The spoke's TrustedPrincipal must be the hub's SpokeTrustedPrincipal, the external IDs must match, and the role must be under /tracehound/. An SCP can also deny sts:AssumeRole, sts:SetSourceIdentity or sts:TagSession. |
| Growth, ingestion and costs are missing | The collector hasn't run yet in that Region, or failed there. See Administration › Collector, and choose Collect now. |
| Some log groups show no tags, streams or filters, and queries on them fail with an access error | They are outside the connection's LogGroupPrefixes, or an SCP or permissions boundary refuses the role. The error names the AWS action. |
| "CloudWatch Logs is busy: too many queries are running. Try again shortly." | The account runs at most 100 Logs Insights QL queries at once (15 in PPL or SQL) per Region, shared with console dashboards and scheduled queries. TraceHound retries dashboard panels and traces on its own. |
| A retention change is refused | The connection's access level is Read, the plan lacks retention changes (Team and above), the person has no manage grant, the log group is of the Delivery class, or it is one of TraceHound's own log groups (see Log group access). |
| Live tail keeps reconnecting or stops with an error | The browser can't hold a connection to the LiveTailUrl address: check proxies and firewalls (see Networking). |
| A user gets "too many grants" | The user's grants make a session policy larger than AWS allows. Use group grants and wildcard patterns (for example /aws/lambda/payments-*) instead of many single names. |
ip_not_allowed | Administration › Settings, network access. The API sees the client's public IP. |
| A user is refused because their email isn't trusted | See Identity providers: the email must be asserted by SAML, verified by OIDC, or marked verified in Cognito. |
| SAML error after sign-in | The ACS URL and audience match the outputs; the email attribute is sent; the user is assigned to the app. |
Something unclear or missing? Email [email protected].