Account & bootstrap
Day One #
You click “Create account,” and suddenly you own a universe. It’s empty, quiet, and a little intimidating. You’re holding the master key (root), and you can feel the weight of it. Before we build anything, we’re going to do two things: lock the master key away and carve out a safe, repeatable path from idea to infrastructure.
Lock the master key #
- Turn on MFA for the root user.
- Delete any root access keys.
- Create an IAM user with
AdministratorAccessfor daily work (call itadmin). Turn on MFA for this user too. Sign out the root user and put it back in the drawer.
Give the key a name #
We’ll teach the AWS CLI how to speak as your admin user using a named profile.
aws configure --profile admin
# Paste Access Key ID and Secret Access Key for your admin user
# Default region: us-east-1 (or your choice)
# Output: json
export AWS_PROFILE=admin
export AWS_REGION=us-east-1
aws sts get-caller-identity --profile admin
Meet CDK #
The AWS Cloud Development Kit (CDK) lets you write infrastructure in code—TypeScript, Python, and friends—then synthesizes it to CloudFormation for safe, trackable deployments. It’s like drawing a blueprint and having a reliable crew build it the same way every time.
Install what we need:
aws --version
node -v
npm -v
npm i -g aws-cdk@2
cdk --version
The ritual: bootstrap #
CDK needs a small staging bucket and a couple of roles. We create them once per account and region.
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text --profile admin)
cdk bootstrap aws://$ACCOUNT_ID/$AWS_REGION --profile admin
A role for future‑you (one‑time stack) #
You’ll deploy one tiny stack now, as admin. It creates a dedicated role named CdkDeployerRole with admin permissions (for now) and requires MFA to assume. After this, you’ll switch to that role for all future CDK work—keeping day‑to‑day building separate from your raw admin keys.
Scaffold a CDK app:
mkdir first-steps && cd first-steps
cdk init app --language typescript
npm i aws-cdk-lib constructs
Replace the generated files with the following minimal app.
lib/deploy-role-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as iam from 'aws-cdk-lib/aws-iam';
export class DeployRoleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Allow your IAM user (default: Admin) to assume the role with MFA
const adminUser = String(this.node.tryGetContext('adminUser') ?? 'Admin');
const account = cdk.Stack.of(this).account;
const assumedBy = new iam.ArnPrincipal(
`arn:aws:iam::${account}:user/${adminUser}`,
).withConditions({
Bool: { 'aws:MultiFactorAuthPresent': 'true' },
});
const role = new iam.Role(this, 'CdkDeployerRole', {
roleName: 'CdkDeployerRole',
description: 'Admin role used to deploy CDK stacks during development',
assumedBy,
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess'),
],
});
new cdk.CfnOutput(this, 'RoleArn', { value: role.roleArn });
}
}
bin/deploy-role.ts
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { DeployRoleStack } from '../lib/deploy-role-stack';
const app = new cdk.App();
new DeployRoleStack(app, 'DeployRoleStack', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
Deploy it as your admin user:
cdk deploy --profile admin -c adminUser=Admin
The output prints the role ARN. That’s your new daily driver.
Switch profiles: become the deployer #
Tell the CLI how to assume the new role from your admin profile. Replace 123456789012 with your account ID.
~/.aws/config
[dev]
role_arn = arn:aws:iam::123456789012:role/CdkDeployerRole
source_profile = admin
mfa_serial = arn:aws:iam::123456789012:mfa/Admin
region = us-east-1
Note: mfa_serial must be the ARN of the MFA device attached to your Admin IAM user. The CLI will prompt for your 6‑digit code when assuming the role. You can fetch the ARN via:
aws iam list-mfa-devices --user-name Admin --profile admin --query 'MFADevices[0].SerialNumber' --output text
Verify the handshake:
aws sts get-caller-identity --profile dev
From now on, run CDK with --profile dev. You can tighten this role later (least privilege), but starting wide lets you move without tripping over permissions while you learn.
If you see an error about a missing SSM parameter during deploy, bootstrap the exact account/region you’re using for that profile and try again.
Guardrails that save you #
- Keep MFA on for both root and admin.
- Create a small budget (e.g., $10) to catch surprises via email.
- Leave CloudTrail on so you can answer “what happened?” when you need to.
Checklist #
- Root locked down; daily
adminuser created with MFA. - AWS CLI + Node.js + CDK installed.
adminprofile configured and verified.- Account bootstrapped for CDK in your region.
CdkDeployerRolecreated via a one‑time stack and assumed through adevprofile.
That’s the doorway. Next, we’ll use your dev profile to deploy your first real stack—something tiny, tangible, and ready to evolve.
Add a $10 budget with CDK (optional) #
You can codify the budget guardrail so it travels with your infrastructure. Use your dev profile (which assumes CdkDeployerRole).
In the same first-steps app, add a Budget stack.
lib/budget-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as budgets from 'aws-cdk-lib/aws-budgets';
export class BudgetStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const amount = Number(this.node.tryGetContext('budgetAmount') ?? 10);
const email = this.node.tryGetContext('budgetEmail');
if (!email) {
throw new Error('Provide -c budgetEmail=you@example.com');
}
new budgets.CfnBudget(this, 'MonthlyBudget', {
budget: {
budgetName: `Monthly-${amount}USD`,
budgetLimit: { amount, unit: 'USD' },
budgetType: 'COST',
timeUnit: 'MONTHLY',
},
notificationsWithSubscribers: [
{
notification: {
comparisonOperator: 'GREATER_THAN',
notificationType: 'FORECASTED',
threshold: 80,
thresholdType: 'PERCENTAGE',
},
subscribers: [{ subscriptionType: 'EMAIL', address: email }],
},
{
notification: {
comparisonOperator: 'GREATER_THAN',
notificationType: 'ACTUAL',
threshold: 100,
thresholdType: 'PERCENTAGE',
},
subscribers: [{ subscriptionType: 'EMAIL', address: email }],
},
],
});
}
}
Update your app entry to include both stacks (deploy them separately). The budget stack is optional and only added if you provide a
-c budgetEmail=...context:
bin/deploy-role.ts
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { DeployRoleStack } from '../lib/deploy-role-stack';
import { BudgetStack } from '../lib/budget-stack';
const app = new cdk.App();
new DeployRoleStack(app, 'DeployRoleStack', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
const haveBudgetEmail = !!app.node.tryGetContext('budgetEmail');
if (haveBudgetEmail) {
new BudgetStack(app, 'BudgetStack', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
}
Deploy the role (admin), then the budget (dev):
# As admin (once)
cdk deploy DeployRoleStack --profile admin -c adminUser=Admin
# As dev (budget email is required)
cdk deploy BudgetStack \
-c budgetEmail=you@example.com \
-c budgetAmount=10 \
--profile dev
That codifies your “$10 alert me” rule: you’ll get an email when the forecasted spend crosses 80% and when the actual spend crosses 100% of your budget.
Well‑Architected Framework #
- Security: lock down the root user, enforce MFA, and separate day‑to‑day work behind an assumable role (
CdkDeployerRole). CDK deployments inherit CloudFormation’s change control, and named profiles reduce key sprawl. - Operational Excellence: infrastructure as code (CDK) enables reviews, versioning, and repeatable procedures. The bootstrap ritual, profile setup, and identity checks are lightweight runbooks you can automate later.
- Reliability: CDK → CloudFormation provides declarative deployments, drift detection, and rollbacks. A single starting region and a dedicated deploy role reduce configuration mistakes that lead to outages.
- Cost Optimization: a budget alarm from day one, plus IaC (Infrastructure as Code) that makes creating and tearing down resources trivial. Starting in one region avoids accidental multi‑region spend.
- Performance Efficiency: higher‑level CDK constructs help you adopt managed, performant services quickly, then iterate with minimal friction as needs evolve.
- Sustainability: IaC promotes ephemeral, right‑sized environments—build only what you need, when you need it, and remove it cleanly.