Infrastructure as code you can read.

Infrena reconciles infrastructure described in YAML with infrastructure that actually exists. Environments belong to the project instead of a wrapper around it, wiring mistakes fail at compile time instead of halfway through an apply, and every cloud sits behind a plugin.

The core is free and stays free. Nothing that works today gets moved behind a paid tier later.

infrena plan dev
Plan for project "demo", environment "dev":

  + fake.database.db
      endpoint: (known after apply)
      engine: "postgres"
      network: (known after apply)
      size: 10 [default, from provider default]

  + fake.network.net
      cidr: "10.0.0.0/16"
      id: (known after apply)

Plan: 2 to create, 0 to update, 0 to replace, 0 to destroy, 0 to forget.

Values carry their origin the whole way through, which is why a plan can tell you size came from a provider default rather than from something you wrote.

What you get that you did not have before

Environments are part of the project

Name dev, staging and production in one file. Each keeps its own state and its own lock, so two applies against different environments never contend. Values differ through variables, not through copied directories.

vars/production.yml

Mistakes fail before anything runs

The provider's schema declares what every attribute means, so a misspelled reference or a field pointed at the wrong kind of resource is a compile error that names the fix, not a create that dies after four resources already exist.

$ infrena validate

You pass the resource, not the id

Write vpc: ${vpc} and Infrena fills in whatever attribute the provider says that field refers to. You stop having to remember whether a given API wants an id, a name or an ARN.

subnet_id: ${public_subnet}

Values remember where they came from

Every value in a plan can say whether you wrote it, a module defaulted it, an environment overrode it or the provider supplied it. Resolution order is fixed and documented, and explicit configuration always beats an implicit default.

size: 10 [default, from provider default]

Adopt the infrastructure you already run

Discovery is read-only. It reports what exists and what each resource would be called, so a name collision shows up before it happens. Import then writes minimal configuration, and never writes a secret into it.

$ infrena import dev --generate

Three dependencies, everything else a plugin

Providers and state backends run as separate processes and ship separately, so the engine never grows an SDK. The AWS SDK lives in the AWS provider, not in your Infrena binary.

- plugin: aws

Start with one file. Split it up when it earns it.

Both forms below produce byte-identical plans. That is pinned by a test, so growing a project is a layout change and never a behaviour change.

Explorer

    infra.yml
    
                

    Conventional directory names are read automatically and nothing has to list them. Alongside resources/, vars/ and modules/, a project may grow templates/ for documents rendered into attributes, secrets/ for per-environment encrypted vaults, and discovered/ for configuration written by import and reviewed by you.

    The project layout declares the fake provider, which keeps its cloud in a hand-editable JSON file. Create, plan, apply, drift detection and destroy all run against it without touching a network, which is how you try the tool before you point it at an account.

    Point it at an account you already run

    Most infrastructure exists before the tool that manages it does. Infrena reads what is already there, tells you what each resource would be called, and writes the configuration for you. Nothing is touched until you say so, and the round trip is a tested guarantee rather than a hope.

    1. Look, without touching anything

      Discovery reads. It never writes, never imports, and never changes state. Names come from a resource's Name tag where it has one, so you get vpc-app1 rather than vpc-1023902339, and a name collision is visible before it happens instead of after.

      Resources this project already manages are excluded, as are the ones the provider says the cloud created for itself. An account's default VPC is not something to adopt by accident. --all shows everything.

      infrena discover
      $ infrena discover
      
      TYPE        ID                     NAME
      aws.subnet  subnet-0a1b2c3d4e5f    subnet-app1a
      aws.vpc     vpc-1023902339         vpc-app1
      
      2 resources found. Nothing has been imported.

      Filters keep it manageable on a real account: infrena discover --tag Name=app1 --exclude-type aws.logs.loggroup

    2. Adopt it, and get configuration back

      Import adopts the resources into state, and --generate writes the configuration that declares them. It lands under discovered/, which loads like any other directory but stays separate so you can read it before deciding where things belong.

      What comes out is minimal. Anything equal to a provider default is left out, so you get a file worth reviewing rather than a dump. Resources reference each other by name instead of pasting ids. A sensitive attribute is never written: it is omitted, with a comment on that line saying so.

      discovered/subnet-app1a.yml
      subnet-app1a:
        type: aws.subnet
        VpcId: ${vpc-app1}        # not vpc-1023902339
        CidrBlock: 10.0.1.0/24

      Written by infrena import dev --generate, reviewed by you.

    3. Plan, and see nothing you did not expect

      Discover, import, generate, plan. That sequence producing no unexpected changes is pinned by a test in the repository, which is the difference between adoption you can trust and a first plan you have to argue with.

      This is the part that usually goes wrong elsewhere: configuration written from a live account that does not match the account it was written from.

      infrena plan dev
      $ infrena plan dev
      
      Plan for project "app1", environment "dev":
      
      Plan: 0 to create, 0 to update, 0 to replace, 0 to destroy, 0 to forget.

    A diagnostic has to be worth reading

    Every message Infrena prints is required to say what is wrong, where it is, what was expected, and an action you can actually take. That is a rule the project enforces on itself, not an aspiration.

    The same check that catches a misspelled attribute catches a field pointed at the wrong kind of resource. If a provider declares that vpc_id holds a VPC's id, then vpc_id: ${database.id} never reaches the API.

    No providers are contacted during infrena validate. It runs offline, in CI, on every commit.

    infrena validate
    $ infrena validate
    Error: fake.network has no attribute "nme"
      at infra.yml:14:5
    
      ${net.nme} reads an attribute that does not exist.
      Attributes of fake.network:
        cidr
        id
    
      Suggested action:
        Correct the attribute name.

    Install it

    Binaries for Linux, macOS and Windows, on Intel and ARM.

    download and verify
    VERSION=0.14.0
    BASE=https://github.com/Infrena/infrena/releases/download/v$VERSION
    
    curl -fsSLO $BASE/infrena_${VERSION}_linux_amd64.tar.gz
    curl -fsSLO $BASE/SHA256SUMS
    sha256sum -c SHA256SUMS --ignore-missing
    
    tar -xzf infrena_${VERSION}_linux_amd64.tar.gz
    sudo mv infrena_${VERSION}_linux_amd64/infrena /usr/local/bin/
    infrena version
    the loop
    infrena init          # scaffolds ./infrena/
    infrena validate      # no providers contacted
    infrena plan dev
    infrena apply dev
    
    infrena apply production --output run.ndjson
    0
    No changes
    1
    Error
    2
    Changes applied
    77
    Changes need an approval this run cannot obtain

    Exit codes are stable, and --output writes newline-delimited JSON while stdout stays empty, so a pipeline never has to parse anything out of human text. Code 77 exists so CI gets a distinguishable answer instead of a prompt nobody can see.