Repository: ansible/ansible

Issue:
lambda role doesn't accept AWS GovCloud ARNs
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
ansible/lib/ansible/modules/cloud/amazon/lambda.py

##### ANSIBLE VERSION
ansible 2.3.2.0

##### CONFIGURATION
standard

##### OS / ENVIRONMENT
AWS Ubuntu 16.04
Linux 4.4.0-45-generic #66-Ubuntu SMP Wed Oct 19 14:12:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

AWS GovCloud (us-gov-west-1)

##### SUMMARY

Attempting to set the role to either just the role name or the full ARN fails the lambda deployment or update because AWS can't find the specified role.


Looking in the lambda.py module, the code assumes the region partition to ever only be 'aws'.  However, the us-gov-west-1 region partition is 'aws-us-gov'.

##### STEPS TO REPRODUCE

<!--- Paste example playbooks or commands between quotes below -->
```
    tasks:
        - name: Deploy hello world
            lambda:
            name: "run_hello_world"
            runtime: "python27"
            s3_bucket: "my_bucket"
            s3_key: "my_key"
            role: "hello_world_role"

        - name: Deploy hello world Again
            lambda:
                name: "run_hello_world"
                runtime: "python27"
                s3_bucket: "my_bucket"
                s3_key: "my_key"
                role: "arn:aws-us-gov:iam::1234567890:role/hello_world_role"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
Lambda function to be created 

##### ACTUAL RESULTS

<!--- Paste verbatim command output between quotes below -->
```
fatal: [xxx.xxx.xxx.xxx]: FAILED! => {"changed": false, "failed": true, "msg": "An error occurred (ValidationException) when calling the UpdateFunctionConfiguration operation: 1 validation error detected: Value 'arn:aws:iam::1234567890:role/arn:aws-us-gov:iam::1234567890:role/hello_world_role' at 'role' failed to satisfy constraint: Member must satisfy regular expression pattern: arn:(aws|aws-us-gov):iam::\\d{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+"}
```



Discussion:
cc @steynovich
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @willthames
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/lambda.py b/lib/ansible/modules/cloud/amazon/lambda.py
--- a/lib/ansible/modules/cloud/amazon/lambda.py
+++ b/lib/ansible/modules/cloud/amazon/lambda.py
@@ -228,6 +228,7 @@
 import base64
 import hashlib
 import traceback
+import re
 
 try:
     from botocore.exceptions import ClientError, BotoCoreError, ValidationError, ParamValidationError
@@ -235,33 +236,41 @@
     pass  # protected by AnsibleAWSModule
 
 
-def get_account_id(module, region=None, endpoint=None, **aws_connect_kwargs):
-    """return the account id we are currently working on
+def get_account_info(module, region=None, endpoint=None, **aws_connect_kwargs):
+    """return the account information (account id and partition) we are currently working on
 
-    get_account_id tries too find out the account that we are working
+    get_account_info tries too find out the account that we are working
     on.  It's not guaranteed that this will be easy so we try in
     several different ways.  Giving either IAM or STS privilages to
     the account should be enough to permit this.
     """
     account_id = None
+    partition = None
     try:
         sts_client = boto3_conn(module, conn_type='client', resource='sts',
                                 region=region, endpoint=endpoint, **aws_connect_kwargs)
-        account_id = sts_client.get_caller_identity().get('Account')
+        caller_id = sts_client.get_caller_identity()
+        account_id = caller_id.get('Account')
+        partition = caller_id.get('Arn').split(':')[1]
     except ClientError:
         try:
             iam_client = boto3_conn(module, conn_type='client', resource='iam',
                                     region=region, endpoint=endpoint, **aws_connect_kwargs)
-            account_id = iam_client.get_user()['User']['Arn'].split(':')[4]
+            arn, partition, service, reg, account_id, resource = iam_client.get_user()['User']['Arn'].split(':')
         except ClientError as e:
             if (e.response['Error']['Code'] == 'AccessDenied'):
                 except_msg = to_native(e.message)
-                account_id = except_msg.search(r"arn:aws:iam::([0-9]{12,32}):\w+/").group(1)
+                m = except_msg.search(r"arn:(aws(-([a-z\-]+))?):iam::([0-9]{12,32}):\w+/")
+                account_id = m.group(4)
+                partition = m.group(1)
             if account_id is None:
                 module.fail_json_aws(e, msg="getting account information")
+            if partition is None:
+                module.fail_json_aws(e, msg="getting account information: partition")
         except Exception as e:
             module.fail_json_aws(e, msg="getting account information")
-    return account_id
+
+    return account_id, partition
 
 
 def get_current_function(connection, function_name, qualifier=None):
@@ -399,12 +408,12 @@ def main():
         module.fail_json_aws(e, msg="Trying to connect to AWS")
 
     if state == 'present':
-        if role.startswith('arn:aws:iam'):
+        if re.match(r'^arn:aws(-([a-z\-]+))?:iam', role):
             role_arn = role
         else:
             # get account ID and assemble ARN
-            account_id = get_account_id(module, region=region, endpoint=ec2_url, **aws_connect_kwargs)
-            role_arn = 'arn:aws:iam::{0}:role/{1}'.format(account_id, role)
+            account_id, partition = get_account_info(module, region=region, endpoint=ec2_url, **aws_connect_kwargs)
+            role_arn = 'arn:{0}:iam::{1}:role/{2}'.format(partition, account_id, role)
 
     # Get function configuration if present, False otherwise
     current_function = get_current_function(client, name)


Test patch: