Jenkins Shared Librariesを使ってAzure VM上でスクリプトを実行

JenkinsからJenkins Shared Libraiesyを使って、AzureのVM上でスクリプトを実行する方法をまとめてみました。

#TerraformでAzure VMのセットアップ

```terraform
resource "azurerm_linux_virtual_machine" "myvm" {
  name                    = "myproject-vm"
  resource_group_name     = data.azurerm_resource_group.myproject.name
  location                = data.azurerm_resource_group.myproject.location
  network_interface_ids   = [azurerm_network_interface.nic.id]
  size                    = "Standard_D2as_v5"
  admin_username          = "myvmadmin"
  patch_mode              = "AutomaticByPlatform"

  disable_password_authentication = true

  admin_ssh_key  {
    username    = "myvmadmin"
    public_key  = file("data/my_key.pub")
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-focal"
    sku       = "20_04-lts"
    version   = "latest"
  }

  os_disk {
    name                  = "myproject-osdisk"
    caching               = "ReadWrite"
    storage_account_type  = "Standard_LRS"
    disk_size_gb          = 60
  }
}

resource "azurerm_network_interface" "nic" {
  name                      = "myproject-nic"
  resource_group_name       = data.azurerm_resource_group.myproject.name
  location                  = data.azurerm_resource_group.myproject.location

  ip_configuration {
    name                          = "myproject-nicconf"
    subnet_id                     = data.azurerm_subnet.myproject.id
    private_ip_address_allocation = "Dynamic"
    private_ip_address_version    = "IPv4"
  }
}

output "ipaddress" {
  value = azurerm_network_interface.nic.private_ip_address
}
```
`azurerm_linux_virtual_machine`でVMをセットアップしますが、その時に後ほどJenkinsよりSSHでアクセスできるためにSSH認証の公開鍵を一緒にインストールしておきます。それが、この個所です。

```terraform
  admin_ssh_key  {
    username    = var.vm_admin_user
    public_key  = file("data/my_key.pub")
  }
```
Jenkins側には秘密鍵を設定して、それを使ってアクセスします。
また、`output "ipaddress"`を入れておくと、Terraformを使って簡単にセットアップしたVMのIPアドレスが取得できます。

#Jenkins Shared Librariesの設定

```yaml
unclassified:
  globalLibraries:
    libraries:
    - defaultVersion: "master"
      name: "mycompany-shared-libraries"
      retriever:
        modernSCM:
          scm:
            git:
              credentialsId: "github-user"
              id: "******-*****-*****-****-*****"
              remote: "https://github.com/mycompany/jenkins.libraries.git"
```
Jenkins Configuration as Codeを使ってJenkinsを設定する場合は、以上のようなコードでShared Librariesを設定します。
JCasCの詳細は以下のサイトにあります。

https://www.jenkins.io/projects/jcasc/

#Jenkins Shared Librariesリポジトリ
Githubにリポジトリ、（例　`https://github.com/mycompany/jenkins.libraries.git`）を作成します。

```
(root)
+- vars
|   +- test.groovy
+- resources               # スクリプト
|   +- myscript.sh
```
パイプライン実行中にShared Librariesリポジトリから`myscript.sh`を使用するので以上のようなリポジトリを作成します。

Jenkins Shared Librariesの詳細は以下のサイトにあります。

https://www.jenkins.io/doc/book/pipeline/shared-libraries/

#JenkinsパイプラインからShared Librariesを読み込む

```groovy
@Library('mycompany-shared-libraries')_  //Shared Librariesを読み込む

pipeline {
    environment {
		PRIVATEKEY = credentials('vm-ssh-key')   //VMアクセス用の秘密鍵
    }
    stages {
		stage ('Setup Script') {
			steps {
				def scriptContent  = libraryResource 'myscript.sh'
				writeFile file: 'myscript.sh', text: scriptContent
```
先ず`@Library('mycompany-shared-libraries')_`でShared Librariesを読み込みます。
実行したパイプラインのログを見ると実際、gitからFetchしているのが分かります。

```
Fetching changes from the remote Git repository
 > git config remote.origin.url https://github.com/mycompany/jenkins.libraries.git # timeout=10
Fetching without tags
Fetching upstream changes from https://github.com/mycompany/jenkins.libraries.git
```
次に、`libraryResource 'myscript.sh'`を使ってスクリプトを読み込みます。今回はJenkinsのエージェント上で実行するのではなく、先ほど作ったVM上で実行するためにファイルとしてスクリプトを作ります。

#JenkinsパイプラインからAzure VM上でスクリプトを実行

```groovy
.
.
    environment {
		PRIVATEKEY = credentials('vm-ssh-key')   //VMアクセス用の秘密鍵
    }
    stages {
.
.
		stage ('Run script') {
			steps {
			  	script {
	                env.IPADDRESS = sh(script:"terraform output -raw ipaddress", returnStdout: true).trim()

					def remote = [:]
					remote.name = 'myproject-vm'
					remote.host = "${IPADDRESS}"
					remote.user = 'myvmadmin'
					remote.identity = "${PRIVATEKEY}"
					remote.allowAnyHosts = true
					remote.pty = true

					sshPut remote: remote, from: 'myscript.sh', into: '.'
					sshCommand remote: remote, sudo: true, command: "chmod 755 myscript.sh"
					sshCommand remote: remote, sudo: true, command: "/home/myvmadmin/myscript.sh"

```
先ほどのパイプラインに新たなStageを追加して、スクリプトをVM上で実行します。
Jenkinsのエージェント上にTerraformをインストールしておくとVMを作成して`terraform output -raw ipaddress`でVMのIPアドレス`IPADDRESS`を実行時に取得できます。もちろんVM自体もパイプライン実行時に`terraform apply -auto-approve <terraform dir>`を実行して作成することも可能です。

https://www.jenkins.io/doc/pipeline/steps/ssh-steps/

SSHのStepsを利用して、VMにアクセスします。Jenkinsのクレデンシャルにあらかじめ秘密鍵`credentials('vm-ssh-key')`を設定しておいて、`remote.identity = "${PRIVATEKEY}"`のようにしてアクセスを設定します。`remote.host = "${IPADDRESS}"`で対象のVMのIPを設定します。
`sshPut`でスクリプトをVM上にコピーし、`sshCommand remote: remote, sudo: true, command: "/home/myvmadmin/myscript.sh"`で実行できます。

`myvmadmin`はTerraformで公開鍵を設定したユーザー名です。`sudo: true`を使うとルートアカウントでスクリプトを実行できます。

最後にもう一つStageを追加して`terraform destroy -auto-approve`と実行すると、パイプライン上でVMの作成、スクリプトの実行、VMの削除まですべて自動でできるようになります。これで、VMを必要な時だけセットアップして、必要が無くなると削除できるのでコスト削減もできます。