In this article, I'll use the HttpTrigger
and TimerTrigger
that I often use in Azure Functions.
I will explain up to the point of deploying and running.
It's been quite long, so I'll give it a table of contents and a brief description.
section | Term | title | Contents |
---|---|---|---|
2 | - | Preparation | - |
1 | -Local environment | Installation of software required to deploy Azure Functions | |
2 | -Azure cloud environment | Pre-create the services needed to deploy Azure Functions | |
3 | - | Creating a FunctionApp | Create a FunctionApp from the console |
4 | - | AzureFunctions type | AvailableTrigger Kind of |
5 | - | Implementation of AzureFunctions | Creating and modifying Python scripts |
1 | - requirements.Fix txt | - | |
2 | - HttpTrigger | - | |
3 | - TimerTrigger | - | |
6 | - | Deploy Azure Functions | - |
1 | -Deploy using Makefile | - | |
7 | - | Debugging AzureFunctions | Running and debugging from the console |
In order to deploy AzureFunctions, the following preparations are required on the local environment and the cloud environment side of Azure.
In this article, we will build the environment on Mac.
To deploy AzureFunctions, you need to install the following two software.
Installation on Mac is easy with brew
.
#Azure CLI installation
$ brew update && brew install azure-cli
#Install Azure Function Core Tools
$ brew tap azure/functions
$ brew install azure-functions-core-tools@3
#Login to Azure using Azure CLI
$ az login
The version used for deploying this time is as follows.
#Azure CLI version
$ az --version
azure-cli 2.16.0
...
#Azure Functions Core Tools version
$ func --version
3.0.2912
It is assumed that you already have an Azure account.
When deploying AzureFunctions It is useful to prepare the following resources in advance.
Service name | Use | Recommended prefix | Mandatory |
---|---|---|---|
Resource Group | Summarize the services required to deploy Azure Functions | rg- |
O |
StorageAccount | Storage to hold files | st- |
O |
Log Analytics | Keep logs | log- |
O |
Application Insights | View log | appi |
O |
These resources basically ** do not need to be created once **. However, this time we will create AzureFunctions from the console. If you already have the above required services, you can skip to the next section.
Also, the prefixes for the names of these services have a format officially recommended by Azure. Therefore, it is recommended to take a look at this Official Document. (Some of the services deployed this time do not follow the example)
The services created this time are as follows.
--appi-gsy0911
: Log related
--gsy0911stdata
: Storage used by the user to store data etc.
--gsy0911stsys
: Storage used by systems such as Azure Functions
--log-gsy0911
: Log related
Create Azure Functions from the console.
Enter Function App
in the search window and it will appear.
Why the name is not Azure Functions
I think this is because we will create multiple Azure Functions in this Function App
.
Click the Create button to create it.
Fill in the required items in Basics
.
item | value |
---|---|
Functions App name | gsy0911-function |
Publish | Code |
Runtime stack | Python |
Version | 3.8 (※1) |
Region | Japan East |
In the Hosting
item, select the gsy0911sys
of the Storage Account
you created earlier.
item | value |
---|---|
Storage Account | gsy0911sys |
Operating System | Linux |
Plan type | Consumption(※2) |
Fill in the Monitoring
item as well.
item | value |
---|---|
Application Insights | appi-gsy0911 |
Set any Tag you like for Tags
.
After creating up to this point, create.
This completes the service to be created.
AzureFunctions has various launch methods (triggers
).
In particular, I use the following four.
As the name suggests, each one will be explained briefly.
GET
or POST
, and start it when the URL is accessed.Check the Official Documentation (https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-triggers-bindings?tabs=csharp) for other trigger examples.
In addition to trigger
, there is also the concept of binding
, which I will not explain because I will not use it this time.
For more information, please read the official documentation above.
This time, I will implement HttpTrigger
and TimerTrigger
among the above functions.
If you want to create it from scratch, execute the following command and it will generate the necessary files. The runtime to run is Python, so enter 3.
$ func init test_function
Select a number for worker runtime:
1. dotnet
2. node
3. python
4. powershell
5. custom
Choose option: 3 {<- type `3`}
python
Found Python version 3.8.6 (python3).
Writing requirements.txt
Writing .gitignore
Writing host.json
Writing local.settings.json
Writing {current_directory}/test_function/.vscode/extensions.json
#After creation, a folder with the name of the function will be created, so move to that
$ cd test_function
#File created by func init command
$ tree
.
├── host.json
├── local.settings.json
└── requirements.txt
You have now generated the files needed to deploy to Azure Functions.
requirements.txt
Add a library called cerberus
for validation.
requirements.txt
# Do not include azure-functions-worker as it may conflict with the Azure Functions platform
azure-functions
+ cerberus
First, create HttpTrigger.
# test_function/Run below
$ func new
Select a number for template:
1. Azure Blob Storage trigger
2. Azure Cosmos DB trigger
3. Azure Event Grid trigger
4. Azure Event Hub trigger
5. HTTP trigger
6. Azure Queue Storage trigger
7. RabbitMQ trigger
8. Azure Service Bus Queue trigger
9. Azure Service Bus Topic trigger
10. Timer trigger
Choose option: 5 {<- type `5`}
HTTP trigger
Function name: [HttpTrigger] http_trigger {<- type `http_trigger`}
Writing {current_directory}/http_trigger/__init__.py
Writing {current_directory}/http_trigger/function.json
The function "http_trigger" was created successfully from the "HTTP trigger" template.
#File structure after creating HttpTrigger function
$ tree
.
├── host.json
├── local.settings.json
├── requirements.txt
└── http_trigger
├── __init__.py
└── function.json
functions.json
The name
here must match the name of the argument to themain ()
function in __init__.py
.
function.json
{
"scriptFile": "__init__.py",
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
]
}
This time I want to create an AzureFunctions that receives a POST
method, so delete get
.
Also, I want to set the URL route when integrating with API Management, so add tech/qiita
.
function.json
{
"scriptFile": "__init__.py",
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
+ "route": "tech/qiita",
"methods": [
- "get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
]
}
__init__.py
The following templates are created by default. However, there are the following disadvantages.
--You can use the logging
module as it is,
--The length of the return value of HttpResponse
exceeds 120 characters (PEP8
violation)
--ValueError exception handling is pass
It is not a good sample program that can be used as a compliment.
Before modification__init__.py
import logging
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
else:
return func.HttpResponse(
"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
status_code=200
)
Therefore, rewrite everything. Here, the input parameters are validated using the library called cerberus that was added to requirements earlier.
After modification__init__.py
import json
from logging import getLogger, INFO
import azure.functions as func
from cerberus import Validator
logger = getLogger()
logger.setLevel(INFO)
# Validation
SCHEMA = {
"name": {"type": "string"}
}
def main(req: func.HttpRequest) -> func.HttpResponse:
"""
A function that receives a payload like the one below
{
"name": "taro",
"age": 10
}
"""
#Because it is intended for POST only
payload = req.get_json()
#Create a validator
#Input of unknown parameters`allow_unknown`Allowed by
validator = Validator(SCHEMA, allow_unknown=True)
#If validation does not pass, throw an error
if not validator.validate(payload, allow_unknown=True):
return func.HttpResponse(
body=json.dumps(validator.errors),
mimetype="application/json",
charset="utf-8",
status_code=400
)
#If there is a correct input, the received payload is returned as it is
return func.HttpResponse(
body=json.dumps(payload),
mimetype="application/json",
charset="utf-8",
status_code=200
)
This completes the HttpTrigger code.
Next, create a TimerTrigger. Create a sample program from the command as before.
$ func new
Select a number for template:
1. Azure Blob Storage trigger
2. Azure Cosmos DB trigger
3. Azure Event Grid trigger
4. Azure Event Hub trigger
5. HTTP trigger
6. Azure Queue Storage trigger
7. RabbitMQ trigger
8. Azure Service Bus Queue trigger
9. Azure Service Bus Topic trigger
10. Timer trigger
Choose option: 10 {<- type `10`}
Timer trigger
Function name: [TimerTrigger] timer_trigger {<- type `timer_trigger`}
Writing {current_directory}/timer_trigger/readme.md
Writing {current_directory}/timer_trigger/__init__.py
Writing {current_directory}/timer_trigger/function.json
The function "timer_trigger" was created successfully from the "Timer trigger" template.
#Check the created file
$ tree
.
├── host.json
├── local.settings.json
├── requirements.txt
├── http_trigger
│ ├── __init__.py
│ └── function.json
└── timer_trigger
├── __init__.py
├── function.json
└── readme.md
functions.json
The execution timing can be modified with the schedule
item.
In this example, the setting is executed every 5 minutes.
functions.json
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "mytimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 */5 * * * *"
}
]
}
__init__.py
TimerTrigger execution script. This is also rewritten.
Before modification__init__.py
import datetime
import logging
import azure.functions as func
def main(mytimer: func.TimerRequest) -> None:
utc_timestamp = datetime.datetime.utcnow().replace(
tzinfo=datetime.timezone.utc).isoformat()
if mytimer.past_due:
logging.info('The timer is past due!')
logging.info('Python timer trigger function ran at %s', utc_timestamp)
Here is the modified execution script. No particular processing is described.
After modification__init__.py
from datetime import datetime, timezone, timedelta
from logging import getLogger, INFO
import azure.functions as func
logger = getLogger()
logger.setLevel(INFO)
def main(mytimer: func.TimerRequest):
#Get date and time
tz_jst = timezone(timedelta(hours=9))
today = datetime.now(tz=tz_jst)
logger.info(f"today: {today}")
#Process the date from the following
This completes the TimerTrigger script.
Deploy to Azure Functions in the environment logged in with $ az login
It will be deployed automatically.
If you have multiple subscriptions, you must explicitly select them.
Use Makefile
for deployment.
You can use shell script
etc., but Makefile is more convenient because it can be divided by command according to purpose by default.
It is available for any Azure Functions deployment by changing FUNCTION_APP_NAME
.
Makefile
# function app name to deploy
FUNCTION_APP_NAME := gsy0911-function
.PHONY: help
help: ## print this message ## make
@echo "publish to $(FUNCTION_APP_NAME)"
@printf "\033[36m%-30s\033[0m %-50s %s\n" "[Sub command]" "[Description]" "[Example]"
@grep -E '^[/a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | perl -pe 's%^([/a-zA-Z_-]+):.*?(##)%$$1 $$2%' | awk -F " *?## *?" '{printf "\033[36m%-30s\033[0m %-50s %s\n", $$1, $$2, $$3}'
.PHONY: clean
clean: ## clean the function app ## make clean
func azure functionapp publish $(FUNCTION_APP_NAME) --nozip
.PHONY: publish
publish: ## publish to the function app ## make publish
func azure functionapp publish $(FUNCTION_APP_NAME)
If you execute the following $ make publish
in the directory where the above Makefile is located,
It can be deployed to Azure Functions specified by a constant.
#Show list of Make commands
$ make
publish to gsy0911-function
[Sub command] [Description] [Example]
help print this message make
clean clean the function app make clean
publish publish to the function app make publish
#Deploy to Azure Functions
$ make publish
...
Remote build succeeded!
Syncing triggers...
Functions in gsy0911-function:
http_trigger - [httpTrigger]
Invoke url: https://gsy0911-function.azurewebsites.net/api/tech/qiita?code=***
timer_trigger - [timerTrigger]
Once the deployment is complete, you can see it from the console. The name of the deployed Functions matches the name of the locally created folder.
For simple testing, it's a good idea to run Azure Functions directly from the console.
Select the function you want to test run and select Test/Run
from Code + Test
.
A screen will appear from the right, so enter the json format data there and execute Run
.
If successful, you can see that the set value is returned.
In addition, the execution result log etc. is output to the place called Monitor
.
(However, the execution log is output with a lag of about 3 minutes after the execution of the function is completed.)
You can run the test with TimerTrigger in the same way.
In this article, I briefly touched on the following:
--Deploying services required for Azure Functions
--Create / deploy / debug Azure Functions
--HttpTrigger
and TimerTrigger
--Deployment using Makefile
There is also an Article about Queue Trigger that I didn't mention this time, so please check it out as well.