JamesWoolfenden / sato

Sato converts ARM or CFN into Terraform
Apache License 2.0
104 stars 6 forks source link
arm aws azure cfn cloudformation terraform

Sato

Maintenance Build Status Latest Release GitHub tag (latest SemVer) Terraform Version pre-commit checkov Github All Releases codecov

Converts CloudFormation (and now also ARM) into Terraform. In Go, but quickerly.

Table of Contents

Install

Compile

Download the latest releases https://github.com/JamesWoolfenden/sato/releases/tag/v0.1.19 or:

Compile locally:

git clone https://github.com/JamesWoolfenden/sato
cd sato
go install

MacOS

brew tap jameswoolfenden/homebrew-tap
brew install jameswoolfenden/tap/pike

Windows

I'm now using Scoop to distribute releases, it's much quicker to update and easier to manage than previous methods, you can install scoop from https://scoop.sh/.

Add my scoop bucket:

scoop bucket add iac https://github.com/JamesWoolfenden/scoop.git

Then you can install a tool:

scoop install sato

Docker

docker pull jameswoolfenden/sato
docker run --tty --volume /local/path/to/tf:/tf jameswoolfenden/sato scan -d /tf

https://hub.docker.com/repository/docker/jameswoolfenden/sato

Usage

Parse

Get yourself some valid CloudFormation*

 git clone https://github.com/JamesWoolfenden/aws-cloudformation-templates
 >cd aws-cloudformation-templates/community/codestar/custom-ci-cd-pipeline
 ❯ ls
 README.md    template.yml
 >sato parse -f template.yml
 9:17PM INF Created .sato\variables.tf
 9:17PM INF Created .sato\data.tf
 9:17PM INF Created .sato\aws_codebuild_project.productionbuild.tf
 9:17PM INF Created .sato\aws_codebuild_project.productiondeploy.tf
 9:17PM INF Created .sato\aws_codebuild_project.stagingbuild.tf
 9:17PM INF Created .sato\aws_codebuild_project.stagingdeploy.tf
 9:17PM INF Created .sato\aws_iam_role.codebuildrole.tf
 9:17PM INF Created .sato\aws_codepipeline_pipeline.pipeline.tf
 9:17PM INF Created .sato\aws_iam_role.pipelinerole.tf
 9:17PM INF Created .sato\aws_s3_bucket.pipelines3bucket.tf

That's it. So by default (overridable) the parsed CloudFormation (now Terraform) will be in a .sato subdirectory. So let's have a look see:

> ls .sato
aws_codebuild_project.productionbuild.tf  aws_codebuild_project.stagingbuild.tf     aws_codepipeline_pipeline.pipeline.tf     aws_iam_role.pipelinerole.tf              variables.tf
aws_codebuild_project.productiondeploy.tf aws_codebuild_project.stagingdeploy.tf    aws_iam_role.codebuildrole.tf             aws_s3_bucket.pipelines3bucket.tf

So there are some files that could be Terraform.

The Cats Pyjamas

Testing...

>terraform init
...
Terraform has been successfully initialized!
....
>terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
...
Plan: 12 to add, 0 to change, 0 to destroy.
...

See

Shows the Terraform resource equivalent to a CloudFormation resource, or vice versa.

This tells you the equivalent resource required, given a CF or an ARM resource;

$ sato see -r Microsoft.Storage/storageAccounts
azurerm_storage_account

or

$sato see -r AWS::EC2::Instance
aws_instance%

Bisect

ARM to Terraform conversion.

What? You've got these legacy ARM templates, and you'd dearly love to drop them, but you really don't fancy Bicep and the rework. I got you covered. Sato now bisects ARM into Terraform.

Take one of the Azure quickstart examples from here https://github.com/Azure/azure-quickstart-templates/tree/master/quickstarts/microsoft.compute/vm-simple-windows:

Clone it:

git clone https://github.com/Azure/azure-quickstart-templates.git

Then bisect it!

$ sato bisect -f /Users/jwoolfenden/code/azure-quickstart-templates/quickstarts/microsoft.compute/vm-simple-windows/azuredeploy.json
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/variables.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/locals.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/azurerm_storage_account.sato0.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/azurerm_public_ip.sato1.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/azurerm_network_security_group.sato2.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/azurerm_virtual_network.sato3.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/azurerm_network_interface.sato4.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/azurerm_virtual_machine.sato5.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/azurerm_virtual_machine_extension.sato6.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/outputs.tf
1:56PM INF Created /Users/jwoolfenden/code/sato/.sato/data.tf

I make an opinionated translation, in Terraform there are no parameters, resources and dependencies are very different, there's no one for one - ARM to Terraform, so the aim is to get you close to 100%.

There needs to be a lot of work supporting resources and built-in functions/template as yet. If you want to use this, let me know so, then I'll know to do so, or even better send me a PR.

Version

$sato version
9.9.9

Help

$ sato
NAME:
sato - Translate Cloudformation to Terraform

USAGE:
sato [global options] command [command options] [arguments...]

VERSION:
9.9.9

AUTHOR:
James Woolfenden <jim.wolf@duck.com>

COMMANDS:
bisect      translate ARM to Terraform
parse       translate CFN to Terraform
see         shows equivalent Terraform resource
version, v  Outputs the application version
help, h     Shows a list of commands or help for one command

GLOBAL OPTIONS:
--help, -h     show help
--version, -v  print the version

Extra credit - Pike

If you use my other tool, Pike you can now apply that and get the policy requirements:

pike scan -d .sato -o json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": [
        "codebuild:BatchGetProjects",
        "codebuild:CreateProject",
        "codebuild:DeleteProject",
        "codebuild:UpdateProject"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "VisualEditor1",
      "Effect": "Allow",
      "Action": [
        "codepipeline:CreatePipeline",
        "codepipeline:DeletePipeline",
        "codepipeline:GetPipeline",
        "codepipeline:ListTagsForResource"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "VisualEditor2",
      "Effect": "Allow",
      "Action": [
        "iam:CreateRole",
        "iam:DeleteRole",
        "iam:DeleteRolePolicy",
        "iam:GetRole",
        "iam:GetRolePolicy",
        "iam:ListAttachedRolePolicies",
        "iam:ListInstanceProfilesForRole",
        "iam:ListRolePolicies",
        "iam:PassRole",
        "iam:PutRolePolicy"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "VisualEditor3",
      "Effect": "Allow",
      "Action": [
        "s3:CreateBucket",
        "s3:DeleteBucket",
        "s3:GetAccelerateConfiguration",
        "s3:GetBucketAcl",
        "s3:GetBucketCORS",
        "s3:GetBucketLogging",
        "s3:GetBucketObjectLockConfiguration",
        "s3:GetBucketPolicy",
        "s3:GetBucketRequestPayment",
        "s3:GetBucketTagging",
        "s3:GetBucketVersioning",
        "s3:GetBucketWebsite",
        "s3:GetEncryptionConfiguration",
        "s3:GetLifecycleConfiguration",
        "s3:GetObject",
        "s3:GetObjectAcl",
        "s3:GetReplicationConfiguration",
        "s3:ListBucket"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

Valid CloudFormation

Ditch it all, OK, but some older samples can play fast and lose with the CloudFormation schema and data types. The Go-formation parser is less accommodating, you may need to be stricter on your typing.