Giter Site home page Giter Site logo

awslabs / aws-iam-generator Goto Github PK

View Code? Open in Web Editor NEW
238.0 14.0 55.0 3.8 MB

Generate Multi-Account IAM users/groups/roles/policies from a simple YAML configuration file and Jinja2 templates.

License: Apache License 2.0

Python 64.46% Dockerfile 0.44% Shell 1.48% Jinja 33.62%
aws-iam aws-cloudformation aws aws-manage aws-multiaccount

aws-iam-generator's Introduction

iam_generator

Generates AWS IAM Users, Groups, Roles, and Managed Policies from a YAML configuration and Jinja2 Templates

Build Environment

A Python interpreter with required libraries installed. Use pip to install the requirements:

sudo pip install -r requirements.txt

NOTE: At present, build is tested on OSX and Linux. Pull requests welcome for Windows build support!

Usage

$ ./build.py --help
usage: build.py [-h] [-c CONFIG] [-f {json,yaml}] [-o OUTPUT_PATH]
                [-p POLICY_PATH]

optional arguments:
  -h, --help            show this help message and exit
  -c CONFIG, --config CONFIG
                        Path to config.yaml (default: ./config.yaml)
  -f {json,yaml}, --format {json,yaml}
                        The CloudFormation format to use (default: json)
  -o OUTPUT_PATH, --output-path OUTPUT_PATH
                        Path into which the CloudFormation templates will be
                        written (default: ./output_templates)
  -p POLICY_PATH, --policy-path POLICY_PATH
                        Path to jinja2 policy templates (default: ./policy)

Docker Image

A Dockerfile is provided to make this project portable.

Building the Image

docker build . -t iam_generator

Running as a docker container

Let's say you have a local config in ./config/iam.yml, some policy templates in ./policy_documents, and you want the generated CloudFormation templates to be created locally in ./cloudformation.

Here's how to run the code from the container:

docker run -ti --rm -v ${PWD}/cloudformation:/iam_generator/cloudformation \
                    -v ${PWD}/config:/iam_generator/config \
                    -v ${PWD}/policy_documents:/iam_generator/policy_documents \
                    iam_generator -c config/iam.yml \
                                  -f yaml \
                                  -o cloudformation/ \
                                  -p policy_documents/

General Function

Everything is driven by the config.yaml file. In this file you describe your account structure, and desired managed policies, roles, users and groups.

Managed policy structure (in JSON or YAML) is kept in jinja2 templates files to allow for variable substitution for specific customization of ARNs and trusts etc.

When build.py is executed a CloudFormation template is built per account. They are availble in the configured OUTPUT_PATH directory to be uploaded to CloudFormation for deployment in each account.

This project wouldn't be possible without the hard work done by the Troposphere and Jinja project teams. Thanks!

config.yaml key sections

There are five main sections of the config.yaml:

global:
  ...
accounts:
  ...
policies:
  ...
roles:
  ...
users:
  ...
groups:
  ...

global: section

Controls our our genereated templates behaviour. There are two key sections. names: and template_outputs.

The names: section looks like this:

global:
  names:
    policies: False
    roles: True
    users: True
    groups: True

This section allows control over the IAM Naming of the resources. When values are set to True they will be explicitly named based on the config.yaml entry. When set to False CloudFormation will generate a name for you.

For example:

policies:
  cloudFormationAdmin:
    description: CloudFormation Administrator
    policy_file: cloudFormationAdmin.j2

if polices: True is set, the name of the managed policy that CloudFormation creates will be cloudFormationAdmin. If polices: False then CloudFormation will generate a unique value using the stack prefix and a suffix eg: PolicyStack-cloudFormationAdmin-ACH753NADF.

If the global: section is omitted, it will function with the following default values:

global:
  names:
    policies: False
    roles: True
    users: True
    groups: True
  template_outputs: enabled

The template_outputs: value allows control over whether the CloudFormation templates will include Output values for the elements they create. There is a limit in CloudFormation templates of 60 output values. You will hit this much sooner than the 200 Resource limit. The main reason to include an output is so it can be imported in a stack layered above. If you don't intend on layering any stacks above this one then disabling outputs is absolutely fine.

Set template_outputs: enabled to include template outputs. Set template_outputs: disabled to disable output values for templates.

accounts: section

Here's an example of the accounts section:

accounts:
  central:
    id: 123456678910
    parent: true
    saml_provider: ProdADFS
  dev1:
    id: 109876543210
  dev2:
    id: 309876543210
  prod:
    id: 209876543210

accounts: is a dictionary of friendly account names. These friendly names can be used throughout the rest of the YAML file and are available to the jinja2 templates.

Note that saml_provider is optional. If it is used in a Role's trust: list it will generate the appropriate trust policy for console federation.

Account resolution in the yaml:

  • Use the keyword all to refer to all of the accounts in the accounts: section.
  • Use the keyword parent to refer to the account you've marked as parent: true.
  • Use the keyword children to refer to all accounts except the parent.
  • You can also refer to a specific account by friendly name or it's ID. eg: dev1
  • You can also use python regular expressions. eg: dev.* to target both developement accounts.

Using these mechanisms it should be easy to specifically manage what account each element lands in.

policies: section

Managed Policies derived from a jinja2 template

Here's an example of a managed policy based on a jinja2 policy template:

policies:
  cloudFormationAdmin:
    description: CloudFormation Administrator
    policy_file: cloudFormationAdmin.j2
    template_vars:
      cloudformation_bucket: bucket-cloudformation
    in_accounts:
      - parent
      - dev.*

This will create a managed policy with the name cloudFormationAdmin (along with the prefix and suffix that CloudFormation Adds automatically). It will base the policy document on the contents of of the cloudFormationAdmin.j2 jinja2 template. It will be placed into the accounts 123456678910 (which is our parent) as well as accounts 109876543210 and 309876543210 because their friendly names (dev1 and dev2) match our regular expression dev.*.

Lets disect this a bit.

policies: is a dictionary of policy names. This assures they are kept unique within the account and generated CloudFormation template.

policy_file: needs to be located in the configurable POLICY_PATH directory, and would look something like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "cloudformation:*"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::{{ template_vars.cloudformation_bucket }}"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": ["arn:aws:s3:::{{ template_vars.cloudformation_bucket }}/*"]
    }
  ]
}

Notice the variable substitution in the template {{ template_vars.cloudformation_bucket }}.

Notice the value for this is derived from this section of the config.yaml:

...
    template_vars:
      cloudformation_bucket: bucket-cloudformation
...

Jinja2 templates will get the following variable namespaces passed to them:

  • template_vars as described above.
  • account which is the account ID of the account that is being worked on.
  • parent_account which is the account ID of the account marked as parent: true.
  • config which is the entire config.yaml file!

Managed Policies for sts:AssumeRole (auto generated!)

Here's an example of the yaml to create a managed policy for sts:AssumeRole with the specified roles into the specified accounts.

policies:
  assumeAdmin:
    description: Allow assumption of the Admin role in all children accounts from the parent
    assume:
      roles:
        - Admin
      accounts:
        - children
    in_accounts:
      - parent

This will create this policy document . . .

"Version": "2012-10-17",
"Statement": [
  {
    "Action": "sts:AssumeRole",
    "Effect": "Allow",
    "Resource": "arn:aws:iam::109876543210:role/Admin"
  },
  {
    "Action": "sts:AssumeRole",
    "Effect": "Allow",
    "Resource": "arn:aws:iam::309876543210:role/Admin"
  },
  {
    "Action": "sts:AssumeRole",
    "Effect": "Allow",
    "Resource": "arn:aws:iam::209876543210:role/Admin"
  },
]

. . . and insert it into the CloudFormation template for account 123456678910 (which was marked parent).

roles: section

Example of a role that can be assumed from another account

roles:
  NetworkAdmin:
    trusts:
      - parent
    managed_policies:
      - arn:aws:iam::aws:policy/job-function/NetworkAdministrator
      - arn:aws:iam::aws:policy/ReadOnlyAccess
      - cloudFormationAdmin
    in_accounts:
      - all
    max_role_duration: 3600

This will create a role called NetworkAdmin. It will have two AWS managed policies, and one policy referenced from the policies: section of the config.yaml. It allows for a list of managed_policies: to attach to the role. It has a MaxSessionDuration of 1 hour.

The assume role policy document will be automatically generated to trust the parent:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456678910:root"
      }
    }
  ]
}

Example of an ec2 role

roles:
  s3andDynamoWrite:
    trusts:
      - ec2.amazonaws.com
    inline_policies: []
    managed_policies:
      - arn:aws:iam::aws:policy/AmazonS3FullAccess
      - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
      - arn:aws:iam::aws:policy/AmazonElasticMapReduceFullAccess
    in_accounts:
      - all

In this case our trusts: is ec2.amazonaws.com. This can be any service like config.amazonaws.com, lambda.amazonaws.com, etc.

Since we're trusting ec2.amazonaws.com we will automatically create an instance profile for this role so it can be used from ec2. No additional configuration required.

Example of a federated role

In cases that we have a saml_provider: in our parent account we can reference it in our trust.

roles:
  AWS_Admins:
    trusts:
      - ProdADFS
    managed_policies:
      - assumeAdmin
    in_accounts:
      - parent

This will generate the following assume role policy document automatically . . .

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRoleWithSAML",
      "Condition": {
        "StringEquals": {
          "SAML:aud": "https://signin.aws.amazon.com/saml"
        }
      },
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456678910:saml-provider/ProdADFS"
      }
    }
  ]
}

. . . and place it (along with the role definition) in the parent CloudFormation template.

users: section

An example of a user that is not a member of a group, and has a managed_policy directly attached

users:
  adam:
    managed_policies:
      - assumeAdmin
    password: CHANGEME
    in_accounts:
      - parent

Optionally specify a password: for the user. The flag is set to force a password change at first login. The password is clear text, so be careful!

An example of a user as a member of groups

users:
  adam:
    groups:
      - Admins
    in_accounts:
      - parent

Our groups: field is the name of a group. This can be a name that already exists, or is in the config.yaml file. Existing groups just need to be the name of the group, not an ARN.

groups: section

groups:
  Admins:
    managed_policies:
      - assumeAdmin
      - arn:aws:iam::aws:policy/ReadOnlyAccess
    in_accounts:
      - parent

groups: is once again a dictionary of group names that you'd like created. Each allows for a list of managed_policies: to attach.

retain_on_delete variable

CloudFormation permits retention on deletion of a resource. This is described here.

For any resource in the policies: roles: groups: or users: section include retain_on_delete: true to configure the CloudFormation template to retain that resource on deletion. This let's you keep a resource that may not necessarily remain under management of this CloudFormation template any longer.

eg:

roles:
  NetworkAdmin:
    retain_on_delete: true
    trusts:
      - parent
    managed_policies:
      - arn:aws:iam::aws:policy/job-function/NetworkAdministrator
      - arn:aws:iam::aws:policy/ReadOnlyAccess
      - cloudFormationAdmin
    in_accounts:
      - all

If this section is removed from the config.yaml, and a stack-update executed, the 'NetworkAdmin' Role will persist in the account and no longer be managed by CloudFormation.

Default value is retain_on_delete: false which does not need to be explicitly declared anywhere. This matches the default behaviour of CloudFormation.

A note about managed_policies

For those config sections that support managed_policies, each entry can be an existing managed policy ARN or the name of a policy created in the policies: section of the YAML. Existing ARN strings can optionally contain AWS Pseudo Parameters. These are especially useful for account-specific customer managed policies that were created out-of-band, e.g.:

roles:
  NetworkAdmin:
    trusts:
      - parent
    managed_policies:
      - arn:aws:iam::aws:policy/job-function/NetworkAdministrator
      - arn:aws:iam::aws:policy/ReadOnlyAccess
      - arn:aws:iam::${AWS::AccountId}:policy/CustomerManagedPolicy
    in_accounts:
      - children

Importing resources from other templates

You are able to specify the keyword of import: within the config.yaml file. Use this for managed_policies, users, groups, or roles. This will substiute the appropraite Fn:ImportValue within the template to import from an existing CloudFormation templates Exports.

Example of an import for a role:

roles:
  NetworkAdmin:
    trusts:
      - parent
    managed_policies:
      - import:name_of_cfn_export
    in_accounts:
      - all

aws-iam-generator's People

Contributors

apmclean avatar chirillc-aws avatar dependabot[bot] avatar hyandell avatar joeymiller avatar marcilio avatar msaum avatar vito-laurenza-zocdoc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aws-iam-generator's Issues

Not handling Managed Policy correctly.

The template came from the script will not run in CloudFormation console when trying to creating a stack from it. Syntax Error message for all managed policies when running this script.

How do I pass account IDs to Jinja templates?

I am trying to specify a list of roles into a policy Resource list, however with

{% for account_name in config.accounts %}
  - "arn:aws:iam::{{ account_name }}:role/MyRole"
{% endfor %}

I only end up with the friendly names - which match neither ID not the account alias (the latter being too long and unwieldy and NOT "friendly").

Staring at the source code, it doesn't seem the accounts dictionary makes the IDs available to Jinja templates? Despite there being a function that maps the friendly names to IDs.

I've tried various combinations of {{ account_name.id }} and such, but to no avail.

The documentation makes no mention either. Is this currently not possible?

Feature request: dynamic config template or allow dynamic roles generation

I would like to be able to dynamically generate Roles based on the accounts.

For example, if I have:

accounts:
  central:
    id: 123456678910
    parent: true
    saml_provider: ProdADFS
  dev1:
    id: 109876543210
  dev2:
    id: 309876543210
  prod:
    id: 209876543210

I would like to be able to generate roles as:

roles:
  <Account>Admin:
    trusts:
      - parent
    managed_policies:
      - arn:aws:iam::aws:policy/AdministratorAccess
    in_accounts:
      - <account>
  <Account>ReadOnly:
      trusts:
        - parent
      managed_policies:
        - arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
      in_accounts:
        - <account>

with <Account> and <account> substituted with all accounts from accounts:. This would generate specific roles for each account. Said roles would have further managed policies that could, for example, make sure of conditions the permit usage based on conditions matching the SAML user ID, etc.

Feature Proposal: via_cloudformation

Hey,

Current Situation

We often have the situation, that we want to allow a Role to do certain things only via cloudformation. This is done this way:

policies:
  cf-deployment:
    description: Allow to pass security-deployment to cloudformation
    policy_file: management-cloudformation.j2
    template_vars:
      cfrole: role-deployment
      cfaccount: 1234567890
    in_accounts:
    - account

roles:
  role:
    trusts:
      - SAMLIDP
    managed_policies:
      - cf-deployment
    in_accounts:
      - account

  role-deployment:
    trusts:
      - cloudformation.amazonaws.com
    managed_policies:
      - <list-of-managed-polices>
    in_accounts:
      - account

with management-cloudformation.j2 looking like this:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PassRoleToCloudFormation",
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::{{ template_vars.cfaccount }}:role/{{ template_vars.cfrole }}"
        }
    ]
}

Since we have this pattern relatively often we are looking for a simplification.

Feature Proposal

Add an option to the role to add certain policy only via cloduformation.

So the a role could be written like this:

roles:
  role:
    trusts:
      - SAMLIDP
    managed_policies:
      - cf-deployment
    policies_via_cloudformation:
      - <list-of-policies>
    cloudformation_role_name: <optional-cf-role-name>
    in_accounts:
      - account

This would result in the following:

  1. An additional Role with name in cloudformation_role_name and trust cloudformation.amazonaws.com is created and the policies under policies_via_cloudformation are added. If cloudformation_role_name is not set, the role is named role-cloudformation
  2. The role role is created and an additonal inline policy, which allows iam:PassRole with the role cloudformation_role_name is added.

What do you think? Would you accept a PR if I would create one?

Thanks!

Feature Proposal: Support for Direct Federation

This toolset works great to create roles for a hub-and-spoke method with a central identity account! It would be nice to also have support for roles where each account has direct federation. Right now if I try to do this, I get an error if a parent_account is not specified.

It would be nice if we could declare the configuration like this and it would have the role in each account have a trust relationship to a SAML provider configured in the same account:

accounts:
  account1:
    id: 000000000000
    saml_provider: OktaIDP
  account2:
    id: 000000000000
    saml_provider: OktaIDP
roles:
  ReadOnly:
    trusts:
      - OktaIDP
    managed_policies:
      - arn:aws:iam::aws:policy/ReadOnlyAccess
    in_accounts:
      - all

I am basing this on some of the new ways we can integrate Okta without the central identity account design: https://saml-doc.okta.com/SAML_Docs/How-to-Configure-SAML-2.0-for-Amazon-Web-Service#scenarioB

Handling of "DependsOn" statement

Does this script handle "DependsOn" statement? I am trying to create a template for kmsSecurity policy but keeps on getting the "Template validation error: Template format error: Unresolved resource dependencies [kmsSecurityKey] in the Resources block of the template" error.

Windows support

Hey, thanks for the great project!

I would like to try to add windows support, and it does not seem to be to difficult.

The iam_template_build.py already works! So, I can call it directly and I am already very happy.

The build.py does not work, so here my questions:

  1. Why does the build.py even exist? Or why is it not just the iam_template_build.py directly? Why the detour via build.py?
  2. Why are you calling iam_template_build.py via python from build.py? Could you not load the file via import and then call the python code?

Thanks!

HOWTO -- Stack Layers

What is the recommended way to overcome the 200 resources limit in CloudFormation? Since the generator creates one template per account, I know I'm eventually going to hit the limit in CloudFormation.

Still maintained?

Is this tool still maintained by awslabs? I find I still use it to automate all things IAM - it's just so clean and simple to use. Would hate for it to just die on the vine. Unless it's been surpassed by something better?

Feature Request: Auto-detect policy document fragment format

As a user, I should be able to write my IAM policy statements in either JSON or YAML and the tool should automatically handle either format.

Ideally this would be a distinct feature from the --format flag introduced in #13 which IMO should be refactored to influence the CloudFormation output format only.

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.