IaCProvisioningService¶
Target
This page describes intended behaviour or structure. No implementation is claimed unless a section is explicitly marked Current implementation or Evidence.
| Bounded context | BC-25 Provisioning & Reconciliation |
| Platform / plane | Runtime Cloud / Execution |
| Deployment granularity | module of host · host RuntimeCloudHost |
| Status | target · ConnectSoft.Factory.RuntimeCloud.Provisioning |
| Built from | connectsoft-microservice · libraries: connectsoft-extensions-saas-abstractions, connectsoft-extensions-messaging-model, connectsoft-extensions-persistence-model-nhibernate, connectsoft-extensions-scheduler-model-hang-fire |
| Decisions | ADR-0004, ADR-0015 |
| Traceability | FR-08 FUX-05 NFR-08 NFR-09 XL-04 XL-10 |
Purpose and responsibilities¶
IaCProvisioningService is where the Factory changes infrastructure, and the only place. A deployment specification is desired state: a reference to the IaC program and its version in Git (the repository's InfrastructureModel Pulumi project, ADR-0004), the release it deploys, the strategy, and configuration and secret references resolved at apply time from the Config Platform and the Secret Broker. A provisioning run plans the specification against the target and records the plan (adds, changes, deletes, inputs hash, program version), waits for the promotion's production-class approval when the environment is production, applies with the runner's workload identity, and records the apply receipt (outputs, duration, result) with logs as artifacts. The GitOps reconciler re-plans every environment on a schedule: non-destructive differences outside production are applied where policy allows; anything destructive or in production becomes a reconciliation proposal for an Operator.
It absorbs DeploymentManifestService, DeploymentService and GitOpsSyncService.
Context and boundaries¶
flowchart LR
S["IaCProvisioningService"]
EnvironmentPromotionService["EnvironmentPromotionService"] -->|event| S
StudioBff["StudioBff"] -->|sync-http| S
S -->|sync-http| ext_pulumi["ext-pulumi"]
S -->|grpc| SecretBrokerService["SecretBrokerService"]
S -->|sync-http| ext_config_platform["ext-config-platform"]
S -->|grpc| PolicyEngineService["PolicyEngineService"]
S -->|grpc| RunControlService["RunControlService"]
S -->|grpc| ArtifactService["ArtifactService"]
S -. "SpecificationDeclared" .-> E_SpecificationDeclared(("event"))
S -. "ProvisioningPlanned" .-> E_ProvisioningPlanned(("event"))
S -. "ProvisioningApplied" .-> E_ProvisioningApplied(("event"))
S -. "ProvisioningFailed" .-> E_ProvisioningFailed(("event"))
The service never edits a program: desired state lives in Git and the specification records which version was acted on. A plan is always recorded before an apply; a re-run is a new run; receipts are immutable and audited.
Domain model¶
DeploymentSpecification — The desired state of one environment or workload: a reference to the IaC program and its version in Git (the repository's InfrastructureModel project), the release it deploys, strategy, configuration references (Config Platform) and secret references (Secret Broker); every applied version is kept. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
specificationId |
guid |
yes | |
tenantId |
string |
yes | |
projectId |
string |
yes | |
environmentId |
string |
yes | |
kind |
enum:SpecificationKind |
yes | |
programRef |
string |
yes | repository + path of the Pulumi program |
programVersion |
string |
yes | Commit of the program. |
releaseId |
guid |
no | |
strategy |
string |
no | |
configRefs |
json |
yes | Config Platform keys by reference. |
secretRefs |
json |
yes | Secret references; never values. |
version |
int |
yes | |
declaredAt |
datetime |
yes |
Invariants:
- Desired state lives in Git; the specification records which version was acted on and never edits the program.
- Secrets and configuration are references resolved by the runner at apply time from the Secret Broker and the Config Platform.
Events: SpecificationDeclared · Tables: DeploymentSpecifications · Repository: DeploymentSpecificationRepository
ProvisioningRun — One plan-and-apply of a specification version against a target: plan receipt (resources to add, change, delete), the approval when the environment class is production, apply receipt (outputs, duration, result), the promotion that requested it, and the artifacts of logs and state. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
provisioningRunId |
guid |
yes | |
tenantId |
string |
yes | |
projectId |
string |
yes | |
environmentId |
string |
yes | |
specificationId |
guid |
yes | |
specificationVersion |
int |
yes | |
kind |
enum:ProvisioningKind |
yes | |
promotionId |
guid |
no | |
approvalId |
guid |
no | |
planReceipt |
json |
no | adds, changes, deletes, inputs hash, program version |
applyReceipt |
json |
no | outputs, duration, result, runner version |
state |
enum:ProvisioningState |
yes | |
logsRef |
string |
no | |
startedAt |
datetime |
yes | |
completedAt |
datetime |
no |
Invariants:
- An apply against a production environment needs the promotion's approval (production class) or, for a baseline or reconciliation, a policy allow with evidence; the plan is recorded before any apply.
- A plan with deletes in production is never applied automatically by reconciliation; it becomes a reconciliation proposal.
- Receipts are immutable; a re-run is a new provisioning run.
stateDiagram-v2
Planned --> AwaitingApproval : ProductionWithoutApproval
Planned --> Applying : ApprovedOrAllowed
AwaitingApproval --> Applying : ApprovalGranted
AwaitingApproval --> Rejected : ApprovalRejectedOrExpired
Applying --> Applied : RunnerSucceeded
Applying --> Failed : RunnerFailed
Planned --> Cancelled : Cancel
Events: ProvisioningPlanned, ProvisioningApplied, ProvisioningFailed · Tables: ProvisioningRuns · Repository: ProvisioningRunRepository
erDiagram
DeploymentSpecification {
guid specificationId PK
string tenantId
string projectId
string environmentId
enum_SpecificationKind kind
string programRef
string programVersion
guid releaseId
string strategy
json configRefs
json secretRefs
int version
datetime declaredAt
}
ProvisioningRun {
guid provisioningRunId PK
string tenantId
string projectId
string environmentId
guid specificationId
int specificationVersion
enum_ProvisioningKind kind
guid promotionId
guid approvalId
json planReceipt
json applyReceipt
enum_ProvisioningState state
string logsRef
datetime startedAt
datetime completedAt
}
Entities and value objects¶
None modelled
No entities or value objects are modelled for this item yet.
Data model and storage¶
| Store | Role | Engine / profile | Tables |
|---|---|---|---|
| Runtime Cloud store | primary | azure-sql / nhibernate-sqlserver | DeploymentSpecifications, ProvisioningRuns |
| Artifact content store | blob | blob / azure-blob | — |
| Table | Owner | Columns | Indexes |
|---|---|---|---|
dbo.DeploymentSpecifications |
DeploymentSpecification | 13 | 1 |
dbo.ProvisioningRuns |
ProvisioningRun | 15 | 2 |
Full DDL: appendix.
APIs¶
| Method | Path | Operation | Scope | Side effects | Idempotency | Gateway | Summary |
|---|---|---|---|---|---|---|---|
GET |
/factory/provisioning/specifications |
ListSpecifications | factory.provisioning.read |
read | natural | GW-03 | Desired-state specifications per environment with versions and program references. |
GET |
/factory/provisioning/runs |
ListProvisioningRuns | factory.provisioning.read |
read | natural | GW-03 | Provisioning runs by environment, kind and state with plan and apply receipts. |
GET |
/factory/provisioning/runs/{provisioningRunId} |
GetProvisioningRun | factory.provisioning.read |
read | natural | GW-03 | One run with its plan, approval, apply receipt and logs. |
POST |
/factory/provisioning/runs |
PlanProvisioning | factory.provisioning.apply |
production | key-header | GW-03 | Plan a specification version against an environment (baseline, reconciliation, decommission); production applies wait for the approval (Operator, Platform admin). |
POST |
/factory/provisioning/runs/{provisioningRunId}/cancel |
CancelProvisioning | factory.provisioning.apply |
shared-write | natural | GW-03 | Cancel a planned run before apply. |
| gRPC service | Method | Streaming | Visibility | Side effects | Summary |
|---|---|---|---|---|---|
Provisioning |
GetCurrentSpecification |
none | internal | read | The specification version last applied to an environment (promotion, inventory and fleet read it). |
Messages¶
| Event | Topic | Scope / v | Aggregate | Consumers | Summary |
|---|---|---|---|---|---|
| SpecificationDeclared | provisioning.specifications.v1.specification-declared |
integration / 1 | DeploymentSpecification | RuntimeEnvironmentService, StudioBff, AuditService | A desired-state version exists for an environment. |
| ProvisioningPlanned | provisioning.runs.v1.provisioning-planned |
integration / 1 | ProvisioningRun | StudioBff, AuditService, EnvironmentPromotionService | A plan exists (adds, changes, deletes) and may await approval. |
| ProvisioningApplied | provisioning.runs.v1.provisioning-applied |
integration / 1 | ProvisioningRun | ExecutionCapacityService, EnvironmentPromotionService, RuntimeEnvironmentService, EnvironmentService, StudioBff, AuditService, DashboardService | The desired state was applied; outputs are recorded. |
| ProvisioningFailed | provisioning.runs.v1.provisioning-failed |
integration / 1 | ProvisioningRun | EnvironmentPromotionService, RuntimeEnvironmentService, StudioBff, AuditService, AlertRuleService | The apply failed; the receipt names the failing resource. |
SpecificationDeclared payload:
| Field | Type | Required | Description |
|---|---|---|---|
specificationId |
guid |
yes | |
tenantId |
string |
yes | |
environmentId |
string |
yes | |
kind |
enum:SpecificationKind |
yes | |
version |
int |
yes | |
programVersion |
string |
yes |
ProvisioningPlanned payload:
| Field | Type | Required | Description |
|---|---|---|---|
provisioningRunId |
guid |
yes | |
tenantId |
string |
yes | |
environmentId |
string |
yes | |
kind |
enum:ProvisioningKind |
yes | |
promotionId |
guid |
no | |
planSummary |
json |
yes |
ProvisioningApplied payload:
| Field | Type | Required | Description |
|---|---|---|---|
provisioningRunId |
guid |
yes | |
tenantId |
string |
yes | |
projectId |
string |
yes | |
environmentId |
string |
yes | |
kind |
enum:ProvisioningKind |
yes | |
promotionId |
guid |
no | |
specificationVersion |
int |
yes | |
outputs |
json |
yes |
ProvisioningFailed payload:
| Field | Type | Required | Description |
|---|---|---|---|
provisioningRunId |
guid |
yes | |
tenantId |
string |
yes | |
environmentId |
string |
yes | |
kind |
enum:ProvisioningKind |
yes | |
promotionId |
guid |
no | |
reason |
string |
yes |
| Command | Handled by | Result events | Summary |
|---|---|---|---|
PlanProvisioning |
ProvisioningRun | ProvisioningPlanned | |
CancelProvisioning |
ProvisioningRun | — |
| Event | Producer | Handler | Purpose |
|---|---|---|---|
| PromotionApproved | EnvironmentPromotionService | ProvisionPromotion |
A promotion hands over its desired state; the approval is reused for the production apply. |
| EnvironmentCreated | EnvironmentService | ProvisionBaseline |
A new environment gets its baseline specification planned. |
| ApprovalDecided | RunControlService | ApplyApproval |
Baseline and reconciliation applies in production wait for their approval. |
Workers and jobs¶
| Worker | Trigger | Output events | Retry | Idempotency key | Summary |
|---|---|---|---|---|---|
| Planner | event | ProvisioningPlanned | exponential | provisioningRunId + plan |
Resolves the program version, configuration and secret references and runs the IaC planner; records the plan receipt; routes to approval when required. |
| Applier | event | ProvisioningApplied, ProvisioningFailed | none | provisioningRunId + apply |
Runs the IaC apply for approved or allowed runs with the runner's workload identity; records outputs and logs as artifacts; reports applied or failed. |
| GitOpsReconciler | schedule 0 */4 * * * |
ProvisioningPlanned, ProvisioningApplied | fixed | environmentId + window |
Re-plans the declared specification of every environment on a schedule; applies non-destructive differences automatically where policy allows, and turns destructive or production differences into reconciliation proposals. |
Actors and grains¶
None modelled
No grains are modelled for this item yet.
Sagas and long-running flows¶
None modelled
No sagas are modelled for this item yet.
stateDiagram-v2
[*] --> Planned
Planned --> AwaitingApproval: ProductionWithoutApproval
Planned --> Applying: ApprovedOrAllowed
AwaitingApproval --> Applying: ApprovalGranted
AwaitingApproval --> Rejected: ApprovalRejectedOrExpired
Applying --> Applied: RunnerSucceeded
Applying --> Failed: RunnerFailed
Planned --> Cancelled: Cancel
sequenceDiagram
participant E as EnvironmentPromotionService
participant P as IaCProvisioningService
participant SB as SecretBrokerService
participant CP as Config Platform
participant R as Pulumi runner
participant I as RuntimeEnvironmentService
E-)P: PromotionApproved (desired state, approval)
P->>P: declare specification version, plan
P->>SB: resolve secret references (lease-bound)
P->>CP: resolve configuration references
P->>R: apply (program version, inputs)
R-->>P: outputs, duration, result
P-)E: ProvisioningApplied
P-)I: ProvisioningApplied → observe after apply
Options and configuration¶
| Section | Key | Type | Default | Reloadable | Secret | Summary |
|---|---|---|---|---|---|---|
RuntimeCloud:Provisioning |
AutoApplyNonDestructive |
bool | true |
yes | no | Reconciliation applies adds and changes automatically outside production. |
RuntimeCloud:Provisioning |
ApplyTimeoutMinutes |
int | 45 |
yes | no | Runner timeout per apply. |
Metrics and observability¶
| Metric | Instrument | Unit | Dimensions | Summary |
|---|---|---|---|---|
connectsoft.factory.provisioning.runs |
counter | {run} | tenant, kind, state | Provisioning runs by kind and outcome. |
connectsoft.factory.provisioning.apply_duration |
histogram | s | kind | Apply duration. |
connectsoft.factory.provisioning.reconciliation_diffs |
counter | {diff} | tenant, destructive | Differences found by reconciliation. |
Security¶
| Authentication | client-credentials |
| Scopes | factory.provisioning.read (Read specifications and runs.); factory.provisioning.apply (Plan, apply and cancel (Operator, Platform admin; promotions through the hand-off).) |
| Roles | Operator, PlatformAdmin, Observer |
| Tenant isolation | row-filter |
| Data classification | confidential |
| Max side-effect class | production |
| Secrets | iac-runner-identity (key-vault, rotate 30 d) |
Dependencies¶
Outbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| ext-pulumi | sync-http | Resource provisioning through the runner. | fail-fast | yes |
| SecretBrokerService | grpc | Secret references resolved at apply time. | fail-fast | yes |
| ext-config-platform | sync-http | Configuration references. | degrade | yes |
| PolicyEngineService | grpc | Deployment-domain decisions for baseline and reconciliation applies. | fail-fast | yes |
| RunControlService | grpc | Approvals for production applies not covered by a promotion. | fail-fast | yes |
| ArtifactService | grpc | Plan and apply logs and state snapshots. | fail-fast | yes |
Inbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| EnvironmentPromotionService | event | Desired-state hand-offs. | — | yes |
| StudioBff | sync-http | Provisioning views and operator actions. | — | yes |
Failure and recovery¶
| Scenario | Detection | Recovery | Data impact | RTO (min) |
|---|---|---|---|---|
| Apply fails mid-way | runner result | receipt records the partial state; the promotion rolls back; reconciliation proposes the fix; nothing is retried blindly | partial infrastructure change | 60 |
| Program version not found | planner error | run Failed with the reference; the specification is corrected in Git | none | 0 |
NFR targets¶
| Target | Class value (pilot-batch) | Override |
|---|---|---|
| latency_p95_ms | 0 | — |
| availability_pct | 99.5 | — |
| throughput_rps | 10 | — |
| rpo_minutes | 15 | — |
| rto_minutes | 240 | — |
| cost_class | medium | — |
Deployment¶
| Granularity | module of host · host RuntimeCloudHost |
| Topologies | saas-default, hybrid, on-prem, hybrid-compute |
| Runtime | —; replicas —–— |
| Ports | — |
Current implementation¶
Each repository's pipeline runs its own Pulumi program with Azure DevOps environment approvals; thirteen Pulumi.yaml stacks exist across the estate. No specification registry, receipt or reconciler exists.
Later capabilities¶
Policy-as-code checks on plans (deny public endpoints, enforce tags) before apply; cost estimates per plan from the FinOps projections; ephemeral preview environments per change proposal.
Decisions and traceability¶
| Family | Ids |
|---|---|
| FR | FR-08 |
| FUX | FUX-05 |
| NFR | NFR-08, NFR-09 |
| XL | XL-04, XL-10 |