Git Product home page Git Product logo

tgmigrate's Introduction

tgmigrate

tgmigrate helps you with migrating state in and across terraform state files in a terragrunt environment.

Why?

Migrating state is necessary when refactoring your terragrunt project, and moving resources between state files or within them can be tricky. There are projects like tfmigrate that's targeted towards terraform, but we found it hard to use with terragrunt. Usually we write shell scripts to move or remove state resources with the terraform state commands, but there is no way to store the history to check if the migration have been applied.

Features

  • Simple and declarative migrations
  • Migration history
  • Automation friendly
  • AWS s3 support
  • Test the migration with dryrun the option

Install

Download the latest package from releases and put it in the executable path.

Usage

You first have to set up a .tgmigrate.hcl file:

Example config file:

migration {
  migration = "./migrations"

  history {
    storage "s3" {
      bucket = "airthings-terraform-states-${ACCOUNT}"
      region = "us-east-1"
      assume_role = "${ASSUME_ROLE}"
      key = "history.json"
    }
  }

  state "s3" {
    bucket = "airthings-terraform-states-${ACCOUNT}"
    region = "us-east-1"
    assume_role = "${ASSUME_ROLE}"
  }
}

this file can be specified by using the -c(config) flag,
if not specified tgmigrate will look in the parent folders up to $HOME

migration = "./migration" refers to the directory where the migration files are located, this path is relative to the config file.
history is where you can configure where to store the history, currently its only storage "s3" that's supported.
state is where you configure where your state is located, also here its only state "s3" that's supported.

A nice feature in the config file is the support for variables using the common ${variable_name} syntax, How to populate the variables is described further down.

Next you need to make a migration file:

Under the migration directory you specified in the config file, create a new file and name it something like this: V1__descriptive_text_of_the_migration.hcl.
The V1__ prefix is required and inspired by flyway's versioning concepts, and makes sure that the order of the migrations are executed correctly.

Example migration file:

migration {
  environments = [
    "dev",
    "prod",
    "developer"
  ]
  description = <<EOF
    - Move the rest2 lambda to a separate module.
EOF
}

migrate "move" "rest_2" {
  from {
    state = "us-east-1/apis/rest"
    resource = "aws_lambda_function.rest_2_api_lambda"
  }

  to {
    state = "us-east-1/apis/rest_2"
    resource = "aws_lambda_function.rest_api_lambda"
  }
}

migrate "remove" "api_gateway_integration_for_rest_2" {
  state = "us-east-1/apis/rest"
  resource = "aws_apigatewayv2_integration.rest_2_api"
}

The migration block contains metadata for the migration file. Here you can specify the environments the migration file is intended for, this is optional to include. You can also include a description for the migration but that is optional.

The migrate blocks have 2 supported types: move and remove.
On move you need to specify a from and to block, while on remove you can provide the state and resource variable.
In both cases state refers to the location within the s3_bucket. resource refers to the resource type + name in the state file.

Integrate with terragrunt:

tgmigrate can be integrated with terragrunt's before hook.
There is currently a issue with the integration because there is no option to only run tgmigrate once, this makes terragrunt run tgmigrate for every module.
You can work around this issue by running it manually before running terragrunt apply or you can use the hook on a module that is always running first by using dependencies.

before_hook "plan_migrations" {
  commands = [
    "plan"
  ]
  execute = [
    "tgmigrate",
    "-y",
    "--cv=ACCOUNT_ID=${local.account_id};ASSUME_ROLE=${local.terraform_role_arn}",
    "plan",
    "prod"
  ]
  run_on_error = false
}
before_hook "run_migrations" {
  commands = [
    "apply"
  ]
  execute = [
    "tgmigrate",
    "-y",
    "--cv=ACCOUNT_ID=${local.account_id};ASSUME_ROLE=${local.terraform_role_arn}",
    "apply",
    "prod"
  ]
  run_on_error = false
}

Notice the --cv(config-variables) flag, here we specify the ACCOUNT_ID and the ASSUME_ROLE variable that we use in the config file.
Also notice the prod sub-command, this is telling tgmigrate to only apply migrations for the prod environment.
This means that the migration file above would be applied in this case, because it has prod in the environments list.

Example output

2021/02/01 14:32:16 Downloading us-east-1/apis/rest/terraform.tfstate to /home/user/terragrunt/.tgmigrate_cache/state/us-east-1/apis/rest/terraform.tfstate
2021/02/01 14:32:16 Downloading us-east-1/apis/rest_2/terraform.tfstate to /home/user/terragrunt/.tgmigrate_cache/state/us-east-1/apis/rest_2/terraform.tfstate
Migrations for sample_migration_file.hcl will be applied
Moving aws_lambda_function.rest_2_api_lambda from us-east-1/apis/rest to us-east-1/apis/rest_2 aws_lambda_function.rest_api_lambda
Removing aws_apigatewayv2_integration.rest_2_api from us-east-1/apis/rest

tgmigrate's People

Contributors

aleksanderaleksic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

assistments

tgmigrate's Issues

Add revert migration command

A revert command can be usefull when a migration have been applied but there was a issue or you need to work on something else where the migration can be blocking.

For aws s3 this requires us to track the e-tag of each state object that is uploaded after the change.
This means that the e-tag needs to be stored in the history file.
The approach is simple, when a user wants to revert, we can simply delete the objects where the e-tag maches the e-tag stored for that state file. If multiple migrations have been applied then we have to remove all changes for those versions.

Conditional migrations

In the migration block I am imagining a way to set conditionals for if a migration should run.

Some conditionals can be:

  • don't run after a given date (this can be nice if you are setting up a new environment with your config and so you don't have to skip all the migrations for it)
  • Variable conditions, to allow you to for instance only run a migration if it matches a variable in the config variables inputed.

Example of the block:

migration {
 condition {
   test = "OnlyBefore"
   Value = "2021-08-14T00:00"
 }
 condition {
  test = "MatchesEnvironment"
  value = [
     "dev",
     "prod"
   ]
 }
}

Apply migrations

Being able to apply migrations is a minimum requirement for this tool.

Plan migrations

The tool should be able to plan migrations and output the changes.

Use datasource to import resources

There should be possible to import resources using data sources. This way we can import i.e route53 zone by name.

data "aws_route53_zone" "test_com" {
  name = "test.com"
}

migrate "import" test_com" {
   state = "dns"
   name = "module.test_com.aws_route53_zone.test_com"
   resource = data.aws_route53_zone.test_com.id
}

flag provided but not defined: -d

on the example given:

  commands = [
    "plan"
  ]
  execute = [
    "tgmigrate",
    "-d",
    "-y",
    "--cv=ACCOUNT_ID=${local.account_id};ASSUME_ROLE=${local.terraform_role_arn}",
    "apply",
    "prod"
  ]
  run_on_error = false
}

there is a reference to a non-existing parameter:
flag provided but not defined: -d

how (if possible) should the plan action be used?

undocumented filename requirement

from readme:

create a new file and name it something that makes sense for you.

but this will only work if the filename starts with a capital V, then some numbers, followed by '__', all without spaces.

Option to run bash script migrations.

Sometimes more logic in the migration might be needed.
Then having support to trigger bash scripts would be a nice additon.
The nice thing with running it from tgmigrate is that you store the history so it is only applied once

migrate "script" "bash" {
  type = "bash"
  file = "./some_advanced_migration.sh"
  parameters = {
     param1 = "val1"
     param2 = "${VARIABLE}"
  }
}

Find .tgmigrate.hcl file in parent folders

The config file can located in the same directory as the executable or the path can be added as a -config flag.
It would also be nice to look for a conif file in the parent directories, if flag is not specified and no config file is found in the same directory as the executable.

Option to run only one execution when using TG hooks with -all option

When running terragrunt apply-all /plan-all multiple executions of tgmigrate meaning there is a migrationcheck for one module at the same time. This will cause race condition.

Suggestion is to add a flag like --max-concurrent-executions 1 where if it is applied the executions above execution nr.1 would be skipped. This "state" would be stored localy in a file, but it must be known where the file is located.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.