# IAM Floyd

[![Source](https://img.shields.io/github/stars/udondan/iam-floyd?logo=github&label=GitHub%20Stars)](https://github.com/udondan/iam-floyd)
[![iam-floyd](https://img.shields.io/github/v/release/udondan/iam-floyd)](https://github.com/udondan/iam-floyd)
[![GitHub](https://img.shields.io/github/license/udondan/iam-floyd)](https://github.com/udondan/iam-floyd/blob/master/LICENSE)
[![Maintainability](https://api.codeclimate.com/v1/badges/cdb84b5646c6805b1a23/maintainability)](https://codeclimate.com/github/udondan/iam-floyd/maintainability)
[![CDKio](https://img.shields.io/badge/awscdk.io-cdk--iam--floyd-orange)](https://awscdk.io/packages/cdk-iam-floyd@0.53.0)

<!-- put back - when we actually have tests
[![Test Coverage](https://api.codeclimate.com/v1/badges/cdb84b5646c6805b1a23/test_coverage)](https://codeclimate.com/github/udondan/iam-floyd/test_coverage)
-->

**AWS [IAM policy statement](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_statement.html) generator with fluent interface.**

<!-- stats -->

Support for:

* 237 Services<br>
* 7818 Actions<br>
* 772 Resource Types<br>
* 456 Conditions

<!-- /stats -->

![EXPERIMENTAL](https://img.shields.io/badge/stability-experimantal-orange?style=for-the-badge)**<br>This is an early version of the package. The API will change while I implement new features. Therefore make sure you use an exact version in your `package.json` before it reaches 1.0.0.**

[![Auto completion demo](https://raw.githubusercontent.com/udondan/iam-floyd/master/docs/movie-preview.png)](https://www.youtube.com/watch?v=ivG6VnbwMB0)

## <a name='Packages'></a>Packages

There are two different package variants available:

* **iam-floyd**: Can be used in AWS SDK, Boto 3 or for whatever you need an IAM policy statement for <br>[![npm](https://img.shields.io/npm/dt/iam-floyd?label=npm&color=blueviolet)](https://www.npmjs.com/package/iam-floyd)
  [![PyPI](https://img.shields.io/pypi/dm/iam-floyd?label=pypi&color=blueviolet)](https://pypi.org/project/iam-floyd/)
  [![NuGet](https://img.shields.io/nuget/dt/IAM.Floyd?label=nuget&color=blueviolet)](https://www.nuget.org/packages/IAM.Floyd/)
* **cdk-iam-floyd**: Integrates into [AWS CDK](https://aws.amazon.com/cdk/) and extends [`iam.PolicyStatement`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.PolicyStatement.html)<br>[![npm](https://img.shields.io/npm/dt/cdk-iam-floyd?label=npm&color=orange)](https://www.npmjs.com/package/cdk-iam-floyd)
  [![PyPI](https://img.shields.io/pypi/dm/cdk-iam-floyd?label=pypi&color=orange)](https://pypi.org/project/cdk-iam-floyd/)
  [![NuGet](https://img.shields.io/nuget/dt/CDK.IAM.Floyd?label=nuget&color=orange)](https://www.nuget.org/packages/CDK.IAM.Floyd/)

---
<!-- vscode-markdown-toc -->

* [Packages](#Packages)
* [Usage](#Usage)
* [Examples](#Examples)
* [Methods](#Methods)

  * [allow](#allow)
  * [deny](#deny)
  * [to*, to](#toto)
  * [allActions](#allActions)
  * [if*, if](#ifif)
  * [on*, on](#onon)
  * [notActions](#notActions)
  * [notResources](#notResources)
  * [notPrincipals](#notPrincipals)
  * [for*](#for)
* [Collections](#Collections)

  * [allowEc2InstanceDeleteByOwner](#allowEc2InstanceDeleteByOwner)
* [Floyd?](#Floyd)
* [Similar projects](#Similarprojects)
* [Legal](#Legal)

<!-- vscode-markdown-toc-config
	numbering=false
	autoSave=true
	/vscode-markdown-toc-config --><!-- /vscode-markdown-toc -->

## <a name='Usage'></a>Usage

The package contains a statement provider for each AWS service, e.g. `Ec2`. A statement provider is a class with methods for each and every available action, resource type and condition. Calling such method will add the action/resource/condition to the statement:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
import iam_floyd as statement

statement.Ec2().to_start_instances()
```

Every method returns the statement provider, so you can chain method calls:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().to_start_instances().to_stop_instances()
```

The default effect of any statement is `Allow`. To add some linguistic sugar you can explicitly call the `allow()` method:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().to_start_instances().to_stop_instances()
```

And of course `deny()`:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().deny().to_start_instances().to_stop_instances()
```

If you don't want to be verbose and add every single action manually to the statement, you discovered the reason why this package was created. You can work with [access levels](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_understand-policy-summary-access-level-summaries.html#access_policies_access-level)!

There are 5 access levels you can use: `LIST`, `READ`, `WRITE`, `PERMISSION_MANAGEMENT` and `TAGGING`:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().all_actions(statement.AccessLevel.LIST, statement.AccessLevel.READ)
```

The `allActions()` method also accepts regular expressions (as a string) which test against the action name:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().deny().all_actions("/vpn/i")
```

If no value is passed, all actions (`ec2:*`) will be added:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().all_actions()
```

For every available condition key, there are `if*()` methods available.

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().to_start_instances().if_encrypted().if_instance_type(["t3.micro", "t3.nano"]).if_associate_public_ip_address(False).if_aws_request_tag("Owner", "John")
```

If you want to add a condition not covered by the available methods, you can define just any condition yourself via `if()`:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().to_start_instances().if("aws:RequestTag/Owner", "John")
```

The default operator for conditions of type [String](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html#Conditions_String) is `StringLike`.

Most of the `if*()` methods allow an optional operator as last argument:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().to_start_instances().if("aws:RequestTag/Owner", "*John*", "StringEquals")
```

By default the statement applies to all resources. To limit to specific resources, add them via `on*()`.

For every resource type an `on*()` method exists:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.S3().allow().all_actions().on_bucket("some-bucket").on_object("some-bucket", "some/path/*")
```

If instead you have an ARN ready, use the `on()` method:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.S3().allow().all_actions().on("arn:aws:s3:::some-bucket", "arn:aws:s3:::another-bucket")
```

To invert the policy you can use `notActions()` and `notResources()`:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.S3().allow().not_actions().not_resources().to_delete_bucket().on_bucket("some-bucket")
```

## <a name='Examples'></a>Examples

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
policy = {
    "Version": "2012-10-17",
    "Statement": [
        statement.Ec2().allow().to_start_instances().if_aws_request_tag("Owner", "${aws:username}"),
        statement.Ec2().allow().to_stop_instances().if_resource_tag("Owner", "${aws:username}"),
        statement.Ec2().allow().all_actions(statement.AccessLevel.LIST, statement.AccessLevel.READ)
    ]
}
```

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
policy = {
    "Version": "2012-10-17",
    "Statement": [
        statement.Cloudformation().allow().all_actions(),
        statement.All().allow().all_actions().if_aws_called_via("cloudformation.amazonaws.com"),
        statement.S3().allow().all_actions().on("arn:aws:s3:::cdktoolkit-stagingbucket-*"),
        statement.Account().deny().all_actions(statement.AccessLevel.PERMISSION_MANAGEMENT, statement.AccessLevel.WRITE),
        statement.Organizations().deny().all_actions(statement.AccessLevel.PERMISSION_MANAGEMENT, statement.AccessLevel.WRITE)
    ]
}
```

## <a name='Methods'></a>Methods

### <a name='allow'></a>allow

Sets the `Effect` of the statement to `Allow`.

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().to_stop_instances()
```

### <a name='deny'></a>deny

Sets the `Effect` of the statement to `Deny`.

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().deny().to_stop_instances()
```

### <a name='toto'></a>to*, to

For every available action, there are `to*()` methods available.

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().to_start_instances().to_stop_instances()
```

### <a name='allActions'></a>allActions

This method allows you to add multiple actions at once. If called without parameters, it adds all actions of the service.

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().all_actions()
```

The method can take regular expressions (as a string) and [access levels](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_understand-policy-summary-access-level-summaries.html#access_policies_access-level) as options and will add only the matching actions:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().all_actions("/vpn/i")
```

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().all_actions(statement.AccessLevel.LIST, statement.AccessLevel.READ)
```

There exist 5 access levels:

* LIST
* READ
* WRITE
* PERMISSION_MANAGEMENT
* TAGGING

### <a name='ifif'></a>if*, if

For every available condition key, there are `if*()` methods available.

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().to_start_instances().if_encrypted().if_instance_type(["t3.micro", "t3.nano"]).if_associate_public_ip_address(False).if_aws_request_tag("Owner", "John")
```

Most of them allow an optional operator as last argument:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().to_start_instances().if_instance_type("*.nano", "StringLike")
```

[Global conditions](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html) are prefixed with `ifAws`, e.g. `ifAwsRequestedRegion()`

If you want to add a condition not covered by the available methods, you can define just any condition yourself via `if()`:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Ec2().allow().to_start_instances().if("aws:RequestTag/Owner", "${aws:username}", "StringEquals")
```

### <a name='onon'></a>on*, on

Limit statement to specified resources.

For every resource type an `on*()` method exists:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.S3().allow().all_actions().on_bucket("some-bucket")
```

If instead you have an ARN ready, use the `on()` method:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.S3().allow().all_actions().on("arn:aws:s3:::some-bucket")
```

If no resources are applied to the statement, it defaults to all resources (`*`). You can also be verbose and set this yourself:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.S3().allow().all_actions().on("*")
```

### <a name='notActions'></a>notActions

Switches the policy provider to use [NotAction](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notaction.html).

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.S3().allow().not_actions().to_delete_bucket().on_bucket("some-bucket")
```

### <a name='notResources'></a>notResources

Switches the policy provider to use [NotResource](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notresource.html).

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.S3().allow().not_resources().to_delete_bucket().on_bucket("some-bucket")
```

### <a name='notPrincipals'></a>notPrincipals

Switches the policy provider to use [NotPrincipal](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notprincipal.html).

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Sts().deny().not_principals().to_assume_role().for_user("1234567890", "Bob")
```

### <a name='for'></a>for*

To create assume policies, use the `for*()` methods. There are methods available for any type of principal:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Sts().allow().to_assume_role().for_account("1234567890")

statement.Sts().allow().to_assume_role_with_sAML().for_service("lambda.amazonaws.com")

statement.Sts().allow().to_assume_role().for_user("1234567890", "Bob")

statement.Sts().allow().to_assume_role().for_role("1234567890", "role-name")

statement.Sts().allow().to_assume_role_with_sAML().for_federated_cognito()

statement.Sts().allow().to_assume_role_with_sAML().for_federated_amazon()

statement.Sts().allow().to_assume_role_with_sAML().for_federated_google()

statement.Sts().allow().to_assume_role_with_sAML().for_federated_facebook()

statement.Sts().allow().to_assume_role_with_sAML().for_saml("1234567890", "saml-provider")

statement.Sts().allow().to_assume_role().for_public()

statement.Sts().allow().to_assume_role().for_assumed_role_session("123456789", "role-name", "session-name")

statement.Sts().allow().to_assume_role().for_canonical_user("userID")

statement.Sts().allow().to_assume_role().for("arn:foo:bar")
```

To reverse the assume policy you can call the `notPrincipals()` method:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Sts().deny().not_principals().to_assume_role().for_user("1234567890", "Bob")
```

If you use the cdk variant of the package you should not have the need to manually create assume policies. But if you do, there is an additional method `forCdkPrincipal()` which takes any number of [`iam.IPrincipal`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.IPrincipal.html) objects:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
statement.Sts().allow().to_assume_role().for_cdk_principal(
    iam.ServicePrincipal("sns.amazonaws.com"),
    iam.ServicePrincipal("lambda.amazonaws.com"))
```

## <a name='Collections'></a>Collections

The package provides commonly used statement collections. These can be called  via `new statement.Collection().allowEc2InstanceDeleteByOwner()`. Collections return a list of statements, which then can be used in a policy like this:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
policy = {
    "Version": "2012-10-17",
    "Statement": [
        (SpreadElement ...new statement.Collection().allowEc2InstanceDeleteByOwner()
          statement.Collection().allow_ec2_instance_delete_by_owner())
    ]
}
```

Available collections are:

* **allowEc2InstanceDeleteByOwner**: Allows stopping EC2 instance only for the user who started them

### <a name='allowEc2InstanceDeleteByOwner'></a>allowEc2InstanceDeleteByOwner

Allows stopping EC2 instance only for the user who started them.

## <a name='Floyd'></a>Floyd?

George Floyd has been murdered by racist police officers on May 25th, 2020.

This package is not named after him to just remind you of him and his death. I want this package to be of great help to you and I want you to use it on a daily base. Every time you use it, I want you to remember our society is ill and needs change. The riots will stop. The news will fade. The issue persists!

If this statement annoys you, this package is not for you.

## <a name='Similarprojects'></a>Similar projects

* [cdk-iam-actions](https://github.com/spacerat/cdk-iam-actions)
* [cdk-iam-generator](https://github.com/srihariph/cdk-iam-generator)
* [iam-policy-generator](https://github.com/aletheia/iam-policy-generator)
* [policyuniverse](https://github.com/Netflix-Skunkworks/policyuniverse)
* [policy_sentry](https://github.com/salesforce/policy_sentry)

## <a name='Legal'></a>Legal

The code contained in the [lib](https://github.com/udondan/iam-floyd/tree/master/lib) folder is generated from the [AWS documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_actions-resources-contextkeys.html). The class- and function-names and their description therefore are property of AWS.

AWS and their services are trademarks, registered trademarks or trade dress of AWS in the U.S. and/or other countries.

This project is not affiliated, funded, or in any way associated with AWS.
