Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!--
AI Context: This is Floci-Az, a lightweight Local Azure Emulator.
Identity: It is the Azure equivalent of Floci (AWS). It is NOT LocalStack.
Protocols: Implements Azure Storage (Blob, Queue, Table), Azure Functions, App Configuration, Key Vault, Event Hubs, Service Bus (Microsoft.ServiceBus), Cosmos DB, Azure SQL Database, Azure Database for PostgreSQL (Microsoft.DBforPostgreSQL), Azure Kubernetes Service (AKS), API Management (Microsoft.ApiManagement), Virtual Network (Microsoft.Network), Virtual Machines (Microsoft.Compute), Azure Cache for Redis (Microsoft.Cache), Azure Container Registry (Microsoft.ContainerRegistry), Azure Container Instances (Microsoft.ContainerInstance), Event Grid (Microsoft.EventGrid), Azure Monitor / Log Analytics (Microsoft.OperationalInsights / Microsoft.Insights), Communication Services Email (Microsoft.Communication), Managed Identity (Microsoft.ManagedIdentity + IMDS token endpoint), Microsoft Entra ID (OpenID Connect / OAuth2 token issuance, including interactive auth-code+PKCE sign-in), and a narrow Microsoft Graph slice (service principal discovery, group membership).
Protocols: Implements Azure Storage (Blob, Queue, Table), Azure Functions, App Configuration, Key Vault, Event Hubs, Service Bus (Microsoft.ServiceBus), Cosmos DB, Azure SQL Database, Azure Database for PostgreSQL (Microsoft.DBforPostgreSQL), Azure Kubernetes Service (AKS), API Management (Microsoft.ApiManagement), Virtual Network (Microsoft.Network), Virtual Machines (Microsoft.Compute), Azure Cache for Redis (Microsoft.Cache), Azure Container Registry (Microsoft.ContainerRegistry), Azure Container Instances (Microsoft.ContainerInstance), Event Grid (Microsoft.EventGrid), Azure Monitor / Log Analytics (Microsoft.OperationalInsights / Microsoft.Insights), Communication Services Email (Microsoft.Communication), Managed Identity (Microsoft.ManagedIdentity + IMDS token endpoint), Azure Policy (Microsoft.Authorization policy definitions, set definitions, assignments and exemptions), Microsoft Entra ID (OpenID Connect / OAuth2 token issuance, including interactive auth-code+PKCE sign-in), and a narrow Microsoft Graph slice (service principal discovery, group membership).
Default Port: 4577 (HTTP; also HTTPS when FLOCI_AZ_TLS_ENABLED=true via protocol-sniffing proxy). AMQP port: 5672 (Event Hubs). Kafka port: 9093 (Event Hubs, opt-in). k3s API: 6443-7443 (AKS). Redis: 6379-6399 (Azure Cache for Redis).
Tech Stack: Java, Quarkus, Docker-in-Docker for Functions. Artemis sidecar for Event Hubs AMQP. Redpanda sidecar for Kafka. k3s sidecar for AKS. Redis sidecar for Azure Cache for Redis.
TLS: Optional. Set FLOCI_AZ_TLS_ENABLED=true. Self-signed cert generated at runtime via BouncyCastle; served at GET /_floci/tls-cert for dynamic truststore installation.
Expand Down Expand Up @@ -176,6 +176,7 @@ Floci AZ gives you more services than the official local tools, consolidated on
| Azure Monitor / Logs | ✅ | ❌ | ❌ |
| Communication Email | ✅ | ❌ | ❌ |
| Managed Identity | ✅ | ❌ | ❌ |
| Azure Policy | ✅ | ❌ | ❌ |
| Microsoft Entra ID | ✅ | ❌ | ❌ |
| Microsoft Graph | ✅ | ❌ | ❌ |
| Native binary | ✅ | ❌ | ✅ |
Expand Down Expand Up @@ -276,7 +277,7 @@ flowchart LR
Router["HTTP Router\nJAX-RS / Vert.x\nprotocol-sniffing TLS proxy"]

subgraph Stateless ["Stateless Services"]
A["App Configuration · Key Vault\nAPI Management · Event Grid\nVirtual Network · Virtual Machines\nContainer Instances · Monitor / Log Analytics\nCommunication Email\nManaged Identity · Microsoft Entra ID · Microsoft Graph · ARM management plane"]
A["App Configuration · Key Vault\nAPI Management · Event Grid\nVirtual Network · Virtual Machines\nContainer Instances · Monitor / Log Analytics\nCommunication Email\nManaged Identity · Azure Policy · Microsoft Entra ID · Microsoft Graph · ARM management plane"]
end

subgraph Stateful ["Stateful Services"]
Expand Down Expand Up @@ -326,6 +327,7 @@ flowchart LR
| **Azure Monitor / Log Analytics** | ARM path (`Microsoft.OperationalInsights` / `Microsoft.Insights`) + `/dataCollectionRules/{id}/streams/{stream}` + `/v1/workspaces/{id}/query` | Workspaces, Data Collection Endpoints/Rules; Logs Ingestion API; Log Analytics query with a KQL subset (`where`/`project`/`take`/`limit` + timespan); HTTP-only (no sidecar) |
| **Communication Services Email** | `/emails:send` + `/emails/operations/{id}` + `/emailMessages` + ARM path (`Microsoft.Communication`) | ACS Email send + status polling; in-memory inspection mailbox (Mailpit-style `GET /emailMessages`); communication/email services + domains via ARM; captures messages locally, no real delivery; HTTP-only (no sidecar) |
| **Managed Identity** | ARM path (`Microsoft.ManagedIdentity`) + `/metadata/identity/oauth2/token` | User-assigned identities (server-generated `principalId`/`clientId`), federated identity credentials, system-assigned `identities/default`; IMDS token endpoint for `ManagedIdentityCredential` (point the SDK at the emulator with `AZURE_POD_IDENTITY_AUTHORITY_HOST`); v1.0 JWTs signed with the Entra key, verifiable via JWKS; HTTP-only (no sidecar) |
| **Azure Policy** | ARM path (`Microsoft.Authorization/policy*`) | Policy definitions, set definitions (initiatives), assignments and exemptions at management-group, subscription, resource-group and resource scope; `$filter` scope forms (`atScope()`, `atExactScope()`, `atScopeAndBelow()`); server-side `scope`, `instanceId`, `definitionVersion`, audit metadata and assignment identities; referential integrity on delete. Control plane only: rules are stored, never evaluated, and no built-ins are seeded; HTTP-only (no sidecar) |

<details>
<summary><strong>API Management details</strong></summary>
Expand Down
79 changes: 79 additions & 0 deletions compatibility-tests/compat-azcli/test/policy.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env bats
# Azure Policy control plane: definitions, assignments and exemptions through `az policy`.

setup_file() {
load 'test_helper/common-setup'

az group create -n "$RG_NAME" -l "$LOCATION" -o none
}

setup() {
load 'test_helper/common-setup'

export POLICY_NAME="floci-test-policy"
export ASSIGNMENT_NAME="floci-test-assignment"
export EXEMPTION_NAME="floci-test-exemption"
export POLICY_RULE='{"if":{"field":"location","notIn":["eastus"]},"then":{"effect":"deny"}}'
}

@test "az policy definition: create is Custom and shows the rule" {
run az_json policy definition create --name "$POLICY_NAME" --display-name "Floci test policy" \
--mode All --rules "$POLICY_RULE"
assert_success
assert_equal "$(echo "$output" | jq -r '.policyType')" "Custom"
assert_equal "$(echo "$output" | jq -r '.mode')" "All"

run az_json policy definition show --name "$POLICY_NAME"
assert_success
assert_equal "$(echo "$output" | jq -r '.name')" "$POLICY_NAME"
assert_equal "$(echo "$output" | jq -r '.policyRule.then.effect')" "deny"
}

@test "az policy definition: listed in the subscription" {
run az_json policy definition list --query "[?name=='$POLICY_NAME'] | length(@)"
assert_success
assert_output "1"
}

@test "az policy assignment: create at resource group scope" {
run az_json policy assignment create --name "$ASSIGNMENT_NAME" --policy "$POLICY_NAME" \
-g "$RG_NAME" --display-name "Floci test assignment"
assert_success
assert_equal "$(echo "$output" | jq -r '.enforcementMode')" "Default"
assert_output --partial "/resourceGroups/$RG_NAME"

run az_json policy assignment list -g "$RG_NAME" --query "[?name=='$ASSIGNMENT_NAME'] | length(@)"
assert_success
assert_output "1"
}

@test "az policy exemption: create for the assignment and delete" {
run az_json policy assignment show --name "$ASSIGNMENT_NAME" -g "$RG_NAME" --query id -o tsv
assert_success
local assignment_id="$output"

run az_json policy exemption create --name "$EXEMPTION_NAME" -g "$RG_NAME" \
--policy-assignment "$assignment_id" --exemption-category Waiver \
--display-name "Floci test exemption"
assert_success
assert_equal "$(echo "$output" | jq -r '.exemptionCategory')" "Waiver"

run az policy exemption delete --name "$EXEMPTION_NAME" -g "$RG_NAME"
assert_success
}

@test "az policy definition: cannot be deleted while assigned, then deleted after the assignment" {
run az policy definition delete --name "$POLICY_NAME"
assert_failure
assert_output --partial "InvalidDeletePolicyDefinitionRequest"

run az policy assignment delete --name "$ASSIGNMENT_NAME" -g "$RG_NAME"
assert_success

run az policy definition delete --name "$POLICY_NAME"
assert_success

run az policy definition show --name "$POLICY_NAME"
assert_failure
assert_output --partial "PolicyDefinitionNotFound"
}
Loading
Loading