How to get additional $100 for newly created AWS account
I haven't set up a new AWS account via Create Account button for ages. Currently I am setting up new AWS accounts via code with @beesolve/aws-accounts. I want the tool to be usable for users with existing AWS Organizations and also users who are trying to onboard to AWS. That's why I needed to create new Organization myself so I could understand how the tool can help the newcomers.
When you decide to create an AWS account today you will be presented with several options 1. You have an ability to try AWS services without commitment for 6 months for free and AWS guarantees you they won't charge you anything at all. Even better, they will give you $100 in credits. If you want to set up an Organization, you will need to choose Pay-as-go model but again you will get starting present of $100 credits.
Either way there is an option to earn another $100 in credits by completing various cloud quests. If you are a newcomer without previous experience with AWS this might help you to understand how things work.
I am not new but I still wanted to earn extra credits2. My first instinct was to automate this process by prompting kiro-cli and asking it to create script which will do all the tasks for me.
Then I realized maybe someone else already did this so I googled3 for "automate getting $100 credits for new aws account" and first article I clicked on was from Jordan Hornblow and it was called Automating the $100 of AWS Credits Available to New Accounts. Actually with the automation you will only get $80 in credits; earning the last $20 cannot be automated. Great work Jordan, thank you very much!
From the start I wasn't really comfortable with using terraform as I wanted this for me to be as painless as possible but then I decided that it is time for me to surrender, install terraform and use it for at least something which already exists and is handed to me for free. Unfortunately after running terraform init and then terraform apply I hit the following error:
╷
│ Error: No valid credential sources found
│
│ with provider["registry.terraform.io/hashicorp/aws"],
│ on credits.tf line 34, in provider "aws":
│ 34: provider "aws" {
│
│ Please see https://registry.terraform.io/providers/hashicorp/aws
│ for more information about providing credentials.
│
│ Error: failed to refresh cached credentials, no EC2 IMDS role found, operation error ec2imds: GetMetadata, request canceled, context deadline exceeded
│
╵
That's it! I am not going to surrender after all. Yes, I could easily ask kiro-cli to help me to fix this or I could even google for the answers but where's the fun in that?
Instead I have asked my LLM friend to convert the terraform template to two bash scripts - deploy.sh and destroy.sh.
If you are like me and you don't want to install terraform but rather you want to use what you already have eg. aws-cli, you can see the scripts below.
deploy.sh - show me the code 🧑💻
#!/usr/bin/env bash
set -euo pipefail
# AWS Credits - deploy via AWS CLI
# Prerequisites: AWS_PROFILE and AWS_REGION set, or use defaults.
#
# Usage:
# ./deploy.sh # deploy all
# ./deploy.sh lambda rds # deploy only lambda and rds
#
# Available targets: ec2, budget, lambda, rds
REGION="${AWS_REGION:-eu-central-1}"
NAME="aws-credits"
export AWS_PAGER=""
# Parse targets (default: all)
if [ $# -gt 0 ]; then
TARGETS=("$@")
else
TARGETS=(ec2 budget lambda rds)
fi
should_run() { printf '%s\n' "${TARGETS[@]}" | grep -qx "$1"; }
echo "=== Deploying AWS Credits resources in $REGION ==="
echo "Targets: ${TARGETS[*]}"
# ── Shared: get default VPC and subnets (needed by ec2 and rds) ──
if should_run ec2 || should_run rds; then
VPC_ID=$(aws ec2 describe-vpcs --filters Name=isDefault,Values=true --query 'Vpcs[0].VpcId' --output text --region "$REGION")
echo "Default VPC: $VPC_ID"
SUBNET_IDS=$(aws ec2 describe-subnets --filters Name=vpc-id,Values="$VPC_ID" --query 'Subnets[*].SubnetId' --output text --region "$REGION")
FIRST_SUBNET=$(echo "$SUBNET_IDS" | awk '{print $1}')
fi
# ── 1. EC2 Instance ──
if should_run ec2; then
echo ""
echo "=== 1. Launching EC2 instance ==="
SG_EC2_ID=$(aws ec2 create-security-group \
--group-name "${NAME}-ec2-sg" \
--description "Temporary SG for credits EC2 instance" \
--vpc-id "$VPC_ID" \
--query 'GroupId' --output text --region "$REGION")
aws ec2 create-tags --resources "$SG_EC2_ID" --tags Key=Name,Value="${NAME}-ec2" --region "$REGION"
aws ec2 authorize-security-group-egress \
--group-id "$SG_EC2_ID" \
--protocol -1 --cidr 0.0.0.0/0 --region "$REGION" 2>/dev/null || true
AMI_ID=$(aws ec2 describe-images \
--owners amazon \
--filters "Name=name,Values=al2023-ami-*-x86_64" "Name=state,Values=available" \
--query 'sort_by(Images, &CreationDate)[-1].ImageId' --output text --region "$REGION")
echo "AMI: $AMI_ID"
INSTANCE_ID=$(aws ec2 run-instances \
--image-id "$AMI_ID" \
--instance-type t3.micro \
--subnet-id "$FIRST_SUBNET" \
--security-group-ids "$SG_EC2_ID" \
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=${NAME}-ec2}]" \
--query 'Instances[0].InstanceId' --output text --region "$REGION")
echo "EC2 Instance: $INSTANCE_ID"
fi
# ── 2. AWS Budget ──
if should_run budget; then
echo ""
echo "=== 2. Creating cost budget ==="
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text --region "$REGION")
aws budgets create-budget --account-id "$ACCOUNT_ID" --region "$REGION" --budget '{
"BudgetName": "'"${NAME}-budget"'",
"BudgetLimit": {"Amount": "10", "Unit": "USD"},
"TimeUnit": "MONTHLY",
"BudgetType": "COST"
}'
echo "Budget created: ${NAME}-budget"
fi
# ── 3. Lambda Function ──
if should_run lambda; then
echo ""
echo "=== 3. Deploying Lambda function ==="
LAMBDA_ROLE_ARN=$(aws iam create-role \
--role-name "${NAME}-lambda-role" \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]
}' \
--query 'Role.Arn' --output text 2>/dev/null) || \
LAMBDA_ROLE_ARN=$(aws iam get-role --role-name "${NAME}-lambda-role" --query 'Role.Arn' --output text)
echo "IAM Role: $LAMBDA_ROLE_ARN"
aws iam attach-role-policy \
--role-name "${NAME}-lambda-role" \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 2>/dev/null || true
LAMBDA_ZIP="/tmp/${NAME}-lambda.zip"
echo 'def handler(event, context):
return {"statusCode": 200, "body": "OK"}' > /tmp/index.py
zip -j "$LAMBDA_ZIP" /tmp/index.py
rm /tmp/index.py
echo "Waiting for IAM role propagation..."
sleep 10
aws lambda create-function \
--function-name "${NAME}-web-app" \
--runtime python3.13 \
--role "$LAMBDA_ROLE_ARN" \
--handler index.handler \
--timeout 30 \
--zip-file "fileb://$LAMBDA_ZIP" \
--tags "Name=${NAME}-lambda" \
--region "$REGION" > /dev/null
rm "$LAMBDA_ZIP"
echo "Lambda function: ${NAME}-web-app"
fi
# ── 4. RDS Database ──
if should_run rds; then
echo ""
echo "=== 4. Creating RDS database ==="
SG_RDS_ID=$(aws ec2 create-security-group \
--group-name "${NAME}-rds-sg" \
--description "Temporary SG for credits RDS instance" \
--vpc-id "$VPC_ID" \
--query 'GroupId' --output text --region "$REGION")
aws ec2 create-tags --resources "$SG_RDS_ID" --tags Key=Name,Value="${NAME}-rds" --region "$REGION"
aws rds create-db-subnet-group \
--db-subnet-group-name "${NAME}-db-subnet-group" \
--db-subnet-group-description "Credits DB subnet group" \
--subnet-ids $SUBNET_IDS \
--tags Key=Name,Value="${NAME}-db" \
--region "$REGION" > /dev/null
RDS_PASSWORD=$(openssl rand -base64 12 | tr -d '/+=' | head -c 16)
aws rds create-db-instance \
--db-instance-identifier "${NAME}-db" \
--engine mysql \
--engine-version "8.0" \
--db-instance-class db.t3.micro \
--allocated-storage 20 \
--db-name credits \
--master-username admin \
--master-user-password "$RDS_PASSWORD" \
--db-subnet-group-name "${NAME}-db-subnet-group" \
--vpc-security-group-ids "$SG_RDS_ID" \
--no-publicly-accessible \
--no-deletion-protection \
--tags Key=Name,Value="${NAME}-db" \
--region "$REGION" > /dev/null
echo "RDS instance: ${NAME}-db (creating, may take several minutes)"
fi
echo ""
echo "=== Deployment complete ==="
destroy.sh - show me the code 🧑💻
#!/usr/bin/env bash
set -euo pipefail
# AWS Credits - destroy all resources created by deploy.sh
REGION="${AWS_REGION:-eu-central-1}"
NAME="aws-credits"
export AWS_PAGER=""
echo "=== Destroying AWS Credits resources in $REGION ==="
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text --region "$REGION")
# ── 1. EC2 Instance ──
echo ""
echo "=== 1. Terminating EC2 instance ==="
INSTANCE_ID=$(aws ec2 describe-instances \
--filters "Name=tag:Name,Values=${NAME}-ec2" "Name=instance-state-name,Values=pending,running,stopping,stopped" \
--query 'Reservations[0].Instances[0].InstanceId' --output text --region "$REGION")
if [ "$INSTANCE_ID" != "None" ] && [ -n "$INSTANCE_ID" ]; then
aws ec2 terminate-instances --instance-ids "$INSTANCE_ID" --region "$REGION" > /dev/null
echo "Terminating: $INSTANCE_ID"
aws ec2 wait instance-terminated --instance-ids "$INSTANCE_ID" --region "$REGION"
echo "Terminated."
else
echo "No EC2 instance found."
fi
# Delete EC2 security group
SG_EC2_ID=$(aws ec2 describe-security-groups \
--filters "Name=group-name,Values=${NAME}-ec2-sg" \
--query 'SecurityGroups[0].GroupId' --output text --region "$REGION" 2>/dev/null || echo "None")
if [ "$SG_EC2_ID" != "None" ] && [ -n "$SG_EC2_ID" ]; then
aws ec2 delete-security-group --group-id "$SG_EC2_ID" --region "$REGION"
echo "Deleted SG: $SG_EC2_ID"
fi
# ── 2. Budget ──
echo ""
echo "=== 2. Deleting budget ==="
aws budgets delete-budget --account-id "$ACCOUNT_ID" --budget-name "${NAME}-budget" --region "$REGION" 2>/dev/null && echo "Deleted budget." || echo "Budget not found."
# ── 3. Lambda ──
echo ""
echo "=== 3. Deleting Lambda function ==="
aws lambda delete-function --function-name "${NAME}-web-app" --region "$REGION" 2>/dev/null && echo "Deleted Lambda." || echo "Lambda not found."
aws iam detach-role-policy \
--role-name "${NAME}-lambda-role" \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 2>/dev/null || true
aws iam delete-role --role-name "${NAME}-lambda-role" 2>/dev/null && echo "Deleted IAM role." || echo "IAM role not found."
# ── 4. RDS Database ──
echo ""
echo "=== 4. Deleting RDS database ==="
aws rds delete-db-instance \
--db-instance-identifier "${NAME}-db" \
--skip-final-snapshot \
--region "$REGION" 2>/dev/null > /dev/null && echo "Deleting RDS (may take several minutes)..." || echo "RDS instance not found."
# Wait for RDS deletion before removing subnet group and SG
if aws rds describe-db-instances --db-instance-identifier "${NAME}-db" --region "$REGION" 2>/dev/null > /dev/null; then
echo "Waiting for RDS deletion..."
aws rds wait db-instance-deleted --db-instance-identifier "${NAME}-db" --region "$REGION"
echo "RDS deleted."
fi
aws rds delete-db-subnet-group --db-subnet-group-name "${NAME}-db-subnet-group" --region "$REGION" 2>/dev/null && echo "Deleted DB subnet group." || echo "DB subnet group not found."
SG_RDS_ID=$(aws ec2 describe-security-groups \
--filters "Name=group-name,Values=${NAME}-rds-sg" \
--query 'SecurityGroups[0].GroupId' --output text --region "$REGION" 2>/dev/null || echo "None")
if [ "$SG_RDS_ID" != "None" ] && [ -n "$SG_RDS_ID" ]; then
aws ec2 delete-security-group --group-id "$SG_RDS_ID" --region "$REGION"
echo "Deleted RDS SG: $SG_RDS_ID"
fi
echo ""
echo "=== Destroy complete ==="
Is this better than terraform? Probably not. Is this alternative for folks who don't want to use terraform? Yes, viable alternative.
Conclusion
If you are new to AWS, I'd recommend skipping the automation and going through the quests manually - AWS did a genuinely good job with the onboarding experience, and you'll learn useful things along the way. That was clearly the point.
If you are already familiar with AWS and just want credits quickly, whether you use terraform or scripts above, you will save approximately 30 minutes of clicking through tutorials and still earn $80 in credits.
Either way, I have my clean testing environment and can get back to making @beesolve/aws-accounts ready for both existing AWS customers and newcomers.
How are you going to spend your free credits?
