Repository: ansible/ansible-modules-core

Issue:
RFE: iam_policy support for jinja templates for policy_document parameter
##### Issue Type:
- Feature Idea
##### Summary:

A good use-case for iam_policy module is to create roles with commonly used policies.  If the policy_document parameter supported templates, this could be much more useful.

For example, a playbook:

``` yaml
vars:
  aws_resource: arn::s3::somebucket/*
tasks:
  - iam_policy: policy_document=somepolicy.json.j2 iam_type=group state=present name=somepolicy
```

With a template somepolicy.json.j2:

``` jinja
{
    ...
    'policy': {
       resource: "{{ aws_resouce }}"
    }
}
```

The resource could be swapped out by variable and reused for many playbooks.



Discussion:
Why don't you use jinja to template?

```
iam_policy:
  policy_json: " {{ lookup( 'template', 'somepolicy.json.j2') }} "
  ...
```

@winmat: For some reason that doesn't work for me (see #2687), does it for you?

I'm getting the same error `The policy is not in the valid JSON format.` when using file lookup

Task example

```
- name: Create Admin Group
  iam_policy:
    iam_name: Admins
    iam_type: group
    state: present
    policy_name: AdministratorAccess
    policy_json: " {{ lookup( 'file', 'AdministratorAccess.policy') }} "
```

AdminstratorAccess.policy

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}
```

I get the following error

```
fatal: [127.0.0.1]: FAILED! => {"changed": true, "failed": true, "invocation": {"module_args": {"aws_access_key": null, "aws_secret_key": null, "ec2_url": null, "iam_name": "Admins", "iam_type": "group", "policy_document": null, "policy_json": " {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"*\",\n      \"Resource\": \"*\"\n    }\n  ]\n} ", "policy_name": "AdministratorAccess", "profile": "test", "region": "us-east-1", "security_token": null, "skip_duplicates": true, "state": "present", "validate_certs": true}, "module_name": "iam_policy"}, "msg": "The policy is not in the valid JSON format."}
```

The problem is the json is being serialized to a json string again
Please merge #2729 

try w/o the space:

```
policy_json: "{{ lookup( 'file', 'AdministratorAccess.policy') }}"
```

the module is not expecting json here, but a data structure, the leading space cast it into a string instead of a structure, though this is a lot of to/from json transformations. Probalby the module needs to examine the type of value that gets passed in and convert or not depending on that.

@bcoca 

That was exactly the problem.  Works w/o the spaces.  The examples for the module has spaces in it.  Thanks for the excellent explanation.


Resolving patch:
diff --git a/cloud/amazon/iam_policy.py b/cloud/amazon/iam_policy.py
--- a/cloud/amazon/iam_policy.py
+++ b/cloud/amazon/iam_policy.py
@@ -146,9 +146,7 @@ def user_action(module, iam, name, policy_name, skip, pdoc, state):
       if urllib.unquote(iam.get_user_policy(name, pol).
                         get_user_policy_result.policy_document) == pdoc:
         policy_match = True
-        if policy_match:
-          msg=("The policy document you specified already exists "
-               "under the name %s." % pol)
+
     if state == 'present' and skip:
       if policy_name not in current_policies and not policy_match:
         changed = True
@@ -189,15 +187,12 @@ def role_action(module, iam, name, policy_name, skip, pdoc, state):
       module.exit_json(changed=False)
     else:
       module.fail_json(msg=e.message)
-      
-  try:    
+
+  try:
     for pol in current_policies:
       if urllib.unquote(iam.get_role_policy(name, pol).
                         get_role_policy_result.policy_document) == pdoc:
         policy_match = True
-        if policy_match:
-          msg=("The policy document you specified already exists "
-               "under the name %s." % pol)
     if state == 'present' and skip:
       if policy_name not in current_policies and not policy_match:
         changed = True
@@ -305,10 +300,12 @@ def main():
           pdoc = json.dumps(json.load(json_data))
           json_data.close()
   elif module.params.get('policy_json') != None:
-      try:
-        pdoc = json.dumps(module.params.get('policy_json'))
-      except Exception as e:
-        module.fail_json(msg=str(e) + '\n' + module.params.get('policy_json'))
+      # if its a string, assume it is already JSON
+      if not isinstance(pdoc, basestring):
+        try:
+          pdoc = json.dumps(module.params.get('policy_json'))
+        except Exception as e:
+          module.fail_json(msg='Failed to convert the policy into valid JSON: %s' % str(e))
   else:
     pdoc=None
 


Test patch: