IAM Console → Users → Create user
→ Username: your_username_here
→ Check: Provide user access to AWS Management Console
→ Select: I want to create an IAM user
→ Custom password: (set a strong password)
→ Uncheck: Users must create new password at next sign-in
→ Next
IAM Console → Users → Create user
→ Username: your_username_here
→ Check: Provide user access to AWS Management Console
→ Select: I want to create an IAM user
→ Custom password: (set a strong password)
→ Uncheck: Users must create new password at next sign-in
→ Next
IAM Console → Users → Create user
→ Username: your_username_here
→ Check: Provide user access to AWS Management Console
→ Select: I want to create an IAM user
→ Custom password: (set a strong password)
→ Uncheck: Users must create new password at next sign-in
→ Next
→ Attach policies directly
→ Search and check: AmazonEC2FullAccess
→ Search and check: AmazonSSMFullAccess
→ Next → Create user
→ Attach policies directly
→ Search and check: AmazonEC2FullAccess
→ Search and check: AmazonSSMFullAccess
→ Next → Create user
→ Attach policies directly
→ Search and check: AmazonEC2FullAccess
→ Search and check: AmazonSSMFullAccess
→ Next → Create user
IAM → Users → your_username_here
→ Security credentials tab
→ Multi-factor authentication (MFA) → Assign MFA device
→ MFA device type: Authenticator app
→ Scan the QR code with your authenticator app
→ Enter two consecutive 6-digit codes to confirm
IAM → Users → your_username_here
→ Security credentials tab
→ Multi-factor authentication (MFA) → Assign MFA device
→ MFA device type: Authenticator app
→ Scan the QR code with your authenticator app
→ Enter two consecutive 6-digit codes to confirm
IAM → Users → your_username_here
→ Security credentials tab
→ Multi-factor authentication (MFA) → Assign MFA device
→ MFA device type: Authenticator app
→ Scan the QR code with your authenticator app
→ Enter two consecutive 6-digit codes to confirm
IAM → Users → your_username_here
→ Security credentials tab
→ Multi-factor authentication (MFA) → Assign MFA device
→ MFA device type: Passkey or security key
→ Insert YubiKey and tap when prompted
IAM → Users → your_username_here
→ Security credentials tab
→ Multi-factor authentication (MFA) → Assign MFA device
→ MFA device type: Passkey or security key
→ Insert YubiKey and tap when prompted
IAM → Users → your_username_here
→ Security credentials tab
→ Multi-factor authentication (MFA) → Assign MFA device
→ MFA device type: Passkey or security key
→ Insert YubiKey and tap when prompted
https://<YOUR_ACCOUNT_ID>.signin.aws.amazon.com/console
https://<YOUR_ACCOUNT_ID>.signin.aws.amazon.com/console
https://<YOUR_ACCOUNT_ID>.signin.aws.amazon.com/console
# Install dependencies
sudo apt update && sudo apt install curl unzip -y # Download from AWS directly
# Note: never use apt install awscli — the Debian repo does not include v2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install # Verify
aws --version
# aws-cli/2.34.9 Python/3.13.11 Linux/6.1.0-43-amd64 exe/x86_64.debian.12
# Install dependencies
sudo apt update && sudo apt install curl unzip -y # Download from AWS directly
# Note: never use apt install awscli — the Debian repo does not include v2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install # Verify
aws --version
# aws-cli/2.34.9 Python/3.13.11 Linux/6.1.0-43-amd64 exe/x86_64.debian.12
# Install dependencies
sudo apt update && sudo apt install curl unzip -y # Download from AWS directly
# Note: never use apt install awscli — the Debian repo does not include v2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install # Verify
aws --version
# aws-cli/2.34.9 Python/3.13.11 Linux/6.1.0-43-amd64 exe/x86_64.debian.12
IAM → Users → your_username_here
→ Security credentials tab
→ Access keys → Create access key
→ Use case: Command Line Interface (CLI)
→ Check the amber warning confirmation box
→ Next → Create access key
→ DOWNLOAD .csv FILE IMMEDIATELY
IAM → Users → your_username_here
→ Security credentials tab
→ Access keys → Create access key
→ Use case: Command Line Interface (CLI)
→ Check the amber warning confirmation box
→ Next → Create access key
→ DOWNLOAD .csv FILE IMMEDIATELY
IAM → Users → your_username_here
→ Security credentials tab
→ Access keys → Create access key
→ Use case: Command Line Interface (CLI)
→ Check the amber warning confirmation box
→ Next → Create access key
→ DOWNLOAD .csv FILE IMMEDIATELY
aws configure
# AWS Access Key ID: AKIA... (from the .csv)
# AWS Secret Access Key: ... (from the .csv) # Default region name: us-east-1
# Default output format: json
aws configure
# AWS Access Key ID: AKIA... (from the .csv)
# AWS Secret Access Key: ... (from the .csv) # Default region name: us-east-1
# Default output format: json
aws configure
# AWS Access Key ID: AKIA... (from the .csv)
# AWS Secret Access Key: ... (from the .csv) # Default region name: us-east-1
# Default output format: json
aws sts get-caller-identity
aws sts get-caller-identity
aws sts get-caller-identity
{ "UserId": "AIDA xxxxxxxxxxxxxxxxxxxx", "Account": "YOUR_ACCOUNT_ID", "Arn": "arn:aws:iam::YOUR_ACCOUNT_ID:user/your_username_here"
}
{ "UserId": "AIDA xxxxxxxxxxxxxxxxxxxx", "Account": "YOUR_ACCOUNT_ID", "Arn": "arn:aws:iam::YOUR_ACCOUNT_ID:user/your_username_here"
}
{ "UserId": "AIDA xxxxxxxxxxxxxxxxxxxx", "Account": "YOUR_ACCOUNT_ID", "Arn": "arn:aws:iam::YOUR_ACCOUNT_ID:user/your_username_here"
}
IAM Console → Roles → Create role
→ Trusted entity: AWS service → Use case: EC2
→ Next
→ Search: AmazonSSMManagedInstanceCore → Check the box
→ Next
→ Role name: suricata-lab-ssm-role
→ Create role
IAM Console → Roles → Create role
→ Trusted entity: AWS service → Use case: EC2
→ Next
→ Search: AmazonSSMManagedInstanceCore → Check the box
→ Next
→ Role name: suricata-lab-ssm-role
→ Create role
IAM Console → Roles → Create role
→ Trusted entity: AWS service → Use case: EC2
→ Next
→ Search: AmazonSSMManagedInstanceCore → Check the box
→ Next
→ Role name: suricata-lab-ssm-role
→ Create role
YOU (your_username_here) → authenticated via IAM credentials can start SSM sessions, launch EC2 EC2 INSTANCE → authenticated via suricata-lab-ssm-role can communicate with SSM can receive your session connection
YOU (your_username_here) → authenticated via IAM credentials can start SSM sessions, launch EC2 EC2 INSTANCE → authenticated via suricata-lab-ssm-role can communicate with SSM can receive your session connection
YOU (your_username_here) → authenticated via IAM credentials can start SSM sessions, launch EC2 EC2 INSTANCE → authenticated via suricata-lab-ssm-role can communicate with SSM can receive your session connection
EC2 → Security Groups → Create security group
→ Name: suricata-lab-sg
→ Inbound rules: DELETE any default rules → NONE
→ Outbound rules: keep default (all traffic)
→ Create security group
EC2 → Security Groups → Create security group
→ Name: suricata-lab-sg
→ Inbound rules: DELETE any default rules → NONE
→ Outbound rules: keep default (all traffic)
→ Create security group
EC2 → Security Groups → Create security group
→ Name: suricata-lab-sg
→ Inbound rules: DELETE any default rules → NONE
→ Outbound rules: keep default (all traffic)
→ Create security group
EC2 → Launch Instance Name: suricata-ids-lab
AMI: Debian 13 (Bookworm) → Browse AMIs → AWS Marketplace AMIs → Search: Debian 13 → Publisher: Debian (official only — verify this)
Instance type: t2.micro (free tier eligible)
Key pair: Proceed without a key pair
Security group: Select existing → suricata-lab-sg Advanced details: IAM instance profile: suricata-lab-ssm-role
EC2 → Launch Instance Name: suricata-ids-lab
AMI: Debian 13 (Bookworm) → Browse AMIs → AWS Marketplace AMIs → Search: Debian 13 → Publisher: Debian (official only — verify this)
Instance type: t2.micro (free tier eligible)
Key pair: Proceed without a key pair
Security group: Select existing → suricata-lab-sg Advanced details: IAM instance profile: suricata-lab-ssm-role
EC2 → Launch Instance Name: suricata-ids-lab
AMI: Debian 13 (Bookworm) → Browse AMIs → AWS Marketplace AMIs → Search: Debian 13 → Publisher: Debian (official only — verify this)
Instance type: t2.micro (free tier eligible)
Key pair: Proceed without a key pair
Security group: Select existing → suricata-lab-sg Advanced details: IAM instance profile: suricata-lab-ssm-role
#!/bin/bash
apt-get update -y
wget https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_amd64/amazon-ssm-agent.deb
dpkg -i amazon-ssm-agent.deb
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
#!/bin/bash
apt-get update -y
wget https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_amd64/amazon-ssm-agent.deb
dpkg -i amazon-ssm-agent.deb
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
#!/bin/bash
apt-get update -y
wget https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_amd64/amazon-ssm-agent.deb
dpkg -i amazon-ssm-agent.deb
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" \ -o "/tmp/session-manager-plugin.deb" sudo dpkg -i /tmp/session-manager-plugin.deb # Verify
session-manager-plugin --version
curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" \ -o "/tmp/session-manager-plugin.deb" sudo dpkg -i /tmp/session-manager-plugin.deb # Verify
session-manager-plugin --version
curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" \ -o "/tmp/session-manager-plugin.deb" sudo dpkg -i /tmp/session-manager-plugin.deb # Verify
session-manager-plugin --version
# set -U creates a universal variable — persists across all sessions permanently
# No source command, no file editing, no restart needed
set -U INSTANCE_ID "i-your_instance_id_here"
# set -U creates a universal variable — persists across all sessions permanently
# No source command, no file editing, no restart needed
set -U INSTANCE_ID "i-your_instance_id_here"
# set -U creates a universal variable — persists across all sessions permanently
# No source command, no file editing, no restart needed
set -U INSTANCE_ID "i-your_instance_id_here"
mkdir -p ~/.config/fish/functions
mkdir -p ~/.config/fish/functions
mkdir -p ~/.config/fish/functions
function lab-status aws ec2 describe-instances --instance-ids $INSTANCE_ID \ --profile lab-sso \ --query "Reservations[0].Instances[0].State.Name" \ --output text
end
function lab-status aws ec2 describe-instances --instance-ids $INSTANCE_ID \ --profile lab-sso \ --query "Reservations[0].Instances[0].State.Name" \ --output text
end
function lab-status aws ec2 describe-instances --instance-ids $INSTANCE_ID \ --profile lab-sso \ --query "Reservations[0].Instances[0].State.Name" \ --output text
end
function lab-start aws ec2 start-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Starting... wait 60 seconds"
end
function lab-start aws ec2 start-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Starting... wait 60 seconds"
end
function lab-start aws ec2 start-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Starting... wait 60 seconds"
end
function lab-stop aws ec2 stop-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Stopping..."
end
function lab-stop aws ec2 stop-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Stopping..."
end
function lab-stop aws ec2 stop-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Stopping..."
end
function lab-connect aws ssm start-session --target $INSTANCE_ID --profile lab-sso
end
function lab-connect aws ssm start-session --target $INSTANCE_ID --profile lab-sso
end
function lab-connect aws ssm start-session --target $INSTANCE_ID --profile lab-sso
end
function lab-login aws sso login --profile lab-sso
end
function lab-login aws sso login --profile lab-sso
end
function lab-login aws sso login --profile lab-sso
end
function lab-terminate echo "WARNING: This will permanently delete the instance and all data on it." echo "Instance: $INSTANCE_ID" read --prompt-str "Type YES to confirm: " confirm if test "$confirm" = "YES" aws ec2 terminate-instances \ --instance-ids $INSTANCE_ID --profile lab-sso echo "Instance terminated. Use lab-create to launch a fresh one." else echo "Cancelled." end
end
function lab-terminate echo "WARNING: This will permanently delete the instance and all data on it." echo "Instance: $INSTANCE_ID" read --prompt-str "Type YES to confirm: " confirm if test "$confirm" = "YES" aws ec2 terminate-instances \ --instance-ids $INSTANCE_ID --profile lab-sso echo "Instance terminated. Use lab-create to launch a fresh one." else echo "Cancelled." end
end
function lab-terminate echo "WARNING: This will permanently delete the instance and all data on it." echo "Instance: $INSTANCE_ID" read --prompt-str "Type YES to confirm: " confirm if test "$confirm" = "YES" aws ec2 terminate-instances \ --instance-ids $INSTANCE_ID --profile lab-sso echo "Instance terminated. Use lab-create to launch a fresh one." else echo "Cancelled." end
end
lab-status # → running
lab-connect # → Starting session with SessionId: your_username_here-....
whoami # → ssm-user
lab-status # → running
lab-connect # → Starting session with SessionId: your_username_here-....
whoami # → ssm-user
lab-status # → running
lab-connect # → Starting session with SessionId: your_username_here-....
whoami # → ssm-user
AWS Console → search "IAM Identity Center"
→ Enable IAM Identity Center
→ Enable with AWS Organizations
→ Continue → wait 1-2 minutes Save from the Settings summary: Access portal URL: https://d-xxxxxxxxxx.awsapps.com/start Primary Region: us-east-1
AWS Console → search "IAM Identity Center"
→ Enable IAM Identity Center
→ Enable with AWS Organizations
→ Continue → wait 1-2 minutes Save from the Settings summary: Access portal URL: https://d-xxxxxxxxxx.awsapps.com/start Primary Region: us-east-1
AWS Console → search "IAM Identity Center"
→ Enable IAM Identity Center
→ Enable with AWS Organizations
→ Continue → wait 1-2 minutes Save from the Settings summary: Access portal URL: https://d-xxxxxxxxxx.awsapps.com/start Primary Region: us-east-1
IAM Identity Center → Settings → Configure MFA Prompt users for MFA: ● Every time they sign in (always-on) MFA types: ☑ Security keys and built-in authenticators (FIDO2) ☐ Authenticator apps ← leave UNCHECKED If no MFA device: ● Require them to register at sign in → Save
IAM Identity Center → Settings → Configure MFA Prompt users for MFA: ● Every time they sign in (always-on) MFA types: ☑ Security keys and built-in authenticators (FIDO2) ☐ Authenticator apps ← leave UNCHECKED If no MFA device: ● Require them to register at sign in → Save
IAM Identity Center → Settings → Configure MFA Prompt users for MFA: ● Every time they sign in (always-on) MFA types: ☑ Security keys and built-in authenticators (FIDO2) ☐ Authenticator apps ← leave UNCHECKED If no MFA device: ● Require them to register at sign in → Save
IAM Identity Center → Permission sets → Create permission set
→ Predefined permission set
→ Policy: PowerUserAccess
→ Permission set name: lab-power-user
→ Session duration: 8 hours
→ Create
IAM Identity Center → Permission sets → Create permission set
→ Predefined permission set
→ Policy: PowerUserAccess
→ Permission set name: lab-power-user
→ Session duration: 8 hours
→ Create
IAM Identity Center → Permission sets → Create permission set
→ Predefined permission set
→ Policy: PowerUserAccess
→ Permission set name: lab-power-user
→ Session duration: 8 hours
→ Create
Allows: ALL AWS actions EXCEPT iam:*, organizations:*, account:* Explicitly allows (narrow exceptions): iam:CreateServiceLinkedRole iam:DeleteServiceLinkedRole iam:ListRoles organizations:DescribeOrganization Does NOT include: iam:PassRole ← this matters (see Part 14)
Allows: ALL AWS actions EXCEPT iam:*, organizations:*, account:* Explicitly allows (narrow exceptions): iam:CreateServiceLinkedRole iam:DeleteServiceLinkedRole iam:ListRoles organizations:DescribeOrganization Does NOT include: iam:PassRole ← this matters (see Part 14)
Allows: ALL AWS actions EXCEPT iam:*, organizations:*, account:* Explicitly allows (narrow exceptions): iam:CreateServiceLinkedRole iam:DeleteServiceLinkedRole iam:ListRoles organizations:DescribeOrganization Does NOT include: iam:PassRole ← this matters (see Part 14)
IAM Identity Center → Users → Add user
→ Username: your_username_here
→ Password: Send an email to this user
→ Email address: [email protected]
→ First name / Last name: fill in
→ Next → Add user Check email → click activation link → set password
IAM Identity Center → Users → Add user
→ Username: your_username_here
→ Password: Send an email to this user
→ Email address: [email protected]
→ First name / Last name: fill in
→ Next → Add user Check email → click activation link → set password
IAM Identity Center → Users → Add user
→ Username: your_username_here
→ Password: Send an email to this user
→ Email address: [email protected]
→ First name / Last name: fill in
→ Next → Add user Check email → click activation link → set password
IAM Identity Center → Users → your_username_here
→ MFA devices tab → Register device
→ Security key (FIDO2)
→ Insert YubiKey and tap when prompted
→ Register
IAM Identity Center → Users → your_username_here
→ MFA devices tab → Register device
→ Security key (FIDO2)
→ Insert YubiKey and tap when prompted
→ Register
IAM Identity Center → Users → your_username_here
→ MFA devices tab → Register device
→ Security key (FIDO2)
→ Insert YubiKey and tap when prompted
→ Register
IAM Identity Center → Users → your_username_here
→ AWS accounts tab → Assign accounts
→ Select: your account (your_account_name_here / YOUR_ACCOUNT_ID)
→ Next
→ Select permission set: lab-power-user
→ Assign
IAM Identity Center → Users → your_username_here
→ AWS accounts tab → Assign accounts
→ Select: your account (your_account_name_here / YOUR_ACCOUNT_ID)
→ Next
→ Select permission set: lab-power-user
→ Assign
IAM Identity Center → Users → your_username_here
→ AWS accounts tab → Assign accounts
→ Select: your account (your_account_name_here / YOUR_ACCOUNT_ID)
→ Next
→ Select permission set: lab-power-user
→ Assign
aws configure sso # Prompts:
# SSO session name: lab-sso
# SSO start URL: https://d-xxxxxxxxxx.awsapps.com/start
# SSO region: us-east-1
# SSO registration scopes: sso:account:access (press Enter) # Browser opens automatically
# Log in → tap YubiKey
# Browser: "Your credentials have been shared successfully" # Back in terminal:
# CLI default client Region: us-east-1
# CLI default output format: json
# CLI profile name: lab-sso
aws configure sso # Prompts:
# SSO session name: lab-sso
# SSO start URL: https://d-xxxxxxxxxx.awsapps.com/start
# SSO region: us-east-1
# SSO registration scopes: sso:account:access (press Enter) # Browser opens automatically
# Log in → tap YubiKey
# Browser: "Your credentials have been shared successfully" # Back in terminal:
# CLI default client Region: us-east-1
# CLI default output format: json
# CLI profile name: lab-sso
aws configure sso # Prompts:
# SSO session name: lab-sso
# SSO start URL: https://d-xxxxxxxxxx.awsapps.com/start
# SSO region: us-east-1
# SSO registration scopes: sso:account:access (press Enter) # Browser opens automatically
# Log in → tap YubiKey
# Browser: "Your credentials have been shared successfully" # Back in terminal:
# CLI default client Region: us-east-1
# CLI default output format: json
# CLI profile name: lab-sso
aws sts get-caller-identity --profile lab-sso
aws sts get-caller-identity --profile lab-sso
aws sts get-caller-identity --profile lab-sso
{ "UserId": "AROA xxxxxxxxxxxxxxxxxxxx:your_username_here", "Account": "YOUR_ACCOUNT_ID", "Arn": "arn:aws:sts::YOUR_ACCOUNT_ID:assumed-role/AWSReservedSSO_lab-power-user_xxxxxxxxxxxxxxxxx/your_username_here"
}
{ "UserId": "AROA xxxxxxxxxxxxxxxxxxxx:your_username_here", "Account": "YOUR_ACCOUNT_ID", "Arn": "arn:aws:sts::YOUR_ACCOUNT_ID:assumed-role/AWSReservedSSO_lab-power-user_xxxxxxxxxxxxxxxxx/your_username_here"
}
{ "UserId": "AROA xxxxxxxxxxxxxxxxxxxx:your_username_here", "Account": "YOUR_ACCOUNT_ID", "Arn": "arn:aws:sts::YOUR_ACCOUNT_ID:assumed-role/AWSReservedSSO_lab-power-user_xxxxxxxxxxxxxxxxx/your_username_here"
}
EC2 → Launch Templates → Create launch template Launch template name: suricata-lab-template
Description: Debian 13 + SSM Agent via User Data AMI: Debian 13 (official Debian publisher)
Instance type: t2.micro
Key pair: Don't include in launch template
Security group: Select existing → suricata-lab-sg Advanced details: IAM instance profile: suricata-lab-ssm-role User data: #!/bin/bash apt-get update -y wget https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_amd64/amazon-ssm-agent.deb dpkg -i amazon-ssm-agent.deb systemctl enable amazon-ssm-agent systemctl start amazon-ssm-agent → Create launch template
EC2 → Launch Templates → Create launch template Launch template name: suricata-lab-template
Description: Debian 13 + SSM Agent via User Data AMI: Debian 13 (official Debian publisher)
Instance type: t2.micro
Key pair: Don't include in launch template
Security group: Select existing → suricata-lab-sg Advanced details: IAM instance profile: suricata-lab-ssm-role User data: #!/bin/bash apt-get update -y wget https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_amd64/amazon-ssm-agent.deb dpkg -i amazon-ssm-agent.deb systemctl enable amazon-ssm-agent systemctl start amazon-ssm-agent → Create launch template
EC2 → Launch Templates → Create launch template Launch template name: suricata-lab-template
Description: Debian 13 + SSM Agent via User Data AMI: Debian 13 (official Debian publisher)
Instance type: t2.micro
Key pair: Don't include in launch template
Security group: Select existing → suricata-lab-sg Advanced details: IAM instance profile: suricata-lab-ssm-role User data: #!/bin/bash apt-get update -y wget https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_amd64/amazon-ssm-agent.deb dpkg -i amazon-ssm-agent.deb systemctl enable amazon-ssm-agent systemctl start amazon-ssm-agent → Create launch template
# Rewrite lab-connect
echo 'function lab-connect aws ssm start-session --target $INSTANCE_ID --profile lab-sso
end' > ~/.config/fish/functions/lab-connect.fish # Rewrite lab-status
echo 'function lab-status aws ec2 describe-instances --instance-ids $INSTANCE_ID \ --profile lab-sso \ --query "Reservations[0].Instances[0].State.Name" \ --output text
end' > ~/.config/fish/functions/lab-status.fish # Rewrite lab-start
echo 'function lab-start aws ec2 start-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Starting... wait 60 seconds"
end' > ~/.config/fish/functions/lab-start.fish # Rewrite lab-stop echo 'function lab-stop aws ec2 stop-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Stopping..."
end' > ~/.config/fish/functions/lab-stop.fish # Rewrite lab-terminate
echo 'function lab-terminate echo "WARNING: Permanently deletes instance and all data on it." echo "Instance: $INSTANCE_ID" read --prompt-str "Type YES to confirm: " confirm if test "$confirm" = "YES" aws ec2 terminate-instances \ --instance-ids $INSTANCE_ID --profile lab-sso echo "Instance terminated. Use lab-create to launch a fresh one." else echo "Cancelled." end
end' > ~/.config/fish/functions/lab-terminate.fish
# Rewrite lab-connect
echo 'function lab-connect aws ssm start-session --target $INSTANCE_ID --profile lab-sso
end' > ~/.config/fish/functions/lab-connect.fish # Rewrite lab-status
echo 'function lab-status aws ec2 describe-instances --instance-ids $INSTANCE_ID \ --profile lab-sso \ --query "Reservations[0].Instances[0].State.Name" \ --output text
end' > ~/.config/fish/functions/lab-status.fish # Rewrite lab-start
echo 'function lab-start aws ec2 start-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Starting... wait 60 seconds"
end' > ~/.config/fish/functions/lab-start.fish # Rewrite lab-stop echo 'function lab-stop aws ec2 stop-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Stopping..."
end' > ~/.config/fish/functions/lab-stop.fish # Rewrite lab-terminate
echo 'function lab-terminate echo "WARNING: Permanently deletes instance and all data on it." echo "Instance: $INSTANCE_ID" read --prompt-str "Type YES to confirm: " confirm if test "$confirm" = "YES" aws ec2 terminate-instances \ --instance-ids $INSTANCE_ID --profile lab-sso echo "Instance terminated. Use lab-create to launch a fresh one." else echo "Cancelled." end
end' > ~/.config/fish/functions/lab-terminate.fish
# Rewrite lab-connect
echo 'function lab-connect aws ssm start-session --target $INSTANCE_ID --profile lab-sso
end' > ~/.config/fish/functions/lab-connect.fish # Rewrite lab-status
echo 'function lab-status aws ec2 describe-instances --instance-ids $INSTANCE_ID \ --profile lab-sso \ --query "Reservations[0].Instances[0].State.Name" \ --output text
end' > ~/.config/fish/functions/lab-status.fish # Rewrite lab-start
echo 'function lab-start aws ec2 start-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Starting... wait 60 seconds"
end' > ~/.config/fish/functions/lab-start.fish # Rewrite lab-stop echo 'function lab-stop aws ec2 stop-instances --instance-ids $INSTANCE_ID --profile lab-sso echo "Stopping..."
end' > ~/.config/fish/functions/lab-stop.fish # Rewrite lab-terminate
echo 'function lab-terminate echo "WARNING: Permanently deletes instance and all data on it." echo "Instance: $INSTANCE_ID" read --prompt-str "Type YES to confirm: " confirm if test "$confirm" = "YES" aws ec2 terminate-instances \ --instance-ids $INSTANCE_ID --profile lab-sso echo "Instance terminated. Use lab-create to launch a fresh one." else echo "Cancelled." end
end' > ~/.config/fish/functions/lab-terminate.fish
aws: [ERROR]: An error occurred (UnauthorizedOperation) when calling
the RunInstances operation: You are not authorized to perform:
iam:PassRole on resource: ...suricata-lab-ssm-role...
aws: [ERROR]: An error occurred (UnauthorizedOperation) when calling
the RunInstances operation: You are not authorized to perform:
iam:PassRole on resource: ...suricata-lab-ssm-role...
aws: [ERROR]: An error occurred (UnauthorizedOperation) when calling
the RunInstances operation: You are not authorized to perform:
iam:PassRole on resource: ...suricata-lab-ssm-role...
IAM Identity Center → Permission sets → lab-power-user
→ Inline policy → Add inline policy
IAM Identity Center → Permission sets → lab-power-user
→ Inline policy → Add inline policy
IAM Identity Center → Permission sets → lab-power-user
→ Inline policy → Add inline policy
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::YOUR_ACCOUNT_ID:role/suricata-lab-ssm-role", "Condition": { "StringEquals": { "iam:PassedToService": "ec2.amazonaws.com" } } } ]
}
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::YOUR_ACCOUNT_ID:role/suricata-lab-ssm-role", "Condition": { "StringEquals": { "iam:PassedToService": "ec2.amazonaws.com" } } } ]
}
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::YOUR_ACCOUNT_ID:role/suricata-lab-ssm-role", "Condition": { "StringEquals": { "iam:PassedToService": "ec2.amazonaws.com" } } } ]
}
→ Save changes
(Reprovisioning happens automatically — wait ~30 seconds)
→ Save changes
(Reprovisioning happens automatically — wait ~30 seconds)
→ Save changes
(Reprovisioning happens automatically — wait ~30 seconds)
function lab-create echo "Launching new suricata-ids-lab instance..." set new_id (aws ec2 run-instances \ --launch-template LaunchTemplateName=suricata-lab-template \ --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=suricata-ids-lab}]" \ --profile lab-sso \ --query "Instances[0].InstanceId" \ --output text) if test -z "$new_id" echo "ERROR: Failed to launch instance." return 1 end echo "Instance launched: $new_id" echo "Updating INSTANCE_ID..." set -U INSTANCE_ID $new_id echo "Waiting for instance to reach running state..." aws ec2 wait instance-running \ --instance-ids $new_id \ --profile lab-sso echo "Instance is running." echo "Waiting 3 minutes for SSM Agent to initialize..." sleep 180 echo "Ready. Run lab-connect to start your session."
end
function lab-create echo "Launching new suricata-ids-lab instance..." set new_id (aws ec2 run-instances \ --launch-template LaunchTemplateName=suricata-lab-template \ --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=suricata-ids-lab}]" \ --profile lab-sso \ --query "Instances[0].InstanceId" \ --output text) if test -z "$new_id" echo "ERROR: Failed to launch instance." return 1 end echo "Instance launched: $new_id" echo "Updating INSTANCE_ID..." set -U INSTANCE_ID $new_id echo "Waiting for instance to reach running state..." aws ec2 wait instance-running \ --instance-ids $new_id \ --profile lab-sso echo "Instance is running." echo "Waiting 3 minutes for SSM Agent to initialize..." sleep 180 echo "Ready. Run lab-connect to start your session."
end
function lab-create echo "Launching new suricata-ids-lab instance..." set new_id (aws ec2 run-instances \ --launch-template LaunchTemplateName=suricata-lab-template \ --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=suricata-ids-lab}]" \ --profile lab-sso \ --query "Instances[0].InstanceId" \ --output text) if test -z "$new_id" echo "ERROR: Failed to launch instance." return 1 end echo "Instance launched: $new_id" echo "Updating INSTANCE_ID..." set -U INSTANCE_ID $new_id echo "Waiting for instance to reach running state..." aws ec2 wait instance-running \ --instance-ids $new_id \ --profile lab-sso echo "Instance is running." echo "Waiting 3 minutes for SSM Agent to initialize..." sleep 180 echo "Ready. Run lab-connect to start your session."
end
# Start of session
lab-login # browser opens → tap YubiKey → 8hr credentials issued # Launch fresh instance
lab-create # launches, waits for running, waits 3min for SSM Agent # Do work
lab-connect # shell as ssm-user — no SSH, no ports, no keys # End of session
lab-terminate # permanently deletes instance → zero ongoing cost
# Start of session
lab-login # browser opens → tap YubiKey → 8hr credentials issued # Launch fresh instance
lab-create # launches, waits for running, waits 3min for SSM Agent # Do work
lab-connect # shell as ssm-user — no SSH, no ports, no keys # End of session
lab-terminate # permanently deletes instance → zero ongoing cost
# Start of session
lab-login # browser opens → tap YubiKey → 8hr credentials issued # Launch fresh instance
lab-create # launches, waits for running, waits 3min for SSM Agent # Do work
lab-connect # shell as ssm-user — no SSH, no ports, no keys # End of session
lab-terminate # permanently deletes instance → zero ongoing cost
aws: [ERROR]: Error when retrieving token from sso: Token has expired and refresh failed
aws: [ERROR]: Error when retrieving token from sso: Token has expired and refresh failed
aws: [ERROR]: Error when retrieving token from sso: Token has expired and refresh failed
lab-status
lab-connect
lab-status
lab-connect
lab-status
lab-connect
lab-status
# → None
lab-status
# → None
lab-status
# → None
SessionManagerPlugin is not found
SessionManagerPlugin is not found
SessionManagerPlugin is not found
# The SSM Agent may still be initializing
# Wait 2-3 more minutes and try again
lab-connect
# The SSM Agent may still be initializing
# Wait 2-3 more minutes and try again
lab-connect
# The SSM Agent may still be initializing
# Wait 2-3 more minutes and try again
lab-connect
echo 'function lab-terminate echo "WARNING: Permanently deletes instance and all data on it." echo "Instance: $INSTANCE_ID" read --prompt-str "Type YES to confirm: " confirm if test "$confirm" = "YES" aws ec2 terminate-instances \ --instance-ids $INSTANCE_ID --profile lab-sso echo "Instance terminated. Use lab-create to launch a fresh one." else echo "Cancelled." end
end' > ~/.config/fish/functions/lab-terminate.fish
echo 'function lab-terminate echo "WARNING: Permanently deletes instance and all data on it." echo "Instance: $INSTANCE_ID" read --prompt-str "Type YES to confirm: " confirm if test "$confirm" = "YES" aws ec2 terminate-instances \ --instance-ids $INSTANCE_ID --profile lab-sso echo "Instance terminated. Use lab-create to launch a fresh one." else echo "Cancelled." end
end' > ~/.config/fish/functions/lab-terminate.fish
echo 'function lab-terminate echo "WARNING: Permanently deletes instance and all data on it." echo "Instance: $INSTANCE_ID" read --prompt-str "Type YES to confirm: " confirm if test "$confirm" = "YES" aws ec2 terminate-instances \ --instance-ids $INSTANCE_ID --profile lab-sso echo "Instance terminated. Use lab-create to launch a fresh one." else echo "Cancelled." end
end' > ~/.config/fish/functions/lab-terminate.fish - Login to AWS securely using either static credentials or a hardware security key (YubiKey)
- Create an EC2 instance (a virtual machine in the cloud) with one command
- Connect to that instance without SSH, without open ports, and without a key pair file
- Stop the instance to pause billing when not in use
- Terminate the instance permanently to pay zero ongoing cost between sessions - Why root credentials are dangerous (and what to use instead)
- Two CLI auth methods: IAM Access Keys (simple) vs IAM Identity Center (secure)
- EC2 instance with zero open inbound ports — no SSH, no bastion host
- SSM Session Manager for terminal access through AWS
- Fish shell lab functions: lab-login, lab-create, lab-connect, lab-terminate
- A Launch Template for repeatable, cost-free instance lifecycle
- The iam:PassRole error you will hit with PowerUserAccess — and the exact fix - Billing and payment settings
- Account closure
- IAM recovery if all other admin access is lost - EC2 Instance Connect → Debian 13 AMI has no ec2-instance-connect package
- CloudShell aws ssm send-command → requires SSM Agent to already be installed
- Temporarily opening port 22 → EC2 Instance Connect still fails (no package) - Resource — only suricata-lab-ssm-role, not any other role in the account
- iam:PassedToService — only to EC2, not Lambda, ECS, or anything else - Calls aws ec2 run-instances using the Launch Template — no need to specify AMI, security group, IAM profile, or User Data manually
- Captures the new instance ID
- Automatically runs set -U INSTANCE_ID — all other functions point to the new instance immediately
- aws ec2 wait instance-running — blocks until AWS confirms the instance is running
- sleep 180 — waits for the User Data SSM Agent install to complete
- Prints "Ready" - No running EC2 instance — no compute charges
- No EBS volume — no storage charges
- No static credentials on disk — credentials expired
- Every lab-create starts completely fresh from the Launch Template - Train developers to write secure code
- Prepare security engineers for technical interviews