terraform-aws-control_tower_account_factory icon indicating copy to clipboard operation
terraform-aws-control_tower_account_factory copied to clipboard

Documentation on how to terraform plan on your local

Open jarrettj opened this issue 3 years ago • 12 comments
trafficstars

Hi

Good day.

Describe the outcome you'd like Documentation on how to "terraform plan" on your local. At the moment you are forced to push changes to the pipeline.

A clear and concise description of what you want to happen. A way to setup terraform locally to run "terraform plan" against a provisioned account to view changes before pushing.

Is your feature request related to a problem you are currently experiencing? If so, please describe. The problem is coding in the dark basically. The lifecycle is made harder by not being able to plan locally.

A clear and concise description of what the problem is.

Additional context

Add any other context or screenshots about the feature request here. We've managed to reference the provisioned account state file on the management account. But the "terraform plan" still shows all to update. It's better than nothing for now. But as the infrastructure increases it will become harder to read.

Regards. Jarrett

jarrettj avatar Sep 20 '22 06:09 jarrettj

@jarrettj I would say this feature might be covered by https://github.com/aws-ia/terraform-aws-control_tower_account_factory/issues/153

v-rosa avatar Sep 20 '22 15:09 v-rosa

Hey @v-rosa , good day. Would this cover working locally though? Looks more like approval gates added to the pipeline.

What I'm asking is simply being able to init and plan locally. Would be way faster, 5-8 mins than pushing to the pipeline.

jarrettj avatar Sep 20 '22 15:09 jarrettj

Ohh by locally you mean from your laptop? It would be a different request then.

v-rosa avatar Sep 20 '22 15:09 v-rosa

Yup, that's correct. Thought I'd add it here in the event that it's been done already, and I missed it in the docs. I'll probably figure it out as I go along working through more terraform examples etc :)

jarrettj avatar Sep 20 '22 16:09 jarrettj

Hey @jarrettj AFT is not intended as a development tool, but rather as a deployment tool. One potential route for leveraging terraform plan locally would be to execute terraform plan against a development/testing account directly before pushing to AFT's customizations

That being said, I've made a backlog with the team to consider this request, thanks!

balltrev avatar Sep 21 '22 18:09 balltrev

Thank you @balltrev.

jarrettj avatar Sep 21 '22 20:09 jarrettj

I think the underlying question really is how to get the plan output. In that respect, it doesn't really matter whether the terraform plan command is executed locally or via CI/CD or anywhere else.

(Housekeeping: perhaps we should rename this issue to remove the "on your local" part? Or should this be raised as a separate issue?)

Example

Assume I already have an AFT account provisioned (in production, to make it extra spicy). The existing Terraform source might look like:

module "foo_prod" {
  source = "./modules/aft-account-request"

  control_tower_parameters = {
    AccountName = "bar-prod"
    ...
  }

  ...
}

But wait, I've just spotted an inconsistency in the resource name, and coincidentally somebody is now requesting for an actual foo-prod account, so I proceed to make a harmless change request to fix the existing resource name:

- module "foo_prod" {
+ module "bar_prod" {

...

+
+ moved {
+   from = module.fooo_prod
+   to   = module.bar_prod
+ }

Guess what? I've now actually made another typo, but there's no easy way to detect this (e.g. no Terraform plan).

So, when my CI/CD pipeline (or whatever) deploys this change, it will actually close my existing production account bar-prod.

The potential impact/disruption of this could be huge! It's not just a simple case of "oops, I misspelled one of my tag values, let me make a follow-up change request to fix forward".

theipster avatar Mar 02 '23 13:03 theipster

@theipster in that case the request here https://github.com/aws-ia/terraform-aws-control_tower_account_factory/issues/153 would prevent it. Still not able to test it from non main branches, but for sure it's much safer.

v-rosa avatar Mar 16 '23 11:03 v-rosa

a bit related to https://github.com/aws-ia/terraform-aws-control_tower_account_factory/issues/249#issuecomment-1451908276, I want to move the account creation process a bit to use for_each. is there a safe way to migrate the state properly? I'm looking to move multiple "module" blocks into one block with for_each specified.

KarlCF avatar Apr 03 '23 13:04 KarlCF

any update on this?

alibvr avatar Apr 12 '23 07:04 alibvr

Hey, I do plan partly from my local. It however fails on an ACM I created in the US East region, which is different from my default region. To setup do the following:

New account requirements

Go to the IAM Identity Center(Billing account) and assign the newly created account and add the Control Tower User with AWSAdministratorAccess permission set. (https://us-east-1.console.aws.amazon.com/iamv2/home?region=eu-west-1#/organization/accounts)

This should allow you to see the account in your AWS access portal. The URL is on the IAM Identity Center landing page to the right. Once in, you should assume the administrator user to get the info of the below.

You will need an IAM admin user in your newly created account, something similar to the below, iam.tf:

# Groups
resource "aws_iam_group" "administrators" {
  name = "administrators"

  lifecycle {
    prevent_destroy = true
  }
}
# Group Policies
resource "aws_iam_group_policy_attachment" "administrators" {
  group      = aws_iam_group.administrators.name
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}
resource "aws_iam_group_policy_attachment" "IAMUserChangePassword" {
  group      = aws_iam_group.administrators.name
  policy_arn = "arn:aws:iam::aws:policy/IAMUserChangePassword"
}
# Create iam user
module "iam_iam-user-administrator" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-user"
  version = "5.3.3"
  
  name = var.name
}
resource "aws_iam_user_group_membership" "administrators" {
  user = var.name
  groups = [aws_iam_group.administrators.name]
  depends_on = [
    module.iam_iam-user-administrator
  ]
}
resource "aws_ssm_parameter" "iam_user_login_profile_password" {
  name  = "/iam/user/${var.name}/iam_user_login_profile_password"
  type  = "SecureString"
  value = module.iam_iam-user-administrator.iam_user_login_profile_password
  depends_on = [
   module.iam_iam-user-administrator
  ]
}
resource "aws_ssm_parameter" "iam_access_key_secret" {
  name  = "/iam/user/${var.name}/iam_access_key_secret"
  type  = "SecureString"
  value = module.iam_iam-user-administrator.iam_access_key_secret
  depends_on = [
   module.iam_iam-user-administrator
  ]
}

Add the above to your new account aft-account-customizations repo and commit/push and run codepipeline. Once complete you should have an account to use locally. And well, to login to your new account as admin as well.

You can log into your new account via your AFT Control account(Billing account). Once in, the saved credentials are in the ssm parameters section. You will need your AWS Key, which is on the user created above and the AWS Secret which is in the ssm parameters to create your AWS profile locally:

vim ~/.aws/credentials

[your_new_account]
aws_access_key_id=aws_access_key_id
aws_secret_access_key=aws_secret_access_key
region=eu-region

Create an alias to your new account using your bash rc file. I use zsh, so inmy ~/.zshrc, added: vim ~/.zshrc

alias terraform_your_new_account = 'AWS_PROFILE=your_new_account terraform'

Save your new account account id as you will need that later below.

Create a backend override config in the customization terraform root

aft-account-customizations/your_new_account/backend_override.tf

terraform {
  required_version = ">= 0.15.0"
  backend "s3" {
    region         = "{aft-management-region}"
    bucket         = "aft-backend-{aft-management-account-id}-primary-region"
    key            = "{account-id}-aft-account-customizations/terraform.tfstate"
    encrypt        = "true"
    kms_key_id     = "{aft-management-kms-key}"
  }
}

Please use your own values for the below variables: aft-management-region aft-management-account-id account-id aft-management-kms-key

Made it an override so as not to commit it to CVS.

Create a local provider in customization terraform root

aft-account-customizations/your_new_account/provider.tf

# Used for local terraform init and validate to work.
provider "aws" {
  alias  = "main"
  region = "{account-region}"
  assume_role {
    role_arn    = "arn:aws:iam::{aft-management-account-id}:role/AWSAFTAdmin"
  }
  default_tags {
    tags = {
      managed_by                  = "AFT"
    }
  }
}

Please use your own values for the below variables: account-region aft-management-account-id

Remember to add provider.tf to .gitignore. You don't want that in CVS.

Update trust entity for AWSAFTAdmin role:

https://us-east-1.console.aws.amazon.com/iamv2/home?region=eu-west-1#/roles/details/AWSAFTAdmin?section=trust_relationships

{
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::{account_id}:user/{iam_user}"
    },
    "Action": "sts:AssumeRole"
},

Please use your own values for the below variables: account_id iam_user

This role can be found in the AWS AFT Management account. Hint: Use your browser profiles to switch between AWS accounts. Instead of the role switching AWS provides as it is very iffy!

Include the user you will use to access terraform as.

Update state file S3 bucket permissions: https://s3.console.aws.amazon.com/s3/buckets/aft-backend-035819274757-primary-region?region=eu-west-1&tab=permissions

 {
    "Sid": "account_id",
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::{account_id}:user/{iam_user}"
    },
    "Action": [
        "s3:GetObject"
        "s3:ListBucket"
    ],
    "Resource": [
        "arn:aws:s3:::aft-backend-{aft-management-account-id}-primary-region"
        "arn:aws:s3:::aft-backend-{aft-management-account-id}-primary-region/{account_id}-aft-account-customizations/*"
    ]
}

Please use your own values for the below variables: account_id iam_user aft-management-account-id

This bucket can be found in the AWS AFT Management account.

Grant KMS access

Add the following terraform to the aft-account-provisioning-customizations repo.

resource "aws_kms_grant" "cross-account-grants-{account_id}-{iam_user}" {
  grantee_principal = "arn:aws:iam::{account_id}:user/{iam_user}"
  key_id = data.aws_kms_key.aft-backend.id
  operations = ["Encrypt", "Decrypt", "DescribeKey", "ReEncryptFrom", "ReEncryptTo", "GenerateDataKey"]
}

Please use your own values for the below variables: account_id iam_user

Push those changes to allow the grants to be added. Once the pipeline has completed running, execute the following to see grants:

aws kms list-grants --key-id {kms_key_arn} --profile aft-management

Running locally

You should now be able to run the following on your local machine: terraform_your_new_account -chdir=your_repo/new_account/terraform init -reconfigure This should reconfigure your state file to be the one on S3 in the AFT Management account. Next you can do a validate and see if all is well: terraform_your_new_account -chdir=your_repo/new_account/terraform validate

And then, after all that: terraform_your_new_account -chdir=your_repo/new_account/terraform plan

Hope that helps.

Regards, Jarrett

jarrettj avatar Apr 14 '23 08:04 jarrettj

https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/validate-account-factory-for-terraform-aft-code-locally.html. I hope this helps to run locally , thanks !

gopinjag avatar Apr 02 '24 17:04 gopinjag