DEV Community

Cover image for Run AWS CodePipeline, CodeBuild and CodeDeploy Locally with Floci
Vedas Kudalkar
Vedas Kudalkar

Posted on AI-assisted

Run AWS CodePipeline, CodeBuild and CodeDeploy Locally with Floci

A Maven WAR through CodeArtifact, CodeBuild, CodeDeploy and CodePipeline on Floci in Docker. No AWS account, no bill.

To run an AWS CI/CD pipeline with no AWS account, I pointed the AWS CLI at Floci, a local AWS emulator, and scripted every stage in PowerShell. The pipeline builds a Java web app, deploys it to an "EC2 instance" that is really a Docker container, and serves it on localhost. Most of it worked as written. CodeDeploy hooks did not, so I wrote a small SSM agent stand-in to run them.

Repo: axololtos/cicd-pipeline-floci. The design follows the free NextWork DevOps challenge; the repo is not affiliated with it.

Why emulate it

The tutorial version needs a real account, IAM setup, a running EC2 instance and careful cleanup. Miss one resource and you pay for it. Floci removes that risk, and the whole thing is repeatable: 07-cleanup.ps1 tears it down and the numbered scripts rebuild it.

The trade-off is fidelity. Floci implements the API surface, not every behavior behind it. Most of this post is about where that gap showed up.

Architecture

Architecture

Stage Service Role
Source GitHub Hosts the Maven webapp
Packages CodeArtifact Maven repo that mvn deploy publishes to
Build CodeBuild Builds the WAR, uploads it to S3
Deploy CodeDeploy + EC2 + SSM Hook scripts fetch the WAR and start Tomcat
Orchestrate CodePipeline Chains Source, Build, Deploy

Stack: Floci in Docker (with the Docker socket mounted, so EC2 instances become containers), PowerShell 5.1+, AWS CLI v2, Docker Desktop, and a Maven 3.5.2 / JDK 8 build image. The scripts live in scripts/:

01-start-floci.ps1   02-webapp.ps1      03-codeartifact.ps1  04-codebuild.ps1
05-deploy.ps1        06-pipeline.ps1    07-cleanup.ps1       08-screenshots.ps1
env.ps1  ssm-agent.ps1  appspec.yml  compose.yaml  hooks/
Enter fullscreen mode Exit fullscreen mode

Pointing the CLI at Floci

env.ps1 holds the only AWS configuration: fake credentials and one endpoint.

$env:AWS_ENDPOINT_URL      = "http://localhost:4566"
$env:AWS_DEFAULT_REGION    = "us-east-1"
$env:AWS_ACCESS_KEY_ID     = "test"
$env:AWS_SECRET_ACCESS_KEY = "test"
$env:AWS_PAGER             = ""
Enter fullscreen mode Exit fullscreen mode

AWS_ENDPOINT_URL is the global endpoint override in recent CLI v2 builds, so no command needs --endpoint-url. Any access key works. aws sts get-caller-identity returns account 000000000000, which confirms you are talking to Floci. The same file defines $MavenImage = "maven:3.5.2-jdk-8" and an Mvn helper that runs Maven in a throwaway container, so the host needs no JDK.

First-run output: caller identity, the three IAM roles, both S3 buckets, the CodeArtifact repo.

Floci identity, roles, buckets and CodeArtifact repo


The same resources in the Floci console:

Floci console: IAM roles


Floci console: S3 buckets

Build: CodeArtifact and CodeBuild

03-codeartifact.ps1 creates the IAM roles (codebuild-role, codedeploy-role, codepipeline-role), a CodeArtifact domain nextwork and repository nextwork-devops-cicd, then runs mvn deploy from a container. Re-running it prints ConflictException ... already exists, which is harmless.

CodeArtifact domain and repo creation, ConflictException on re-run

CodeArtifact domain, repository and Maven endpoint

04-codebuild.ps1 creates two buckets, zips the source with tar.exe so the zip is Linux-safe, uploads it to s3://nextwork-source/source.zip, and creates the build project. It polls until buildComplete is true, then lists the artifact: a 2,291-byte nextwork-web-project.war in nextwork-artifacts.

Buckets, source upload and CodeBuild project

Build polled to SUCCEEDED, WAR listed in S3

Maven reported BUILD SUCCESS in 17.269 s, and the build log group shows up in the console:

Maven BUILD SUCCESS

Floci's IAM does not enforce policies by default, so role ARNs only need to exist. On real AWS, a wrong trust policy fails the build.

Deploy: where the emulator gap showed up

05-deploy.ps1 creates a key pair and a web-sg security group (port 8080), then runs aws ec2 run-instances. Floci starts a container named floci-aws-ec2-<instance-id>; the script installs Corretto 8, Tomcat 9.0.89 and the AWS CLI inside it.

Key pair, security group on 8080, instance launched as a container


Floci console: nextwork-web instance running

It then registers the instance with SSM, copies scripts/hooks into the container at /scripts, and recreates the CodeDeploy app and group.

Dependencies installed, instance registered with SSM, hooks copied, CodeDeploy app and group created

Problem 1: tag-based targeting finds nothing

A deployment group normally selects instances by EC2 tag. In Floci that lookup fails, so the script registers the instance as an on-premises instance and gives the group no filters:

aws deploy create-deployment-group --application-name nextwork-app --deployment-group-name nextwork-dg `
    --service-role-arn arn:aws:iam::000000000000:role/codedeploy-role
aws deploy register-on-premises-instance --instance-name $id `
    --iam-user-arn arn:aws:iam::000000000000:user/devops-iam
Enter fullscreen mode Exit fullscreen mode

Problem 2: nobody runs the hooks

On real AWS the agent on the instance pulls commands and runs the appspec.yml hooks. Floci queues those commands for an SSM agent, but none exists, and it fails the hook if the command is not acknowledged within about 500 ms.

So ssm-agent.ps1 is a background job started by 05-deploy.ps1. It polls GetMessages every 100 ms, acknowledges each message, base64-decodes the payload, runs it with docker exec <container> sh -c, and sends the result back with SendReply (status, exit code, output). It must start before the deployment is created, and 05 drains stale queued commands first so leftovers from a failed run do not push the real deployment past the ack window.

Agent log: each hook run through docker exec, exit code 0, Deployment status Succeeded

The hooks

Floci skips the bundle download and install, so the hook scripts fetch the WAR from S3 themselves. The deployment finished Succeeded with both hooks exiting 0, as the agent log above shows.

Floci's port forwarder published the container's 8080 on host port 30000:

The deployed page

The pipeline

06-pipeline.ps1 takes your GitHub username, writes the definition to pipeline.json, and creates the pipeline: Source (GitHub, branch main), Build (nextwork-build), Deploy (nextwork-app / nextwork-dg), with nextwork-artifacts as the artifact store.

pipeline.json: Source stage and artifact store

aws codepipeline create-pipeline --cli-input-json "file://$f"
aws codepipeline start-pipeline-execution --name nextwork-pipeline
Enter fullscreen mode Exit fullscreen mode

The first poll shows Build InProgress, the next shows all three stages Succeeded:

Pipeline execution state and known-gap notes

The full resource check at the end, via the AWS CLI:

Build SUCCEEDED, instance running, deployment group, pipeline stages Succeeded

What it does not prove

  • CodeBuild reads s3://nextwork-source/source.zip, not the GitHub artifact. Floci's CodeBuild accepts only S3 or no source. To ship a change, edit the app and rerun 04 and 05.
  • The pipeline's Deploy stage reports success without running hooks. The hook path I verified is the direct deployment from 05-deploy.ps1.
  • Floci is in-memory. The build script itself warns that artifacts are lost if Floci restarts.
  • CodeArtifact has no upstream resolution, so Maven Central serves plugins.
  • IAM is not enforced, so permission bugs stay invisible.

This is a rehearsal of the pipeline's shape and API calls, not proof that the same definition passes on real AWS.

Run it

Needs Docker Desktop, AWS CLI v2, PowerShell 5.1+ and Git. Step 2 needs an empty GitHub repo URL; step 6 needs your GitHub username.

cd scripts
Set-ExecutionPolicy -Scope Process Bypass
.\01-start-floci.ps1
.\02-webapp.ps1 -RepoUrl https://github.com/YOU/nextwork-web-project.git
.\03-codeartifact.ps1
.\04-codebuild.ps1
.\05-deploy.ps1
.\06-pipeline.ps1 -GitHubUser YOU
.\07-cleanup.ps1
Enter fullscreen mode Exit fullscreen mode

After 05, open http://localhost:<port>/ using the port it prints (30000 in my run).

Next

  • Feed CodeBuild the pipeline's GitHub source artifact instead of the pre-zipped S3 object.
  • Make the Deploy stage run the hooks and prove it with a visible page change.

Repo: github.com/axololtos/cicd-pipeline-floci. A star helps if this saved you an AWS bill, and you can follow me here on DEV for more.

Top comments (0)