├── .gitignore ├── slides └── terraform-aws-meetup-may-2020.pdf ├── .pre-commit-config.yaml ├── 5-refactoring └── main.tf ├── 1-terraform └── main.tf ├── .editorconfig ├── 6-awscc └── main.tf ├── 2-cloudformation └── main.tf ├── 3-cli └── main.tf ├── README.md └── 4-partial-terraform └── main.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | terraform.tfstate 3 | *.tfstate* 4 | terraform.tfvars 5 | -------------------------------------------------------------------------------- /slides/terraform-aws-meetup-may-2020.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonbabenko/terraform-aws-anything/HEAD/slides/terraform-aws-meetup-may-2020.pdf -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: git://github.com/antonbabenko/pre-commit-terraform 3 | rev: v1.30.0 4 | hooks: 5 | - id: terraform_fmt 6 | - id: terraform_docs 7 | - repo: git://github.com/pre-commit/pre-commit-hooks 8 | rev: v3.1.0 9 | hooks: 10 | - id: check-merge-conflict 11 | -------------------------------------------------------------------------------- /5-refactoring/main.tf: -------------------------------------------------------------------------------- 1 | //provider "aws" { 2 | // region = "us-west-2" 3 | //} 4 | // 5 | //resource "random_pet" "this" { 6 | // length = 2 7 | //} 8 | // 9 | //resource "aws_devicefarm_project" "this" { 10 | // name = random_pet.this.id 11 | //} 12 | // 13 | //output "this_devicefarm_project_arn" { 14 | // value = aws_devicefarm_project.this.arn 15 | //} 16 | -------------------------------------------------------------------------------- /1-terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "random_pet" "this" { 2 | length = 2 3 | 4 | # You can use keepers to seed randomness in your configs instead of running "terraform taint" every time :) 5 | keepers = { 6 | hash = filemd5("main.tf") 7 | } 8 | } 9 | 10 | resource "aws_s3_bucket" "this" { 11 | bucket = "hello-${random_pet.this.id}" 12 | } 13 | 14 | output "this_s3_bucket_id" { 15 | value = aws_s3_bucket.this.id 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | # Uses editorconfig to maintain consistent coding styles 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | indent_size = 2 12 | indent_style = space 13 | insert_final_newline = true 14 | max_line_length = 80 15 | trim_trailing_whitespace = true 16 | 17 | [*.{tf,tfvars}] 18 | indent_size = 2 19 | indent_style = space 20 | 21 | [*.md] 22 | max_line_length = 0 23 | trim_trailing_whitespace = false 24 | 25 | [Makefile] 26 | tab_width = 2 27 | indent_style = tab 28 | 29 | [COMMIT_EDITMSG] 30 | max_line_length = 0 -------------------------------------------------------------------------------- /6-awscc/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | provider "awscc" { 6 | region = "eu-west-1" 7 | } 8 | 9 | resource "random_pet" "this" { 10 | length = 2 11 | } 12 | 13 | resource "awscc_..." "this" { 14 | name = random_pet.this.id 15 | } 16 | 17 | # output "this_devicefarm_project_arn" { 18 | # value = aws_devicefarm_project.this.arn 19 | # } 20 | 21 | resource "aws_athena_workgroup" "test" { 22 | force_destroy = true 23 | name = "sdgsg" 24 | # actions { 25 | # crawler_name = "sdg" 26 | # arguments = "sdg" 27 | # } 28 | } 29 | 30 | resource "awscc_glu" "test" { 31 | name = "test" 32 | } 33 | 34 | resource "aws_cloudformation_stack_set" "name" { 35 | name = "test" 36 | } 37 | -------------------------------------------------------------------------------- /2-cloudformation/main.tf: -------------------------------------------------------------------------------- 1 | resource "random_pet" "this" { 2 | length = 2 3 | } 4 | 5 | // S3 bucket is obviously well supported by both Terraform and Cloudformation, but still using it for the sake of the simplicity in this example. If you want to try a resource type which is supported just by AWS Cloudformation, try [AWS ManagedBlockchain](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_ManagedBlockchain.html) 6 | resource "aws_cloudformation_stack" "this" { 7 | name = "s3-bucket-${random_pet.this.id}" 8 | template_body = <&3 29 | delete = "${local.aws_cli_command} ec2 delete-key-pair --key-name ${random_pet.this.id}" 30 | 31 | // read = "" 32 | // update = "" 33 | } 34 | } 35 | 36 | output "aws_cli_shell_script_output" { 37 | value = shell_script.aws_cli.output 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository contains content for my talk "Manage any AWS resource with Terraform". 2 | 3 | 4 | ## Available types of AWS resources: 5 | 6 | 1. Supported by Terraform AWS provider. See `1-terraform`. 7 | 2. Supported only by AWS CloudFormation, but not by Terraform AWS provider. See `2-cloudformation`. 8 | 3. Supported by AWS SDK/CLI and not in AWS CloudFormation. See `3-cli`. 9 | 4. Partially supported by Terraform AWS provider (some arguments are not supported). See `4-partial-terraform`. 10 | 5. Not supported in AWS CLI/SDK. [Good luck with Selenium!](https://aws.amazon.com/about-aws/whats-new/2020/01/aws-device-farm-announces-desktop-browser-testing-using-selenium/) or take a look at [Ian's work with Puppeteer](https://github.com/iann0036/aws-account-controller) :trollface: :trollface: :trollface: 11 | 6. New since 2024! AWS Cloud Control API (AWS CC). Potentially, better support than AWS CloudFormation. 12 | 13 | 14 | ## Available solutions to manage AWS resources using Terraform include: 15 | 16 | 1. Use `local-exec` provisioner with `aws-cli` 17 | 2. Use [`shell` provider](https://github.com/scottwinkler/terraform-provider-shell) 18 | 3. Patch the [Terraform AWS provider](https://github.com/terraform-providers/terraform-provider-aws/) using golang 19 | 20 | 21 | ## Flow: 22 | 23 | 1. Import existing resources into state 24 | 25 | 2. Refactoring states: 26 | - from/to module 27 | - to another state-file 28 | - `terraform state list | grep -v module..... | xargs -Ixx terraform state rm xx` 29 | 30 | 3. Migrate from AWS Cloudformation to Terraform 31 | 32 | 33 | ## See also 34 | 35 | 1. Talk "I Can’t Do This With Terraform, Now What?" by Patrick Conheady given at HashiTalks 2020. [See this repository](https://github.com/pacon-vib/tfcando) and [this recording](https://www.hashicorp.com/resources/i-can-t-do-this-with-terraform-now-what). 36 | -------------------------------------------------------------------------------- /4-partial-terraform/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | aws_cli_command = "aws --output json --profile anton-demo --region eu-west-1" 3 | } 4 | 5 | # Logical directory mappings for SFTP users are not supported by Terraform AWS Provider v2.62.0 (21.5.2020) 6 | # Related open issue: https://github.com/terraform-providers/terraform-provider-aws/issues/11281 7 | # 8 | # This is not supported yet! 9 | //resource "aws_transfer_user" "sftp_user_dbs" { 10 | // # other options 11 | // # ... 12 | // home_directory_type = "LOGICAL" 13 | // home_directory_mapping = { 14 | // entry = "your-personal-report.pdf" 15 | // target = "/bucket3/customized-reports/${transfer:UserName}.pdf" 16 | // } 17 | //} 18 | 19 | // Download and compile from https://github.com/scottwinkler/terraform-provider-shell 20 | provider "shell" {} 21 | 22 | resource "shell_script" "user1_home_directory_mapping" { 23 | lifecycle_commands { 24 | create = "aws transfer update-user --server-id ${aws_transfer_server.this.id} --user-name ${aws_transfer_user.user1.user_name} --home-directory-type LOGICAL --home-directory-mappings '${jsonencode([{ Entry : "/my_home", Target : "/home/user1" }])}'" 25 | delete = "aws transfer update-user --server-id ${aws_transfer_server.this.id} --user-name ${aws_transfer_user.user1.user_name} --home-directory-type PATH" 26 | } 27 | } 28 | 29 | ###################################### 30 | # The rest of Terraform code is below 31 | ###################################### 32 | 33 | resource "aws_transfer_server" "this" { 34 | identity_provider_type = "SERVICE_MANAGED" 35 | } 36 | 37 | resource "aws_transfer_user" "user1" { 38 | server_id = aws_transfer_server.this.id 39 | role = aws_iam_role.user1.arn 40 | user_name = "user1" 41 | } 42 | 43 | resource "aws_iam_role" "user1" { 44 | name_prefix = "user1-" 45 | assume_role_policy = data.aws_iam_policy_document.user1_assume_role.json 46 | } 47 | 48 | data "aws_iam_policy_document" "user1_assume_role" { 49 | statement { 50 | actions = ["sts:AssumeRole"] 51 | 52 | principals { 53 | type = "Service" 54 | identifiers = ["transfer.amazonaws.com"] 55 | } 56 | } 57 | } 58 | 59 | ########## 60 | # Outputs 61 | ########## 62 | 63 | output "user1_home_directory_mapping_shell_script_output" { 64 | value = shell_script.user1_home_directory_mapping.output 65 | } 66 | 67 | output "user1_transfer_user_id" { 68 | value = aws_transfer_user.user1.id 69 | } 70 | --------------------------------------------------------------------------------