Repository: ansible-collections/amazon.aws

Issue:
Add support for FunctionResponseTypes in lambda_event module
### Summary


Please add support to set FunctionResponseTypes when creating lambda event source mappings

https://docs.aws.amazon.com/lambda/latest/dg/API_CreateEventSourceMapping.html#API_CreateEventSourceMapping_RequestSyntax

### Issue Type

Feature Idea

### Component Name

lambda_event

### Additional Information

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)

```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/lambda_event.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/lambda_event.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jillr @markuman @pjodouin @ryansb @s-hertel @tremble
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/lambda_event.py b/plugins/modules/lambda_event.py
--- a/plugins/modules/lambda_event.py
+++ b/plugins/modules/lambda_event.py
@@ -79,6 +79,13 @@
           - Required when I(event_source=stream).
         choices: [TRIM_HORIZON,LATEST]
         type: str
+      function_response_types:
+        description:
+          - (Streams and Amazon SQS) A list of current response type enums applied to the event source mapping.
+        type: list
+        elements: str
+        choices: [ReportBatchItemFailures]
+        version_added: 5.2.0
     required: true
     type: dict
 extends_documentation_fragment:
@@ -97,10 +104,25 @@
     function_name: "{{ function_name }}"
     alias: Dev
     source_params:
-    source_arn: arn:aws:dynamodb:us-east-1:123456789012:table/tableName/stream/2016-03-19T19:51:37.457
-    enabled: True
-    batch_size: 100
-    starting_position: TRIM_HORIZON
+      source_arn: arn:aws:dynamodb:us-east-1:123456789012:table/tableName/stream/2016-03-19T19:51:37.457
+      enabled: True
+      batch_size: 100
+      starting_position: TRIM_HORIZON
+  register: event
+
+# Example that creates a lambda event notification for a DynamoDB stream
+- name: DynamoDB stream event mapping
+  amazon.aws.lambda_event:
+    state: present
+    event_source: stream
+    function_name: "{{ function_name }}"
+    source_params:
+      source_arn: arn:aws:dynamodb:us-east-1:123456789012:table/tableName/stream/2016-03-19T19:51:37.457
+      enabled: True
+      batch_size: 100
+      starting_position: LATEST
+      function_response_types:
+        - ReportBatchItemFailures
   register: event
 
 - name: Show source event
@@ -344,6 +366,8 @@ def lambda_event_stream(module, aws):
                 api_params.update(Enabled=source_param_enabled)
             if source_params.get('batch_size'):
                 api_params.update(BatchSize=source_params.get('batch_size'))
+            if source_params.get('function_response_types'):
+                api_params.update(FunctionResponseTypes=source_params.get('function_response_types'))
 
             try:
                 if not module.check_mode:


Test patch:
diff --git a/tests/integration/targets/lambda_event/aliases b/tests/integration/targets/lambda_event/aliases
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/lambda_event/aliases
@@ -0,0 +1 @@
+cloud/aws
diff --git a/tests/integration/targets/lambda_event/defaults/main.yml b/tests/integration/targets/lambda_event/defaults/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/lambda_event/defaults/main.yml
@@ -0,0 +1,10 @@
+# defaults file for lambda integration test
+# IAM role names have to be less than 64 characters
+# we hash the resource_prefix to get a shorter, unique string
+lambda_function_name: 'test-lambda-{{ tiny_prefix }}'
+lambda_role_name: ansible-test-{{ tiny_prefix }}-lambda
+
+dynamodb_table_name: ansible-test-{{ tiny_prefix }}
+
+lambda_python_runtime: python3.9
+lambda_python_handler: mini_lambda.handler
diff --git a/tests/integration/targets/lambda_event/files/mini_lambda.py b/tests/integration/targets/lambda_event/files/mini_lambda.py
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/lambda_event/files/mini_lambda.py
@@ -0,0 +1,48 @@
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+import os
+
+
+def handler(event, context):
+    """
+    The handler function is the function which gets called each time
+    the lambda is run.
+    """
+    # printing goes to the cloudwatch log allowing us to simply debug the lambda if we can find
+    # the log entry.
+    print("got event:\n" + json.dumps(event))
+
+    # if the name parameter isn't present this can throw an exception
+    # which will result in an amazon chosen failure from the lambda
+    # which can be completely fine.
+
+    name = event["name"]
+
+    # we can use environment variables as part of the configuration of the lambda
+    # which can change the behaviour of the lambda without needing a new upload
+
+    extra = os.environ.get("EXTRA_MESSAGE")
+    if extra is not None and len(extra) > 0:
+        greeting = "hello {0}. {1}".format(name, extra)
+    else:
+        greeting = "hello " + name
+
+    return {"message": greeting}
+
+
+def main():
+    """
+    This main function will normally never be called during normal
+    lambda use.  It is here for testing the lambda program only.
+    """
+    event = {"name": "james"}
+    context = None
+    print(handler(event, context))
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tests/integration/targets/lambda_event/files/minimal_trust_policy.json b/tests/integration/targets/lambda_event/files/minimal_trust_policy.json
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/lambda_event/files/minimal_trust_policy.json
@@ -0,0 +1,12 @@
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Principal": {
+        "Service": "lambda.amazonaws.com"
+      },
+      "Action": "sts:AssumeRole"
+    }
+  ]
+}
diff --git a/tests/integration/targets/lambda_event/meta/main.yml b/tests/integration/targets/lambda_event/meta/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/lambda_event/meta/main.yml
@@ -0,0 +1,5 @@
+dependencies:
+- role: setup_remote_tmp_dir
+- role: setup_botocore_pip
+  vars:
+    botocore_version: 1.21.51
\ No newline at end of file
diff --git a/tests/integration/targets/lambda_event/tasks/main.yml b/tests/integration/targets/lambda_event/tasks/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/lambda_event/tasks/main.yml
@@ -0,0 +1,117 @@
+- name: set connection information for AWS modules and run tests
+  module_defaults:
+    group/aws:
+      aws_access_key: '{{ aws_access_key }}'
+      aws_secret_key: '{{ aws_secret_key }}'
+      security_token: '{{ security_token | default(omit) }}'
+      region: '{{ aws_region }}'
+  collections:
+  - community.general
+  block:
+
+  - name: Create test resources setup
+    import_tasks: setup.yml
+
+# TEST CREATE LAMBDA EVENT ========================================================================================
+  - name: Create DynamoDB stream event mapping (trigger) - check_mode
+    amazon.aws.lambda_event:
+      state: present
+      event_source: stream
+      function_arn: '{{ lambda_function_arn }}'
+      source_params:
+        source_arn: '{{ dynamo_stream_arn }}'
+        enabled: True
+        batch_size: 500
+        starting_position: LATEST
+        function_response_types:
+          - ReportBatchItemFailures
+    check_mode: true
+    register: create_lambda_event_result
+
+  - assert:
+      that:
+        - create_lambda_event_result is changed
+        - create_lambda_event_result is not failed
+        - '"lambda:CreateEventSourceMapping" not in create_lambda_event_result.resource_actions'
+
+  - name: Create DynamoDB stream event mapping (trigger)
+    amazon.aws.lambda_event:
+      state: present
+      event_source: stream
+      function_arn: '{{ lambda_function_arn }}'
+      source_params:
+        source_arn: '{{ dynamo_stream_arn }}'
+        enabled: True
+        batch_size: 500
+        starting_position: LATEST
+        function_response_types:
+          - ReportBatchItemFailures
+    register: create_lambda_event_result
+
+  - name: Get info on above trigger
+    command: 'aws lambda get-event-source-mapping --uuid {{ create_lambda_event_result.events.uuid }}'
+    environment:
+      AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
+      AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
+      AWS_SESSION_TOKEN: "{{ security_token | default('') }}"
+      AWS_DEFAULT_REGION: "{{ aws_region }}"
+    register: lambda_function_details
+
+  - name: convert it to an object
+    set_fact:
+      lambda_function_details_obj: "{{ lambda_function_details.stdout | from_json }}"
+
+  - assert:
+      that:
+        - lambda_function_details_obj.FunctionResponseTypes is defined
+        - lambda_function_details_obj.FunctionResponseTypes | length > 0
+        - lambda_function_details_obj.FunctionResponseTypes[0] == "ReportBatchItemFailures"
+        - '"lambda:CreateEventSourceMapping" in create_lambda_event_result.resource_actions'
+
+  - name: Create DynamoDB stream event mapping (trigger) - check_mode - idempotency
+    amazon.aws.lambda_event:
+      state: present
+      event_source: stream
+      function_arn: '{{ lambda_function_arn }}'
+      source_params:
+        source_arn: '{{ dynamo_stream_arn }}'
+        enabled: True
+        batch_size: 500
+        starting_position: LATEST
+        function_response_types:
+          - ReportBatchItemFailures
+    check_mode: true
+    register: create_lambda_event_result
+
+  - assert:
+      that:
+        - create_lambda_event_result is not changed
+        - create_lambda_event_result is not failed
+        - '"lambda:CreateEventSourceMapping" not in create_lambda_event_result.resource_actions'
+
+  - name: Create DynamoDB stream event mapping (trigger) - idempotency
+    amazon.aws.lambda_event:
+      state: present
+      event_source: stream
+      function_arn: '{{ lambda_function_arn }}'
+      source_params:
+        source_arn: '{{ dynamo_stream_arn }}'
+        enabled: True
+        batch_size: 500
+        starting_position: LATEST
+        function_response_types:
+          - ReportBatchItemFailures
+    register: create_lambda_event_result
+
+  - assert:
+      that:
+        - create_lambda_event_result is not changed
+        - create_lambda_event_result is not failed
+        - '"lambda:CreateEventSourceMapping" not in create_lambda_event_result.resource_actions'
+
+
+# ========================================================================================
+
+  always:
+    - name: Clean up test resources setup
+      import_tasks: teardown.yml
diff --git a/tests/integration/targets/lambda_event/tasks/setup.yml b/tests/integration/targets/lambda_event/tasks/setup.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/lambda_event/tasks/setup.yml
@@ -0,0 +1,83 @@
+---
+- debug: msg="Starting test setup......"
+
+# CREATE DYNAMO DB TABLE
+- name: Create minimal dynamo table
+  community.aws.dynamodb_table:
+    name: "{{ dynamodb_table_name }}"
+    hash_key_name: id
+    hash_key_type: STRING
+    tags:
+      Usage: Created By Integration Test
+  register: create_table_result
+
+# ENABLE DYNAMODB STREAM AND GET STREAM ARN
+- name: Enable DynamoDB stream (currently not supported by community.aws.dynamodb_table)
+  command: aws dynamodb update-table --table-name "{{ dynamodb_table_name }}" --stream-specification StreamEnabled=True,StreamViewType=KEYS_ONLY
+  environment:
+    AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
+    AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
+    AWS_SESSION_TOKEN: "{{ security_token | default('') }}"
+    AWS_DEFAULT_REGION: "{{ aws_region }}"
+  register: enable_stream_result
+- name: convert it to an object
+  set_fact:
+    enable_stream_result: "{{ enable_stream_result.stdout | from_json }}"
+- name: Get DynamoDB stream ARN
+  set_fact:
+    dynamo_stream_arn: "{{ enable_stream_result.TableDescription.LatestStreamArn }}"
+
+# CREATE MINIMAL LAMBDA FUNCTION
+# https://github.com/ansible/ansible/issues/77257
+- name: Set async_dir for HOME env
+  ansible.builtin.set_fact:
+    ansible_async_dir: "{{ lookup('env', 'HOME') }}/.ansible_async_{{ tiny_prefix }}/"
+  when: (lookup('env', 'HOME'))
+
+- name: create minimal lambda role
+  iam_role:
+    name: '{{ lambda_role_name }}'
+    assume_role_policy_document: '{{ lookup("file", "minimal_trust_policy.json")}}'
+    create_instance_profile: false
+    managed_policies:
+    - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess
+    - arn:aws:iam::aws:policy/AWSLambdaInvocation-DynamoDB
+    - arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole
+  register: iam_role
+- name: wait 10 seconds for role to become available
+  pause:
+    seconds: 10
+  when: iam_role.changed
+
+- name: move lambda into place for archive module
+  copy:
+    src: mini_lambda.py
+    dest: '{{ output_dir }}/mini_lambda.py'
+    mode: preserve
+- name: bundle lambda into a zip
+  register: zip_res
+  archive:
+    format: zip
+    path: '{{ output_dir }}/mini_lambda.py'
+    dest: '{{ output_dir }}/mini_lambda.zip'
+
+- name: test state=present - upload the lambda
+  lambda:
+    name: '{{ lambda_function_name }}'
+    runtime: '{{ lambda_python_runtime }}'
+    handler: '{{ lambda_python_handler }}'
+    role: '{{ lambda_role_name }}'
+    zip_file: '{{ zip_res.dest }}'
+    architecture: x86_64
+  register: result
+  vars:
+    ansible_python_interpreter: '{{ botocore_virtualenv_interpreter }}'
+
+- name: assert lambda upload succeeded
+  assert:
+    that:
+    - result.changed
+
+- name: Get lambda function ARN
+  ansible.builtin.set_fact:
+    lambda_function_arn: "{{ result.configuration.function_arn }}"
diff --git a/tests/integration/targets/lambda_event/tasks/teardown.yml b/tests/integration/targets/lambda_event/tasks/teardown.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/lambda_event/tasks/teardown.yml
@@ -0,0 +1,33 @@
+---
+- debug: msg="Starting test Teardown......"
+
+- name: Delete DynamoDB stream event mapping (trigger)
+  amazon.aws.lambda_event:
+    state: absent
+    event_source: stream
+    function_arn: '{{ lambda_function_arn }}'
+    source_params:
+      source_arn: "{{ dynamo_stream_arn }}"
+      enabled: True
+      batch_size: 500
+      starting_position: LATEST
+      function_response_types:
+        - ReportBatchItemFailures
+  register: create_lambda_event_result
+  ignore_errors: true
+
+- name: Delete lambda function
+  lambda:
+    name: '{{ lambda_function_name }}'
+    state: absent
+
+- name: Delete dynamo table
+  community.aws.dynamodb_table:
+    name: "{{ dynamodb_table_name }}"
+    state: absent
+
+- name: Delete the role
+  community.aws.iam_role:
+    name: '{{ lambda_role_name }}'
+    assume_role_policy_document: '{{ lookup("file", "minimal_trust_policy.json")}}'
+    state: absent