diff --git a/samcli/commands/_utils/experimental.py b/samcli/commands/_utils/experimental.py index 240c5b80da..b8b75570c1 100644 --- a/samcli/commands/_utils/experimental.py +++ b/samcli/commands/_utils/experimental.py @@ -10,7 +10,7 @@ from samcli.cli.context import Context from samcli.cli.global_config import ConfigEntry, GlobalConfig from samcli.commands._utils.parameterized_option import parameterized_option -from samcli.lib.utils.colors import Colored +from samcli.lib.utils.colors import Colored, Colors LOG = logging.getLogger(__name__) @@ -162,7 +162,7 @@ def update_experimental_context(show_warning=True): if not Context.get_current_context().experimental: Context.get_current_context().experimental = True if show_warning: - LOG.warning(Colored().yellow(EXPERIMENTAL_WARNING)) + LOG.warning(Colored().color_log(EXPERIMENTAL_WARNING, color=Colors.WARNING), extra=dict(markup=True)) def _experimental_option_callback(ctx, param, enabled: Optional[bool]): diff --git a/samcli/commands/local/start_api/cli.py b/samcli/commands/local/start_api/cli.py index 145b0a58a2..9de4d7982c 100644 --- a/samcli/commands/local/start_api/cli.py +++ b/samcli/commands/local/start_api/cli.py @@ -9,8 +9,13 @@ from samcli.cli.cli_config_file import TomlProvider, configuration_option from samcli.cli.main import aws_creds_options, pass_context, print_cmdline_args from samcli.cli.main import common_options as cli_framework_options +from samcli.commands._utils.experimental import ExperimentalFlag, is_experimental_enabled from samcli.commands._utils.option_value_processor import process_image_options -from samcli.commands._utils.options import generate_next_command_recommendation +from samcli.commands._utils.options import ( + generate_next_command_recommendation, + hook_name_click_option, + skip_prepare_infra_option, +) from samcli.commands.local.cli_common.options import ( invoke_common_options, local_common_options, @@ -54,6 +59,10 @@ context_settings={"max_content_width": 120}, ) @configuration_option(provider=TomlProvider(section="parameters")) +@hook_name_click_option( + force_prepare=False, invalid_coexist_options=["t", "template-file", "template", "parameter-overrides"] +) +@skip_prepare_infra_option @service_common_options(3000) @click.option( "--static-dir", @@ -98,6 +107,8 @@ def cli( container_host, container_host_interface, invoke_image, + hook_name, + skip_prepare_infra, ): """ `sam local start-api` command entry point @@ -128,6 +139,7 @@ def cli( container_host, container_host_interface, invoke_image, + hook_name, ) # pragma: no cover @@ -155,6 +167,7 @@ def do_cli( # pylint: disable=R0914 container_host, container_host_interface, invoke_image, + hook_name, ): """ Implementation of the ``cli`` method, just separated out for unit testing purposes @@ -170,6 +183,14 @@ def do_cli( # pylint: disable=R0914 LOG.debug("local start-api command is called") + if ( + hook_name + and ExperimentalFlag.IaCsSupport.get(hook_name) is not None + and not is_experimental_enabled(ExperimentalFlag.IaCsSupport.get(hook_name)) + ): + LOG.info("Terraform Support beta feature is not enabled.") + return + processed_invoke_images = process_image_options(invoke_image) # Pass all inputs to setup necessary context to invoke function locally. @@ -202,14 +223,15 @@ def do_cli( # pylint: disable=R0914 ) as invoke_context: service = LocalApiService(lambda_invoke_context=invoke_context, port=port, host=host, static_dir=static_dir) service.start() - command_suggestions = generate_next_command_recommendation( - [ - ("Validate SAM template", "sam validate"), - ("Test Function in the Cloud", "sam sync --stack-name {{stack-name}} --watch"), - ("Deploy", "sam deploy --guided"), - ] - ) - click.secho(command_suggestions, fg="yellow") + if not hook_name: + command_suggestions = generate_next_command_recommendation( + [ + ("Validate SAM template", "sam validate"), + ("Test Function in the Cloud", "sam sync --stack-name {{stack-name}} --watch"), + ("Deploy", "sam deploy --guided"), + ] + ) + click.secho(command_suggestions, fg="yellow") except NoApisDefined as ex: raise UserException( diff --git a/samcli/commands/local/start_api/core/options.py b/samcli/commands/local/start_api/core/options.py index d9b89145e0..21bb1bf822 100644 --- a/samcli/commands/local/start_api/core/options.py +++ b/samcli/commands/local/start_api/core/options.py @@ -17,6 +17,8 @@ "parameter_overrides", ] +EXTENSION_OPTIONS: List[str] = ["hook_name", "skip_prepare_infra"] + CONTAINER_OPTION_NAMES: List[str] = [ "host", "port", @@ -53,6 +55,7 @@ + ARTIFACT_LOCATION_OPTIONS + CONFIGURATION_OPTION_NAMES + ALL_COMMON_OPTIONS + + EXTENSION_OPTIONS ) OPTIONS_INFO: Dict[str, Dict] = { @@ -65,6 +68,7 @@ "Artifact Location Options": { "option_names": {opt: {"rank": idx} for idx, opt in enumerate(ARTIFACT_LOCATION_OPTIONS)} }, + "Extension Options": {"option_names": {opt: {"rank": idx} for idx, opt in enumerate(EXTENSION_OPTIONS)}}, "Configuration Options": { "option_names": {opt: {"rank": idx} for idx, opt in enumerate(CONFIGURATION_OPTION_NAMES)}, "extras": [ diff --git a/samcli/hook_packages/terraform/hooks/prepare/property_builder.py b/samcli/hook_packages/terraform/hooks/prepare/property_builder.py index add29c68b2..a8910f112a 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/property_builder.py +++ b/samcli/hook_packages/terraform/hooks/prepare/property_builder.py @@ -1,6 +1,9 @@ """ Terraform prepare property builder """ +import logging +from json import loads +from json.decoder import JSONDecodeError from typing import Any, Dict, Optional from samcli.hook_packages.terraform.hooks.prepare.resource_linking import _resolve_resource_attribute @@ -24,6 +27,8 @@ from samcli.lib.utils.resources import AWS_LAMBDA_FUNCTION as CFN_AWS_LAMBDA_FUNCTION from samcli.lib.utils.resources import AWS_LAMBDA_LAYERVERSION as CFN_AWS_LAMBDA_LAYER_VERSION +LOG = logging.getLogger(__name__) + REMOTE_DUMMY_VALUE = "<>" TF_AWS_LAMBDA_FUNCTION = "aws_lambda_function" TF_AWS_LAMBDA_LAYER_VERSION = "aws_lambda_layer_version" @@ -211,6 +216,35 @@ def _check_image_config_value(image_config: Any) -> bool: return True +def _get_json_body(tf_properties: dict, resource: TFResource) -> Any: + """ + Gets the JSON formatted body value from the API Gateway if there is one + + Parameters + ---------- + tf_properties: dict + Properties of the terraform AWS Lambda function resource + resource: TFResource + Configuration terraform resource + + Returns + ------- + Any + Returns a dictonary if there is a valid body to parse, otherwise return original value + """ + body = tf_properties.get("body") + + if isinstance(body, str): + try: + return loads(body) + except JSONDecodeError: + pass + + LOG.debug(f"Failed to load JSON body for API Gateway body, returning original value: '{body}'") + + return body + + AWS_LAMBDA_FUNCTION_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = { "FunctionName": _get_property_extractor("function_name"), "Architectures": _get_property_extractor("architectures"), @@ -234,7 +268,7 @@ def _check_image_config_value(image_config: Any) -> bool: AWS_API_GATEWAY_REST_API_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = { "Name": _get_property_extractor("name"), - "Body": _get_property_extractor("body"), + "Body": _get_json_body, "Parameters": _get_property_extractor("parameters"), "BinaryMediaTypes": _get_property_extractor("binary_media_types"), } diff --git a/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py b/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py index 05f1676624..356c744e12 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py +++ b/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py @@ -66,7 +66,7 @@ def _unsupported_reference_field(field: str, resource: Dict, config_resource: TF False otherwise """ return bool( - not resource.get(field) + not (resource.get(field) or resource.get("values", {}).get(field)) and config_resource.attributes.get(field) and isinstance(config_resource.attributes.get(field), References) ) diff --git a/samcli/hook_packages/terraform/hooks/prepare/translate.py b/samcli/hook_packages/terraform/hooks/prepare/translate.py index 62f8d4da12..14f9e73733 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/translate.py +++ b/samcli/hook_packages/terraform/hooks/prepare/translate.py @@ -52,7 +52,7 @@ get_sam_metadata_planned_resource_value_attribute, ) from samcli.lib.hook.exceptions import PrepareHookException -from samcli.lib.utils.colors import Colored +from samcli.lib.utils.colors import Colored, Colors from samcli.lib.utils.resources import AWS_LAMBDA_FUNCTION as CFN_AWS_LAMBDA_FUNCTION SAM_METADATA_RESOURCE_TYPE = "null_resource" @@ -134,9 +134,12 @@ def _check_unresolvable_values(root_module: dict, root_tf_module: TFModule) -> N if config_values and not planned_values: LOG.warning( - Colored().yellow( - "\nUnresolvable attributes discovered in project, run terraform apply to resolve them.\n" - ) + Colored().color_log( + msg="\nUnresolvable attributes discovered in project, " + "run terraform apply to resolve them.\n", + color=Colors.WARNING, + ), + extra=dict(markup=True), ) return diff --git a/samcli/lib/providers/api_collector.py b/samcli/lib/providers/api_collector.py index d0c0f5b2a8..7cb5d0c1d1 100644 --- a/samcli/lib/providers/api_collector.py +++ b/samcli/lib/providers/api_collector.py @@ -9,7 +9,7 @@ from typing import Dict, Iterator, List, Optional, Set, Tuple, Union from samcli.lib.providers.provider import Api, Cors -from samcli.lib.utils.colors import Colored +from samcli.lib.utils.colors import Colored, Colors from samcli.local.apigw.authorizers.authorizer import Authorizer from samcli.local.apigw.route import Route @@ -197,7 +197,7 @@ def get_api(self) -> Api: be validated thoroughly before deploying to production. Testing application behaviour against authorizers deployed on AWS can be done using the sam sync command.{os.linesep}""" - LOG.warning(Colored().yellow(message)) + LOG.warning(Colored().color_log(message, color=Colors.WARNING), extra=dict(markup=True)) break diff --git a/tests/integration/local/start_api/start_api_integ_base.py b/tests/integration/local/start_api/start_api_integ_base.py index b1f9a6a785..77f755aec8 100644 --- a/tests/integration/local/start_api/start_api_integ_base.py +++ b/tests/integration/local/start_api/start_api_integ_base.py @@ -33,6 +33,9 @@ class StartApiIntegBaseClass(TestCase): do_collect_cmd_init_output: bool = False + command_list = None + project_directory = None + @classmethod def setUpClass(cls): # This is the directory for tests/integration which will be used to file the testdata @@ -84,7 +87,8 @@ def start_api_with_retry(cls, retries=3): def start_api(cls): command = get_sam_command() - command_list = [command, "local", "start-api", "-t", cls.template, "-p", cls.port] + command_list = cls.command_list or [command, "local", "start-api", "-t", cls.template] + command_list.extend(["-p", cls.port]) if cls.container_mode: command_list += ["--warm-containers", cls.container_mode] @@ -99,7 +103,11 @@ def start_api(cls): for image in cls.invoke_image: command_list += ["--invoke-image", image] - cls.start_api_process = Popen(command_list, stderr=PIPE, stdout=PIPE) + cls.start_api_process = ( + Popen(command_list, stderr=PIPE, stdout=PIPE) + if not cls.project_directory + else Popen(command_list, stderr=PIPE, stdout=PIPE, cwd=cls.project_directory) + ) cls.start_api_process_output = wait_for_local_process( cls.start_api_process, cls.port, collect_output=cls.do_collect_cmd_init_output ) diff --git a/tests/integration/local/start_api/test_start_api_with_terraform_application.py b/tests/integration/local/start_api/test_start_api_with_terraform_application.py new file mode 100644 index 0000000000..e8ab21d01d --- /dev/null +++ b/tests/integration/local/start_api/test_start_api_with_terraform_application.py @@ -0,0 +1,268 @@ +import logging +import shutil +import os +from pathlib import Path +from subprocess import CalledProcessError, CompletedProcess, run +from typing import Optional +from unittest import skipIf +from parameterized import parameterized, parameterized_class + +import pytest +import requests + +from tests.integration.local.common_utils import random_port +from tests.integration.local.start_api.start_api_integ_base import StartApiIntegBaseClass +from tests.testing_utils import get_sam_command, CI_OVERRIDE + +LOG = logging.getLogger(__name__) + + +class TerraformStartApiIntegrationBase(StartApiIntegBaseClass): + run_command_timeout = 300 + terraform_application: Optional[str] = None + + @classmethod + def setUpClass(cls): + command = get_sam_command() + cls.template_path = "" + cls.build_before_invoke = False + cls.command_list = [command, "local", "start-api", "--hook-name", "terraform", "--beta-features"] + cls.test_data_path = Path(cls.get_integ_dir()) / "testdata" / "start_api" + cls.project_directory = cls.test_data_path / "terraform" / cls.terraform_application + super(TerraformStartApiIntegrationBase, cls).setUpClass() + + @staticmethod + def get_integ_dir(): + return Path(__file__).resolve().parents[2] + + @classmethod + def tearDownClass(cls) -> None: + super(TerraformStartApiIntegrationBase, cls).tearDownClass() + cls._remove_generated_directories() + + @classmethod + def _remove_generated_directories(cls): + shutil.rmtree(str(Path(cls.project_directory / ".aws-sam-iacs")), ignore_errors=True) + shutil.rmtree(str(Path(cls.project_directory / ".terraform")), ignore_errors=True) + try: + os.remove(str(Path(cls.project_directory / ".terraform.lock.hcl"))) + except (FileNotFoundError, PermissionError): + pass + + @classmethod + def _run_command(cls, command, check) -> CompletedProcess: + test_data_folder = ( + Path(cls.get_integ_dir()) / "testdata" / "start_api" / "terraform" / cls.terraform_application # type: ignore + ) + return run(command, cwd=test_data_folder, check=check, capture_output=True, timeout=cls.run_command_timeout) + + +class TerraformStartApiIntegrationApplyBase(TerraformStartApiIntegrationBase): + terraform_application: str + + @classmethod + def setUpClass(cls): + # init terraform project to populate deploy-only values + cls._run_command(["terraform", "init", "-input=false"], check=True) + cls._run_command(["terraform", "apply", "-auto-approve", "-input=false"], check=True) + + super(TerraformStartApiIntegrationApplyBase, cls).setUpClass() + + @staticmethod + def get_integ_dir(): + return Path(__file__).resolve().parents[2] + + @classmethod + def tearDownClass(cls) -> None: + try: + cls._run_command(["terraform", "apply", "-destroy", "-auto-approve", "-input=false"], check=True) + except CalledProcessError: + # skip, command can fail here if there isn't an applied project to destroy + # (eg. failed to apply in setup) + pass + + try: + os.remove(str(Path(cls.project_directory / "terraform.tfstate"))) # type: ignore + os.remove(str(Path(cls.project_directory / "terraform.tfstate.backup"))) # type: ignore + except (FileNotFoundError, PermissionError): + pass + + super(TerraformStartApiIntegrationApplyBase, cls).tearDownClass() + + +@skipIf( + not CI_OVERRIDE, + "Skip Terraform test cases unless running in CI", +) +@pytest.mark.flaky(reruns=3) +class TestStartApiTerraformApplication(TerraformStartApiIntegrationBase): + terraform_application = "terraform-v1-api-simple" + + def setUp(self): + self.url = "http://127.0.0.1:{}".format(self.port) + + def test_successful_request(self): + response = requests.get(self.url + "/hello", timeout=300) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {"message": "hello world"}) + + +@skipIf( + not CI_OVERRIDE, + "Skip Terraform test cases unless running in CI", +) +@pytest.mark.flaky(reruns=3) +@parameterized_class( + [ + { + "terraform_application": "lambda-auth-openapi", + "expected_error_message": "Error: AWS SAM CLI is unable to process a Terraform project that uses an OpenAPI" + " specification to define the API Gateway resource.", + }, + { + "terraform_application": "terraform-api-simple-multiple-resources-limitation", + "expected_error_message": "Error: AWS SAM CLI could not process a Terraform project that contains a source " + "resource that is linked to more than one destination resource.", + }, + { + "terraform_application": "terraform-api-simple-local-variables-limitation", + "expected_error_message": "Error: AWS SAM CLI could not process a Terraform project that uses local " + "variables to define linked resources.", + }, + ] +) +class TestStartApiTerraformApplicationLimitations(TerraformStartApiIntegrationBase): + @classmethod + def setUpClass(cls): + command = get_sam_command() + cls.command_list = [ + command, + "local", + "start-api", + "--hook-name", + "terraform", + "--beta-features", + "-p", + str(random_port()), + ] + cls.test_data_path = Path(cls.get_integ_dir()) / "testdata" / "start_api" + cls.project_directory = cls.test_data_path / "terraform" / cls.terraform_application + + @classmethod + def tearDownClass(cls) -> None: + cls._remove_generated_directories() + + def test_unsupported_limitations(self): + apply_disclaimer_message = "Unresolvable attributes discovered in project, run terraform apply to resolve them." + + process = self._run_command(self.command_list, check=False) + + LOG.info(process.stderr) + output = process.stderr.decode("utf-8") + self.assertEqual(process.returncode, 1) + self.assertRegex(output, self.expected_error_message) + self.assertRegex(output, apply_disclaimer_message) + + +@skipIf( + not CI_OVERRIDE, + "Skip Terraform test cases unless running in CI", +) +@pytest.mark.flaky(reruns=3) +@parameterized_class( + [ + { + "terraform_application": "terraform-api-simple-multiple-resources-limitation", + }, + { + "terraform_application": "terraform-api-simple-local-variables-limitation", + }, + ] +) +class TestStartApiTerraformApplicationLimitationsAfterApply(TerraformStartApiIntegrationApplyBase): + def setUp(self): + self.url = "http://127.0.0.1:{}".format(self.port) + + def test_successful_request(self): + response = requests.get(self.url + "/hello", timeout=300) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {"message": "hello world"}) + + +@skipIf( + not CI_OVERRIDE, + "Skip Terraform test cases unless running in CI", +) +@pytest.mark.flaky(reruns=3) +class TestStartApiTerraformApplicationV1LambdaAuthorizers(TerraformStartApiIntegrationBase): + terraform_application = "v1-lambda-authorizer" + + def setUp(self): + self.url = "http://127.0.0.1:{}".format(self.port) + + @parameterized.expand( + [ + ("/hello", {"headers": {"myheader": "123"}}), + ("/hello-request", {"headers": {"myheader": "123"}, "params": {"mystring": "456"}}), + ("/hello-request-empty", {}), + ("/hello-request-empty", {"headers": {"foo": "bar"}}), + ] + ) + def test_invoke_authorizer(self, endpoint, parameters): + response = requests.get(self.url + endpoint, timeout=300, **parameters) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {"message": "from authorizer"}) + + @parameterized.expand( + [ + ("/hello", {"headers": {"blank": "invalid"}}), + ("/hello-request", {"headers": {"blank": "invalid"}, "params": {"blank": "invalid"}}), + ] + ) + def test_missing_authorizer_identity_source(self, endpoint, parameters): + response = requests.get(self.url + endpoint, timeout=300, **parameters) + + self.assertEqual(response.status_code, 401) + + def test_fails_token_header_validation_authorizer(self): + response = requests.get(self.url + "/hello", timeout=300, headers={"myheader": "not valid"}) + + self.assertEqual(response.status_code, 401) + + +@skipIf( + not CI_OVERRIDE, + "Skip Terraform test cases unless running in CI", +) +@pytest.mark.flaky(reruns=3) +class TestStartApiTerraformApplicationOpenApiAuthorizer(TerraformStartApiIntegrationApplyBase): + terraform_application = "lambda-auth-openapi" + + def setUp(self): + self.url = "http://127.0.0.1:{}".format(self.port) + + @parameterized.expand( + [ + ("/hello", {"headers": {"myheader": "123"}}), + ("/hello-request", {"headers": {"myheader": "123"}, "params": {"mystring": "456"}}), + ] + ) + def test_successful_request(self, endpoint, params): + response = requests.get(self.url + endpoint, timeout=300, **params) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {"message": "from authorizer"}) + + @parameterized.expand( + [ + ("/hello", {"headers": {"missin": "123"}}), + ("/hello-request", {"headers": {"notcorrect": "123"}, "params": {"abcde": "456"}}), + ] + ) + def test_missing_identity_sources(self, endpoint, params): + response = requests.get(self.url + endpoint, timeout=300, **params) + + self.assertEqual(response.status_code, 401) diff --git a/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/lambda-functions.zip b/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/lambda-functions.zip new file mode 100644 index 0000000000..36c2644634 Binary files /dev/null and b/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/lambda-functions.zip differ diff --git a/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/main.tf b/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/main.tf new file mode 100644 index 0000000000..8005fb4e95 --- /dev/null +++ b/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/main.tf @@ -0,0 +1,112 @@ +provider "aws" {} + +data "aws_region" "current" {} + +resource "aws_api_gateway_authorizer" "header_authorizer" { + name = "header-authorizer-open-api" + rest_api_id = aws_api_gateway_rest_api.api.id + authorizer_uri = aws_lambda_function.authorizer.invoke_arn + authorizer_credentials = aws_iam_role.invocation_role.arn + identity_source = "method.request.header.myheader" + identity_validation_expression = "^123$" +} + +resource "aws_lambda_function" "authorizer" { + filename = "lambda-functions.zip" + function_name = "authorizer-open-api" + role = aws_iam_role.invocation_role.arn + handler = "handlers.auth_handler" + runtime = "python3.8" + source_code_hash = filebase64sha256("lambda-functions.zip") +} + +resource "aws_lambda_function" "hello_endpoint" { + filename = "lambda-functions.zip" + function_name = "hello-lambda-open-api" + role = aws_iam_role.invocation_role.arn + handler = "handlers.hello_handler" + runtime = "python3.8" + source_code_hash = filebase64sha256("lambda-functions.zip") +} + +resource "aws_api_gateway_rest_api" "api" { + name = "api-open-api" + body = jsonencode({ + swagger = "2.0" + info = { + title = "api-body" + version = "1.0" + } + securityDefinitions = { + TokenAuthorizer = { + type = "apiKey" + in = "header" + name = "myheader" + x-amazon-apigateway-authtype = "custom" + x-amazon-apigateway-authorizer = { + type = "TOKEN" + authorizerUri = "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.authorizer.arn}/invocations" + } + } + RequestAuthorizer = { + type = "apiKey" + in = "unused" + name = "unused" + x-amazon-apigateway-authtype = "custom" + x-amazon-apigateway-authorizer = { + type = "REQUEST" + identitySource = "method.request.header.myheader, method.request.querystring.mystring" + authorizerUri = "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.authorizer.arn}/invocations" + } + } + } + paths = { + "/hello" = { + get = { + security = [ + {TokenAuthorizer = []} + ] + x-amazon-apigateway-integration = { + httpMethod = "GET" + payloadFormatVersion = "1.0" + type = "AWS_PROXY" + uri = "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.hello_endpoint.arn}/invocations" + } + } + } + "/hello-request" = { + get = { + security = [ + {RequestAuthorizer = []} + ] + x-amazon-apigateway-integration = { + httpMethod = "GET" + payloadFormatVersion = "1.0" + type = "AWS_PROXY" + uri = "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.hello_endpoint.arn}/invocations" + } + } + } + } + }) +} + +resource "aws_iam_role" "invocation_role" { + name = "iam-lambda-open-api" + path = "/" + assume_role_policy = <