AzureのFunctionsをCLIで使う（ローカルリソースの生成からデプロイまで）with Javascript

# 概要

公式のドキュメントにはリソースグループを作成するところからの説明があるのですが、既に存在するリソースグループの中にFunctionsをデプロイする方法については書かれていないようでしたので書きます。

公式ドキュメント: 
コマンド ラインから JavaScript 関数を作成する - Azure Functions | Microsoft Learn
https://learn.microsoft.com/ja-jp/azure/azure-functions/create-first-function-cli-node?tabs=azure-cli%2Cbrowser&pivots=nodejs-model-v4

なおJavascript関数を作成すると書いてありますが、基本的にデプロイまではシェルスクリプトを用いますので、あまり言語によって限定されるお話でもないと思いますが、やはりJavascriptを使っていただくのが参考コードをそのまま使えて良いと思います。

この記事のPythonバージョンは以下よりご覧いただけます。

AzureのFunctionsをCLIで使う（ローカルリソースの生成からデプロイまで）with Python - Qiita
https://qiita.com/fsd_yoshikawa/items/11a0b67c89588be47576



# 環境
Windows11, WSL2
```
❯ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.1 LTS"
```


node.js
```
❯ node --version
v18.15.0
```


Azure CLI 
```
❯ az --version
azure-cli                         2.46.0 *

core                              2.46.0 *
telemetry                          1.0.8

Dependencies:
msal                              1.20.0
azure-mgmt-resource             21.1.0b1

```

Azure Functions Core Tools 
```
❯ func --version
4.0.5030
```

# 手順

:::note
前提として、まずはAzure上にリソースグループを持っていることが必要です。
:::

## ログイン
まずはCLIでAzureにログインします。

```
az login --use-device-code
```
上記のコマンドを実行すると、https\://microsoft.com/deviceloginというURLとcode(大文字英数字)のペアがターミナル上に表示されます。このcodeをコピーして表示されたURLに行って入力するとAzureにログインできます。

## Azure上にストレージの作成
Functionsを使うにはストレージが必要になりますので、まずはこれを作成します。
```
az storage account create --name <STORAGE_NAME> --resource-group <RESOURCE_GROUP_NAME>

```
:::note
<>の中の文字列はご自分の環境に合わせて入力してください。
:::


## Azure上にFunctions Appリソースの作成
```
az functionapp create --consumption-plan-location japaneast --runtime node --runtime-version 18 --functions-version 4 --name <APP_NAME> --os-type linux --storage-account <STORAGE_NAME> --resource-group <RESOURCE_GROUP>
```

:::note
私の場合では上記\<LOCATION>はjapaneastを設定しました。
\<STORAGE_NAME>はストレージの作成の項目で作ったストレージの名前を入力します。

上記で ```--runtime-version 18```の指定はローカルにインストールされているnodeのバージョンに依ります。
```--functions-version 4```の指定については下記のURLに説明があります。

Azure Functions ランタイム バージョンの概要 | Microsoft Learn
https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-versions?tabs=v4&pivots=programming-language-csharp
:::

# Functionsにデプロイするローカル環境の作成

```
func init azure-functions --node
```
上記のコマンドを実行すると、下記のようなファイル群が生成されます。

```
.
└── azure-functions
    ├── host.json
    ├── local.settings.json
    └── package.json
```

生成したディレクトリに移動します。
```
cd azure-functions
```

次に、Functions上で実行されるエントリーポイントをテンプレートを用いて作成します。
```
func new --name test --template "HTTP trigger"
```

するとtestディレクトリが新たに生成されて、下記のようなディレクトリ構造になります。

```
.
└── azure-functions
    ├── host.json
    ├── local.settings.json
    ├── package.json
    └── test
        ├── function.json
        └── index.js
```


このtestディレクトリ内のindex.jsにAzure Functionsで動作するコードを書くことになります。
デフォルトでは以下のような状況になってます。


```python:index.js
module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const name = (req.query.name || (req.body && req.body.name));
    const responseMessage = name
        ? "Hello, " + name + ". This HTTP triggered function executed successfully."
        : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: responseMessage
    };
}

```

これがどういう動作をするかテストするにはazure-functionsディレクトリで以下のコマンドを実行します。

```
func start
```

すると以下のような出力が得られます。

```
❯ func start

Azure Functions Core Tools
Core Tools Version:       4.0.5030 Commit hash: N/A  (64-bit)
Function Runtime Version: 4.15.2.20177


Functions:

        test: [GET,POST] http://localhost:7071/api/test

For detailed output, run func with --verbose flag.
[2023-04-11T04:34:07.934Z] Worker process started and initialized.
[2023-04-11T04:34:12.365Z] Host lock lease acquired by instance ID '000000000000000000000000017436A7'.
```

この http\://localhost:7071/api/test にアクセスすると、このコードをFunctions上にデプロイするとどういう振る舞いをするかのテストがローカルマシン上でできます。

試しにアクセスすると
```
This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.
```
とブラウザ上に表示されたかと思います。


# Azure上にデプロイ

次に下記のコマンドを実行するとAzure上にFunctionsをデプロイできます。
```
func azure functionapp publish <APP_NAME>
```
:::note
\<APP_NAME>はFunctionsリソースの作成の項で設定した文字列を用います。
:::

# Azure Functions上での機能ごとのファイル分割について

これでAzure Portal上で作成したFunctionsを使用することができるようになるのですが、実際的にFunctionsを使用する際、外部のモジュールを使用したり、機能ごとにファイルを分割したいということがあります。

そこで、index.jsと同じ階層に、libディレクトリを切って、そこにクラスが記述されたファイルを配置し、それをindex.jsで呼び出すというテストを行います。

まずは、azure-functions/testディレクトリで```mkdir lib```を実行します。

そして```touch lib/write-hello.js```でwrite-hello.jsを作成します。

次にwrite-hello.jsの内容を次の通りに変更します。

```javascript:write-hello.js
class TestClass {
    writeHello(){
        return "HELLO from a external file."
    }
}

module.exports = TestClass;

```

つづいて、index.jsを以下の通りに変更します。

```javascript:index.js
module.exports = async function (context, req) {
    const TestClass = require("./lib/write-hello")
    const test = new TestClass();
    const responseMessage = test.writeHello();

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: responseMessage
    };
}

```


コードの内容としてはindex.jsからwrite-hello.jsの"HELLO from a external file."の文字列を呼び出してindex.jsのresponseMessageに代入、これをhttp responseとして返すという形です。

この構造にして```publish```（または```func start```でテスト）して、エンドポイントURLにアクセスした結果以下の表示が得られれば成功です。

```
HELLO from a external file.
```

ファイルを分割してAzure Functions上に乗せることができました。

ただし、このような構造でAzure Functions上にアップロードしたとして、現在のところAzure Portal上でlib以下のファイルについては見ることができないようなのでご注意ください。

![スクリーンショット 2023-04-11 134916.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3249097/423d5c9d-3dfe-5072-45ae-c377f78cf543.png)



以上です。