second first commit

This commit is contained in:
Zakaria
2026-09-22 08:57:00 -04:00
parent e88d7e2ff3
commit 0aef7fb2d8
4766 changed files with 1236986 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
# AWS CLI & SDK Setup
Floci accepts any non-empty credentials — no real AWS account is needed.
## Environment Variables
The simplest approach for local development:
```bash
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_DEFAULT_REGION=us-east-1
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
```
## AWS CLI Profile
Add a dedicated profile to `~/.aws/config` and `~/.aws/credentials`:
```ini title="~/.aws/config"
[profile floci]
region = us-east-1
output = json
```
```ini title="~/.aws/credentials"
[floci]
aws_access_key_id = test
aws_secret_access_key = test
```
Then use it with every command:
```bash
aws s3 ls --profile floci --endpoint-url http://localhost:4566
```
Or set it as the default for your shell session:
```bash
export AWS_PROFILE=floci
export AWS_ENDPOINT_URL=http://localhost:4566
```
## SDK Configuration
### Java (AWS SDK v2)
```java
// Reusable endpoint override
URI endpoint = URI.create("http://localhost:4566");
AwsCredentialsProvider creds = StaticCredentialsProvider.create(
AwsBasicCredentials.create("test", "test"));
Region region = Region.US_EAST_1;
// Build any client the same way
DynamoDbClient dynamo = DynamoDbClient.builder()
.endpointOverride(endpoint)
.region(region)
.credentialsProvider(creds)
.build();
SqsClient sqs = SqsClient.builder()
.endpointOverride(endpoint)
.region(region)
.credentialsProvider(creds)
.build();
```
### Python (boto3)
```python
import boto3
def floci_client(service):
return boto3.client(
service,
endpoint_url="http://localhost:4566",
region_name="us-east-1",
aws_access_key_id="test",
aws_secret_access_key="test",
)
s3 = floci_client("s3")
sqs = floci_client("sqs")
dynamo = floci_client("dynamodb")
```
### Node.js / TypeScript
```typescript
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { SQSClient } from "@aws-sdk/client-sqs";
const config = {
endpoint: "http://localhost:4566",
region: "us-east-1",
credentials: { accessKeyId: "test", secretAccessKey: "test" },
};
const dynamo = new DynamoDBClient(config);
const sqs = new SQSClient(config);
```
!!! tip "S3 path-style URLs"
When using S3 with the AWS SDK v3 (Node.js), add `forcePathStyle: true` to the config object. Floci serves S3 in path-style mode (`http://localhost:4566/bucket-name`).
### Go
```go
import (
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
)
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion("us-east-1"),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("test", "test", "")),
config.WithEndpointResolverWithOptions(
aws.EndpointResolverWithOptionsFunc(
func(service, region string, opts ...interface{}) (aws.Endpoint, error) {
return aws.Endpoint{URL: "http://localhost:4566"}, nil
},
),
),
)
```
## Account ID
Floci uses account ID `000000000000` in all ARNs and queue URLs by default:
```
arn:aws:sqs:us-east-1:000000000000:my-queue
http://localhost:4566/000000000000/my-queue
```
Change the default with `FLOCI_DEFAULT_ACCOUNT_ID`:
```bash
FLOCI_DEFAULT_ACCOUNT_ID=123456789012
```
**Multi-account isolation** is also supported: if your access key ID is exactly 12 digits, Floci uses it directly as the account ID and fully isolates that account's resources from all others. See [Multi-Account Isolation](../configuration/multi-account.md) for details.
+85
View File
@@ -0,0 +1,85 @@
# Installation
Floci can be run three ways: as a Docker image, as a pre-built native binary, or built from source.
## Docker (Recommended)
No installation required beyond Docker itself.
```bash
docker pull floci/floci:latest
```
### Requirements
- Docker 20.10+
- `docker compose` v2+ (plugin syntax, not standalone `docker-compose`)
## Image Tags
Each tag combines a **variant** (what's inside) and a **channel** (how stable).
| | Standard | Baseline (ARM64 only) | Compat (+ AWS CLI + boto3) |
|---|---|---|---|
| **Release (latest)** | `latest` ✅ | `latest-baseline` | `latest-compat` |
| **Release (pinned)** | `x.y.z` | `x.y.z-baseline` | `x.y.z-compat` |
| **Nightly (floating)** | `nightly` | — | `nightly-compat` |
| **Nightly (dated)** | `nightly-mmddyyyy` | — | `nightly-mmddyyyy-compat` |
For the full breakdown see [Docker Images](../configuration/docker-images.md).
## Choosing a tag
```yaml title="docker-compose.yml"
# Standard release — recommended for most use cases
services:
floci:
image: floci/floci:latest
ports:
- "4566:4566"
```
Use the compat image if your workflow requires the AWS CLI or boto3 available inside the container:
```yaml title="docker-compose.yml"
services:
floci:
image: floci/floci:latest-compat
ports:
- "4566:4566"
```
Standard and compat have identical startup time (~24 ms) and memory footprint (~13 MiB). On Raspberry Pi 4-class ARM64 CPUs that lack LSE, use `floci/floci:latest-baseline` (or a pinned `x.y.z-baseline` release).
## Build from Source
### Prerequisites
- Java 25+
- Maven 3.9+
- (Optional) GraalVM Mandrel for native compilation
### Clone and run
```bash
git clone https://github.com/floci-io/floci.git
cd floci
mvn quarkus:dev # dev mode with hot reload on port 4566
```
### Build a production JAR
```bash
mvn clean package -DskipTests
java -jar target/quarkus-app/quarkus-run.jar
```
### Build a native executable
```bash
mvn clean package -Pnative -DskipTests
./target/floci-runner
```
!!! note
Native compilation requires GraalVM or Mandrel with the `native-image` tool on your PATH. Build time is typically 2–5 minutes.
@@ -0,0 +1,256 @@
# Migrate from LocalStack
Floci is a drop-in replacement for LocalStack Community. The wire protocol, port, credentials, and SDK configuration are identical, so most migrations require only an image swap. This page documents every change and provides a compatibility mode for projects that need a gentler transition.
## Compatibility mode
LocalStack environment variable translation is **on by default**. Floci automatically maps LocalStack variables to their Floci equivalents at startup, so you can keep your existing environment variables unchanged:
```yaml title="docker-compose.yml"
services:
floci:
image: floci/floci:latest
ports:
- "4566:4566"
environment:
# These LocalStack vars are automatically translated — no extra config needed:
PERSISTENCE: "1" # → FLOCI_STORAGE_MODE=persistent
LOCALSTACK_HOST: floci # → FLOCI_HOSTNAME=floci
LAMBDA_DOCKER_NETWORK: mynet # → FLOCI_SERVICES_LAMBDA_DOCKER_NETWORK=mynet
LAMBDA_REMOVE_CONTAINERS: "1" # → FLOCI_SERVICES_LAMBDA_EPHEMERAL=true
DEBUG: "1" # → QUARKUS_LOG_LEVEL=DEBUG
```
Explicitly set Floci variables always win — the translation only fills in values that haven't been set. To disable the translation entirely, set `LOCALSTACK_PARITY=false`.
Unless parity is disabled, the startup log also ends with a LocalStack-style `Ready.` line (in addition to Floci's own banner), so tooling that watches the container log for LocalStack's readiness message — such as the default wait strategy of Testcontainers' `LocalStackContainer` — works without a custom wait.
## Step-by-step migration
### 1 — Change the image
Pick the variant that matches your needs:
```yaml title="docker-compose.yml"
# Before
image: localstack/localstack
# After — no init scripts, or init scripts that don't call aws / boto3
image: floci/floci:latest
# After — init scripts that use aws CLI or boto3 (AWS CLI + Python 3 + boto3 pre-installed)
image: floci/floci:latest-compat
```
To pin a specific release, replace `latest` / `latest-compat` with a version tag:
```yaml
image: floci/floci:1.5.11
image: floci/floci:1.5.11-compat
```
The port (`4566`), credentials (`test` / `test`), and AWS SDK configuration are unchanged.
### 2 — Map environment variables
| LocalStack variable | Floci equivalent | Notes |
|---|---|---|
| `LOCALSTACK_HOST` | `FLOCI_HOSTNAME` | Hostname embedded in response URLs |
| `LOCALSTACK_HOSTNAME` | `FLOCI_HOSTNAME` | Alias — same effect |
| `PERSISTENCE=1` | `FLOCI_STORAGE_MODE=persistent` | Enable disk persistence |
| `PERSIST_STATE=1` | `FLOCI_STORAGE_MODE=persistent` | Alias for `PERSISTENCE` — same effect |
| `EDGE_PORT` | `FLOCI_PORT` | Bind port override |
| `GATEWAY_LISTEN` | `QUARKUS_HTTP_HOST` | Bind address override. Also sets `FLOCI_SECURITY_ALLOW_UNSAFE_NETWORK_EXPOSURE=true`, since LocalStack needs no opt-in for a non-loopback address |
| `LS_LOG` / `DEBUG=1` | `QUARKUS_LOG_LEVEL` | Log verbosity |
| `DOCKER_HOST` | `FLOCI_DOCKER_DOCKER_HOST` | Docker daemon socket path or TCP address |
| `LAMBDA_DOCKER_NETWORK` | `FLOCI_SERVICES_LAMBDA_DOCKER_NETWORK` | Network for Lambda containers |
| `DOCKER_NETWORK` | `FLOCI_SERVICES_DOCKER_NETWORK` | Network for all spawned containers |
| `LAMBDA_REMOVE_CONTAINERS=1` | `FLOCI_SERVICES_LAMBDA_EPHEMERAL=true` | Remove Lambda containers after invocation |
| `USE_SSL=1` | `FLOCI_TLS_ENABLED=true` | Enable TLS/HTTPS — see [TLS / HTTPS](../configuration/tls.md) |
| `CUSTOM_SSL_CERT_PATH` | `FLOCI_TLS_CERT_PATH` + `FLOCI_TLS_KEY_PATH` | LocalStack accepts a single combined PEM; Floci accepts it in both fields |
| `SERVICES` | _(not needed)_ | Floci starts every service instantly; no selection required |
| `LAMBDA_EXECUTOR` | _(not needed)_ | Floci always runs Lambda in Docker containers |
| `LAMBDA_REMOTE_DOCKER` | _(not supported)_ | Use per-function `S3Bucket=hot-reload` instead — see [Lambda](../services/lambda.md) |
### 3 — Init scripts (no change required)
LocalStack init scripts mounted under `/etc/localstack/init/` run unchanged in Floci:
```yaml title="docker-compose.yml"
volumes:
- ./init/ready.d:/etc/localstack/init/ready.d:ro # works as-is
```
Floci reads both `/etc/localstack/init/` (compat) and `/etc/floci/init/` (native). When the same filename exists in both, the Floci copy takes priority.
To use native Floci paths going forward:
```yaml title="docker-compose.yml"
volumes:
- ./init/ready.d:/etc/floci/init/ready.d:ro
```
See [Initialization Hooks](../configuration/initialization-hooks.md) for the full four-phase lifecycle (`boot`, `start`, `ready`, `stop`) and script type details (`.sh`, `.py`).
### 4 — Init script tooling (compat image)
If your init scripts call `aws` or `boto3`, switch from `localstack/localstack` to `floci/floci:latest-compat`:
```yaml title="docker-compose.yml"
# Before
image: localstack/localstack
# After (includes Python 3, AWS CLI, boto3 — pre-configured for localhost:4566)
image: floci/floci:latest-compat
```
The compat image pre-configures the AWS CLI to talk to `http://localhost:4566` — no `--endpoint-url` flag is needed in scripts:
```sh
#!/bin/sh
aws sqs create-queue --queue-name orders # no --endpoint-url needed
aws s3 mb s3://assets
```
### 5 — Health and status endpoints
Floci serves the LocalStack-compatible status endpoint at both paths:
```
GET /_localstack/init # LocalStack compat path — still works
GET /_floci/init # native path
```
If you wait on `/_localstack/init` or `/_localstack/health` in CI or scripts, no change is needed.
### 6 — Inspection endpoints
| Endpoint | Notes |
|---|---|
| `GET /_aws/ses` | Captured emails — identical |
| `GET /_aws/ses?id=<id>` | Single message — identical |
| `DELETE /_aws/ses` | Clear mailbox — identical |
| `GET /_aws/sqs/messages?QueueUrl=<url>` | Non-destructive queue peek — identical |
| `DELETE /_aws/sqs/messages?QueueUrl=<url>` | Purge queue — identical |
### 7 — Testcontainers
=== "Java"
Replace the `@LocalStackContainer` module with the Floci module:
```xml title="pom.xml"
<!-- Before -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>localstack</artifactId>
<version>1.21.4</version>
<scope>test</scope>
</dependency>
<!-- After -->
<dependency>
<groupId>io.floci</groupId>
<artifactId>testcontainers-floci</artifactId>
<version>1.14.0</version>
<scope>test</scope>
</dependency>
```
See the [Java Testcontainers guide](../testcontainers/java.md) for full setup.
=== "Python"
See the [Python Testcontainers guide](../testcontainers/python.md).
=== "Node.js"
See the [Node.js Testcontainers guide](../testcontainers/nodejs.md).
=== "Go"
See the [Go Testcontainers guide](../testcontainers/go.md).
## Complete before / after example
```yaml title="docker-compose.yml (before — LocalStack)"
services:
localstack:
image: localstack/localstack
ports:
- "4566:4566"
environment:
LOCALSTACK_HOST: localstack
PERSISTENCE: "1"
LAMBDA_DOCKER_NETWORK: myapp_default
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/var/lib/localstack
- ./init/ready.d:/etc/localstack/init/ready.d:ro
```
```yaml title="docker-compose.yml (after — Floci, minimal change)"
services:
floci:
image: floci/floci:latest-compat # (1)
ports:
- "4566:4566"
environment:
LOCALSTACK_HOST: floci # translated automatically — no rename needed
PERSISTENCE: "1"
LAMBDA_DOCKER_NETWORK: myapp_default
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/app/data # (2)
- ./init/ready.d:/etc/localstack/init/ready.d:ro # compat path — unchanged
```
1. Switch to `latest-compat` if your init scripts use `aws` or `boto3`.
2. LocalStack stores data in `/var/lib/localstack`; Floci uses `/app/data`.
## S3 virtual-hosted style DNS
If you use LocalStack's public wildcard DNS (`*.s3.localhost.localstack.cloud`) for S3 virtual-hosted style addressing, Floci supports it without any change:
```java
// This LocalStack endpoint works unchanged with Floci
S3Client s3 = S3Client.builder()
.endpointOverride(URI.create("http://s3.localhost.localstack.cloud:4566"))
.build();
// SDK sends to: my-bucket.s3.localhost.localstack.cloud:4566 → Floci
```
Floci also registers its own wildcard DNS domains for virtual-hosted style:
| Domain | Usage |
|---|---|
| `*.s3.localhost.floci.io` | S3 virtual-hosted style (`bucket.s3.localhost.floci.io`) |
| `*.localhost.floci.io` | Direct subdomain style (`bucket.localhost.floci.io`) |
DNS resolution works differently depending on where the client runs:
**From the host machine** — both `*.localhost.localstack.cloud` and `*.localhost.floci.io` are registered in public DNS and resolve to `127.0.0.1`. Requests reach Floci via the Docker port binding (`4566:4566`) with no extra configuration.
**From inside a Docker container** — `127.0.0.1` is the container's own loopback, not Floci. Floci's embedded DNS server handles this: it resolves `*.localhost.floci.io` and `*.localhost.localstack.cloud` (and `*.localhost.localstack.cloud` subdomains) to Floci's container IP on the Docker network. Spawned containers (Lambda, RDS, ElastiCache) are automatically configured to use Floci as their DNS resolver, so virtual-hosted S3 URLs work inside them without any extra setup.
See [S3 → Virtual-Hosted Style](../services/s3.md#virtual-hosted-style) for full details and SDK examples.
## What stays the same
- Port `4566`
- All AWS SDK and CLI calls — no code changes
- Dummy credentials (`test` / `test`)
- Init scripts under `/etc/localstack/init/` (compat paths)
- `/_localstack/init` and `/_localstack/health` endpoints
- `/_aws/ses` and `/_aws/sqs/messages` inspection endpoints
- Docker socket mount for Lambda, RDS, and ElastiCache
## Known differences
| Area | LocalStack | Floci |
|---|---|---|
| Lambda executor | Configurable (`LAMBDA_EXECUTOR`) | Always Docker containers |
| `LAMBDA_REMOTE_DOCKER` | Supported | Not supported — use per-function `S3Bucket=hot-reload` instead |
| Service selection | `SERVICES=sqs,s3,...` | All services start automatically; no selection |
| Data directory | `/var/lib/localstack` | `/app/data` |
| Log variable | `LS_LOG` / `DEBUG` | `QUARKUS_LOG_LEVEL` |
+213
View File
@@ -0,0 +1,213 @@
# Quick Start
This guide gets Floci running and verifies that AWS CLI commands work against it in under five minutes.
## Step 1 — Start Floci
=== "Native (recommended)"
`latest` is the native image — sub-second startup, minimal memory:
```yaml
services:
floci:
image: floci/floci:latest
ports:
- "4566:4566"
volumes:
# Local directory bind mount (default)
- ./data:/app/data
# OR named volume (optional):
# - floci-data:/app/data
# volumes:
# floci-data:
```
```bash
docker compose up -d
```
=== "Compat (+ AWS CLI + boto3)"
Use `latest-compat` if you need the AWS CLI and boto3 inside the container:
```yaml
services:
floci:
image: floci/floci:latest-compat
ports:
- "4566:4566"
volumes:
# Local directory bind mount (default)
- ./data:/app/data
# OR named volume (optional):
# - floci-data:/app/data
# volumes:
# floci-data:
```
```bash
docker compose up -d
```
=== "Build from source"
```bash
git clone https://github.com/floci-io/floci.git
cd floci
mvn quarkus:dev # hot reload, port 4566
```
## Step 2 — Configure AWS CLI
Floci accepts any dummy credentials — no real AWS account needed.
```bash
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_DEFAULT_REGION=us-east-1
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
```
Add these to your shell profile (`.bashrc` / `.zshrc`) so they persist across sessions.
## Step 3 — Verify the Setup
Run a few quick smoke tests:
```bash
# S3 — create a bucket and upload a file
aws s3 mb s3://my-bucket --endpoint-url $AWS_ENDPOINT_URL
echo "hello floci" | aws s3 cp - s3://my-bucket/hello.txt --endpoint-url $AWS_ENDPOINT_URL
aws s3 ls s3://my-bucket --endpoint-url $AWS_ENDPOINT_URL
# SQS — create a queue and send a message
aws sqs create-queue --queue-name orders --endpoint-url $AWS_ENDPOINT_URL
aws sqs send-message \
--queue-url $AWS_ENDPOINT_URL/000000000000/orders \
--message-body '{"event":"order.placed"}' \
--endpoint-url $AWS_ENDPOINT_URL
# DynamoDB — create a table
aws dynamodb create-table \
--table-name Users \
--attribute-definitions AttributeName=id,AttributeType=S \
--key-schema AttributeName=id,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--endpoint-url $AWS_ENDPOINT_URL
```
You should see successful responses for all three commands.
## Step 4 — Use in Your Application
Point your AWS SDK to Floci the same way:
=== "Java"
```java
S3Client s3 = S3Client.builder()
.endpointOverride(URI.create("http://localhost:4566"))
.region(Region.US_EAST_1)
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create("test", "test")))
.build();
```
=== "Python (boto3)"
```python
import boto3
s3 = boto3.client(
"s3",
endpoint_url="http://localhost:4566",
region_name="us-east-1",
aws_access_key_id="test",
aws_secret_access_key="test",
)
```
=== "Node.js"
```javascript
import { S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "http://localhost:4566",
region: "us-east-1",
credentials: { accessKeyId: "test", secretAccessKey: "test" },
forcePathStyle: true,
});
```
=== "Go"
```go
cfg, _ := config.LoadDefaultConfig(context.TODO(),
config.WithRegion("us-east-1"),
config.WithEndpointResolverWithOptions(
aws.EndpointResolverWithOptionsFunc(func(service, region string, opts ...interface{}) (aws.Endpoint, error) {
return aws.Endpoint{URL: "http://localhost:4566"}, nil
}),
),
)
client := s3.NewFromConfig(cfg)
```
## Step 5 — (Optional) Push and pull a container image to emulated ECR
Floci emulates ECR with a real OCI registry behind it, so the stock `docker` client works against repositories you create through the AWS CLI. No daemon configuration needed — Floci returns repository URIs that resolve to loopback, which `docker` auto-trusts as insecure.
```bash
# Create the repository (lazy-starts the backing registry container)
aws ecr create-repository --repository-name floci-it/app --endpoint-url $AWS_ENDPOINT_URL
# Authenticate
aws ecr get-login-password --endpoint-url $AWS_ENDPOINT_URL \
| docker login --username AWS --password-stdin \
000000000000.dkr.ecr.us-east-1.localhost:4566
# Push
docker pull alpine:3.19
docker tag alpine:3.19 000000000000.dkr.ecr.us-east-1.localhost:4566/floci-it/app:v1
docker push 000000000000.dkr.ecr.us-east-1.localhost:4566/floci-it/app:v1
# Pull from a clean local image store
docker rmi 000000000000.dkr.ecr.us-east-1.localhost:4566/floci-it/app:v1
docker pull 000000000000.dkr.ecr.us-east-1.localhost:4566/floci-it/app:v1
```
See the [ECR service docs](../services/ecr.md) for the full action surface, image-backed Lambda integration, and CDK `DockerImageFunction` support.
## Lambda on native Linux Docker (UFW)
When Floci runs **natively on a Linux host** (not Docker Desktop), Lambda function containers reach Floci's Runtime API server via the docker bridge gateway. On Ubuntu / Pop!_OS / Debian boxes with **UFW enabled**, the default `INPUT DROP` policy silently drops these packets and Lambda invocations time out with `Function.TimedOut`. This affects every Lambda packaging type — Zip *and* image-backed functions deployed via emulated ECR.
**One-time fix**, scoped to the docker bridge only (does not expose anything to the network — `docker0` is internal):
```bash
sudo ufw allow in on docker0 comment 'floci: containers reach host'
```
If you want to scope it tighter to the Lambda Runtime API and Floci's ECR data plane:
```bash
sudo ufw allow in on docker0 to any port 12000:12499 proto tcp comment 'floci lambda runtime api'
sudo ufw allow in on docker0 to any port 4566 proto tcp comment 'floci ecr data plane'
```
**Docker Desktop** (macOS / Windows / Linux) does not need this — it routes container → host through the Docker VM, which Floci's `DockerHostResolver` detects automatically.
**Floci-in-Docker** (running the published Floci image inside a container) does not need this either — Lambda containers and Floci share the same docker network and reach each other via container IPs.
## Next Steps
- [Configure Docker Compose with ElastiCache and RDS ports](../configuration/docker-compose.md)
- [Environment variables reference](../configuration/environment-variables.md)
- [application.yml reference (source builds)](../configuration/advanced/application-yml.md)
- [Browse per-service documentation](../services/index.md)
+144
View File
@@ -0,0 +1,144 @@
# Terraform with Floci
Terraform can use Floci as a local AWS-compatible endpoint. Terraform sends requests through the standard `hashicorp/aws` provider, while Floci stores and emulates the resources locally. No real AWS account or cloud resources are used.
## Start Floci
```yaml title="docker-compose.yml"
services:
floci:
image: floci/floci:latest
ports:
- "4566:4566"
volumes:
- ./data:/app/data
```
```bash
docker compose up -d
```
## Configure the AWS provider
Create `provider.tf`:
```hcl
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "us-east-1"
access_key = "test"
secret_key = "test"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
s3_use_path_style = true
endpoints {
dynamodb = "http://localhost:4566"
secretsmanager = "http://localhost:4566"
s3 = "http://localhost:4566"
sns = "http://localhost:4566"
sqs = "http://localhost:4566"
ssm = "http://localhost:4566"
}
}
```
The endpoint must be configured for every AWS service used by the Terraform configuration. See the [services overview](../services/index.md) for the services implemented by Floci.
## Create local AWS resources
Create `main.tf`:
```hcl
resource "aws_s3_bucket" "app" {
bucket = "floci-terraform-example"
}
resource "aws_sqs_queue" "jobs" {
name = "floci-terraform-jobs"
}
resource "aws_dynamodb_table" "items" {
name = "floci-terraform-items"
billing_mode = "PAY_PER_REQUEST"
hash_key = "id"
attribute {
name = "id"
type = "S"
}
}
resource "aws_ssm_parameter" "environment" {
name = "/floci/environment"
type = "String"
value = "local"
}
```
Initialize and apply the configuration:
```bash
terraform init
terraform validate
terraform plan
terraform apply
```
Verify a resource through the AWS CLI:
```bash
aws --endpoint-url http://localhost:4566 \
s3api head-bucket --bucket floci-terraform-example
```
Remove the local resources when finished:
```bash
terraform destroy
```
## Terraform state in Floci
Terraform can keep state locally by using its default local backend. For a fully emulated AWS workflow, Floci also supports the S3 backend and DynamoDB locking used by the compatibility suite:
```hcl
terraform {
backend "s3" {
bucket = "tfstate"
key = "terraform.tfstate"
region = "us-east-1"
endpoint = "http://localhost:4566"
dynamodb_endpoint = "http://localhost:4566"
dynamodb_table = "tflock"
access_key = "test"
secret_key = "test"
skip_credentials_validation = true
skip_region_validation = true
use_path_style = true
}
}
```
Create the backend resources before running `terraform init`:
```bash
aws --endpoint-url http://localhost:4566 s3api create-bucket --bucket tfstate
aws --endpoint-url http://localhost:4566 dynamodb create-table \
--table-name tflock \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
```
For a complete automated example covering more services and the full lifecycle, see [`compatibility-tests/compat-terraform`](https://github.com/floci-io/floci/tree/main/compatibility-tests/compat-terraform).