# aws2tf reads existing AWS resources and writes Terraform files for them

> aws2tf is a Python tool that discovers running AWS infrastructure, converts it into Terraform HCL, and ends by running terraform plan so you can see whether the generated configuration matches reality. It de-references hardcoded values, finds dependent resources, and can scan a whole account, though the README advises against that.

**aws-samples/aws2tf** — aws2tf - automates the importing of existing AWS resources into Terraform and outputs the Terraform HCL code.

- Repository: https://github.com/aws-samples/aws2tf
- Stars: 768 · Forks: 119
- Language: Python
- License: MIT-0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/aws-samples-aws2tf

## The tool reads a live account rather than a template

aws2tf imports existing AWS infrastructure into Terraform and produces the corresponding Terraform HCL files. That framing is the whole product: the source of truth is the account you already have, and the output is configuration you can review, commit and then apply.

The mechanism is a sequence of steps that the README describes. The tool reads resources through the AWS APIs, writes a .tf file per resource by default, and additionally tries to de-reference hardcoded values into their Terraform addresses, so that a value that happens to be a VPC id becomes a reference to aws_vpc.something.id rather than a literal string. It finds dependent resources and imports them too, so a security group referenced by a load balancer is not left dangling.

There is a third transformation. Where possible the tool removes region and account references and replaces them with Terraform data values, so the generated configuration does not hardcode the account number it was generated in.

Then it runs terraform plan, and the expectation stated in the README is that there should be no subsequent additions or deletions reported, because all the appropriate configuration files have been created automatically.

That expectation is the acceptance test for the whole tool, and it is also where the tool's limits show. The generated configuration is only as good as the tool's understanding of each resource, and the README says plainly that trying to test everyone's AWS combinations in advance is not possible, so errors are expected and should be reported as issues.

## terraform plan is the check on the generated code

Most import tools stop at writing the file. aws2tf does not, and that final step is what makes the output usable.

By the time the plan runs, the tool has already written configuration for the resources it found. If the configuration is correct and the resources are already present in the account, the plan should show no changes: nothing to create, nothing to destroy. A plan that proposes to create or delete anything means the generated code does not match the live infrastructure, and that difference is the output you need to look at.

This changes what the tool is for. It is not a one shot conversion to be trusted blindly and applied. It is a generator whose output has to be read, with the plan as a cheap machine check on the reading. A plan that is clean tells you the HCL is at least consistent with the account; it does not tell you the code is good, and the generated files are still code someone has to own.

There is a related operational note about the tool's own dependencies. The requirements list Terraform v1.12.0 or higher, and the README recommends avoiding early point releases such as 1.9.0 and 1.9.1. The provider version used in the generated code can be overridden with the -tv flag, for example ./aws2tf.py -t vpc -tv 5.86.0, and the README warns that the version is passed straight through without any validation checks, so a typo in that flag produces a confusing failure rather than a clear error message.

The scan of everything in an account, with no type filter, is possible with a bare ./aws2tf.py invocation, and the README marks it as not recommended because it takes a long time.

## Resource selection works by type name, by identifier, or by list

The command line is a single script, aws2tf.py, and the type argument is how you scope a run.

The simplest invocation asks for a category, such as all the VPCs in the account and region:

```bash
./aws2tf.py -t vpc
```

You can narrow to one specific VPC by identifier:

```bash
./aws2tf.py -t aws_vpc -i vpc-xxxxxxxxxx
```

And you can skip the shorthand categories entirely and use the direct Terraform resource names, which is what you want when the short alias does not exist for the service you care about:

```bash
./aws2tf.py -t aws_sagemaker_domain
```

Several types can be requested at once with a comma delimited list:

```bash
./aws2tf.py -t vpc,efs,aws_sagemaker_domain
```

The full list of supported types is not in the README, and that is deliberate. The long help option is the reference: ./aws2tf.py -l prints the extended help with every supported type, while ./aws2tf.py -h gives the short version.

The output layout is also controllable. By default aws2tf generates a separate aws_xxxx.tf file for every resource it finds, and the -s option merges everything into a single main.tf instead. One file per resource is easier to review in a pull request; one merged file is easier to read as a whole. Most teams will start with the default and split the difference later.

## Merge mode and stack imports cover the two awkward cases

Two situations need more than a plain run, and the tool has a flag for each.

The first is adding to a configuration that already exists. The -m, merge, flag adds whatever resources you name to the existing output rather than replacing it:

```bash
./aws2tf.py -t ecs -m
```

The same flag works with a specific resource identifier, and combined with -s everything, existing and new, lands in a single main.tf:

```bash
./aws2tf.py -t eks -i my-cluster-name -m -s
```

A single bucket is the same pattern:

```bash
./aws2tf -t aws_s3_bucket -i my_bucket_name -m
```

The second case is infrastructure that was deployed as a stack, which is how organisations and the AWS workshops tend to roll out resources. The -s option with a stack name converts a deployed stack:

```bash
./aws2tf.sh -s <stack name>
```

Stack imports are where the tool asks for help. The README says that for stack sets, that is the -s option, you should look for two files in the generated/tf directory and paste their contents into an issue: stack-unprocessed.err and stack-null.err. The existence of two specific error files for this path tells you the stack set handling is the least settled part of the tool.

Two supporting documents in the repository root, Stack-Resources.md and StackSet-Resources.md, sit alongside Terraform-Resources.md, which is the resource mapping reference for the whole tool.

## Data source flags let another team keep ownership of the network

The most interesting feature for an organisation is the set of flags that turn a resource into a data source.

A Terraform resource is something Terraform manages and can change. A data source is something Terraform reads but does not own. In a large company the network layer is often built by a platform team, and the application team that writes Terraform should refer to those subnets and security groups rather than trying to manage them.

The flags make that explicit. The -dnet flag uses Terraform data sources for aws_vpc and aws_subnet, the -dsgs flag for aws_security_group, the -dkms flag for aws_kms_key and the -dkey flag for aws_key_pair. The README describes the use case directly: enterprises where networking components are provided by a different team.

There is a second, narrower use for the same mechanism. A resource that is not modified by Terraform at all, but is read as part of the configuration, belongs in the data source section, and the tool does not make that judgement for you.

The set is deliberately short, and the README asks for issues if you want it extended to other types. That is a fair way to treat a feature that changes the ownership semantics of your configuration: it should be asked for, not guessed at.

The other targeted selection flag is -ec2tag, which imports EC2 instances selectively by tag, for example ./aws2tf.py -t aws_instance -ec2tag "project:my value", importing only the instances carrying that tag key and value pair. On an account with hundreds of instances this is the difference between a usable run and a slow one.

## The dependency floor is high and deliberately so

The requirements section is specific enough to tell you that the tool is used against real accounts rather than as a toy.

You need macOS or Linux, Python 3.12.0 or newer, and boto3 1.42.16 or later installed from requirements.txt. The AWS CLI must be version 2.31.4 or higher, and you need a login with at least Read privileges. Terraform must be v1.12.0 or higher. jq 1.6 or higher is also required. The README notes the project is currently developed and tested with Python 3.13.7 on macOS 15.7.

The Read privileges requirement is the one to sit with. The tool needs to enumerate and read resources, and it does not need write access to AWS at all, since the output is local files. A read only profile is therefore the right way to run it, and the fact that the plan step is the final acceptance test reinforces that: nothing in the intended workflow writes to the account.

Installation itself is a clone plus one pip command, and the branch matters. For AWS provider version 6.0.0 and above you clone the default branch, while the legacy 5.x provider lives on a v5 branch:

```bash
git clone https://github.com/aws-samples/aws2tf.git
```

```bash
git clone -b v5 https://github.com/aws-samples/aws2tf.git
```

```bash
pip3 install -r requirements.txt
```

Optional tooling is listed for version management, pyenv for Python and tfenv for Terraform, and trivy 0.67.0 or later is recommended, which is a security scanner and fits the picture of a tool that reads a whole account.

## MIT 0, a 6.x version line, and a codebase rebuilt in late 2024

The licence is MIT-0, with the terms in the LICENSE file, and the repository also carries a DISCLAIMER.md next to the CODE_OF_CONDUCT.md and CONTRIBUTING.md.

The version line is unusual and worth understanding. The recent releases are v6.53.2 on 2026-07-23, v6.53.1 on 2026-07-21 and v6.53.0 on 2026-07-05. A version number in the 50s within the 6 series means the project increments on almost every change, so the tag itself carries little information and the release history is the real changelog. The last push was on 2026-08-10 and the repository is not archived.

The December 2024 code optimisation is the notable structural change. The README reports an 86.5% code reduction in resource handlers, with 1,265 boilerplate functions eliminated, and a handler system built on __getattr__ for automatic default handling, plus base handler utilities for common transformation patterns. Coverage is stated as complete for the Terraform AWS provider, with 1,612 resources supported, and aws_dict_extended.py described as a complete mapping of all 1,582 Terraform AWS resources, with 177 new resources added. The file organisation is maintained with 201 service specific files preserved.

The top level layout matches that description. aws2tf.py is the entry point, code/ holds the implementation with a .automation/ subdirectory for its generation tooling and a fixtf_aws_resources/ directory for the handler system, and documentation/ sits beside it. The test suite is described as comprehensive with 267 tests, run from requirements-test.txt.

For a tool that touches a whole account, the point of that rework was to keep the per service handling consistent, because a single mis-handled resource type is exactly where a generated configuration goes wrong.

## Conclusion

aws2tf fits a team inheriting an AWS account built by hand and needing it under Terraform management, with read only credentials and a few hours to review the generated files. It does not fit a team that expects a one command conversion, because the tool states that testing every AWS combination in advance is not possible and asks you to report errors, and it does not fit a Linux only pipeline on Windows. Before you run it, create a read only AWS CLI profile, clone the branch that matches your provider version, since the v5 branch exists for the legacy 5.x provider, and treat the first terraform plan as a review artefact rather than a deploy script, reading every file it writes before anything is applied.

## FAQ

### What does aws2tf do with the resources it imports?

It writes Terraform HCL for them, de-referencing hardcoded values into their Terraform addresses, finding dependent resources and importing those too, and replacing region and account references with Terraform data values where possible. It then runs terraform plan, which should show no additions or deletions if the generated configuration matches the live infrastructure.

### What does aws2tf need installed before it will run?

macOS or Linux, Python 3.12.0 or newer, boto3 1.42.16 or later, AWS CLI version 2.31.4 or higher with at least Read privileges, Terraform v1.12.0 or higher, and jq 1.6 or higher. The Docker image is based on python:3.12.9-alpine3.21 and installs Terraform itself.

### How do I import a single resource into an existing Terraform configuration?

Use the -m merge flag together with the type and the identifier, for example ./aws2tf.py -t eks -i my-cluster-name -m -s, where -s additionally merges the output into a single main.tf.

### Can aws2tf generate configuration for networking owned by another team?

Yes, through the data source flags. The -dnet flag applies to aws_vpc and aws_subnet, -dsgs to aws_security_group, -dkms to aws_kms_key and -dkey to aws_key_pair, so those resources are read rather than managed.

### Which branch should I clone for my Terraform AWS provider version?

For provider version 6.0.0 and above, clone the default branch with git clone https://github.com/aws-samples/aws2tf.git. The legacy 5.x provider is on the v5 branch, cloned with git clone -b v5.

## Sources

- [aws-samples/aws2tf on GitHub](https://github.com/aws-samples/aws2tf)
- [Issues](https://github.com/aws-samples/aws2tf/issues)
- [License: MIT-0](https://github.com/aws-samples/aws2tf/blob/master/LICENSE)
- [README](https://github.com/aws-samples/aws2tf/blob/master/README.md)
- [Releases](https://github.com/aws-samples/aws2tf/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/aws-samples-aws2tf
