Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cross-account parameter resolving #292

Merged
merged 21 commits into from
Dec 23, 2019

Conversation

petervandoros
Copy link
Contributor

This PR adds support for assuming a role when resolving parameters.

We've recently had to configure VPC peering and found it error prone to have to hard code VPC and peering IDs in the stacks' parameters files. It's also hard and tedious to find all the relevant values, especially for old VPCs.

Being able to reference stack outputs for stacks in other accounts would make this much more maintainable and less error prone, especially if the stacks need re-creating.

Here's some examples of what it looks like:

vpc_peering_id:
  role: cross-account-parameter-resolver
  account: 1234567890
  stack_output: vpc-peering-stack-in-other-account/peering_id
an_array_param:
  role: cross-account-parameter-resolver
  account: 1234567890
  stack_outputs:
    - stack-in-account1/output
    - stack-in-account1/another_output
another_array_param:
  - role: cross-account-parameter-resolver
    account: 1234567890
    stack_output: stack-in-account1/output
  - role: cross-account-parameter-resolver
    account: 0987654321
    stack_output: stack-in-account2/output

Considerations

  • The relevant IAM permissions to allow cross-account parameter resolving is left as an exercise for the reader.

This class is responsible to actually assuming a role and
ensuring StackMaster and AWS SDK global state is updated
properly.
It adds optional 'account' and 'role' properties to the parameter resolver
definition. If set, the RoleAssumer class is used to assume the specified
role before resolving the parameter.
This is needed because the integration tests swap the driver to the test driver
which is needed to run the tests. This ensures the test driver is instantiated.
README.md Show resolved Hide resolved
Copy link
Contributor

@stevehodgkiss stevehodgkiss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍 👏 💯

Copy link
Member

@orien orien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

Copy link
Contributor

@patrobinson patrobinson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

role_arn: "arn:aws:iam::#{account}:role/#{role}",
role_session_name: "stack-master-assume-role-parameter-resolver"
)
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we want to save credentials to an instance variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it doesn't have to assume the role for every parameter wishing to assume the same role.

expect(StackMaster::RoleAssumer).to receive(:new).once

parameter_resolver.resolve
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right I see now

Copy link
Contributor

@viraptor viraptor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks good


private

def replace_aws_global_config
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the replace/restore functions are not very pretty. Part of that logic also exists in assume_role.

How about:

def assume_role(...)
...
  role_credentials = assume_role_credentials(account, role)
  with_temporary_role(role_credentials) do
    with_temporary_driver do
      block.call
    end
  end
...
end

def with_temporary_role(credentials, &block)
  original_aws_config = Aws.config
  Aws.config = original_aws_config.deep_dup
  block.call
ensure
  Aws.config = original_aws_config
end

def with_temporary_driver(&block)
  original_driver = StackMaster.cloud_formation_driver
  StackMaster.cloud_formation_driver = original_driver.class.new
  block.call
ensure
  StackMaster.cloud_formation_driver = original_driver
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea. Done in 95a8825.

end

def restore_cf_driver(driver)
return if driver.nil?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the driver is nil, driver.class.new in replace_cf_driver would fail earier.

@orien
Copy link
Member

orien commented Nov 18, 2019

Do you mind adding to the changelog?

It's unnecessary as it doesn't cache API calls and is only
used within the resolve method.

It also will make it easier to support the assume role
functionality.
It's unnecessary and makes supporting assuming a role more
difficult to implement.
This is required to support assuming a role.
@petervandoros
Copy link
Contributor Author

@orien Updated changelog in 41592b3.

@petervandoros
Copy link
Contributor Author

petervandoros commented Dec 16, 2019

@orien @stevehodgkiss @patrobinson I've made a few more changes to resolve an issue I found.

It would be great if you could re-review the PR.

@petervandoros petervandoros force-pushed the assume-role-parameter-resolving branch from a8b8670 to 56cc3a6 Compare December 16, 2019 12:15
README.md Outdated Show resolved Hide resolved
lib/stack_master/parameter_resolvers/ejson.rb Show resolved Hide resolved
def assume_role_credentials(account, role)
credentials_key = "#{account}:#{role}"
@credentials.fetch(credentials_key) do
@credentials[credentials_key] = Aws::AssumeRoleCredentials.new(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to set the hash here if fetch is being used?

Suggested change
@credentials[credentials_key] = Aws::AssumeRoleCredentials.new(
Aws::AssumeRoleCredentials.new(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The block passed to the fetch method specifies the return value if the key is not found. It doesn't set the value in the hash if it's missing. We need to set the hash value for it to persist.

irb(main):001:0> h = {a: 'a', b: 'b'}
=> {:a=>"a", :b=>"b"}
irb(main):002:0> h.fetch(:z) do
irb(main):003:1* 'z'
irb(main):004:1> end
=> "z"
irb(main):005:0> h
=> {:a=>"a", :b=>"b"}
irb(main):006:0> h.fetch(:z) do
irb(main):007:1* h[:z] = 'z'
irb(main):008:1> end
=> "z"
irb(main):009:0> h
=> {:a=>"a", :b=>"b", :z=>"z"}

@@ -0,0 +1,57 @@
Feature: Apply command with assume role parameter resolvers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

There's a build error with the following message:
bundler's executable bundle conflicts with /home/travis/.rvm/rubies/ruby-2.3.8/bin/bundle
Overwrite the executable? [yN]
Copy link
Member

@orien orien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

@petervandoros petervandoros merged commit 0d4c63d into master Dec 23, 2019
@petervandoros petervandoros deleted the assume-role-parameter-resolving branch December 23, 2019 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants