dtan4 / terraforming

Export existing AWS resources to Terraform style (tf, tfstate) / No longer actively maintained
http://terraforming.dtan4.net/
MIT License
4.29k stars 659 forks source link

Support For AssumeRole #356

Open armenr opened 7 years ago

armenr commented 7 years ago

We run multiple accounts in AWS, one is a sandbox, others serve their own purposes. Is there some way to specify which account I want terraforming to use?

I have assume_role policies set up for the key/secret key I'm sourcing in my env, so the credentials work across any of the accounts.

I'm just trying to figure out what param to feed terraforming (if it supports it) so that it knows which account's resources to hit.

coingraham commented 7 years ago

Put the keys in your credentials file in ~/.aws/credentials under a profile name and use the --profile switch when calling terraforming commands.

armenr commented 7 years ago

First, please know how grateful I am that a tool like this exists out there. It has been very, very useful for me for many, many years. I think I first forked this project nearly two years ago. I've been a fan ever since.

Main account ID w/ AssumeRole user: 000000000000 Managed account ID where I can AssumeRole into AccountAdministrator: 111111111111

Confirmed working:

Steps to reproduce:

Using the AWS CLI instead of Terraforming, I have a ~/.aws/config file where I stored "myuser" AWS keys (from IAM credentials provided in the Main account. It looks like this:

[profile default]
aws_access_key_id = <REDACTED>         #myuser credential from IAM in Main account
aws_secret_access_key = <REDACTED>     #myuser credential from IAM in Main account

[profile dev-east]
role_arn = arn:aws:iam::111111111111:role/OrganizationAccountAccessRole
source_profile = default
aws_region = us-east-1

When I run the AWS CLI, it works:

╰─ aws iam list-users --profile dev-east
{
    "Users": [
        {
            "UserName": "something1",
            "Path": "/",
            "PasswordLastUsed": BLEEP,
            "CreateDate": BLEEP,
            "UserId": "BLEEP",
            "Arn": "arn:aws:iam::111111111111:user/something1"
        },
        {
            "UserName": "something2",
            "Path": "/",
            "PasswordLastUsed": BLEEP,
            "CreateDate": BLEEP,
            "UserId": "BLEEP",
            "Arn": "arn:aws:iam::111111111111:user/something2"
        }
    ]
}

I set up an identical profile in my ~/.aws/credentials file which looks like this:

[default]
aws_access_key_id = <REDACTED>         #myuser credential from IAM in Main account
aws_secret_access_key = <REDACTED>     #myuser credential from IAM in Main account

[dev-east]
aws_access_key_id = <REDACTED>         #myuser credential from IAM in Main account
aws_secret_access_key = <REDACTED>     #myuser credential from IAM in Main account
role_arn = arn:aws:iam::111111111111:role/OrganizationAccountAccessRole
source_profile = default
aws_region = us-east-1

When I run terraforming, here's what I get:

╰─ terraforming iamu --profile dev-east
/Users/armenr/.rvm/gems/ruby-2.4.0/gems/aws-sdk-core-
2.10.28/lib/seahorse/client/plugins/raise_response_errors.rb:15:in `call': User: 
arn:aws:iam::000000000000:user/myuser is not authorized to perform: iam:ListUsers on resource: 
arn:aws:iam::000000000000:user/ (Aws::IAM::Errors::AccessDenied)

The really troubling part is:

arn:aws:iam::000000000000:user/myuser is not authorized to perform: iam:ListUsers on resource: 
arn:aws:iam::000000000000:user/ (Aws::IAM::Errors::AccessDenied)

I get the same issue with any other type of resource - ec2, rds, etc...

It seems terraforming/AWS RubySDK is running listUsers on the 000000000000 account, instead of assuming the role in account 111111111111, and running IAM listUsers on that account...but I don't have that problem wit the AWS CLI version of the same command.

Moreover, it seems like it's a known issue elsewhere with the way in which the AWS Client is first constructed in the RubySDK. I'm also parsing through your code and can't seem to find anything in the repo regarding Aws::STS::Client.new (creating STS client to acquire temporary AssumeRole credentials) or Aws::SharedCredentials.net (building shared credentials profile objects).

For example, in your iam_user.rb, all I see is:

      def self.tf(client: Aws::IAM::Client.new)
        self.new(client).tf
      end

      def self.tfstate(client: Aws::IAM::Client.new)
        self.new(client).tfstate
      end
  1. I guess we'd have to build a wrapper that would initialize all user profiles by parsing ~/.aws/credentials and construct profile objects first, using a tool like: https://github.com/a2ikm/aws_config

  2. Then, we'd have to create an STS client

  3. Then, we'd have to construct a credentials object

  4. Then, we'd have to create an IAM client, passing in the credentials object.

Please forgive the code below, I'm doing most of this off of a tablet, while in transit, so it's probably totally messed up -

module Terraforming
  module Resource
    class IAMUser
      include Terraforming::Util

      def self.sts(sts_client: Aws::STS::Client.new(profile: 'dev-east')
        self.new(sts_client).tf
      end

      def self.credentials(Aws::AssumeRoleCredentials.new(
        client: client,
        role_arn: role_arn,
        role_session_name: "some_session",
        token_code: token_code
      ))
        self.new(credentials)
      end

      def

      def self.tf(client: Aws::IAM::Client.new(credentials: self.credentials))
        self.new(client).tf
      end

      def self.tfstate(client: Aws::IAM::Client.new(credentials: self.credentials))
        self.new(client).tfstate
      end

      def initialize(client)
        @client = client
      end

Basically, I'm wondering if terraforming is capable of supporting a way to AssumeRole to other account resources, in order to list them?

IF not, is it cool if I open a PR to try to work that out?

cmedley commented 7 years ago

@armenr we have multiple accounts and had same issue ... see if this works for you https://github.com/cmedley/terraforming/tree/support-assume-role (if it does, will open PR)

armenr commented 7 years ago

@cmedley --> Thanks for that! This is clearly more elegant than any Ruby I was going to be able to hack together.

I'm gonna take this for a test drive and see how it fares. Definitely glad someone around these parts forked and wrote it before I had the chance to mutilate the code.

Thank you!

varunchandak commented 7 years ago

@cmedley version is not working for me using --assume

armenr commented 7 years ago

I can confirm that I was able to get it working back in August/September when he shared it to me.

I can try to test again and let you know...maybe help to repro the issue.

cmedley commented 7 years ago

I will test as well and update over weekend ... need to remove the options[:client] from cli.rb and update the tests (but that wouldn't stop it from working)

On Fri, Oct 13, 2017 at 9:18 PM, Armen Rostamian notifications@github.com wrote:

I can confirm that I was able to get it working back in August/September when he shared it to me.

I can try to test again and let you know...maybe help to repro the issue.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dtan4/terraforming/issues/356#issuecomment-336599642, or mute the thread https://github.com/notifications/unsubscribe-auth/AAMSY38EUk4py5o68DPUD4xQChcfjcAQks5ssAvJgaJpZM4O74eh .

cmedley commented 7 years ago

@varunchandak @armenr created a new pull request for assume role support ... https://github.com/dtan4/terraforming/pull/379

rmishra-github commented 6 years ago

Amazing tool.Thank you so much for this ...is it possible to do terraforming for a specific resource? something like terraforming ec2.{ec2-id} ?

o6uoq commented 6 years ago

+1

dadreggors commented 5 years ago

I still cannot make this work. I can use aws with --profile flag working perfectly but with terraforming, I use --profile <as seen in .aws/credentials> and --assume <my ARN as seen in .aws/config> and I get nothing returned....

Example Credentials profile used:

[MY-PROFILE]
aws_access_key_id =<REDACTED>
aws_secret_access_key = <REDACTED>
aws_session_token = <REDACTED>

Example Config file in use:

[profile MY-ACCOUNT/MY-ROLE]
region = us-west-2
output = json
role_arn = arn:aws:iam::<ACCOUNT ID>:role/<ROLE>
source_profile = MY-PROFILE

Example command and output...

$ terraforming --region=us-west-2 --profile=MY-PROFILE --assume=arn:aws:iam::<ACCOUNT ID>:role/<ROLE> ec2
Usage:
  terraforming ec2

Options:
  [--merge=MERGE]                                # tfstate file to merge
  [--overwrite], [--no-overwrite]                # Overwrite existing tfstate
  [--tfstate], [--no-tfstate]                    # Generate tfstate
  [--profile=PROFILE]                            # AWS credentials profile
  [--region=REGION]                              # AWS region
  [--assume=ASSUME]                              # Role ARN to assume
  [--use-bundled-cert], [--no-use-bundled-cert]  # Use the bundled CA certificate from AWS SDK

EC2

Example aws usage and output

$ aws --profile MY-ACCOUNT/MY-ROLE sts get-caller-identity
{
    "UserId": "<REDACTED>:botocore-session-<REDACTED>",
    "Account": "<REDACTED>",
    "Arn": "arn:aws:sts::<ACCOUNT ID>:assumed-role/<ROLE>/botocore-session-<REDACTED>"
}

I can also describe instances and see proper results (too large to list here)... yet terraforming does not return anything for ec2 when using this config setup.

cmedley commented 5 years ago

@ddreggors think ec2 needs to go before the options $ terraforming ec2 --region ...

dadreggors commented 5 years ago

@ddreggors think ec2 needs to go before the options $ terraforming ec2 --region ...

You are correct, that fixed it right up thanks!

dimisjim commented 5 years ago

@ddreggors @cmedley Using both --assume and --profile still doesn't work for me. I am getting:

/var/lib/gems/2.5.0/gems/aws-sigv4-1.1.0/lib/aws-sigv4/signer.rb:670:in `credentials_set?': undefined method `access_key_id' for nil:NilClass (NoMethodError)

My ~/.aws/credentials looks like this:

[default]
aws_access_key_id = ...
aws_secret_access_key = ...

[profile1]
role_arn = arn:aws:iam::<account-id-1>:role/<role-name-1>
source_profile = default
mfa_serial = arn:aws:iam::<central-account-id>:mfa/<my-username>

[profile2]
role_arn = arn:aws:iam::<account-id-2>:role/<role-name-2>
source_profile = default
mfa_serial = arn:aws:iam::<central-account-id>:mfa/<my-username>
cmedley commented 5 years ago

@dimisjim what does the command look like that you are using?

dimisjim commented 5 years ago

@cmedley terraforming s3 --region=eu-west-1 --assume=arn:aws:iam::<account-id-1>:role/<role-name-1> --profile=<profile-1>

Getting the same if I put --profile arg before --assume or if I have exported AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_REGION or not

cmedley commented 5 years ago

@dimisjim getting same error as you ... to make this work it looks like we need to pass serial number and token code in the params to support MFA devices. I can get a PR going soon.

Short term, you can use aws cli to create the credentials aws sts assume-role --serial arn:aws:iam::<central-account-id>:mfa/<my-username> --role-session-name=<a-name> --role-arn="arn:aws:iam::<account-id-1>:role/<role-name-1>" --token-code <your-mfa-token> then export AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN and then run terraforming s3

armenr commented 5 years ago

It's been 2 years, and you guys are still helping/supporting this feature and issue. So incredibly cool to see.

//end gushy pointless post.

cmedley commented 5 years ago

@dimisjim got a PR https://github.com/dtan4/terraforming/pull/463 that adds MFA support to assume role. Should work using terraforming s3 --profile=<your_profile> --assume=<role_arn> --mfa-serial=<mfa_arn> --token-code=<mfa_token>

dimisjim commented 5 years ago

@cmedley Awesome! How can I test this?

cmedley commented 5 years ago

@dimisjim can test this way

 $ git clone https://github.com/dtan4/terraforming.git
 $ cd terraforming
 $ git fetch origin pull/463/head:assume-role-with-mfa-support
 $ git checkout assume-role-with-mfa-support
 $ bundle exec rake install
 $ terraforming s3 --profile=<your_profile> --assume=<role_arn> --mfa-serial=<mfa_arn> --token-code=<mfa_token> --region=<region>
dimisjim commented 5 years ago

@cmedley

Ok, having some "require" difficulties with running the produced binary in terraforming/bin, I did the following:

Assuming your instructions up to the bundle exec command (+ having ran the scripts/setup prior to that) and by also editing after it:

Then, having my ~/.aws/credentials file containing my default mfa profile that links to all the other accounts that it can assume a role there, I ran the command you proposed by omitting the --profile flag:

ruby /path/to/terraforming/lib/terraforming.rb s3 --assume=arn:aws:sts::<AccountIdThatDefaultAccountCanAssumeItsRole>:role/<RoleName> --mfa-serial=arn:aws:iam::<AccountIdOfDefaultAccount>:mfa/<Username> --token-code=<code> --region=<region>

This worked beautifully!

Can we proceed with merging the relevant PR #463 ?