A hands-on, service-wise playground for learning the AWS CLI against Floci — a fast, free, local AWS emulator (a drop-in LocalStack replacement) — so you can practice real AWS workflows with zero cost and no AWS account.
Every service lives in its own folder under services/ with small,
numbered, runnable scripts that each teach one concept, plus a teardown.sh
so labs are repeatable. A capstone scenarios/ folder shows how the
primitives compose into a real event-driven system.
practice-aws-cli-with-floci/
├── bin/
│ ├── awslocal # thin `aws` wrapper pinned to Floci (use for ad-hoc commands)
│ └── doctor.sh # environment preflight — run this first
├── lib/
│ └── common.sh # shared library every script sources (config, logging, aws wrapper)
├── services/ # ONE folder per AWS service
│ ├── s3/ # 01-create-bucket · 02-objects · 03-presign · teardown
│ ├── dynamodb/ # 01-create-table · 02-items · teardown
│ ├── sqs/ # 01-queues (+DLQ) · 02-send-receive · teardown
│ ├── sns/ # 01-topic-fanout (→ SQS + filter policy) · teardown
│ ├── iam/ # 01-roles-policies · teardown
│ ├── secretsmanager/ # 01-secrets · teardown
│ ├── ssm/ # 01-parameters · teardown
│ ├── lambda/ # handler.py · 01-deploy-invoke · teardown
│ └── kinesis/ # 01-stream · teardown
├── scenarios/
│ └── order-pipeline/ # capstone: S3 + DynamoDB + SNS + SQS working together
├── Makefile # `make doctor`, `make s3`, `make scenario`, `make all`, `make clean`
├── .env.example # optional config overrides
└── README.md
| Principle | How it shows up here |
|---|---|
| Single source of truth | The endpoint/profile/region live in one place — lib/common.sh's aws_local wrapper. Point at real AWS by changing one line. |
| Idempotent & repeatable | Every create checks for existence first; every service ships a teardown.sh. Re-running never errors. |
| Fail fast | set -euo pipefail everywhere; a doctor preflight verifies tools, connectivity, and credentials before you start. |
| Readable output | Structured, colour-coded logging that auto-disables when piped to a file/CI. |
| Composable | Service labs teach primitives; the scenario shows them composed into a system. |
| Scales cleanly | Adding a service = add a services/<name>/ folder following the same pattern. Nothing in the root grows. |
- Floci running locally (you already have it):
docker ps --filter name=floci # should show 0.0.0.0:4566->4566 (healthy) - AWS CLI v2 with a
flociprofile (already configured):aws configure list-profiles # should include: floci jq(JSON processing) andcurl. On macOS:brew install jq.
The
flociprofile uses dummy credentials (test/test). Floci accepts any credentials — they're required by the CLI but never validated.
# 1. Verify everything is wired up correctly
./bin/doctor.sh
# 2. Run a single service lab, script by script
./services/s3/01-create-bucket.sh
./services/s3/02-objects.sh
./services/s3/03-presign.sh
./services/s3/teardown.sh
# 3. …or use the Makefile to run a whole lab + teardown in one go
make s3
make dynamodb
make scenario # the cross-service capstone
# 4. Run absolutely everything, then clean up
make all
make cleanSkip the flags for one-off exploration — bin/awslocal always
targets Floci:
bin/awslocal s3 ls
bin/awslocal dynamodb list-tables
bin/awslocal sqs list-queues
bin/awslocal sts get-caller-identityAdd it to your PATH for the classic ergonomics:
export PATH="$(pwd)/bin:$PATH"
awslocal s3 ls| Service | Folder | Concepts covered |
|---|---|---|
| S3 | services/s3 |
Buckets, versioning, upload/download, object metadata, presigned URLs |
| DynamoDB | services/dynamodb |
Partition + sort keys, GSI, put/get, batch-write, query (PK & index) |
| SQS | services/sqs |
Queues, dead-letter queue + redrive, batch send, long-poll consumer loop |
| SNS | services/sns |
Topics, SQS fan-out, subscription filter policies, raw delivery |
| IAM | services/iam |
Roles + trust policies, inline vs managed policies, correct teardown order |
| Secrets Manager | services/secretsmanager |
Store/retrieve JSON secrets, versioning, rotation, field extraction with jq |
| SSM | services/ssm |
Hierarchical config, String/StringList/SecureString, get-parameters-by-path |
| Lambda | services/lambda |
Zip packaging, deploy, waiter, synchronous invoke with JSON payloads |
| Kinesis | services/kinesis |
Streams, shards, produce records, shard iterators, consume + decode |
Capstone — scenarios/order-pipeline: a client
places orders that are persisted to DynamoDB, receipted to S3, and
announced via SNS → SQS, where a worker drains the queue and marks each order
SHIPPED. One script, four services, fully event-driven.
Everything works out of the box. To override defaults, copy the example env file:
cp .env.example .env| Variable | Default | Purpose |
|---|---|---|
FLOCI_ENDPOINT |
http://localhost:4566 |
Where Floci listens |
AWS_PROFILE_NAME |
floci |
AWS CLI profile to use |
AWS_REGION_NAME |
us-east-1 |
Region on every call |
NS |
lab |
Prefix for all created resource names |
You can also override per-run inline, e.g. NS=demo make s3 or
BUCKET=my-bucket ./services/s3/01-create-bucket.sh.
- SecureString isn't really encrypted. Floci returns SSM
SecureStringvalues in plaintext even without--with-decryption. On real AWS the un-decrypted call returns ciphertext. The workflow you practice is identical. - Account id is always
000000000000and any credentials are accepted. - Lambda needs Docker. Floci runs your function code in a container, so the
Floci container must be able to reach the Docker socket. If
invokefails with a runtime error, that's an environment limitation, not the script. - Check what's available any time:
curl -s localhost:4566/_localstack/health | jq '.services'.
To add a new service (say, Step Functions):
mkdir services/stepfunctions- Copy the shape of an existing lab: numbered
NN-*.shscripts +teardown.sh, each sourcinglib/common.shand callingensure_floci states. - Add it to the
SERVICESlist in theMakefile.
That's it — the root never changes, and every new lab inherits the shared wrapper, logging, and safety rails for free.