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 | AvailableTriggerKind 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.txtAdd 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.jsonThe 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__.pyThe 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.jsonThe 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__.pyTimerTrigger 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.