| name | aws-cloud-security-essentials-lab |
| description | Educational lab repository for AWS cloud computing security fundamentals including IAM, VPC, encryption, monitoring, and incident detection |
| triggers | ["how do I set up AWS IAM security labs","show me cloud security lab exercises","help with AWS security essentials coursework","configure AWS encryption and key management lab","set up AWS monitoring and logging lab","create secure multi-tenant cloud environment","implement AWS access control and network security","troubleshoot AWS CloudTrail and CloudWatch setup"] |
AWS Cloud Security Essentials Lab
Skill by ara.so — Security Skills collection.
This skill provides guidance for working with the IKB42603 Cloud Computing Security Essentials lab repository, which contains hands-on exercises covering fundamental AWS security concepts including IAM, VPC isolation, encryption, access control, and security monitoring.
What This Project Does
This is an educational repository structured around five core AWS security labs:
- Lab 1: Account Security and IAM (Identity and Access Management)
- Lab 2: Secure Isolation and Multitenancy (VPC, Security Groups)
- Lab 3: Encryption and Key Management (KMS, data protection)
- Lab 4: Access Control and Network Security (Network ACLs, Security Groups)
- Lab 5: Monitoring, Logging, and Incident Detection (CloudTrail, CloudWatch)
Each lab teaches practical AWS security implementation through hands-on exercises.
Repository Setup
Initial Repository Creation
git clone https://github.com/<username>/IKB42603-CLOUD-COMPUTING-SECURITY-ESSENTIALS.git
cd IKB42603-CLOUD-COMPUTING-SECURITY-ESSENTIALS
mkdir -p Lab{1..5}
touch Lab0_Environment_Setup.md
touch Lab1_Account_Security_and_IAM.md
touch Lab2_Secure_Isolation_and_Multitenancy.md
touch Lab3_Encryption_and_Key_Management.md
touch Lab4_Access_Control_and_Network_Security.md
touch Lab5_Monitoring_Logging_and_Incident_Detection.md
git add .
git commit -m "Initial lab structure setup"
git push origin main
Standard Lab Documentation Structure
Each lab markdown file should follow this structure:
# Lab X - [Lab Title]
## Objective
[What you will accomplish]
## Learning Outcomes
- Outcome 1
- Outcome 2
## Prerequisites
- AWS Account
- AWS CLI installed
- Appropriate IAM permissions
## Environment Setup
[Required tools and configuration]
## Implementation Steps
### Step 1: [Task Name]
[Description]
**Commands:**
```bash
# Command with explanation
aws [service] [action] --options
Screenshot:

Step 2: [Next Task]
...
Verification
[How to verify successful completion]
Challenges Encountered
[Document any issues and solutions]
Lessons Learned
[Key takeaways]
Cleanup
References
## Lab 1: Account Security and IAM
### Creating IAM Users with MFA
```bash
# Create IAM user
aws iam create-user --user-name lab-user-01
# Create login profile
aws iam create-login-profile \
--user-name lab-user-01 \
--password 'TempPassword123!' \
--password-reset-required
# Attach policy for MFA enforcement
aws iam attach-user-policy \
--user-name lab-user-01 \
--policy-arn arn:aws:iam::aws:policy/IAMUserChangePassword
# Enable virtual MFA device
aws iam create-virtual-mfa-device \
--virtual-mfa-device-name lab-user-01-mfa \
--outfile QRCode.png \
--bootstrap-method QRCodePNG
Creating Custom IAM Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3ReadOnly",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::lab-bucket-*",
"arn:aws:s3:::lab-bucket-*/*"
]
},
{
"Sid": "DenyWithoutMFA",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"BoolIfExists":
aws iam create-policy \
--policy-name LabS3ReadOnlyWithMFA \
--policy-document file://policy.json
aws iam attach-user-policy \
--user-name lab-user-01 \
--policy-arn arn:aws:iam::ACCOUNT_ID:policy/LabS3ReadOnlyWithMFA
Lab 2: Secure Isolation and Multitenancy
Creating Isolated VPC
aws ec2 create-vpc \
--cidr-block 10.0.0.0/16 \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=lab-secure-vpc}]'
VPC_ID=$(aws ec2 describe-vpcs \
--filters "Name=tag:Name,Values=lab-secure-vpc" \
--query 'Vpcs[0].VpcId' \
--output text)
aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 10.0.1.0/24 \
--availability-zone us-east-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=lab-public-subnet}]'
aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 10.0.2.0/24 \
--availability-zone us-east-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=lab-private-subnet}]'
aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=lab-igw}]'
IGW_ID=$(aws ec2 describe-internet-gateways \
--filters "Name=tag:Name,Values=lab-igw" \
--query 'InternetGateways[0].InternetGatewayId' \
--output text)
aws ec2 attach-internet-gateway \
--vpc-id $VPC_ID \
--internet-gateway-id $IGW_ID
Security Groups for Multi-tier Architecture
aws ec2 create-security-group \
--group-name lab-web-sg \
--description "Security group for web tier" \
--vpc-id $VPC_ID
WEB_SG_ID=$(aws ec2 describe-security-groups \
--filters "Name=group-name,Values=lab-web-sg" \
--query 'SecurityGroups[0].GroupId' \
--output text)
aws ec2 authorize-security-group-ingress \
--group-id $WEB_SG_ID \
--protocol tcp \
--port 80 \
--cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress \
--group-id $WEB_SG_ID \
--protocol tcp \
--port 443 \
--cidr 0.0.0.0/0
aws ec2 create-security-group \
--group-name lab-db-sg \
--description "Security group for database tier" \
--vpc-id $VPC_ID
DB_SG_ID=$(aws ec2 describe-security-groups \
--filters "Name=group-name,Values=lab-db-sg" \
--query 'SecurityGroups[0].GroupId' \
--output text)
aws ec2 authorize-security-group-ingress \
--group-id $DB_SG_ID \
--protocol tcp \
--port 3306 \
--source-group $WEB_SG_ID
Lab 3: Encryption and Key Management
Creating and Using KMS Keys
aws kms create-key \
--description "Lab encryption key for S3" \
--key-usage ENCRYPT_DECRYPT \
--origin AWS_KMS
KEY_ID=$(aws kms list-keys --query 'Keys[0].KeyId' --output text)
aws kms create-alias \
--alias-name alias/lab-s3-key \
--target-key-id $KEY_ID
aws s3api create-bucket \
--bucket lab-encrypted-bucket-$(date +%s) \
--region us-east-1
BUCKET_NAME=$(aws s3api list-buckets \
--query 'Buckets[?contains(Name, `lab-encrypted-bucket`)].Name' \
--output text)
aws s3api put-bucket-encryption \
--bucket $BUCKET_NAME \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "'$KEY_ID'"
},
"BucketKeyEnabled": true
}]
}'
echo "Sensitive data" > test-file.txt
aws s3 cp test-file.txt s3://$BUCKET_NAME/ \
--server-side-encryption aws:kms \
--ssekms-key-id $KEY_ID
Encrypting EBS Volumes
aws ec2 create-volume \
--availability-zone us-east-1a \
--size 10 \
--volume-type gp3 \
--encrypted \
--kms-key-id $KEY_ID \
--tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=lab-encrypted-volume}]'
aws ec2 describe-volumes \
--filters "Name=tag:Name,Values=lab-encrypted-volume" \
--query 'Volumes[0].[Encrypted,KmsKeyId]' \
--output table
Lab 4: Access Control and Network Security
Network ACL Configuration
SUBNET_ID=$(aws ec2 describe-subnets \
--filters "Name=tag:Name,Values=lab-public-subnet" \
--query 'Subnets[0].SubnetId' \
--output text)
aws ec2 create-network-acl \
--vpc-id $VPC_ID \
--tag-specifications 'ResourceType=network-acl,Tags=[{Key=Name,Value=lab-nacl}]'
NACL_ID=$(aws ec2 describe-network-acls \
--filters "Name=tag:Name,Values=lab-nacl" \
--query 'NetworkAcls[0].NetworkAclId' \
--output text)
aws ec2 create-network-acl-entry \
--network-acl-id $NACL_ID \
--rule-number 100 \
--protocol tcp \
--port-range From=80,To=80 \
--cidr-block 0.0.0.0/0 \
--rule-action allow \
--ingress
aws ec2 create-network-acl-entry \
--network-acl-id $NACL_ID \
--rule-number 110 \
--protocol tcp \
--port-range From=443,To=443 \
--cidr-block 0.0.0.0/0 \
--rule-action allow \
--ingress
aws ec2 create-network-acl-entry \
--network-acl-id $NACL_ID \
--rule-number 50 \
--protocol -1 \
--cidr-block 10.0.100.0/24 \
--rule-action deny \
--ingress
aws ec2 create-network-acl-entry \
--network-acl-id $NACL_ID \
--rule-number 100 \
--protocol -1 \
--cidr-block 0.0.0.0/0 \
--rule-action allow \
--egress
VPC Flow Logs
aws logs create-log-group --log-group-name /aws/vpc/flowlogs
cat > trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "vpc-flow-logs.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
aws iam create-role \
--role-name VPCFlowLogsRole \
--assume-role-policy-document file://trust-policy.json
cat > flow-logs-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
],
"Resource": "*"
}
]
}
EOF
aws iam put-role-policy \
--role-name VPCFlowLogsRole \
--policy-name VPCFlowLogsPolicy \
--policy-document file://flow-logs-policy.json
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids $VPC_ID \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name /aws/vpc/flowlogs \
--deliver-logs-permission-arn arn:aws:iam::ACCOUNT_ID:role/VPCFlowLogsRole
Lab 5: Monitoring, Logging, and Incident Detection
CloudTrail Setup
aws s3api create-bucket \
--bucket lab-cloudtrail-logs-$(date +%s) \
--region us-east-1
TRAIL_BUCKET=$(aws s3api list-buckets \
--query 'Buckets[?contains(Name, `lab-cloudtrail-logs`)].Name' \
--output text)
cat > bucket-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::$TRAIL_BUCKET"
},
{
"Sid": "AWSCloudTrailWrite",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::$TRAIL_BUCKET/AWSLogs/ACCOUNT_ID/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
EOF
aws s3api put-bucket-policy \
--bucket $TRAIL_BUCKET \
--policy file://bucket-policy.json
aws cloudtrail create-trail \
--name lab-security-trail \
--s3-bucket-name $TRAIL_BUCKET \
--is-multi-region-trail
aws cloudtrail start-logging --name lab-security-trail
aws cloudtrail update-trail \
--name lab-security-trail \
--enable-log-file-validation
CloudWatch Alarms for Security Events
aws sns create-topic --name lab-security-alerts
TOPIC_ARN=$(aws sns list-topics \
--query 'Topics[?contains(TopicArn, `lab-security-alerts`)].TopicArn' \
--output text)
aws sns subscribe \
--topic-arn $TOPIC_ARN \
--protocol email \
--notification-endpoint ${ADMIN_EMAIL}
aws logs put-metric-filter \
--log-group-name CloudTrail/logs \
--filter-name RootAccountUsage \
--filter-pattern '{ $.userIdentity.type = "Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType != "AwsServiceEvent" }' \
--metric-transformations \
metricName=RootAccountUsageCount,metricNamespace=CloudTrailMetrics,metricValue=1
aws cloudwatch put-metric-alarm \
--alarm-name RootAccountUsageAlarm \
--alarm-description "Alarm when root account is used" \
--metric-name RootAccountUsageCount \
--namespace CloudTrailMetrics \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions $TOPIC_ARN
aws logs put-metric-filter \
--log-group-name CloudTrail/logs \
--filter-name UnauthorizedAPICalls \
--filter-pattern '{ ($.errorCode = "*UnauthorizedOperation") || ($.errorCode = "AccessDenied*") }' \
--metric-transformations \
metricName=UnauthorizedAPICallsCount,metricNamespace=CloudTrailMetrics,metricValue=1
aws cloudwatch put-metric-alarm \
--alarm-name UnauthorizedAPICallsAlarm \
--alarm-description "Alarm for unauthorized API calls" \
--metric-name UnauthorizedAPICallsCount \
--namespace CloudTrailMetrics \
--statistic Sum \
--period 300 \
--threshold 5 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 1 \
--alarm-actions $TOPIC_ARN
GuardDuty Setup
aws guardduty create-detector --enable
DETECTOR_ID=$(aws guardduty list-detectors --query 'DetectorIds[0]' --output text)
aws sns create-topic --name lab-guardduty-findings
GUARDDUTY_TOPIC_ARN=$(aws sns list-topics \
--query 'Topics[?contains(TopicArn, `lab-guardduty-findings`)].TopicArn' \
--output text)
aws sns subscribe \
--topic-arn $GUARDDUTY_TOPIC_ARN \
--protocol email \
--notification-endpoint ${SECURITY_EMAIL}
cat > guardduty-rule.json <<EOF
{
"source": ["aws.guardduty"],
"detail-type": ["GuardDuty Finding"],
"detail": {
"severity": [7, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9]
}
}
EOF
aws events put-rule \
--name lab-guardduty-high-severity \
--event-pattern file://guardduty-rule.json \
--state ENABLED
aws events put-targets \
--rule lab-guardduty-high-severity \
--targets "Id"="1","Arn"="$GUARDDUTY_TOPIC_ARN"
Common Patterns
Resource Tagging for Security Compliance
aws ec2 create-tags \
--resources $VPC_ID $SUBNET_ID $SG_ID \
--tags \
Key=Environment,Value=Lab \
Key=Owner,Value=Student \
Key=Course,Value=IKB42603 \
Key=CostCenter,Value=Education \
Key=DataClassification,Value=Public
Automated Cleanup Script
#!/bin/bash
VPC_NAME="lab-secure-vpc"
VPC_ID=$(aws ec2 describe-vpcs \
--filters "Name=tag:Name,Values=$VPC_NAME" \
--query 'Vpcs[0].VpcId' \
--output text)
if [ "$VPC_ID" != "None" ]; then
for NAT_ID in $(aws ec2 describe-nat-gateways \
--filter "Name=vpc-id,Values=$VPC_ID" \
--query 'NatGateways[*].NatGatewayId' \
--output text); do
aws ec2 delete-nat-gateway --nat-gateway-id $NAT_ID
echo "Deleted NAT Gateway: $NAT_ID"
done
for INSTANCE_ID in $(aws ec2 describe-instances \
--filters "Name=vpc-id,Values=$VPC_ID" "Name=instance-state-name,Values=running,stopped" \
--query 'Reservations[*].Instances[*].InstanceId' \
--output text); do
aws ec2 terminate-instances --instance-ids $INSTANCE_ID
echo "Terminated instance: $INSTANCE_ID"
done
aws ec2 wait instance-terminated \
--filters "Name=vpc-id,Values=$VPC_ID"
for SG_ID $(aws ec2 describe-security-groups \
--filters \
--query \
--output text);
aws ec2 delete-security-group --group-id
IGW_ID $(aws ec2 describe-internet-gateways \
--filters \
--query \
--output text);
aws ec2 detach-internet-gateway --internet-gateway-id --vpc-id
aws ec2 delete-internet-gateway --internet-gateway-id
SUBNET_ID $(aws ec2 describe-subnets \
--filters \
--query \
--output text);
aws ec2 delete-subnet --subnet-id
aws ec2 delete-vpc --vpc-id
Troubleshooting
AWS CLI Authentication Issues
aws sts get-caller-identity
aws configure list
aws configure --profile lab-profile
aws s3 ls --profile lab-profile
export AWS_PROFILE=lab-profile
export AWS_DEFAULT_REGION=us-east-1
Permission Denied Errors
aws iam get-user
aws iam list-attached-user-policies --user-name $(aws iam get-user --query 'User.UserName' --output text)
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::ACCOUNT_ID:user/lab-user \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::bucket-name/*
VPC Resource Deletion Blocked
aws ec2 describe-vpcs --vpc-ids $VPC_ID
aws ec2 describe-instances \
--filters "Name=vpc-id,Values=$VPC_ID" "Name=instance-state-name,Values=running"
aws ec2 describe-network-interfaces \
--filters "Name=vpc-id,Values=$VPC_ID"
for ENI_ID in $(aws ec2 describe-network-interfaces \
--filters "Name=vpc-id,Values=$VPC_ID" \
--query 'NetworkInterfaces[*].NetworkInterfaceId' \
--output text); do
ATTACHMENT_ID=$(aws ec2 describe-network-interfaces \
--network-interface-ids $ENI_ID \
--query 'NetworkInterfaces[0].Attachment.AttachmentId' \
--output text)
if [ "$ATTACHMENT_ID" != "None" ]; then
aws ec2 detach-network-interface --attachment-id $ATTACHMENT_ID --force
fi
aws ec2 delete-network-interface --network-interface-id $ENI_ID
done
CloudTrail Not Logging
aws cloudtrail get-trail-status --name lab-security-trail
aws cloudtrail describe-trails --trail-name-list lab-security-trail
aws s3api get-bucket-policy --bucket $TRAIL_BUCKET
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=CreateUser \
--max-results 10
KMS Key Access Issues
aws kms get-key-policy \
--key-id $KEY_ID \
--policy-name default
aws kms list-grants --key-id $KEY_ID
aws kms describe-key --key-id $KEY_ID --query 'KeyMetadata.KeyState'
aws kms encrypt \
--key-id $KEY_ID \
--plaintext "test" \
--output text \
--query CiphertextBlob
Git Workflow for Labs
git checkout -b lab-3-encryption
mkdir -p Lab3/screenshots
git add Lab3/
git commit -m "Lab 3: Create KMS key and configure encryption"
git add Lab3/
git commit -m "Lab 3: Implement S3 bucket encryption"
git add Lab3/
git commit -m "Lab 3: Complete encryption lab with documentation"
git checkout main
git merge lab-3-encryption
git push origin main
Environment Variables
Always use environment variables for sensitive information:
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export AWS_DEFAULT_REGION=us-east-1
export ADMIN_EMAIL=admin@example.com
export SECURITY_EMAIL=security@example.com
Additional Resources