minimal fastish bootstrap cdk application that creates a handshake role for cross-account access to aws cdk default resources
the bootstrap stack creates a minimal set of resources required for fastish to securely access your aws account. it leverages the standard aws cdk bootstrap resources (created by cdk bootstrap) and only adds a handshake role for cross-account access.
the bootstrap stack provisions only one resource:
- purpose: establishes secure cross-account trust with the fastish host account
- trust policy: allows the fastish host account to assume this role using external id verification
- permissions: can assume aws cdk default bootstrap roles in your account:
cdk-hnb659fds-cfn-exec-role-{account}-{region}- cloudformation executioncdk-hnb659fds-deploy-role-{account}-{region}- deployment operationscdk-hnb659fds-file-publishing-role-{account}-{region}- s3 asset publishingcdk-hnb659fds-image-publishing-role-{account}-{region}- ecr image publishingcdk-hnb659fds-lookup-role-{account}-{region}- resource discovery
- additional permissions:
- access to cdk s3 assets bucket and ecr repository
- describe availability zones
- simulate principal policies (for verification)
- read secrets manager secrets with fastish prefix
- get route53 hosted zone information
- get service quotas
┌─────────────────────────────────────────────────────┐
│ Your AWS Account │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ AWS CDK Bootstrap (you create first) │ │
│ │ • cdk-hnb659fds-cfn-exec-role-* │ │
│ │ • cdk-hnb659fds-deploy-role-* │ │
│ │ • cdk-hnb659fds-file-publishing-role-* │ │
│ │ • cdk-hnb659fds-image-publishing-role-* │ │
│ │ • cdk-hnb659fds-lookup-role-* │ │
│ │ • cdk-hnb659fds-assets-* (S3 bucket) │ │
│ │ • cdk-hnb659fds-container-assets-* (ECR) │ │
│ └──────────────────────────────────────────────┘ │
│ ▲ │
│ │ assumes │
│ ┌──────────────────────┼──────────────────────┐ │
│ │ Fastish Bootstrap (this repo) │ │
│ │ • fastish-{name}-handshake │ │
│ │ (cross-account trust role) │ │
│ └────────────────────────────────────────────┘ │
│ ▲ │
└──────────────────────────┼──────────────────────────┘
│ assumes from
┌──────────────────────────┼──────────────────────────┐
│ Fastish Host Account │ │
│ • subscriber role ──────┘ │
│ (with external id verification) │
└─────────────────────────────────────────────────────┘
- node.js 18+
- npm
- aws cli configured
- aws cdk cli:
npm install -g aws-cdk - aws account with administrator access
you must run the standard aws cdk bootstrap command first. this creates the foundational cdk toolkit resources that fastish will use. the fastish bootstrap only creates a handshake role that provides secure access to these existing cdk resources.
cdk bootstrap aws://<account-id>/<region>what the default cdk bootstrap creates:
- s3 bucket:
cdk-hnb659fds-assets-<account-id>-<region>for staging cdk assets - ecr repository:
cdk-hnb659fds-container-assets-<account-id>-<region>for container images - 5 iam roles:
cdk-hnb659fds-cfn-exec-role-<account-id>-<region>- cloudformation executioncdk-hnb659fds-deploy-role-<account-id>-<region>- deployment orchestrationcdk-hnb659fds-file-publishing-role-<account-id>-<region>- s3 asset uploadscdk-hnb659fds-image-publishing-role-<account-id>-<region>- ecr image uploadscdk-hnb659fds-lookup-role-<account-id>-<region>- resource discovery
- ssm parameter:
/cdk-bootstrap/hnb659fds/versionfor tracking bootstrap version
example:
# for account 123456789012 in us-west-2
cdk bootstrap aws://123456789012/us-west-2
# output will show:
# ✅ Environment aws://123456789012/us-west-2 bootstrappedimportant: this is a one-time operation per account/region combination. for more details:
npm installnpm run buildthe cdk.json file tells the cdk toolkit how to execute the app. customize cdk.context.json to configure the cross-account trust:
example cdk.context.json:
{
"host": {
"account": "111111111111"
},
"synthesizer": {
"name": "prod",
"account": "123456789012",
"region": "us-west-2",
"externalId": "your-unique-external-id-from-fastish",
"subscriberRoleArn": "arn:aws:iam::111111111111:role/fastish/subscriber/xxx",
"cdk": {
"version": "21"
}
}
}field descriptions:
host.account- the fastish host aws account id (provided by fastish)synthesizer.name- unique name for this handshake (e.g., "prod", "staging")synthesizer.account- your aws account idsynthesizer.region- aws region where cdk bootstrap was runsynthesizer.externalId- secure external id for cross-account trust (provided by fastish)synthesizer.subscriberRoleArn- the fastish subscriber role arn (provided by fastish)synthesizer.cdk.version- cdk version number (for tracking)
the stack will be deployed as fastish-prod (using the name field).
npx cdk synththis generates the cloudformation template without deploying.
npx cdk deploywhat gets deployed:
- 1 cloudformation stack:
fastish-<synthesizer-name> - 1 iam role:
fastish-<synthesizer-name>-handshake- can assume cdk default roles in your account
- trusted by fastish host account with external id verification
after deployment, the stack outputs a json object containing the handshake role and references to cdk default resources:
{
"roles": {
"handshake": "arn:aws:iam::123456789012:role/fastish-prod-handshake"
},
"cdk": {
"roles": {
"cfnExec": "arn:aws:iam::123456789012:role/cdk-hnb659fds-cfn-exec-role-123456789012-us-west-2",
"deploy": "arn:aws:iam::123456789012:role/cdk-hnb659fds-deploy-role-123456789012-us-west-2",
"filePublishing": "arn:aws:iam::123456789012:role/cdk-hnb659fds-file-publishing-role-123456789012-us-west-2",
"imagePublishing": "arn:aws:iam::123456789012:role/cdk-hnb659fds-image-publishing-role-123456789012-us-west-2",
"lookup": "arn:aws:iam::123456789012:role/cdk-hnb659fds-lookup-role-123456789012-us-west-2"
},
"storage": {
"assets": "arn:aws:s3:::cdk-hnb659fds-assets-123456789012-us-west-2",
"containerAssets": "arn:aws:ecr:us-west-2:123456789012:repository/cdk-hnb659fds-container-assets-123456789012-us-west-2"
}
}
}save the handshake role arn - you'll need to provide this to the fastish platform when creating your synthesizer configuration.
the project includes comprehensive unit tests using jest and aws cdk assertions. tests verify the infrastructure code creates the expected aws resources with correct configurations.
npm testthis runs all tests once and displays results.
npm test -- --coveragecoverage reports show which parts of the code are tested. reports are generated in the coverage/ directory.
npm test -- --watchwatch mode automatically re-runs tests when files change, useful during development.
the test suite validates:
- cloudformation template snapshot validation
- handshake iam role with trust policy for cross-account access
- iam policy allowing handshake role to assume cdk default roles
- cloudformation outputs include references to cdk bootstrap resources
tests use the aws-cdk-lib/assertions library:
Template.fromStack()- generates cloudformation from cdk stacksMatch.objectLike()- validates resource propertiesMatch.arrayWith()- checks array contentstoMatchSnapshot()- detects unexpected template changes
example test:
test('creates handshake role with correct trust policy', () => {
template.hasResourceProperties('AWS::IAM::Role', {
AssumeRolePolicyDocument: Match.objectLike({
Statement: Match.arrayWith([
Match.objectLike({
Effect: 'Allow',
Principal: { AWS: 'arn:aws:iam::111111111111:root' },
Condition: {
StringEquals: { 'sts:ExternalId': 'test-external-id' }
}
})
])
})
});
});| command | description |
|---|---|
npm run build |
compile typescript to javascript |
npm run watch |
watch for file changes and auto-compile |
npm run test |
run jest unit tests |
npx cdk synth |
generate cloudformation template (preview) |
npx cdk diff |
compare deployed stack with current code |
npx cdk deploy |
deploy stack to aws account/region |
npx cdk destroy |
destroy stack (requires s3 bucket to be empty first) |
to remove the bootstrap stack:
npx cdk destroythis will delete only the handshake role. the aws cdk default bootstrap resources (s3, ecr, roles) will remain intact and can continue to be used by other cdk applications.
note: if you want to completely remove all cdk resources, you would need to manually delete the cdk bootstrap stack (CDKToolkit) from cloudformation console, but this is typically not recommended if you use cdk for other applications.
- manually create an access policy and token to enable creating access policies and tokens
- https://grafana.com/orgs/changeme/access-policies
- allow
- accesspolicies:read
- accesspolicies:write
- accesspolicies:delete
- update
inputs.jsonconfiguration fields described below - execute script
cd scripts/grafana ./create.sh inputs.json - what happens?
- creates required access policy for aws eks to communicate with grafana cloud
- creates aws secret in a structure that eks/druid expects when installing the grafana k8s-monitoring helm chart
- Description: A token used for authenticating access to the Grafana cloud services. This token typically has permissions to access specific Grafana resources and services.
- Example:
"glc_eyJ..."(This token should be provided by Grafana Cloud to enable authenticated access.)
- Description: The URL of your Prometheus instance in the Grafana Cloud.
- Example:
"https://prometheus-prod-1-prod-us-west-0.grafana.net" - Usage: Used to access the Prometheus service, typically for querying metrics.
- Description: The username associated with your Prometheus instance. This is often tied to your Grafana account and is used to authenticate API requests to Prometheus.
- Example:
"0000000" - Usage: Required for API access to Prometheus endpoints.
- Description: The URL of your Loki instance in the Grafana Cloud.
- Example:
"https://logs-prod-1.grafana.net" - Usage: This is the base URL for accessing Loki logs.
- Description: The username associated with your Loki instance.
- Example:
"000000" - Usage: Used to authenticate API requests to Loki.
- Description: The URL for accessing your Tempo instance (for tracing data) on Grafana Cloud.
- Example:
"https://tempo-prod-1-prod-us-west-0.grafana.net:443" - Usage: Used to send or query trace data from your Tempo instance.
- Description: The username associated with your Tempo instance.
- Example:
"000000" - Usage: Used for authenticating access to Tempo's trace data API.
- Description: An arbitrary name that can be assigned to this access policy. It can be any identifier you choose, and it will be mapped to an IAM policy statement for access control.
- Example:
"changeme" - Usage: This alias helps identify the access policy within your organization and map it to relevant IAM roles or access controls.
- Description: The unique identifier for your Grafana Cloud stack instance. This value is essential for API calls to the Grafana service to fetch or manage resources specific to your organization.
- Example:
"000000" - Usage: You can retrieve your organization ID from the URL when you are logged into Grafana Cloud:
https://grafana.com/api/orgs/{your-organization}/instances.
- Description: The region in which your Grafana Cloud services are located. This field helps define which region-specific access policies or configurations apply to your cloud access.
- Example:
"prod-us-west-0" - Usage: Used for applying access policies regionally (e.g., in IAM permissions or when defining which services to connect to based on region).
Here is an example of how this configuration file might look:
{
"cloud_access_policy_token": "glc_eyJ",
"prometheus_host": "https://prometheus-prod-1-prod-us-west-0.grafana.net",
"prometheus_username": "0000000",
"loki_host": "https://logs-prod-1.grafana.net",
"loki_username": "000000",
"tempo_host": "https://tempo-prod-1-prod-us-west-0.grafana.net:443",
"tempo_username": "000000",
"alias": "changeme",
"instance_id": "000000",
"region": "prod-us-west-0"
}- Set up Grafana Cloud: You will need an active Grafana Cloud account and the required services (Prometheus, Loki, Tempo) enabled.
- Obtain your API Tokens: You can generate an API token for authenticating with the Grafana Cloud API from the Grafana Cloud dashboard.
- Find your
instance_id: You can find your Grafana Cloud instance ID by visiting the API URL:https://grafana.com/api/orgs/<your-organization>. The instance ID will be available in the response. - Define your IAM Policy: The
aliasfield maps to an IAM policy statement. Ensure your IAM policies are properly configured to allow the necessary access to Prometheus, Loki, and Tempo services based on the alias. - Set the Region: Make sure the
regionfield corresponds to the region where your Grafana Cloud services are hosted. The region might influence access policies or how you manage your services.
- Access Policies: Based on the
alias, you may create specific IAM roles or permission statements that restrict or allow access to Grafana services (e.g., Prometheus, Loki, Tempo). - Service-Specific Configuration: You may need additional configuration per service, such as Prometheus alerting settings or Loki log retention policies. These can be set in the relevant Grafana dashboards or API endpoints once connected.
- Invalid Token: Ensure that the
cloud_access_policy_tokenis valid and has the required scope to access Grafana Cloud services. - Connection Issues: Verify the URLs in the
prometheus_host,loki_host, andtempo_hostfields to ensure they are correct and reachable. - IAM Policy: If you experience access issues, verify that the IAM policies associated with the
aliasallow the appropriate permissions for the services you are attempting to access.