Metadata-Version: 2.5
Name: cfn-typed
Version: 0.2.1
Summary: CloudFormation templates as typed Python dicts, with types generated from the registry schemas
Project-URL: Homepage, https://github.com/vivainio/cfn-typed
Project-URL: Documentation, https://vivainio.github.io/cfn-typed/
Project-URL: Repository, https://github.com/vivainio/cfn-typed
Project-URL: Issues, https://github.com/vivainio/cfn-typed/issues
License-Expression: MIT
License-File: LICENSE
License-File: NOTICE
License-File: licenses/AWACS-LICENSE.txt
License-File: licenses/AWS-CDK-LICENSE.txt
Keywords: aws,cloudformation,codegen,infrastructure-as-code,typeddict,typing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Code Generators
Classifier: Typing :: Typed
Requires-Python: >=3.14
Requires-Dist: typing-extensions>=4.13
Provides-Extra: yaml
Requires-Dist: pyyaml>=6; extra == 'yaml'
Description-Content-Type: text/markdown

# cfn-typed

CloudFormation templates as typed Python dicts. Resource factories and their
`TypedDict`s are **generated from the official CloudFormation registry
schemas** (1,873 resource types), so a type checker catches typos, wrong types
and missing required properties before CloudFormation does. Checked with
[`ty`](https://docs.astral.sh/ty/).

**Facts first, opinions fenced:** the generated types and the permission names are the stable core, and anything
that is a judgement (grants, patterns) lives in a place marked as an opinion. The book has a
[comparison](https://vivainio.github.io/cfn-typed/comparison/) with Troposphere, the AWS CDK and Pulumi.

**[Read the book](https://vivainio.github.io/cfn-typed/)** for a getting-started guide, a gallery of
pattern helpers (a Lambda role, a queue with its dead-letter queue, ...) and a
reference page per resource. Install from [PyPI](https://pypi.org/project/cfn-typed/):
`uv add cfn-typed` (or `uv add 'cfn-typed[yaml]'` for YAML output).

```python
import json
import cfn_typed as cfn
from cfn_types import sqs  # generated, see below

template: cfn.Template = {
    "Resources": {
        "DLQ": sqs.queue(),
        "Queue": sqs.queue(
            VisibilityTimeout=180,
            RedrivePolicy={"deadLetterTargetArn": cfn.get_att("DLQ", "Arn"), "maxReceiveCount": 3},
        ),
    },
}
print(json.dumps(template, indent=2))
```

```text
error[unknown-argument]: Argument `MemorySise` does not match any known parameter of function `function`
error[invalid-argument-type]: Expected `Expr[int]`, found `Literal["30"]`
error[missing-argument]: No arguments provided for required parameters `Code`, `Role`
```

## Generating only what you use

The package ships no resource types. You generate the ones you need into your
own project, so the output stays small (four resources is about 44 KB):

```sh
cfn-typegen --out cfn_types AWS::SQS::Queue 'AWS::Lambda::*'
cfn-typegen --out cfn_types --template template.json   # types a JSON template uses
cfn-typegen --out cfn_types --all                      # everything, ~5.8 MB, ~4 MB with --no-docs
cfn-typegen --out cfn_types --no-docs AWS::SQS::Queue  # leave out the description docstrings
cfn-typegen search load balancer                       # find a type name
```

Type names are case-insensitive globs and an unmatched name is an error. The
schema zip is downloaded once into `~/.cache/cfn-typegen/`; `--schemas` points at a local copy,
and `--refresh` downloads it again to pick up new AWS resources. Then rerun your generate command.
Rerunning replaces earlier generated files in the output directory.

## Layout of the generated modules

One module per service. For `AWS::Lambda::Function` there is
`lambda_.function(**props)` (the factory), `lambda_.Function` (the resource dict),
`lambda_.FunctionProps`, and one type per nested definition
(`lambda_.FunctionCode`, ...). Python keywords get a trailing `_`. Factories also
take `depends_on`, `deletion_policy`, `update_replace_policy`, `condition` and
`metadata` as snake_case keywords, so they never collide with a property name.

`cfn_typed` itself holds the runtime pieces the generated code imports:
`intrinsics` (`Ref`, `GetAtt`, ..., and `Expr[T]` = `T | Intrinsic`, which every
generated property uses), `base` (`Template`, `Tag`, `build`) and a few helpers.
`examples/` has a worked SQS + Lambda stack and `examples/cfn_types/` is its
generated output.

## Parameters as variables

`cfn.Params` declares template parameters (plain or SSM-backed) once and returns the `Ref` to use,
so a shared "defaults" package can expose a VPC id or subnet list as variables. See the book.

## JSON or YAML

`cfn.to_json(template)` and `cfn.to_yaml(template)` (YAML needs `cfn-typed[yaml]`) render
the same template. The YAML uses short-form intrinsics (`!Ref`, `!GetAtt A.Arn`, ...).

## Testing

Commit the rendered YAML and let CI check that the code still renders exactly that:
`cfn_typed.testing.assert_matches_file(template, "rendered/stack.yaml")`. Rerun with
`CFN_TYPED_UPDATE=1` to accept an intended change. See the book. This repo does it for its own
examples (`examples/rendered/`).

## Permission names

`cfn_typed.permissions` gives IAM actions names, so `Sqs.ReceiveMessage` replaces the string
`"sqs:ReceiveMessage"` and a typo is an attribute error. The names are taken from
[awacs](https://github.com/cloudtools/awacs) (BSD-2-Clause, credited in `NOTICE`) for a short list of major services, by
`scripts/sync-permissions.py`; awacs is used only by that script and is not a dependency. Missing a service? Open a
pull request (see `CONTRIBUTING.md`). See the book.

## Grants (opinion layer)

`cfn_typed.grants` has the AWS CDK's permission sets, such as `grants.Sqs.Queue.consume_messages.on(queue_arn)`,
returning identity-policy statements. **This layer is opinion, not fact, and is less permanent than the generated
types and the permission names**: it can change or move between releases, so pin the version if you rely on it.
Copied from the CDK (Apache-2.0, credited in `NOTICE`) by `scripts/sync-cdk-grants.py`.

## Intrinsic functions

`ref`, `get_att`, `sub`, `join`, `select`, `split`, `find_in_map`, `if_`, `get_azs`, `base64`, `cidr`, `import_value`, and
the condition functions `equals`, `and_`, `or_`, `not_`, `condition_ref` build the dicts CloudFormation expects, with
typed arguments, and `Template["Conditions"]` is typed. See the book.

## Policy statements

`cfn_typed.policy` builds IAM statements with principals, conditions and the `Not` forms (`allow`, `deny`, `aws(...)`,
`arn_like(...)`, `assume_role_policy`), and grants become resource-policy statements when you give them a `principal`.
Statements are plain dicts that the type checker checks, whether you use the builders, `Statement(...)` or a literal.
See the book.

## cfn-typed-sumo: the fat pattern library

This repository also publishes `cfn-typed-sumo`, a second package on top of `cfn-typed`: the generated
types for the core services plus ready-made patterns, with nothing to generate first. The lots of
pre-generated types make it the quickest way to try things out.

```sh
uv add cfn-typed-sumo
```

`cfn-typed` is the core, and its API is meant to stay reliable. `cfn-typed-sumo` is deliberately fat and its
API is volatile: patterns and names change between releases, so pin the version. See
[the book](https://vivainio.github.io/cfn-typed/sumo/).

## Patterns

Helper functions that return a resource or a small fragment (a queue with its
dead-letter queue, a Lambda role, ...) are documented in the book, with working
code in `packages/sumo/src/cfn_typed_sumo/patterns.py`. Build it locally with `uv run zensical serve`.

## Type checking notes

Read-only properties (`Arn`, `Id`, ...) are left out of the inputs. Shapes whose
schema says `additionalProperties: false` are emitted as PEP 728 `closed=True`
TypedDicts, which is what makes `ty` reject unknown names.

Known gaps: enums are plain `str` (no `Literal`, so newly added values never
break you); `oneOf` constraints between properties are not modelled; arrays are
`list`, which is invariant, so a variable typed `list[Tag]` needs
`list[Expr[Tag]]`.

## Development

The repo contains no generated types. The examples and tests need a few, which are
generated into `examples/cfn_types/` (gitignored) the first time pytest runs, or by hand:

```sh
cfn-typegen --out examples/cfn_types AWS::SQS::Queue AWS::Lambda::Function \
    AWS::Lambda::EventSourceMapping AWS::IAM::Role
uv run ty check
uv run pytest
scripts/build-docs.sh        # generate types and reference pages, build the book (needs zensical)
```

## Releasing

Releases are published to PyPI from GitHub with trusted publishing (no token). The
version in `pyproject.toml` stays `0.0.0.dev0`; the workflow sets it from the tag.

```sh
gh release create v0.1.0 --generate-notes
```

That runs the tests, builds with `uv build` and publishes. One-time setup on PyPI:
add a trusted publisher for the project `cfn-typed` (owner `vivainio`, repository
`cfn-typed`, workflow `publish.yml`, environment `pypi`), and create the `pypi`
environment under the repository's Settings, Environments.
