ARM Template の書き方 その6 CustomScript

  • ShellScript
  • VirtualMachine
  • VSCode
  • AzureResourceManager
  • Linux
  • やりたいこと

    目的

    VirtualMachine を構築した後にこう思います。普段使うツールはサーバーを立てるときに自動的にインストールしておきたいなと。新たにサーバーを立てる度にツールをインストールしてと、毎回同じことを繰り返すのは面倒ですよね。

    そこで、今回紹介するのは AWS でいうところの user_data に "近い" 機能です(同じとは言ってない)。ARM で Linux サーバーをデプロイする際に任意のスクリプトを root で実行してくれます。

    公式の ARM Template はこちらの GitHub で、公式の解説はこちら Use the Azure Custom Script Extension Version 2 with Linux virtual machines です。

    構成

    今回は GitHub にあるスクリプトを ARM でデプロイするときに一緒に流し込みます。

    customscript

    CustomScript と CustomData

    私は最初、名前が似ている機能が2つ存在しているということが理解できず、大変迷いました。

    • Customdata:VirtualMachine のプロパティの1つ
    • CustomScript:VirtualMachin の Extensions の1つ

    CustomData はパラメータで指定した文字列(コマンド)をサーバー上で実行させるもので、実行ユーザーなども自由に設定できます。ただし、CustomData は文字列で指定しなければならないため、複数のコマンドを実行するのには不便です。

    CustomScript は Microsoft.Compute virtualMachines/extensions の一種です。CustomScript では複数のスクリプトを実行することもできますし、GitHub、ローカル、StorageAccount といった様々な場所にあるスクリプトを利用できるのでとても便利です。

    今回は CustomScript で実施します。

    やってみた

    実際に用意したのが以下のテンプレートです。

    ARM Template サンプル

    {
      "comments": "Virtual Machine",
      "name": "[variables('virtualMachines').vm01.name]",
      "type": "Microsoft.Compute/virtualMachines",
      "apiVersion": "2019-07-01",
      "location": "[variables('location')]",
      "dependsOn": [
        "[variables('resourceID').nic01]"
      ],
      "tags": "[variables('tags')]",
      "properties": {
        "osProfile": {
          "computerName": "[variables('virtualMachines').vm01.computerName]",
          "adminUsername": "[variables('virtualMachines').vm01.adminUsername]",
          "adminPassword": "[parameters('adminPassword')]",
          "linuxConfiguration": {
            "disablePasswordAuthentication": true,
            "ssh": {
              "publicKeys": [
                {
                  "path": "[concat('/home/',variables('virtualMachines').vm01.adminUsername, '/.ssh/authorized_keys')]",
                  "keyData": "[reference(parameters('publicSshKeyID'),'2020-12-01').publicKey]"
                }
              ]
            }
          }
        },
        "hardwareProfile": {
          "vmSize": "Standard_DS2_v2"
        },
        "storageProfile": {
          "imageReference": {
            // Get-AzVMImagePublisher -Location eastus | Out-File publisher.txt
            "publisher": "OpenLogic",
            // Get-AzVMImageOffer -Location eastus -PublisherName OpenLogic | Out-File offer.txt
            "offer": "CentOS",
            // Get-AzVMImageSku -Location eastus -PublisherName OpenLogic -Offer CentOS | Out-File sku.txt
            "sku": "8_3",
            "version": "latest"
          },
          "osDisk": {
            "name": "[variables('virtualMachines').vm01.osDiskName]",
            "osType": "Linux",
            "diskSizeGB": 127,
            "caching": "ReadWrite",
            "createOption": "FromImage",
            "managedDisk": {
              "storageAccountType": "StandardSSD_LRS"
            }
          }
        },
        "networkProfile": {
          "networkInterfaces": [
            {
              "properties": {
                "primary": true
              },
              "id": "[variables('resourceID').nic01]"
            }
          ]
        }
      },
      "resources": [
        {
          // https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/custom-script-linux
          "name": "installDBAtools",
          "type": "extensions",
          "location": "[variables('location')]",
          "apiVersion": "2019-03-01",
          "dependsOn": [
            "[variables('resourceID').vm01]"
          ],
          "tags": "[variables('tags')]",
          "properties": {
            "publisher": "Microsoft.Azure.Extensions",
            "type": "CustomScript",
            "typeHandlerVersion": "2.1",
            "autoUpgradeMinorVersion": true,
            "settings": {
              "fileUris": [
                "https://raw.githubusercontent.com/atsushikoizumi/azure_arm_v1/master/dba/customscript.sh"
              ]
            },
            "protectedSettings": {
              "commandToExecute": "[concat('bash customscript.sh ',variables('virtualMachines').vm01.adminUsername)]"
            }
          }
        }
      ]
    }

    customscript.sh サンプル

    #!/bin/bash
    
    USERNAME=$1
    LOGFILE=/var/log/customscript.log
    
    # start
    touch $LOGFILE
    whoami                    >> $LOGFILE
    pwd                       >> $LOGFILE
    echo "USERNAME=$USERNAME" >> $LOGFILE

    ポイント

    上記のテンプレート内にもコメントとして記載しておりますが、公式のドキュメントを読むことが最重要です。

    fileUris の記入箇所が公式ドキュメントと異なりますが、これは、protectedSettings で書くと、デプロイ時の予想結果にスクリプトが表示されないので settings に書いています。

    vscode の罠

    おそらく、ほとんどの人が ARM template を書く時、VSCode を使っていると思いますが、なんと、そこに罠がありました。上記のテンプレートを VSCode で展開すると以下のエラーが表示されます。

    customscript

    メッセージの内容は以下です。

    • Missing required property "storageAccountName"
    • Missing required property "storageAccountKey"

    言われるがままに StorageAccount の情報を入れるとエラーになります。そんなの知らねーよ、って感じですが、このエラーは無視しましょう。これで3時間くらいハマりました。

    分かったこと

    スクリプトの実行結果を確認

    サーバーにログインして確認してみます。

    [dbastgvm01admin@dba-stg-vm01 ~]$ cat /var/log/customscript.log root /var/lib/waagent/custom-script/download/0 USERNAME=dbastgvm01admin

    上記の結果から分かることは以下です。

    • 実行ユーザー:root
    • 実行パス:/var/lib/waagent/custom-script/download/0
    • テンプレート内で指定した引数をスクリプトで受け取れる

    Azure Linux Agent(waagent)

    実は内部で Azure Linux Agent なるものが CustomScript を実行しています。それは上記の結果における実行パスからも予想できます。

    ちょっと覗いてみましょう。

    [root@dba-stg-vm01 ~]# ls /var/lib/waagent/ 11FDC3D66C29BE2B5050CB945021DB76A26DB312.crt Incarnation waagent_status.json 11FDC3D66C29BE2B5050CB945021DB76A26DB312.prv Microsoft.Azure.Extensions.CustomScript-2.1.3 WALinuxAgent-2.2.48.1 290D46DAA792BDC698EF5E36B21921E3A1DB2DB6.crt Microsoft.Azure.Extensions.CustomScript__2.1.3.zip WALinuxAgent-2.2.48.1.zip Certificates.p7m Microsoft.Azure.Extensions.CustomScript.2.manifest.xml WALinuxAgent-2.2.49.2 Certificates.pem ovf-env.xml WALinuxAgent-2.2.49.2.zip Certificates.xml partition WALinuxAgent-2.2.51 custom-script Prod.2.manifest.xml WALinuxAgent-2.2.51.zip events Protocol WALinuxAgent-2.2.52 event_status.json provisioned WALinuxAgent-2.2.52.zip ExtensionsConfig.2.xml published_hostname WALinuxAgent-2.2.53 GoalState.2.xml SharedConfig.xml WALinuxAgent-2.2.53.zip history TransportCert.pem WireServerEndpoint HostingEnvironmentConfig.xml TransportPrivate.pem [root@dba-stg-vm01 ~]# ls /var/lib/waagent/custom-script/download/0/ customscript.sh stderr stdout [root@dba-stg-vm01 ~]# cat /etc/waagent.conf # # Microsoft Azure Linux Agent Configuration # # Enable extension handling. Do not disable this unless you do not need password reset, # backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Which provisioning agent to use. Supported values are "auto" (default), "waagent", # "cloud-init", or "disabled". Provisioning.Agent=auto # Password authentication for root account will be unavailable. Provisioning.DeleteRootPassword=y # Generate fresh host key pair. Provisioning.RegenerateSshHostKeyPair=y # Supported values are "rsa", "dsa", "ecdsa", "ed25519", and "auto". # The "auto" option is supported on OpenSSH 5.9 (2011) and later. Provisioning.SshHostKeyPairType=rsa # Monitor host name changes and publish changes via DHCP requests. Provisioning.MonitorHostName=y # Decode CustomData from Base64. Provisioning.DecodeCustomData=n # Execute CustomData after provisioning. Provisioning.ExecuteCustomData=n # Algorithm used by crypt when generating password hash. #Provisioning.PasswordCryptId=6 # Length of random salt used when generating password hash. #Provisioning.PasswordCryptSaltLength=10 # Allow reset password of sys user Provisioning.AllowResetSysUser=n # Format if unformatted. If 'n', resource disk will not be mounted. ResourceDisk.Format=n # File system on the resource disk # Typically ext3 or ext4. FreeBSD images should use 'ufs2' here. ResourceDisk.Filesystem=ext4 # Mount point for the resource disk ResourceDisk.MountPoint=/mnt/resource # Create and use swapfile on resource disk. ResourceDisk.EnableSwap=n # Size of the swapfile. ResourceDisk.SwapSizeMB=0 # Comma-seperated list of mount options. See man(8) for valid options. ResourceDisk.MountOptions=None # Enable verbose logging (y|n) Logs.Verbose=n # Enable Console logging, default is y # Logs.Console=y # Root device timeout in seconds. OS.RootDeviceScsiTimeout=300 # If "None", the system default version is used. OS.OpensslPath=None # Set the SSH ClientAliveInterval # OS.SshClientAliveInterval=180 # Set the path to SSH keys and configuration files OS.SshDir=/etc/ssh # If set, agent will use proxy server to access internet #HttpProxy.Host=None #HttpProxy.Port=None # Detect Scvmm environment, default is n # DetectScvmmEnv=n # # Lib.Dir=/var/lib/waagent # # DVD.MountPoint=/mnt/cdrom/secure # # Pid.File=/var/run/waagent.pid # # Extension.LogDir=/var/log/azure # # Home.Dir=/home # Enable RDMA management and set up, should only be used in HPC images # OS.EnableRDMA=y # Enable checking RDMA driver version and update # OS.CheckRdmaDriver=y # Enable or disable goal state processing auto-update, default is enabled # AutoUpdate.Enabled=y # Determine the update family, this should not be changed # AutoUpdate.GAFamily=Prod # Determine if the overprovisioning feature is enabled. If yes, hold extension # handling until inVMArtifactsProfile.OnHold is false. # Default is enabled # EnableOverProvisioning=y # Allow fallback to HTTP if HTTPS is unavailable # Note: Allowing HTTP (vs. HTTPS) may cause security risks # OS.AllowHTTP=n # Add firewall rules to protect access to Azure host node services OS.EnableFirewall=y # Enforce control groups limits on the agent and extensions CGroups.EnforceLimits=n # CGroups which are excluded from limits, comma separated CGroups.Excluded=customscript,runcommand

    テンプレートで設定したスクリプトがサーバーに配置されていることが確認できました。また、waagent の設定ファイルの一番下の行で、customscript が除外されていることも確認できました。

    最後に

    VirtualMachine を1つとっても、細かくみていくと色々な機能があるものですね。AWS もそうですが、Azure もすごいなと感心いたしました。もっと使いこなしていきたいと思います。

  • ShellScript
  • VirtualMachine
  • VSCode
  • AzureResourceManager
  • Linux