troposphere icon indicating copy to clipboard operation
troposphere copied to clipboard

awacs.aws.Policy versus troposphere.Policy- how to add policies to a role?

Open daugustus opened this issue 7 years ago • 9 comments

Real confusion here... :(

Using: troposphere 2.3.1 awacs 0.8.0

Installed with "pip install troposphere[policy]"

I am attempting to add multiple policies to a iam.role. I have tried several different varieties of coding this but I am unable to get this to work using awacs with troposphere.

from troposphere import Template
from troposphere.iam import Policy
from troposphere.iam import Role
from awacs.aws import PolicyDocument
from awacs.aws import Principal
from awacs.aws import Statement

    self.role_snapshots = self.template.add_resource(
            Role(
                "Snapshots",
                AssumeRolePolicyDocument=PolicyDocument(
                    Statement=[
                        Statement(
                            Effect=Allow,
                            Action=[
                                Action("sts", "AssumeRole")
                            ],
                            Principal=Principal("Service", "lambda.amazonaws.com")
                        )
                    ]
                ),
                Policies=[
                    Policy(
                        PolicyName="inline_policy_snapshots_cw_logs",
                        PolicyDocument=PolicyDocument(
                            Id="inline_policy_snapshots_cw_logs",
                            Version="2012-10-17",
                            Statement=[
                                Statement(
                                    Effect=Allow,
                                    Action=[
                                        Action("logs", "CreateLogGroup"),
                                        Action("logs", "CreateLogStream"),
                                        Action("logs", "PutLogEvents"),
                                    ],
                                    Resource=["arn:aws:logs:*:*:*"]
                                )
                            ]
                        )
                    ),
                ]
            )
        )

daugustus avatar Aug 01 '18 13:08 daugustus

I believe you had almost everything right. I took your sample and tweaked it slightly to run standalone and added a couple of additional imports. Try this:

from troposphere import Template
from troposphere.iam import Policy
from troposphere.iam import Role
from awacs.aws import Action
from awacs.aws import Allow
from awacs.aws import PolicyDocument
from awacs.aws import Principal
from awacs.aws import Statement

t = Template()

role_snapshots = t.add_resource(
        Role(
            "Snapshots",
            AssumeRolePolicyDocument=PolicyDocument(
                Statement=[
                    Statement(
                        Effect=Allow,
                        Action=[
                            Action("sts", "AssumeRole")
                        ],
                        Principal=Principal("Service", "lambda.amazonaws.com")
                    )
                ]
            ),
            Policies=[
                Policy(
                    PolicyName="inline_policy_snapshots_cw_logs",
                    PolicyDocument=PolicyDocument(
                        Id="inline_policy_snapshots_cw_logs",
                        Version="2012-10-17",
                        Statement=[
                            Statement(
                                Effect=Allow,
                                Action=[
                                    Action("logs", "CreateLogGroup"),
                                    Action("logs", "CreateLogStream"),
                                    Action("logs", "PutLogEvents"),
                                ],
                                Resource=["arn:aws:logs:*:*:*"]
                            )
                        ]
                    )
                ),
            ]
        )
    )

print t.to_json()

markpeek avatar Aug 01 '18 15:08 markpeek

OK I found my issue after testing your code posted here. I am using Python3 with the top line of the file being: #!/usr/bin/env python3

Your print statement was the clue....

Is there a different way of doing this with Python3?

TIA

daugustus avatar Aug 01 '18 18:08 daugustus

So I changed my shebang to be: #!/usr/bin/env python2

Still no joy

'awacs.aws.Policy' object does not support attribute 'PolicyName'

daugustus avatar Aug 01 '18 18:08 daugustus

Changing only the print statement in my example and running it with python3 gave me good, identical json results. Given your error above, are you importing Policy from awacs? Likely you just need to import it from troposphere per the example but use awacs to create the policy documents.

markpeek avatar Aug 01 '18 19:08 markpeek

I see the error of my ways but I am unsure of the solution.

I have multiple troposphere scripts and a common file included into all of them with "from mycommon import *". At the top of that, I have some imports like:

import troposphere.iam as Policy from awacs.aws import Action from awacs.aws import Allow from awacs.aws import PolicyDocument from awacs.aws import Principal from awacs.aws import Statement

Within that common file, I have some policies that are used by more than one script and I just call them as needed. Another script includes that same common file and does not use that shared policy but does create some other policies AND has the SAME imports. And this is where I experienced the problem first.

So I am trying to determine which way to do this. I will keep trying as I am sure my problem is just my lack of Python expertise.

Any direction is helpful.

daugustus avatar Aug 01 '18 20:08 daugustus

It will be difficult for me to tell you how to structure your multiple scripts. A couple of points though:

  • Your above usage of "import troposphere.iam as Policy" seems really odd. Did you mean "from troposphere.iam import Policy"?
  • In general I tend to import multiple per line rather than one import per line.
  • Perhaps being more intentional with your namespaces would help. You should check out this example that delineates troposphere from awacs objects (but is more verbose).

But really if you just use Policy from troposphere.iam and then build your documents using awacs then you shouldn't get any conflicts. If you really need the awacs Policy you can explicitly use it with:

>>> import awacs.aws
>>> awacs.aws.Policy()
<awacs.aws.Policy object at 0x101d9e0d0>

or

>>> from awacs.aws import Policy as APolicy
>>> APolicy()
<awacs.aws.Policy object at 0x103c17050>

markpeek avatar Aug 01 '18 21:08 markpeek

Now I am running into problems with KMS key policies. :(

#!/usr/bin/env python3
from troposphere import Template
from troposphere.iam import Policy
from troposphere.kms import Key
from awacs.aws import Action
from awacs.aws import Allow
from awacs.aws import PolicyDocument
from awacs.aws import Principal
from awacs.aws import Statement

t = Template()

key = t.add_resource(
    Key(
        "keylabel",
        Description="Key for ",
        Enabled=True,
        EnableKeyRotation=True,
        KeyPolicy=Policy(
            PolicyName="key-default-1",
            PolicyDocument=PolicyDocument(
                Version="2012-10-17",
                Statement=[
                    Statement(
                        Sid="Enable IAM User Permissions for the Key",
                        Effect=Allow,
                        Principal=Principal("AWS", '123454567'),
                        Action=[
                            Action("kms", "CancelKeyDeletion"),
                            Action("kms", "CreateAlias"),
                            Action("kms", "CreateGrant"),
                            Action("kms", "CreateKey"),
                            Action("kms", "DeleteAlias"),
                            Action("kms", "DeleteImportedKeyMaterial"),
                            Action("kms", "DescribeKey"),
                            Action("kms", "DisableKey"),
                            Action("kms", "DisableKeyRotation"),
                            Action("kms", "EnableKey"),
                            Action("kms", "EnableKeyRotation"),
                            Action("kms", "GetKeyPolicy"),
                            Action("kms", "GetKeyRotationStatus"),
                            Action("kms", "ListAliases"),
                            Action("kms", "ListGrants"),
                            Action("kms", "ListKeyPolicies"),
                            Action("kms", "ListKeys"),
                            Action("kms", "ListResourceTags"),
                            Action("kms", "ListRetirableGrants"),
                            Action("kms", "PutKeyPolicy"),
                            Action("kms", "RevokeGrant"),
                            Action("kms", "ScheduleKeyDeletion"),
                            Action("kms", "UpdateAlias"),
                            Action("kms", "UpdateKeyDescription"),
                        ],
                        Resource=["*"],
                    ),
                ]
            )
        )
    )
)

print(t.to_json())

Errors with:

TypeError: <class 'troposphere.kms.Key'>: keylabel.KeyPolicy is <class 'troposphere.iam.Policy'>, expected (<class 'dict'>, <class 'awacs.aws.Policy'>)

daugustus avatar Aug 02 '18 15:08 daugustus

Fixed with this: :)

#!/usr/bin/env python3

from troposphere import Template
from troposphere.kms import Key

import awacs
import awacs.aws
import awacs.iam as iam

from awacs.aws import Policy
from awacs.aws import Action
from awacs.aws import Allow
from awacs.aws import Principal
from awacs.aws import Statement

aws_account_number='122334456'
t = Template()

key = t.add_resource(
     Key(
        "keylabel",
        Description="Key for RDS",
        Enabled=True,
        EnableKeyRotation=True,
        KeyPolicy=(Policy(
            Version="2012-10-17",
            Statement=[
                Statement(
                    Sid="Enable IAM User Permissions for the Key",
                    Effect=Allow,
                    Principal=Principal("AWS", iam.ARN(account=aws_account_number, resource='root')),
                    Action=[
                        Action("kms", "CancelKeyDeletion"),
                        Action("kms", "CreateAlias"),
                        Action("kms", "CreateGrant"),
                        Action("kms", "CreateKey"),
                        Action("kms", "DeleteAlias"),
                        Action("kms", "DeleteImportedKeyMaterial"),
                        Action("kms", "DescribeKey"),
                        Action("kms", "DisableKey"),
                        Action("kms", "DisableKeyRotation"),
                        Action("kms", "EnableKey"),
                        Action("kms", "EnableKeyRotation"),
                        Action("kms", "GetKeyPolicy"),
                        Action("kms", "GetKeyRotationStatus"),
                        Action("kms", "ListAliases"),
                        Action("kms", "ListGrants"),
                        Action("kms", "ListKeyPolicies"),
                        Action("kms", "ListKeys"),
                        Action("kms", "ListResourceTags"),
                        Action("kms", "ListRetirableGrants"),
                        Action("kms", "PutKeyPolicy"),
                        Action("kms", "RevokeGrant"),
                        Action("kms", "ScheduleKeyDeletion"),
                        Action("kms", "UpdateAlias"),
                        Action("kms", "UpdateKeyDescription"),
                    ],
                    Resource=["*"],
                ),
                Statement(
                    Sid="Allow administration of the key",
                    Effect=Allow,
                    Principal=Principal("AWS", iam.ARN(account=aws_account_number, resource='role/aurora-kms-admins')),
                    Action=[
                        Action("kms", "CancelKeyDeletion"),
                        Action("kms", "CreateAlias"),
                        Action("kms", "CreateGrant"),
                        Action("kms", "DeleteAlias"),
                        Action("kms", "DeleteImportedKeyMaterial"),
                        Action("kms", "DescribeKey"),
                        Action("kms", "DisableKey"),
                        Action("kms", "DisableKeyRotation"),
                        Action("kms", "EnableKey"),
                        Action("kms", "EnableKeyRotation"),
                        Action("kms", "GetKeyPolicy"),
                        Action("kms", "GetKeyRotationStatus"),
                        Action("kms", "ListAliases"),
                        Action("kms", "ListGrants"),
                        Action("kms", "ListKeyPolicies"),
                        Action("kms", "ListKeys"),
                        Action("kms", "ListResourceTags"),
                        Action("kms", "ListRetirableGrants"),
                        Action("kms", "PutKeyPolicy"),
                        Action("kms", "RevokeGrant"),
                        Action("kms", "ScheduleKeyDeletion"),
                        Action("kms", "UpdateAlias"),
                        Action("kms", "UpdateKeyDescription"),
                    ],
                    Resource=["*"],
                ),
                Statement(
                    Sid="Allow use of the key",
                    Effect=Allow,
                    Principal=Principal("AWS", iam.ARN(account=aws_account_number, resource='role/aurora-kms-users')),
                    Action=[
                        Action("kms", "Encrypt"),
                        Action("kms", "Decrypt"),
                        Action("kms", "ReEncrypt"),
                        Action("kms", "ReEncryptFrom"),
                        Action("kms", "ReEncryptTo"),
                        Action("kms", "GenerateDataKey"),
                        Action("kms", "GenerateDataKeyWithoutPlaintext"),
                        Action("kms", "GenerateRandom"),
                        Action("kms", "DescribeKey")
                    ],
                    Resource=["*"],
                ),
            ]
        ))
    )
)

print(t.to_json())

daugustus avatar Aug 02 '18 16:08 daugustus

I tried some variance of that and for example wanted to use Ref(AWS_ACCOUNT_ID) for account_id and get

"Effect": "Allow",
 "Principal": {
 "AWS": "arn:aws:iam::<troposphere.Ref object at 0x7fc5d1bca5b0>:role/aurora-kms-admins"
},

So is there a path to get troposphere and awacs merge ? That'd be very helpful to manage policies and statements (document) in code to make it easier to avoid duplicates etc.

JohnPreston avatar Sep 20 '21 20:09 JohnPreston