From a7f992e7f3fc129300513352f003a34bbd8e7ee0 Mon Sep 17 00:00:00 2001 From: Mahdi Date: Sun, 15 Dec 2024 16:41:55 +0800 Subject: [PATCH 1/2] added the new folder to the main list --- quickstart/201-azure-api-integration-function-app | 1 + quickstart/README.md | 1 + 2 files changed, 2 insertions(+) create mode 160000 quickstart/201-azure-api-integration-function-app diff --git a/quickstart/201-azure-api-integration-function-app b/quickstart/201-azure-api-integration-function-app new file mode 160000 index 000000000..5416ab221 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app @@ -0,0 +1 @@ +Subproject commit 5416ab221dc2fff09c14253ab574ab3aae5b66d9 diff --git a/quickstart/README.md b/quickstart/README.md index e9e96a1b7..734b4b30f 100644 --- a/quickstart/README.md +++ b/quickstart/README.md @@ -34,6 +34,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope - [Azure Kubernetes Service with ACR](./201-aks-acr-identity/) - [Azure Virtual Machine Disk Encryption Extension](./201-vm-disk-encryption-extension) - [Azure Virtual Machine Scale Set Disk Encryption Extension](./201-vmss-disk-encryption-extension) +- [Azure API Management Integration with Azure Function App](./201-azure-api-integration-function-app) - [Azure virtual machine scale set with jumpbox](./201-vmss-jumpbox) - [Azure virtual machine scale set with jumpbox from Packer custom image](./201-vmss-packer-jumpbox) - [Azure PostgreSQL Flexible Server Database](./201-postgresql-fs-db) From 9617ab4e8e1c9cf6f7a17cfaa47a712a606e9a8a Mon Sep 17 00:00:00 2001 From: Mahdi Date: Sun, 15 Dec 2024 17:11:42 +0800 Subject: [PATCH 2/2] added content of the folder --- .../201-azure-api-integration-function-app | 1 - .../.funcignore | 1 + .../.gitignore | 2 + .../README.md | 92 ++++ .../function_code/.funcignore | 10 + .../function_code/.gitignore | 48 ++ .../function_code/host.json | 15 + .../function_code/local.settings.json | 7 + .../function_code/package-lock.json | 437 ++++++++++++++++++ .../function_code/package.json | 16 + .../function_code/src/functions/op1.js | 23 + .../terraform_configs/.gitignore | 6 + .../terraform_configs/main.tf | 99 ++++ .../terraform_configs/outputs.tf | 15 + .../terraform_configs/policy.xml | 15 + .../terraform_configs/providers.tf | 18 + .../terraform_configs/variables.tf | 24 + 17 files changed, 828 insertions(+), 1 deletion(-) delete mode 160000 quickstart/201-azure-api-integration-function-app create mode 100644 quickstart/201-azure-api-integration-function-app/.funcignore create mode 100644 quickstart/201-azure-api-integration-function-app/.gitignore create mode 100644 quickstart/201-azure-api-integration-function-app/README.md create mode 100644 quickstart/201-azure-api-integration-function-app/function_code/.funcignore create mode 100644 quickstart/201-azure-api-integration-function-app/function_code/.gitignore create mode 100644 quickstart/201-azure-api-integration-function-app/function_code/host.json create mode 100644 quickstart/201-azure-api-integration-function-app/function_code/local.settings.json create mode 100644 quickstart/201-azure-api-integration-function-app/function_code/package-lock.json create mode 100644 quickstart/201-azure-api-integration-function-app/function_code/package.json create mode 100644 quickstart/201-azure-api-integration-function-app/function_code/src/functions/op1.js create mode 100644 quickstart/201-azure-api-integration-function-app/terraform_configs/.gitignore create mode 100644 quickstart/201-azure-api-integration-function-app/terraform_configs/main.tf create mode 100644 quickstart/201-azure-api-integration-function-app/terraform_configs/outputs.tf create mode 100644 quickstart/201-azure-api-integration-function-app/terraform_configs/policy.xml create mode 100644 quickstart/201-azure-api-integration-function-app/terraform_configs/providers.tf create mode 100644 quickstart/201-azure-api-integration-function-app/terraform_configs/variables.tf diff --git a/quickstart/201-azure-api-integration-function-app b/quickstart/201-azure-api-integration-function-app deleted file mode 160000 index 5416ab221..000000000 --- a/quickstart/201-azure-api-integration-function-app +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5416ab221dc2fff09c14253ab574ab3aae5b66d9 diff --git a/quickstart/201-azure-api-integration-function-app/.funcignore b/quickstart/201-azure-api-integration-function-app/.funcignore new file mode 100644 index 000000000..ca6d4bb09 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/.funcignore @@ -0,0 +1 @@ +terraform_configs/* diff --git a/quickstart/201-azure-api-integration-function-app/.gitignore b/quickstart/201-azure-api-integration-function-app/.gitignore new file mode 100644 index 000000000..687f8724d --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/.gitignore @@ -0,0 +1,2 @@ +.vscode/ + diff --git a/quickstart/201-azure-api-integration-function-app/README.md b/quickstart/201-azure-api-integration-function-app/README.md new file mode 100644 index 000000000..677f24512 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/README.md @@ -0,0 +1,92 @@ +# Azure API Management Integration with Azure Function App + +This template provisions an Azure API Management instance and a Linux Function App, integrating them seamlessly. It includes the creation of API Management operations, the application of policies, and the configuration of front-end and back-end URLs for a fully functional API setup. + +# Requirements + +The following versions were used and confirmed to work. While other versions may also work, they have not been tested with this setup: + +- **Azure CLI**: Version 2.65.0 +- **Terraform CLI**: Version 1.9.7 +- **Terraform AzureRM Provider**: Version 4.0 or later +- **Azure Functions Core Tools**: Version 4.0.6543 +- **Node.js**: Version 22.9.0 +- **Node Package Manager (NPM)**: Version 10.8.3 + +# How to Run + +## Deploy Infrastructure + +1. Log in to your Azure account: + ``` + az login + ``` + +2. Retrieve the subscription ID and resource group name from your Azure environment. Update the `subscription_id` and `resource_group_name` values in the `terraform_configs/variables.tf` file accordingly. To obtain the subscription ID and resource group name, you can use: + ``` + az group list + ``` + +3. Navigate to the `terraform_configs` folder: + ``` + cd terraform_configs + ``` + +4. Initialize and deploy the Terraform configuration: + ``` + terraform init + terraform plan -out main.tfplan + terraform apply main.tfplan + ``` + + **Note:** Deploying the Azure API Management instance can take up to 90 minutes. Please be patient. + +5. Take note of the following outputs: + - **`function_app_name`**: The name of your Function App, such as `myfuncappsbigwbgyzdync`, which you will need to deploy your function code. + - **`frontend_url`**: The frontend URL of your API Management instance. + +## Deploy the Code to the Function App + +Azure Functions Core Tools has been used to package and deploy the Function App code. While other tools can be used, Terraform currently does not fully integrate with such deployment tools, requiring the functions to be deployed separately after each Terraform deployment. Automation can simplify this process using scripts. + +1. Navigate to the `function_code` folder: + ``` + cd ../function_code + ``` + +2. Install the code dependencies: + ``` + npm install + ``` + +3. Deploy the code: + ``` + func azure functionapp publish your_function_app_name + ``` + Replace `your_function_app_name` with the `function_app_name` from the Terraform outputs. + + **Note:** The `func` CLI may occasionally encounter errors, even if it reports `Deployment completed successfully`. If the `func azure functionapp publish` command does not return the `Invoke URL`, rerun the command. + +4. Verify the deployment: + - **Backend URL (`Invoke URL`)**: Send a GET request to this URL. The response should return a 200 status code with the following message: + ``` + "Hello world, this is coming from Function App!" + ``` + + - **Frontend URL (`frontend_url`)**: Send a GET request to this URL. If everything is correctly configured, the frontend should return the same 200 status code and message. + +5. Remember: After each Terraform deployment, you may need to redeploy the Function App code. + +> [!NOTE] +> For easier development and debugging, CORS restrictions have been disabled by setting `Access-Control-Allow-Origin: *` within the function code. Once the application is running successfully, ensure CORS is re-enabled and properly configured to secure the application. + +# Additional Notes + +Integration between Azure Function App and Azure API Management can be challenging and prone to various issues. Due to the limited documentation, I have automated variables to reduce potential errors. If you plan to modify the code, keep the following considerations in mind: + +- Ensure the `url_template` property in the `azurerm_api_management_api_operation` resource aligns with the function name specified in the `Invoke URL`. Avoid appending slashes to the `url_template` value. +- Ensure that `https://your-function-app-name.azurewebsites.net/api` is consistently used in both the policy and the `Service URL` in the backend configuration, and ensure it overrides any existing value in the backend. + +--- + +This sample aims to bridge gaps in existing documentation and provide a working, reproducible example of Azure Function App and API Management integration. Contributions and feedback are welcome! diff --git a/quickstart/201-azure-api-integration-function-app/function_code/.funcignore b/quickstart/201-azure-api-integration-function-app/function_code/.funcignore new file mode 100644 index 000000000..df5ed316c --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/function_code/.funcignore @@ -0,0 +1,10 @@ +*.js.map +*.ts +.git* +.vscode + +test +getting_started.md +node_modules/@types/ +node_modules/azure-functions-core-tools/ +node_modules/typescript/ \ No newline at end of file diff --git a/quickstart/201-azure-api-integration-function-app/function_code/.gitignore b/quickstart/201-azure-api-integration-function-app/function_code/.gitignore new file mode 100644 index 000000000..8a9f3bd84 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/function_code/.gitignore @@ -0,0 +1,48 @@ +bin +obj +csx +.vs +edge +Publish + +*.user +*.suo +*.cscfg +*.Cache +project.lock.json + +/packages +/TestResults + +/tools/NuGet.exe +/App_Data +/secrets +/data +.secrets +appsettings.json + + +node_modules +dist + +# Local python packages +.python_packages/ + +# Python Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Azurite artifacts +__blobstorage__ +__queuestorage__ +__azurite_db*__.json \ No newline at end of file diff --git a/quickstart/201-azure-api-integration-function-app/function_code/host.json b/quickstart/201-azure-api-integration-function-app/function_code/host.json new file mode 100644 index 000000000..9df913614 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/function_code/host.json @@ -0,0 +1,15 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[4.*, 5.0.0)" + } +} \ No newline at end of file diff --git a/quickstart/201-azure-api-integration-function-app/function_code/local.settings.json b/quickstart/201-azure-api-integration-function-app/function_code/local.settings.json new file mode 100644 index 000000000..76b99eed8 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/function_code/local.settings.json @@ -0,0 +1,7 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "node" + } + } + \ No newline at end of file diff --git a/quickstart/201-azure-api-integration-function-app/function_code/package-lock.json b/quickstart/201-azure-api-integration-function-app/function_code/package-lock.json new file mode 100644 index 000000000..0d3b776df --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/function_code/package-lock.json @@ -0,0 +1,437 @@ +{ + "name": "function_code", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "dependencies": { + "@azure/functions": "^4.0.0" + }, + "devDependencies": { + "azure-functions-core-tools": "^4.x" + } + }, + "node_modules/@azure/functions": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@azure/functions/-/functions-4.6.0.tgz", + "integrity": "sha512-vGq9jXlgrJ3KaI8bepgfpk26zVY8vFZsQukF85qjjKTAR90eFOOBNaa+mc/0ViDY2lcdrU2fL/o1pQyZUtTDsw==", + "license": "MIT", + "dependencies": { + "cookie": "^0.7.0", + "long": "^4.0.0", + "undici": "^5.13.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/azure-functions-core-tools": { + "version": "4.0.6280", + "resolved": "https://registry.npmjs.org/azure-functions-core-tools/-/azure-functions-core-tools-4.0.6280.tgz", + "integrity": "sha512-DVSgYNnT4POLbj/YV3FKtNdo9KT/M5Dl//slWEmVwZo1y4aJEsUApn6DtkZswut76I3S9eKGC5IaC84j5OGNaA==", + "dev": true, + "hasInstallScript": true, + "hasShrinkwrap": true, + "license": "MIT", + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "chalk": "3.0.0", + "extract-zip": "^2.0.1", + "https-proxy-agent": "5.0.0", + "progress": "2.0.3", + "rimraf": "4.4.1" + }, + "bin": { + "azfun": "lib/main.js", + "azurefunctions": "lib/main.js", + "func": "lib/main.js" + }, + "engines": { + "node": ">=6.9.1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "node_modules/azure-functions-core-tools/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", + "dev": true, + "optional": true + }, + "node_modules/azure-functions-core-tools/node_modules/@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/azure-functions-core-tools/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/ansi-styles": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz", + "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==", + "dev": true, + "dependencies": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-functions-core-tools/node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/azure-functions-core-tools/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/azure-functions-core-tools/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-functions-core-tools/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/azure-functions-core-tools/node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/azure-functions-core-tools/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/azure-functions-core-tools/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/azure-functions-core-tools/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/azure-functions-core-tools/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-functions-core-tools/node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/azure-functions-core-tools/node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/azure-functions-core-tools/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/azure-functions-core-tools/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/azure-functions-core-tools/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/azure-functions-core-tools/node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/azure-functions-core-tools/node_modules/supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-functions-core-tools/node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/azure-functions-core-tools/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0" + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + } + } +} diff --git a/quickstart/201-azure-api-integration-function-app/function_code/package.json b/quickstart/201-azure-api-integration-function-app/function_code/package.json new file mode 100644 index 000000000..8ba10afa6 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/function_code/package.json @@ -0,0 +1,16 @@ +{ + "name": "", + "version": "1.0.0", + "description": "", + "main": "src/functions/*.js", + "scripts": { + "start": "func start", + "test": "echo \"No tests yet...\"" + }, + "dependencies": { + "@azure/functions": "^4.0.0" + }, + "devDependencies": { + "azure-functions-core-tools": "^4.x" + } +} \ No newline at end of file diff --git a/quickstart/201-azure-api-integration-function-app/function_code/src/functions/op1.js b/quickstart/201-azure-api-integration-function-app/function_code/src/functions/op1.js new file mode 100644 index 000000000..39d2caee2 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/function_code/src/functions/op1.js @@ -0,0 +1,23 @@ +const { app } = require('@azure/functions'); + +app.http('FuncFromCli', { + methods: ['GET', 'POST'], + authLevel: 'anonymous', + handler: async (request, context) => { + context.log(`Http function processed request for url "${request.url}"`); + + const name = request.query.get('name') || await request.text() || 'world'; + +/* For easier development and debugging, CORS restrictions have been disabled by setting Access-Control-Allow-Origin: *. +Once the application is running successfully, ensure CORS is re-enabled and properly configured to secure the application. */ + return { + body: `Hello ${name}, this is coming from Function App!`, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type", + } + } + } +}); diff --git a/quickstart/201-azure-api-integration-function-app/terraform_configs/.gitignore b/quickstart/201-azure-api-integration-function-app/terraform_configs/.gitignore new file mode 100644 index 000000000..436d5c608 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/terraform_configs/.gitignore @@ -0,0 +1,6 @@ +.terraform/ +*.tfstate +*.tfstate.backup +*.tfplan + +functionapp.zip \ No newline at end of file diff --git a/quickstart/201-azure-api-integration-function-app/terraform_configs/main.tf b/quickstart/201-azure-api-integration-function-app/terraform_configs/main.tf new file mode 100644 index 000000000..b72b2242b --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/terraform_configs/main.tf @@ -0,0 +1,99 @@ +data "azurerm_resource_group" "rg" { + name = var.resource_group_name +} + +resource "random_string" "random_name" { + length = 13 + lower = true + numeric = false + special = false + upper = false +} + +resource "azurerm_api_management" "example" { + name = "myapi${random_string.random_name.result}" + location = data.azurerm_resource_group.rg.location + resource_group_name = data.azurerm_resource_group.rg.name + publisher_email = "test@contoso.com" + publisher_name = "example publisher" + sku_name = "Developer_1" + +} + +resource "azurerm_api_management_api" "example" { + name = "example-api" + resource_group_name = data.azurerm_resource_group.rg.name + api_management_name = azurerm_api_management.example.name + revision = "1" + display_name = "Example API" + api_type = "http" + protocols = ["https"] + subscription_required = false +} + +resource "azurerm_api_management_api_operation" "example" { + operation_id = "op1" + api_name = azurerm_api_management_api.example.name + api_management_name = azurerm_api_management.example.name + resource_group_name = data.azurerm_resource_group.rg.name + display_name = "GET Resource" + method = "GET" + url_template = "funcfromcli" # Ensure this value matches the Azure Function's name exactly as it appears in the invoke URL. + response { + status_code = 200 + description = "Successful GET request" + } +} + +resource "azurerm_api_management_api_policy" "example" { + api_name = azurerm_api_management_api.example.name + resource_group_name = data.azurerm_resource_group.rg.name + api_management_name = azurerm_api_management.example.name + xml_content = templatefile("policy.xml", { + base-url = "https://${azurerm_linux_function_app.example.default_hostname}/api" + }) +} + + +resource "azurerm_api_management_backend" "example" { + name = "example-backend" + resource_group_name = data.azurerm_resource_group.rg.name + api_management_name = azurerm_api_management.example.name + protocol = "http" + url = "https://${azurerm_linux_function_app.example.default_hostname}/api" #make sure this ends exactly like this. just "api" without slash +} + +resource "azurerm_storage_account" "example" { + name = random_string.random_name.result + resource_group_name = data.azurerm_resource_group.rg.name + location = data.azurerm_resource_group.rg.location + account_tier = "Standard" + account_replication_type = "LRS" + +} + +resource "azurerm_service_plan" "example" { + name = "example-app-service-plan" + resource_group_name = data.azurerm_resource_group.rg.name + location = data.azurerm_resource_group.rg.location + os_type = "Linux" + sku_name = "B1" + +} + +resource "azurerm_linux_function_app" "example" { + name = "myfuncapp${random_string.random_name.result}" + resource_group_name = data.azurerm_resource_group.rg.name + location = data.azurerm_resource_group.rg.location + service_plan_id = azurerm_service_plan.example.id + + storage_account_name = azurerm_storage_account.example.name + storage_account_access_key = azurerm_storage_account.example.primary_access_key + site_config { + + application_stack { + node_version = "20" + + } + } +} \ No newline at end of file diff --git a/quickstart/201-azure-api-integration-function-app/terraform_configs/outputs.tf b/quickstart/201-azure-api-integration-function-app/terraform_configs/outputs.tf new file mode 100644 index 000000000..35dbfa69e --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/terraform_configs/outputs.tf @@ -0,0 +1,15 @@ +output "resource_group_name" { + value = data.azurerm_resource_group.rg.name +} + +output "resource_group_location" { + value = data.azurerm_resource_group.rg.location +} + +output "function_app_name" { + value = azurerm_linux_function_app.example.name +} + +output "frontend_url" { + value = "${azurerm_api_management.example.gateway_url}/${azurerm_api_management_api.example.path}${azurerm_api_management_api_operation.example.url_template}" +} diff --git a/quickstart/201-azure-api-integration-function-app/terraform_configs/policy.xml b/quickstart/201-azure-api-integration-function-app/terraform_configs/policy.xml new file mode 100644 index 000000000..001b21311 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/terraform_configs/policy.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/quickstart/201-azure-api-integration-function-app/terraform_configs/providers.tf b/quickstart/201-azure-api-integration-function-app/terraform_configs/providers.tf new file mode 100644 index 000000000..d9ee568b1 --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/terraform_configs/providers.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">=1.0" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>4.0" + } + random = { + source = "hashicorp/random" + version = "~>3.0" + } + } +} +provider "azurerm" { + features {} + resource_provider_registrations = "none" + subscription_id = var.subscription_id +} \ No newline at end of file diff --git a/quickstart/201-azure-api-integration-function-app/terraform_configs/variables.tf b/quickstart/201-azure-api-integration-function-app/terraform_configs/variables.tf new file mode 100644 index 000000000..014c9c12c --- /dev/null +++ b/quickstart/201-azure-api-integration-function-app/terraform_configs/variables.tf @@ -0,0 +1,24 @@ + + +# these are sandbox credentials so dear hackers, don't bother ;) +variable "subscription_id" { + default = "YOUR-SUBSCRIPTION-ID" + type = string +} + +variable "resource_group_name" { + default = "YOUR-RESOURCEGROUP-NAME" + type = string +} + + +variable "sku" { + description = "The pricing tier of this API Management service" + default = "Developer" + type = string + validation { + condition = contains(["Developer", "Standard", "Premium"], var.sku) + error_message = "The sku must be one of the following: Developer, Standard, Premium." + } +} +