Metadata-Version: 2.4
Name: stackmason
Version: 0.1.2
Summary: Interview-driven Terraform repositories, secure by default.
Author-email: Ehtisham Mubarik <ehtisham@eprecisio.com>
License: MIT
Project-URL: Homepage, https://github.com/ehtishammubarik/stackmason
Project-URL: Issues, https://github.com/ehtishammubarik/stackmason/issues
Keywords: terraform,infrastructure-as-code,aws,eks,scaffolding,devops,platform-engineering,iac,generator
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov>=4; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Dynamic: license-file

# stackmason

**Answer some questions. Get a Terraform repository that is secure by default.**

![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-3776AB?logo=python&logoColor=white)
![Dependencies](https://img.shields.io/badge/dependencies-none-success)
![License](https://img.shields.io/badge/license-MIT-blue)
![Status](https://img.shields.io/badge/status-alpha-orange)

## What it does

```
$ stackmason new acme --stack eks --stack rds -e dev,staging,prod

Kubernetes version
  options: 1.31, 1.30, 1.29
  default: 1.31
> k8s_version: 

Node instance type
  suggestion: m6i.large for mixed workloads. c6i for CPU-bound, r6i for
  memory-bound, g5 for GPU. t3 burstable only for dev, because CPU credit
  exhaustion under load looks exactly like a mysterious latency problem.
> node_instance_type [suggest]: suggest

[NOTE] COST000: Baseline before compute and storage: ~$369/month.
    -> Order of magnitude only, us-east-1 on-demand. Not a quote.

wrote 20 files to acme/
```

Out comes a repository, not a snippet:

```
acme/
  environments/dev/      main.tf  versions.tf  providers.tf  backend.tf  variables.tf
  environments/staging/
  environments/prod/
  .github/workflows/terraform.yml
  ARCHITECTURE.md        why it looks like this
  DECISIONS.md           every value chosen, and why
  README.md
  .gitignore
```

One state file per environment. Providers pinned and configured with an explicit
region, never one inherited from whichever shell happens to run it. Every
resource tagged with its project and environment. CI that checks formatting and
scans for credentials. `terraform validate` passes against the real upstream
modules, which is verified in this repo's own tests.

Pass `--region` to put both the resources and the state bucket somewhere other
than `us-east-1`.

## What it is trying to be

**The first ninety minutes of a new infrastructure repository, without the
mistakes that take six months to surface.**

Not a platform. Not a control plane. A generator that writes a first draft you
own outright, in plain Terraform, with no runtime dependency on this tool. You
should be able to delete `stackmason` afterwards and lose nothing.

- **The defaults are the product**, and they are not negotiable silently.
- **There will never be a flag that turns a refusal into a warning.** It is the
  most common request a scaffolding tool gets and the answer is permanently no:
  an escape hatch on a security default is the same as not having the default,
  because the hatch is what ends up in the example someone copies.
- **It never emits a credential**, in any form, for any reason.
- **Every choice is explicable six months later**, via `DECISIONS.md`.

Full detail, plus what is deliberately **not** planned, in the
[roadmap](ROADMAP.md). Work in flight is tracked in the
[0.2](https://github.com/ehtishammubarik/stackmason/milestone/1) and
[0.3](https://github.com/ehtishammubarik/stackmason/milestone/2) milestones.

## Why not just copy a blog post

Because the defaults are the product.

A scaffolding tool multiplies whatever it emits. One insecure default becomes a
hundred insecure deployments, run by people who reasonably assumed the
generator knew better. So these are enforced, not suggested:

| Guardrail | Behaviour |
|---|---|
| `0.0.0.0/0` on a data port | **Refuses to generate.** Ports 22, 3389, 5432, 3306, 1433, 27017, 6379, 9092, 5439, 9200 |
| Publicly accessible database | **Refuses to generate** |
| Bastion with no source CIDR | **Refuses to generate** |
| `skip_final_snapshot` in production | **Refuses to generate** |
| Local state | Warns. State holds every secret the plan touched, in plaintext |
| NAT gateway per AZ | Costs it out before you commit |
| An environment named `prod` configured like a dev one | Warns, naming the specific attribute and what it costs |

And what it always emits:

- **No credential, anywhere.** Not a placeholder, not a generated one, not
  base64. Secrets are `sensitive` variables with **no default**, because a
  default is what ends up committed.
- Encryption at rest, deletion protection and final snapshots in production,
  backup retention, IMDSv2, public-access blocks, private EKS endpoints.
- A `validation` block that rejects `0.0.0.0/0` at plan time, so the rule
  survives after the generator is gone.

Run `stackmason plan` to see all of it without writing a file.

## Install

```bash
pip install stackmason        # no dependencies
```

## Use

```bash
stackmason stacks                          # what is available
stackmason plan acme -s eks -s rds         # dry run, writes nothing
stackmason new acme -s eks -s rds          # interactive
stackmason new acme --answers a.json --yes # non-interactive, for CI
```

`--yes` takes the suggestion where one exists, so the same command is
reproducible in a pipeline.

## Stacks

**Three are configured. Six are stubs.** Saying so here rather than letting you
find out at `apply`:

| Stack | What | From | |
|---|---|---|---|
| `vpc` | Subnets, routing, NAT, flow logs | `terraform-aws-modules/vpc/aws` | ready |
| `eks` | Control plane, node groups, IRSA | `terraform-aws-modules/eks/aws` | ready |
| `rds` | PostgreSQL or MySQL, private, encrypted | `terraform-aws-modules/rds/aws` | ready |
| `msk` | Kafka | `terraform-aws-modules/msk-kafka-cluster/aws` | **stub** |
| `redshift` | Data warehouse | `terraform-aws-modules/redshift/aws` | **stub** |
| `elasticache` | Redis | `terraform-aws-modules/elasticache/aws` | **stub** |
| `s3` | Buckets, public access blocked | `terraform-aws-modules/s3-bucket/aws` | **stub** |
| `alb` | Load balancer with TLS | `terraform-aws-modules/alb/aws` | **stub** |
| `observability` | Prometheus, Grafana, Loki | on `eks` | **stub** |

A stub emits the module reference and a name and nothing else. `terraform
validate` passes on it, because every argument it omits defaults to null or `[]`
upstream, and `apply` then fails at the AWS API. Selecting one warns, the
generated block opens with `INCOMPLETE. This block will NOT apply.`, and
`stackmason stacks` marks it. Filling them in is
[#10](https://github.com/ehtishammubarik/stackmason/issues/10), and each one is
a self-contained contribution.

Dependencies resolve automatically: ask for `observability` and you get `vpc`,
`eks`, `observability`, in that order.

**Modules are referenced by version, never vendored.** Copying
`terraform-aws-modules` here would mean inheriting their maintenance burden,
freezing their security fixes, and taking on their licensing. A pinned
reference gets upstream fixes for free and keeps the provenance honest.

## "I do not know" is a valid answer

Every sizing question accepts `suggest` and returns a recommendation **with the
reasoning attached**. The reasoning is printed and written to `DECISIONS.md`,
so six months later the choice is explicable rather than archaeological.

The alternative is a user picking at random and blaming the tool, which is what
most scaffolding tools produce.

## What it is not

- **Not a replacement for understanding Terraform.** It writes the first draft.
  Reading the plan is still your job, and the generated README says so.
- **Not a compliance guarantee.** The defaults are good practice, not an audit.
- **Not a secret manager.** It emits `sensitive` variables and expects values
  from yours.
- **Not multi-cloud yet.** AWS only. The registry is structured for more.
- **Alpha.** The stack catalogue is nine entries, three of them configured, and
  the guardrails are the part that has had the most thought.

If you want a full platform with drift detection and a control plane, look at
Terragrunt, Atmos, or Cluster.dev. This does one thing: the first ninety
minutes of a new infrastructure repository, without the mistakes.

## Releases

Published on tag. The pipeline installs the built wheel and sdist into clean
environments and runs the suite against the **installed** package, generates a
repository with every stack and runs `terraform validate` against the real
upstream modules, then publishes to TestPyPI and verifies there before touching
PyPI. Package index versions cannot be reused, so a bad publish is permanent.

`scripts/verify-published.sh` verifies a release afterwards in Docker on stock
`python:3.x-slim` images, testing what `pip install stackmason` actually
delivers rather than an artifact CI just built.

## Testing

```bash
pip install -e ".[dev]" && pytest
```

No network and no cloud credentials required. CI enforces a coverage floor of
88% on Python 3.10, 3.11, and 3.12, so that figure is a gate rather than a
claim: it cannot be true here and false in the repository.

There is deliberately no test count in this file. The last one said 57 when the
suite was 96, and a number nobody maintains is worse than no number, because it
gives the reader no way to tell which of the other claims here are also stale.

Generated output is checked to be `terraform fmt` clean, so a generated repo
passes the CI it ships with on its first commit rather than failing it.

## License

MIT.

---

If `stackmason` saved you the first ninety minutes of an infrastructure repo, a
star helps the next person find it. Contributions welcome, and the
[roadmap](ROADMAP.md) says which issues are load-bearing:
[`good first issue`](https://github.com/ehtishammubarik/stackmason/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
is pre-scoped, and [CONTRIBUTING.md](CONTRIBUTING.md) says how to claim one so
two people do not build it twice. Read the `terraform validate` section first if
you are touching what gets emitted.
