Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

Ops.Infrastructure -- Containers, Storage, Certificates, DNS

"The certificate expired at 3 AM. The renewal was manual. The person who knew how to renew it left in October."


The Problem

Infrastructure configuration lives in at least five places:

  1. Dockerfiles -- scattered across repositories, each with slightly different base images, hardcoded memory limits, and copy-pasted multi-stage build patterns.
  2. Storage provisioning -- done through a cloud web console by someone with admin access. The settings are not version-controlled.
  3. TLS certificates -- managed by "the infrastructure person." Renewals are calendar reminders. When the calendar reminder fails, the certificate expires.
  4. DNS records -- created in a domain registrar's web UI. Nobody knows which records point where without logging in.
  5. CDN configuration -- set up once during initial deployment and never touched again because nobody remembers how.

The Infrastructure DSL eliminates all five. You declare C# attributes. dotnet build generates every artifact: Dockerfiles, docker-compose files, Kubernetes manifests, Terraform modules, cert-manager certificates, DNS records. You never write YAML, HCL, or Dockerfiles by hand. Change an attribute, rebuild, and every infrastructure artifact regenerates consistently.

Each of these is a critical operational concern. Each is managed differently. None of them are connected to the application code that depends on them. When the OrderService needs PostgreSQL, the connection string is in a config file, but the PostgreSQL storage provisioning (size, redundancy, encryption) is in a Terraform file that lives in a different repository maintained by a different team.

The Infrastructure DSL collapses these five concerns into typed attributes on the application classes that need them. The source generator emits docker-compose files, Dockerfiles, Terraform modules, Kubernetes manifests, and cert-manager resources. Everything is generated. Nothing is manually configured.

This DSL operates in two tiers: Container (Docker Compose, local development) and Cloud (Terraform, Kubernetes, production). There is no InProcess tier because infrastructure is inherently about external resources.


ContainerSpec

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class ContainerSpecAttribute : Attribute
{
    public ContainerSpecAttribute(string name, string image) { }

    /// <summary>Image tag. Defaults to "latest".</summary>
    public string Tag { get; init; } = "latest";

    /// <summary>Port mappings in "host:container" format.</summary>
    public string[] Ports { get; init; } = Array.Empty<string>();

    /// <summary>CPU limit (e.g., "0.5" for half a core).</summary>
    public string? CpuLimit { get; init; }

    /// <summary>Memory limit (e.g., "512m", "1g").</summary>
    public string? MemoryLimit { get; init; }

    /// <summary>Volume mounts in "host:container" format.</summary>
    public string[] Volumes { get; init; } = Array.Empty<string>();

    /// <summary>Environment variables in "KEY=VALUE" format.</summary>
    public string[] Environment { get; init; } = Array.Empty<string>();

    /// <summary>Container depends on these other container names.</summary>
    public string[] DependsOn { get; init; } = Array.Empty<string>();

    /// <summary>Health check command.</summary>
    public string? HealthCheck { get; init; }

    /// <summary>Restart policy: no, always, on-failure, unless-stopped.</summary>
    public string RestartPolicy { get; init; } = "unless-stopped";

    /// <summary>Network names this container joins.</summary>
    public string[] Networks { get; init; } = Array.Empty<string>();
}

Every property that matters for a container is a named parameter. No more editing YAML indentation by hand.

StorageSpec

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class StorageSpecAttribute : Attribute
{
    public StorageSpecAttribute(string name, StorageKind kind) { }

    /// <summary>Storage capacity (e.g., "50Gi", "100Gb").</summary>
    public string Capacity { get; init; } = "10Gi";

    /// <summary>Redundancy model.</summary>
    public StorageRedundancy Redundancy { get; init; }
        = StorageRedundancy.LocallyRedundant;

    /// <summary>Encryption at rest.</summary>
    public bool Encryption { get; init; } = true;

    /// <summary>Backup schedule in cron format. Null = no backup.</summary>
    public string? BackupSchedule { get; init; }

    /// <summary>Retention period for backups (e.g., "30d", "1y").</summary>
    public string? RetentionPeriod { get; init; }

    /// <summary>IOPS limit. 0 = default/unlimited.</summary>
    public int Iops { get; init; } = 0;

    /// <summary>Storage tier (Standard, Premium, Archive).</summary>
    public string Tier { get; init; } = "Standard";
}

public enum StorageKind
{
    Blob,
    FileShare,
    Database,
    Queue,
    Table
}

public enum StorageRedundancy
{
    LocallyRedundant,
    ZoneRedundant,
    GeoRedundant,
    ReadAccessGeoRedundant
}

CertificateSpec

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly,
    AllowMultiple = true)]
public sealed class CertificateSpecAttribute : Attribute
{
    public CertificateSpecAttribute(string domain) { }

    /// <summary>Certificate provider.</summary>
    public CertificateProvider Provider { get; init; }
        = CertificateProvider.LetsEncrypt;

    /// <summary>Days before expiry to trigger renewal.</summary>
    public int RenewBeforeDays { get; init; } = 30;

    /// <summary>Subject alternative names.</summary>
    public string[] SubjectAlternativeNames { get; init; }
        = Array.Empty<string>();

    /// <summary>Key algorithm (RSA2048, RSA4096, ECDSA256, ECDSA384).</summary>
    public string KeyAlgorithm { get; init; } = "RSA2048";
}

public enum CertificateProvider
{
    LetsEncrypt,
    ACM,
    AzureKeyVault,
    SelfSigned,
    Custom
}

DnsRecord

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly,
    AllowMultiple = true)]
public sealed class DnsRecordAttribute : Attribute
{
    public DnsRecordAttribute(string name, DnsRecordType type, string value) { }

    /// <summary>Time to live in seconds.</summary>
    public int Ttl { get; init; } = 300;

    /// <summary>Priority (for MX and SRV records).</summary>
    public int Priority { get; init; } = 0;

    /// <summary>Weight (for SRV records).</summary>
    public int Weight { get; init; } = 0;

    /// <summary>Whether this record is managed by external DNS controller.</summary>
    public bool ExternalDnsManaged { get; init; } = false;
}

public enum DnsRecordType
{
    A, AAAA, CNAME, TXT, MX, SRV, NS, CAA
}

CdnEndpoint

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class CdnEndpointAttribute : Attribute
{
    public CdnEndpointAttribute(string origin) { }

    /// <summary>Cache duration (e.g., "1h", "7d", "30d").</summary>
    public string CacheDuration { get; init; } = "1d";

    /// <summary>Purge CDN cache on every deployment.</summary>
    public bool PurgeOnDeploy { get; init; } = true;

    /// <summary>Custom domain for the CDN endpoint.</summary>
    public string? CustomDomain { get; init; }

    /// <summary>Compression enabled.</summary>
    public bool Compression { get; init; } = true;

    /// <summary>WAF enabled on the CDN edge.</summary>
    public bool WafEnabled { get; init; } = false;

    /// <summary>Cache key query string behavior: Include, Exclude, IgnoreAll.</summary>
    public string QueryStringBehavior { get; init; } = "Include";
}

Usage: OrderService Infrastructure

// --- Containers ---

[ContainerSpec("order-api", "order-service",
    Tag = "#{Version}#",
    Ports = new[] { "8080:80", "8443:443" },
    CpuLimit = "1.0",
    MemoryLimit = "1g",
    HealthCheck = "curl -f http://localhost:80/health || exit 1",
    Networks = new[] { "order-net", "shared-net" },
    DependsOn = new[] { "order-db", "order-broker" })]

[ContainerSpec("order-db", "postgres",
    Tag = "16-alpine",
    Ports = new[] { "5432:5432" },
    CpuLimit = "0.5",
    MemoryLimit = "512m",
    Volumes = new[] { "order-db-data:/var/lib/postgresql/data" },
    Environment = new[] {
        "POSTGRES_DB=orders",
        "POSTGRES_USER=order_svc",
        "POSTGRES_PASSWORD=#{DbPassword}#" },
    Networks = new[] { "order-net" })]

[ContainerSpec("order-broker", "rabbitmq",
    Tag = "3-management-alpine",
    Ports = new[] { "5672:5672", "15672:15672" },
    CpuLimit = "0.5",
    MemoryLimit = "256m",
    Networks = new[] { "order-net", "shared-net" })]

// --- Storage ---

[StorageSpec("order-db-storage", StorageKind.Database,
    Capacity = "100Gi",
    Redundancy = StorageRedundancy.ZoneRedundant,
    Encryption = true,
    BackupSchedule = "0 2 * * *",
    RetentionPeriod = "30d",
    Tier = "Premium",
    Iops = 3000)]

[StorageSpec("order-attachments", StorageKind.Blob,
    Capacity = "500Gi",
    Redundancy = StorageRedundancy.GeoRedundant,
    Encryption = true)]

// --- TLS ---

[CertificateSpec("orders.example.com",
    Provider = CertificateProvider.LetsEncrypt,
    RenewBeforeDays = 30,
    SubjectAlternativeNames = new[] { "api.orders.example.com" },
    KeyAlgorithm = "ECDSA256")]

// --- DNS ---

[DnsRecord("orders.example.com", DnsRecordType.A, "#{LoadBalancerIp}#",
    Ttl = 300)]
[DnsRecord("api.orders.example.com", DnsRecordType.CNAME,
    "orders.example.com", Ttl = 300)]
[DnsRecord("orders.example.com", DnsRecordType.TXT,
    "v=spf1 include:_spf.example.com ~all", Ttl = 3600)]
[DnsRecord("orders.example.com", DnsRecordType.CAA,
    "0 issue \"letsencrypt.org\"", Ttl = 3600)]

// --- CDN ---

[CdnEndpoint("orders.example.com",
    CacheDuration = "7d",
    PurgeOnDeploy = true,
    CustomDomain = "static.orders.example.com",
    Compression = true,
    WafEnabled = true)]

public partial class OrderServiceInfrastructure { }

Thirteen attributes. The entire infrastructure footprint for the OrderService. Containers, database, message broker, storage, TLS, DNS, and CDN -- all declared on one class, all version-controlled, all compiler-validated.


docker-compose.generated.yaml

# <auto-generated from [ContainerSpec] attributes on OrderServiceInfrastructure>
version: "3.8"

services:
  order-api:
    image: order-service:latest
    ports:
      - "8080:80"
      - "8443:443"
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 1g
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:80/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    restart: unless-stopped
    depends_on:
      order-db:
        condition: service_healthy
      order-broker:
        condition: service_started
    networks:
      - order-net
      - shared-net

  order-db:
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 512m
    environment:
      POSTGRES_DB: orders
      POSTGRES_USER: order_svc
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - order-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U order_svc -d orders"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - order-net

  order-broker:
    image: rabbitmq:3-management-alpine
    ports:
      - "5672:5672"
      - "15672:15672"
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 256m
    healthcheck:
      test: ["CMD-SHELL", "rabbitmq-diagnostics -q ping"]
      interval: 15s
      timeout: 10s
      retries: 5
    restart: unless-stopped
    networks:
      - order-net
      - shared-net

volumes:
  order-db-data:
    driver: local

networks:
  order-net:
    driver: bridge
    internal: true
  shared-net:
    driver: bridge

Every container has resource limits (because the attribute requires them or the analyzer fires). The database has a health check auto-generated from the image type. The #{DbPassword}# template variable is replaced with a ${DB_PASSWORD} docker-compose variable reference. Networks are declared and internal: true is set for the database network to prevent accidental external access.

Dockerfile.generated

# <auto-generated from [ContainerSpec("order-api")] attributes>
# Multi-stage build for order-service

FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
WORKDIR /src

COPY ["OrderService/OrderService.csproj", "OrderService/"]
RUN dotnet restore "OrderService/OrderService.csproj"

COPY . .
WORKDIR "/src/OrderService"
RUN dotnet publish -c Release -o /app/publish \
    --no-restore \
    /p:UseAppHost=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS runtime
WORKDIR /app

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

COPY --from=build /app/publish .

EXPOSE 80
EXPOSE 443

HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=40s \
    CMD curl -f http://localhost:80/health || exit 1

ENTRYPOINT ["dotnet", "OrderService.dll"]

The Dockerfile is generated with security defaults: non-root user, alpine base, multi-stage build, no unnecessary layers. The health check mirrors the HealthCheck property from the attribute.


terraform/infra/main.tf

# <auto-generated from [ContainerSpec], [StorageSpec], [CertificateSpec],
# [DnsRecord], [CdnEndpoint] attributes on OrderServiceInfrastructure>

terraform {
  required_version = ">= 1.5"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.80"
    }
  }
}

# --- Storage: order-db-storage (Database, ZoneRedundant) ---

resource "azurerm_postgresql_flexible_server" "order_db" {
  name                = "order-db-storage"
  resource_group_name = var.resource_group_name
  location            = var.location
  sku_name            = "GP_Standard_D2s_v3"

  storage_mb                   = 102400
  backup_retention_days        = 30
  geo_redundant_backup_enabled = false
  zone                         = "1"
  high_availability {
    mode                      = "ZoneRedundant"
    standby_availability_zone = "2"
  }

  authentication {
    active_directory_auth_enabled = true
    password_auth_enabled         = false
  }

  tags = var.tags
}

resource "azurerm_postgresql_flexible_server_database" "orders" {
  name      = "orders"
  server_id = azurerm_postgresql_flexible_server.order_db.id
  charset   = "UTF8"
  collation = "en_US.utf8"
}

# --- Storage: order-attachments (Blob, GeoRedundant) ---

resource "azurerm_storage_account" "order_attachments" {
  name                     = "orderattachments"
  resource_group_name      = var.resource_group_name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "GRS"

  blob_properties {
    versioning_enabled = true
  }

  min_tls_version = "TLS1_2"

  tags = var.tags
}

resource "azurerm_storage_container" "order_attachments_container" {
  name                  = "attachments"
  storage_account_name  = azurerm_storage_account.order_attachments.name
  container_access_type = "private"
}

# --- DNS Records ---

resource "azurerm_dns_a_record" "orders" {
  name                = "orders"
  zone_name           = var.dns_zone_name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  records             = [var.load_balancer_ip]
}

resource "azurerm_dns_cname_record" "api_orders" {
  name                = "api.orders"
  zone_name           = var.dns_zone_name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  record              = "orders.example.com"
}

resource "azurerm_dns_txt_record" "orders_spf" {
  name                = "orders"
  zone_name           = var.dns_zone_name
  resource_group_name = var.resource_group_name
  ttl                 = 3600
  record {
    value = "v=spf1 include:_spf.example.com ~all"
  }
}

resource "azurerm_dns_caa_record" "orders_caa" {
  name                = "orders"
  zone_name           = var.dns_zone_name
  resource_group_name = var.resource_group_name
  ttl                 = 3600
  record {
    flags = 0
    tag   = "issue"
    value = "letsencrypt.org"
  }
}

# --- CDN ---

resource "azurerm_cdn_profile" "order_cdn" {
  name                = "order-cdn"
  resource_group_name = var.resource_group_name
  location            = "global"
  sku                 = "Standard_Microsoft"

  tags = var.tags
}

resource "azurerm_cdn_endpoint" "order_cdn_endpoint" {
  name                = "order-cdn-endpoint"
  profile_name        = azurerm_cdn_profile.order_cdn.name
  resource_group_name = var.resource_group_name
  location            = "global"

  origin {
    name      = "order-origin"
    host_name = "orders.example.com"
  }

  is_compression_enabled = true
  content_types_to_compress = [
    "text/html", "text/css", "application/javascript",
    "application/json", "image/svg+xml"
  ]

  querystring_caching_behaviour = "UseQueryString"

  tags = var.tags
}

# --- Variables ---

variable "resource_group_name" { type = string }
variable "location" { type = string }
variable "dns_zone_name" { type = string }
variable "load_balancer_ip" { type = string }
variable "tags" {
  type    = map(string)
  default = {}
}

One Terraform file. Generated from the same attributes that produce the docker-compose file. The StorageKind.Database generates a PostgreSQL Flexible Server. The StorageKind.Blob generates a Storage Account. DNS records map directly. CDN maps directly. No manual Terraform authoring.

k8s-deployment.yaml

# <auto-generated from [ContainerSpec("order-api")] attributes>
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-api
  labels:
    app: order-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-api
  template:
    metadata:
      labels:
        app: order-api
    spec:
      containers:
        - name: order-api
          image: order-service:latest
          ports:
            - containerPort: 80
            - containerPort: 443
          resources:
            limits:
              cpu: "1.0"
              memory: "1Gi"
            requests:
              cpu: "0.25"
              memory: "256Mi"
          livenessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 40
            periodSeconds: 30
            timeoutSeconds: 10
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /health/ready
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 5
          securityContext:
            runAsNonRoot: true
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false

cert-manager-certificate.yaml

# <auto-generated from [CertificateSpec("orders.example.com")] attributes>
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: orders-example-com-tls
  namespace: order-service
spec:
  secretName: orders-example-com-tls-secret
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - orders.example.com
    - api.orders.example.com
  privateKey:
    algorithm: ECDSA
    size: 256
  renewBefore: 720h  # 30 days

The certificate never expires silently. cert-manager handles renewal automatically. The renewBefore: 720h comes from RenewBeforeDays = 30 in the attribute. No calendar reminders. No 3 AM pages.

k8s-ingress.yaml

# <auto-generated from [DnsRecord] + [CertificateSpec] attributes>
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: order-api-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
    - hosts:
        - orders.example.com
        - api.orders.example.com
      secretName: orders-example-com-tls-secret
  rules:
    - host: orders.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: order-api
                port:
                  number: 80
    - host: api.orders.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: order-api
                port:
                  number: 80

The ingress is generated by combining DNS records and certificate specs. The TLS secret name matches the cert-manager certificate. The hosts match the DNS records. Everything is cross-referenced automatically.


INF001: Container Without Resource Limits

error INF001: [ContainerSpec("order-worker")] does not specify CpuLimit or
MemoryLimit. Containers without resource limits can consume unbounded
resources and cause node eviction in Kubernetes.

This is a hard error, not a warning. A container without resource limits is a production incident waiting to happen. The analyzer enforces this at compile time, before the container is ever built.

INF002: Certificate Without Renewal

error INF002: [CertificateSpec("orders.example.com")] has Provider =
Custom but RenewBeforeDays = 0. Custom certificates require a renewal
policy or they will expire silently.

LetsEncrypt and ACM handle renewal automatically, so RenewBeforeDays is a safety margin. Custom certificates need explicit renewal logic. The analyzer ensures this is not forgotten.

INF003: Storage Without Encryption

error INF003: [StorageSpec("order-logs", StorageKind.Blob)] has Encryption
= false. All storage must be encrypted at rest. Set Encryption = true or
suppress with #pragma if this is intentional.

Encryption at rest is a default. Turning it off requires a conscious decision (pragma suppression with a comment). The analyzer prevents accidental exposure of data.

INF004: DNS Record Without TTL

warning INF004: [DnsRecord("orders.example.com", A, ...)] uses the default
TTL of 300 seconds. Consider whether this is appropriate for production.
Long TTLs reduce DNS query load; short TTLs enable faster failover.

This is a warning, not an error. The default TTL of 300 seconds is reasonable. But the analyzer nudges the team to make a conscious choice rather than accepting defaults silently.


Infrastructure to Cost (Ops.Cost)

Every [ContainerSpec] and [StorageSpec] implies a cost. The Cost DSL (Part 11) reads infrastructure attributes to calculate budgets:

// Infrastructure DSL declares the resource
[StorageSpec("order-db-storage", StorageKind.Database,
    Capacity = "100Gi", Tier = "Premium", Iops = 3000)]

// Cost DSL auto-generates a budget line item:
// Database: Premium 100Gi @ 3000 IOPS = ~$X/month
// The cost analyzer warns if the budget is exceeded.

No separate cost spreadsheet. The infrastructure attributes are the input to cost estimation.

Infrastructure to Networking (Ops.Networking)

Every exposed port in a [ContainerSpec] needs a firewall rule or network policy. The Networking DSL (Part 16) cross-references:

// Infrastructure says: port 5432 is exposed
[ContainerSpec("order-db", "postgres", Ports = new[] { "5432:5432" })]

// Networking DSL must have a firewall rule for 5432.
// If not, cross-analyzer INF-NET001 fires:
// "Container 'order-db' exposes port 5432 but no [FirewallRule] or
//  [NetworkPolicy] restricts access to this port."

Infrastructure to Observability (Ops.Observability)

Every container needs a health check endpoint. The Observability DSL (Part 7) cross-references:

// Infrastructure says: health check is /health
[ContainerSpec("order-api", "order-service",
    HealthCheck = "curl -f http://localhost:80/health || exit 1")]

// Observability DSL should have a matching health probe:
[HealthProbe(typeof(OrderService), "/health", Interval = "30s")]

Infrastructure to Deployment (Ops.Deployment)

Every [ContainerSpec] is a deployable unit. The Deployment DSL (Part 5) references containers as deployment targets:

// Infrastructure defines the container
[ContainerSpec("order-api", "order-service")]

// Deployment DSL orchestrates it
[DeploymentApp("order-api",
    Strategy = DeployStrategy.RollingUpdate,
    DependsOn = new[] { "order-db" })]

The Deployment DSL generator validates that every [DeploymentApp] name matches a [ContainerSpec] name. Typos are caught at compile time. Deployment ordering references real containers, not strings in a wiki.


The Infrastructure Gap

Most teams have a gap between "the code" and "the infrastructure the code runs on." The code lives in one repository with CI, code review, and type checking. The infrastructure lives in another repository (or a web console) with different review processes, different ownership, and no type checking.

The Infrastructure DSL closes this gap by putting infrastructure declarations next to the code that depends on them. When a developer adds a new service that needs PostgreSQL, they add a [StorageSpec] attribute in the same pull request. The Terraform is generated. The docker-compose is generated. The Kubernetes manifests are generated. One PR, one review, one source of truth.

The certificate that expired at 3 AM? It is now a [CertificateSpec] attribute with RenewBeforeDays = 30 that generates a cert-manager resource with automatic renewal. The DNS record created in a web console? It is now a [DnsRecord] attribute that generates Terraform. The Dockerfile with hardcoded memory limits? It is now a [ContainerSpec] with explicit MemoryLimit enforced by analyzer INF001.

Infrastructure as code is not new. Infrastructure as typed, compiler-validated, cross-referenced code is.

⬇ Download