Dev Factory Code Wiki

devfactory-core-control

AWS provisioning automation API for the Dev Factory. A C# ASP.NET Core

C# · 249 arquivos JSON · 6 arquivos MSBuild · 2 arquivos Markdown · 1 arquivos 262 arquivos

Endpoints HTTP 227

MétodoRotaArquivo
GET/Program.cs
GET/.metadataProgram.cs
GET/admin/activityActivityEndpoints.cs
GET/AdminEntraAppsEndpoints.cs
DELETE/{appId}AdminEntraAppsEndpoints.cs
POST/admin/git/reconcile-teamsAdminGitAccessEndpoints.cs
GET/AdminGitAccessEndpoints.cs
PUT/AdminGitAccessEndpoints.cs
GET/{subject}AdminGitAccessEndpoints.cs
PUT/{subject}/profileAdminGitAccessEndpoints.cs
DELETE/{subject}AdminGitAccessEndpoints.cs
POST/{subject}/git/syncAdminGitAccessEndpoints.cs
DELETE/{subject}/orphan-groups/{projectId}AdminGitAccessEndpoints.cs
GET/AdminGitAccessEndpoints.cs
PUT/AdminGitAccessEndpoints.cs
GET/validationAdminGitAccessEndpoints.cs
POST/syncAdminGitAccessEndpoints.cs
GET/AdminSecretsEndpoints.cs
GET/{collection}AdminSecretsEndpoints.cs
POST/{collection}AdminSecretsEndpoints.cs
GET/{collection}/{id}AdminSecretsEndpoints.cs
PUT/{collection}/{id}AdminSecretsEndpoints.cs
PATCH/{collection}/{id}AdminSecretsEndpoints.cs
DELETE/{collection}/{id}AdminSecretsEndpoints.cs
POST/{collection}/{id}/rotateAdminSecretsEndpoints.cs
GET/healthApiEndpoints.cs
GET/ApiEndpoints.cs
GET/overviewApiEndpoints.cs
POST/ApiEndpoints.cs
GET/{id}ApiEndpoints.cs
PATCH/{id}ApiEndpoints.cs
POST/{id}/on-demandApiEndpoints.cs
DELETE/{id}ApiEndpoints.cs
GET/{id}/activityApiEndpoints.cs
GET/{id}/resourcesApiEndpoints.cs
POST/{id}/resourcesApiEndpoints.cs
GET/{id}ApiEndpoints.cs
DELETE/{id}ApiEndpoints.cs
PATCH/{id}ApiEndpoints.cs
POST/{id}/retryApiEndpoints.cs
POST/{id}/disableApiEndpoints.cs
GET/catalogApiEndpoints.cs
POST/ApplicationEndpoints.cs
GET/ApplicationEndpoints.cs
GET/{appId}ApplicationEndpoints.cs
PATCH/{appId}ApplicationEndpoints.cs
DELETE/{appId}ApplicationEndpoints.cs
POST/{appId}/unarchiveApplicationEndpoints.cs
POST/{appId}/retryApplicationEndpoints.cs
PUT/{appId}/defaultApplicationEndpoints.cs
DELETE/{appId}/defaultApplicationEndpoints.cs
GET/ApplicationTemplateEndpoints.cs
PUT/ApplicationTemplateEndpoints.cs
GET/validationApplicationTemplateEndpoints.cs
GET/catalog/applicationsApplicationTemplateEndpoints.cs
POST/ArtifactEndpoints.cs
POST/{releaseId}/completeArtifactEndpoints.cs
GET/ArtifactEndpoints.cs
GET/{releaseId}/downloadArtifactEndpoints.cs
GET/projects/{id}/applications/{appId}/publish-guideArtifactEndpoints.cs
GET/admin/aws-costAwsCostEndpoints.cs
GET/admin/aws-cost/projects/{projectId}AwsCostEndpoints.cs
GET/admin/aws-cost/sharedAwsCostEndpoints.cs
GET/admin/bedrock-keysBedrockKeysEndpoints.cs
POST/admin/bedrock-keys/{userName}/credentials/{credentialId}/invalidateBedrockKeysEndpoints.cs
POST/admin/bedrock-keys/{userName}/credentials/{credentialId}/reactivateBedrockKeysEndpoints.cs
GET/admin/bedrock-keys/{userName}/timelineBedrockKeysEndpoints.cs
GET/admin/bedrock-usageBedrockUsageEndpoints.cs
GET/admin/bedrock-usage/summaryBedrockUsageEndpoints.cs
GET/admin/bedrock-usage/users/{user}BedrockUsageEndpoints.cs
GET/me/bedrock-usageBedrockUsageEndpoints.cs
GET/admin/bedrock-usage/installmentBedrockUsageEndpoints.cs
GET/admin/capacityCapacityEndpoints.cs
POST/ecr-loginCiEndpoints.cs
GET/latestClarityAnalyticsEndpoints.cs
GET/trendClarityAnalyticsEndpoints.cs
GET/configClarityAnalyticsEndpoints.cs
PUT/configClarityAnalyticsEndpoints.cs
POST/syncClarityAnalyticsEndpoints.cs
GET/deep-linkClarityAnalyticsEndpoints.cs

Estrutura

📁 contracts/1 arquivos
· openapi.json
📁 src/183 arquivos
📁 DevFactory.ApisAws/
📁 tests/72 arquivos
📁 DevFactory.ApisAws.Tests/
· .gitattributes
· .gitignore
· README.md
· smoke.ps1
· smoke.sh
· version.json

README

devfactory-apis-aws

AWS provisioning automation API for the Dev Factory. A C# ASP.NET Core (net10.0) minimal-API service that receives REST calls (from the apis-console YARP gateway) and provisions AWS infrastructure — S3 / ECR / DynamoDB / EKS — via the AWS SDK for .NET.

It runs in two modes: a mock mode (zero AWS calls, for local dev and CI) and an aws mode (real SDK calls under the default credential chain). Provisioning is asynchronous: the create endpoint returns 202 immediately and a background worker drives each resource through its lifecycle.


Endpoints

All paths are forwarded 1:1 from the gateway. JSON is camelCase.

MethodPathAuthDescription
GET/healthpublic`{ status:"ok", mode:"mock\aws", time }`
GET/projectsapi-keyList projects
POST/projectsapi-keyCreate a project { name, description? }201
GET/projects/{id}api-keyGet a project (includes its resources[]) — 404 if absent
DELETE/projects/{id}api-keyDelete a project and deprovision its resources → 204
GET/projects/{id}/resourcesapi-keyList a project's resources
POST/projects/{id}/resourcesapi-keyProvision a resource { type, name, config? }202
GET/projects/{id}/activityapi-keyList a project's activity events (newest first)
GET/resources/{id}api-keyGet a resource
DELETE/resources/{id}api-keyDeprovision a resource → 204
GET/catalogapi-keyProvisionable types + their config form fields
PUT/projects/{id}/tagsproject.updateReplace a project's tags { tags:{k:v} }
PATCH/projects/{id}/tagsproject.updateMerge tags (a null value removes the key)
GET/tagsapi-keyAggregated tag facets (keys → values + counts) over visible projects
GET/tags/taxonomyapi-keyKnown tag keys, drill-down order, suggestions
PUT/tags/taxonomyplatform.adminReplace the platform tag taxonomy
GET/saved-viewsapi-keyGlobal views + the caller's personal saved filters
POST/saved-viewsapi-key/platform.adminCreate a saved view (global ⇒ admin) → 201
PUT/saved-views/{id}owner/adminUpdate a saved view (name/filters/pin/icon/color)
DELETE/saved-views/{id}owner/adminDelete a saved view → 204
POST/projects/{id}/observability/signalsproject-key / project.updateRegister telemetry signals (batch) → 202
GET/projects/{id}/observability/signalsproject.readRecent signals (filter: env/kind/level/since/q/top)
GET/projects/{id}/observability/summaryproject.readCounts by level/kind, error rate, 24h buckets

Swagger UI is served at /swagger (the root / redirects there); the OpenAPI document is at /swagger/v1/swagger.json.

Models (camelCase JSON)


// Project — id like prj_<8hex>
{ "id", "name", "description", "status", "createdAt", "resourceCount",
  "tags": { "organization": "energia", "workspace": "ops" } /* AWS-style key/value */ }

// Resource — id like res_<8hex>; status ∈ pending|provisioning|active|failed|deleting
{ "id", "projectId", "type", "name", "status", "arn", "endpoint",
  "region", "config", "createdAt", "message" }

// ActivityEvent — id like evt_<8hex>
{ "id", "projectId", "resourceId", "action", "status", "message", "at" }

// ResourceTypeSpec (GET /catalog)
{ "type", "label", "icon",
  "fields": [ { "key", "type" /* bool|string|number|select */, "label", "default", "options?" } ] }

types3 | ecr | dynamodb | eks.

Project tags & saved views

Projects carry AWS-style key/value tags (Dictionary<string,string>, validated by TagPolicy: ≤50 tags, key ≤128 / value ≤256 chars, devfactory: prefix reserved). They are console-only today (organize + filter; not yet stamped onto provisioned AWS resources). The console builds an *emergent* organization → workspace → team → … hierarchy from them via faceted, drill-down filtering — there are no first-class org/workspace entities; the levels are just tag keys.

Tags, the taxonomy and saved views all persist through the reserved registry partition (devfactory-platform-registry, unreachable from the public data-plane route grammar), like the rest of the control-plane registry, so they survive pod restarts.


// SavedView — id like view_<8hex>
{ "id", "name", "scope": "personal|global", "ownerSubject", "pinned",
  "filters": { "organization": ["energia"], "workspace": ["ops"] },
  "icon", "color", "order", "createdAt", "updatedAt", "createdBy" }

Observability (telemetry)

Apps register observability signals — event / log / metric / trace — and the console explores them (the data explorer, but for app telemetry). Ingest accepts a project API key (x-project-key, app→platform) or an Entra user with project.update; reads require project.read.


// TelemetrySignal — id like sig_<8hex>
{ "id", "projectId", "env", "kind": "event|log|metric|trace", "name", "level": "debug|info|warn|error",
  "message", "value", "unit", "attributes": {"route":"/orders"}, "timestamp", "receivedAt" }

The IAM role apis-aws runs under needs logs:CreateLogGroup|CreateLogStream|PutLogEvents and cloudwatch:PutMetricData for the sink (ingest still works without them).


Modes

Selected by the PROVISIONING_MODE env var (default mock).

mock (default)

No AWS calls whatsoever. The worker simulates a realistic delay (≈1–3 s for s3/ecr/dynamodb, longer for eks) then marks the resource active with a synthesized, obviously-fake arn (it contains mock) and endpoint. This lets the whole API run and be exercised with zero AWS access.

aws

Uses the AWS SDK for .NET. Credentials resolve from the default chain (env vars / shared config / instance or App Runner instance role). Per-type provisioners:

TypeWhat it does
s3PutBucketAsync (us-east-1), applies tags, optional versioning. arn:aws:s3:::<bucket>, endpoint https://<bucket>.s3.us-east-1.amazonaws.com.
ecrCreateRepositoryAsync + tags (honours scanOnPush, tagMutability). Returns the repo arn + repositoryUri as endpoint.
dynamodbCreateTableAsync (PK id S, PAY_PER_REQUEST) + tags, then polls until ACTIVE (bounded by a 2 min timeout).
eksGuarded. Only calls CreateClusterAsync if both EKS_ROLE_ARN and EKS_SUBNET_IDS are set — and then returns immediately with status provisioning (it never blocks for the ~15 min creation). Otherwise it returns active with message *"EKS provisioning delegated to Terraform IaC (devfactory-iac)"* and a synthesized arn.

On any AWS exception in aws-mode the resource is set to failed, the AWS error message is captured in message + an ActivityEvent, and the worker keeps running (it never crashes).

Resource naming & tags

Resources are named <short>-<env>-devfactory-df-<project>-<name>, sanitized per service (lowercase, dns-safe, length-capped). S3 additionally appends the account id for global uniqueness — mirroring the existing devfactory-files-992151248006 bucket, e.g. s3-dev-devfactory-df-myproj-assets-992151248006. env comes from ENVIRONMENT (default dev).

Every AWS resource is tagged: Project, Resource, Environment, Tenant=devfactory, ManagedBy=devfactory-apis-aws.


Environment variables

VarDefaultPurpose
PROVISIONING_MODEmockmock (no AWS) or aws (real SDK calls).
ENVIRONMENTdevLogical env slug embedded in resource names.
SERVICE_API_KEY_(unset)_If set, every route except /health requires a matching api-key header. Unset ⇒ open (local dev). The gateway injects this header.
EKS_ROLE_ARN_(unset)_IAM role ARN for the EKS control plane. Required to actually create an EKS cluster.
EKS_SUBNET_IDS_(unset)_Comma-separated subnet ids for EKS. Required to actually create an EKS cluster.
AWS_REGIONus-east-1Region for the SDK clients and synthesized arns/endpoints.
AWS_ACCOUNT_ID992151248006Appended to S3 bucket names for global uniqueness.
GITHUB_TOKEN_(unset)_PAT with repo-creation rights. Unset ⇒ GitHub provisioning runs in mock mode.
GITHUB_ORGdevfactoryPlatform org: source of generate-from-template + connection-kit reference links.
GITHUB_APPS_ORGdevfactory-appsApps org: where created project repos (app/docs) are created and release CI is wired.
ASPNETCORE_URLShttp://0.0.0.0:8080Bind address. Keep :8080 for App Runner.
Auth model: the Entra JWT is validated upstream at the gateway. This service trusts the
gateway and only checks the shared SERVICE_API_KEY. CORS is wide-open here for dev — the
gateway handles real CORS.

Run it locally

Requires the .NET 10 SDK (dotnet --version10.0.x).


# mock mode (default) — no AWS credentials needed
cd src/DevFactory.ApisAws
PROVISIONING_MODE=mock dotnet run

# then, in another shell:
curl http://localhost:8080/health
curl -X POST http://localhost:8080/projects \
  -H 'content-type: application/json' -d '{"name":"demo"}'

Open <http://localhost:8080/swagger> for the interactive UI.

To run against real AWS (creates real resources, costs money):


PROVISIONING_MODE=aws AWS_REGION=us-east-1 \
  AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... \
  dotnet run

Smoke test

smoke.sh (bash + curl + jq) and smoke.ps1 (PowerShell) start the app in mock mode, wait for /health, create a project + an S3 resource, poll the resource until active, print the activity feed, and exercise tags + saved views end-to-end (create a tagged project, PATCH tags, read facets/taxonomy, create→list→delete a saved view).


./smoke.sh           # or:  pwsh ./smoke.ps1

Build


dotnet build -c Release        # 0 warnings, 0 errors

Publish the container (no Dockerfile / no daemon)

The csproj carries the container props (ContainerRepository, base image, exposed port 8080, default env). The SDK's built-in container tooling builds the OCI image directly:


cd src/DevFactory.ApisAws

# A) push straight to a registry (e.g. the DevFactory ECR repo) — no Docker daemon required:
dotnet publish -c Release --os linux --arch x64 -t:PublishContainer \
  -p:ContainerRegistry=992151248006.dkr.ecr.us-east-1.amazonaws.com \
  -p:ContainerRepository=devfactory-apis-aws \
  -p:ContainerImageTag=latest
# (authenticate first: aws ecr get-login-password | docker login ... — or use the registry's
#  credential helper; the SDK reads ~/.docker/config.json auths)

# B) or emit a portable tarball (handy where no daemon/registry is reachable):
dotnet publish -c Release --os linux --arch x64 -t:PublishContainer \
  -p:ContainerArchiveOutputPath=./apis-aws-image.tar.gz

# C) or, on a machine WITH Docker, the default target loads it into the local daemon:
dotnet publish -c Release --os linux --arch x64 -t:PublishContainer
docker run --rm -p 8080:8080 -e PROVISIONING_MODE=mock devfactory-apis-aws:latest

The produced image runs DevFactory.ApisAws on top of mcr.microsoft.com/dotnet/aspnet:10.0 and listens on :8080.

Deploy (AWS App Runner)

Same shape as the sibling APIs that are already live. Push the image to ECR (step A above), then point an App Runner service at it:

The apis-console gateway's AwsProvisioning cluster should point at the resulting App Runner URL in production (it defaults to http://apis-aws:8080 for local compose).

Deployment itself is handled by the orchestrator / IaC — this repo does not deploy.

Project layout


src/DevFactory.ApisAws/
  DevFactory.ApisAws.csproj   net10.0 web app + AWS SDK pkgs + container publish props
  Program.cs                      DI wiring, JSON, Swagger, CORS, middleware, endpoints, seed
  appsettings.json                logging config
  Models/                         Project, Resource, ActivityEvent, ResourceTypeSpec, Catalog, Requests
  Stores/                         IRegistryStore + InMemoryRegistryStore, ActivityRecorder, Ids, DemoSeeder
  Provisioning/                   IResourceProvisioner + S3/Ecr/DynamoDb/Eks/Mock provisioners,
                                  ProvisionerDispatcher, ProvisioningQueue, ProvisioningWorker (BackgroundService),
                                  ProvisioningOptions, ResourceNaming, ResourceTags, ConfigReader
  Endpoints/                      ApiEndpoints (minimal-API mapping), ApiKeyMiddleware
smoke.sh / smoke.ps1              end-to-end mock-mode smoke test

Design notes