相信有那麼一天,我們將可以像畢凱艦長一樣用嘴巴叫所有主機做事!


Cheng Wei Chen



以 Ansible 建立 Azure Virtual Machines

因為一些緣故,要再次嘗試透過 Ansible 建立 Azure 的一些資源,於是重新踏入這個坑進行了一番研究。Azure 和其他雲端供應商一樣,提供的服務越來越多樣,同時也支援越來越多服務都能以 Infrastructure as Code 的方式創建與管理,不過既然是重新入坑,還是先從簡單的動作開始著手,首先嘗試以 Ansible 來建立一個 Azure Virtual machines。
(本文同步發表於 Medium、Laravel 道場

為 Ansible 取得必要的權限

不管是使用哪一種 Infrastructure as Code 或 Configuration Management 工具,如要透過工具直接管理或操作雲端服務,首先都要為工具取得必要的授權及權限。而目前在 Azure 和 Ansible 的官網上,皆已經有文件在教學如何透過 Ansible 來管理 Azure,但在查閱了數篇之後發現,Azure 能夠提供授權給 Ansible 使用的方式主要有兩種:
  • Active Directory Username/Password
  • Service Principal Credentials
而本文將會採用第二種「Service Principal Credentials」。

如果你打算按著文件,逐一步驟建立「Service Principal Credentials」,我可以預告你很有可能會遇見一個坑,那就是明明是按著文件圖文一步步地操作,但最終就是無法輕易找到下面這四個所需參數。

subscription_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
secret=xxxxxxxxxxxxxxxxx
tenant=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

在嘗試幾種操作之後,個人發現目前最簡單的做法應該是這份文件提到的指令。
只要按著文件中說明的 Azure CLI 指令 az ad sp create-for-rbac,即可順利透過它取得上述四個參數中的後三項。而最簡單執行 Azure CLI 的方式,則是透過 Azure portal 的 Cloud Shell。

只要登入 Azure portal,在點擊上方工具列 Cloud Shell 的 Icon 即可操作它。



如下圖,可以直接在瀏覽器操作 Cloud Shell,而其中已經內建安裝好各種 Commands。(貼心小提醒,Cloud Shell 一段時間沒操作就會自己斷線,因此要做任何操作時最好是一氣呵成為佳。)


因此我們就直接在 Cloud Shell 中執行指令來建立 Service principal。

az ad sp create-for-rbac --name YourServicePrincipalName --password YourPassword

(Service principal 除了用密碼驗證之外,還可以用 Key 驗證,這部分就請自行查看文件。)
沒意外應該就會出現類似下圖的結果,順利取得所需參數。


你也可以在 Azure Active Directory 的 App registrations 中,查看剛才建立的 Service principal。


取得所需的參數之後,我們準備回到 Ansible 這邊。

首先,請先確保你欲執行 ansible 的 Client 有安裝所需的 Ansible Azure 相關 Modules,按 Ansible 官方文件說明,可以透過 pip 指令安裝。

pip install 'ansible[azure]'

接著利用前面取得的重要參數,建立 credentials

因為 Ansible 的 Azure Module 預設會直接查看 ~/.azure/ 之下有沒有 credentials 的相關檔案。這裡我們就直接建立 ~/.azure/credentials,然後將上述四個參數存放在內。

#建立資料夾與檔案
mkdir ~/.azure/
vim ~/.azure/credentials

#檔案內容如下
[default]
subscription_id=這個可以在 Azure portal 的 Subscriptions 找到。
client_id=就是前面透過 az 得到的 appId
secret=就是前面透過 az 得到的 password
tenant=就是前面透過 az 得到的 tenant

測試執行

既然 Ansible 已經擁有了權限,接著就來測試看看,首先有一個非常適合用來測試的 task,即是 Create resource group,如下所示。

# CreateResourceGroup.yml

- hosts: localhost
  connection: local

  tasks:
  - name: Create resource group
    azure_rm_resourcegroup:
      name: YourResourceGroupName
      location: japanwest

Azure 提供了 Rresource group 的概念,你可以將單一專案所需的資源全都放在同一個 Resource group 內方便管理,因此要正式開始創建 Azure 的服務前,第一步就是要建立一個 Resource group。

(小提醒,如果想知道 location 有哪些選擇,可以在 Cloud Shell 輸入指令 az account list-locations 查詢。位於台灣,可以考慮選 japanwest。)

將上述的 task 建立成 Ansible Playbook 之後,就執行 ansible-playbook 試試看吧!
ansible-playbook CreateResourceGroup.yml

如順利執行,應該在 Azure portal 的 Resource groups,即可看見剛才透過 Ansible 建立的 Resource group。


建立 Virtual Machines

最後,終於進入了我們的主要目標「透過 Ansible 建立 Azure Virtual Machines」。

不過雖然是建立 Virtual machine,但除了 VM 本身之外,還要建立其他所需的資源,包含了 Virtual Network、Subnet、Public IP、Network Security Group、Network Interface Card。

所以真正要使用的 Ansible Playbook,會如下所示,還需要加上好幾個 tasks。

  - name: Create virtual network
    azure_rm_virtualnetwork:
      resource_group: AnsibleResourceGroup
      name: AnsibleVnet
      address_prefixes: "11.0.0.0/16"

  - name: Add subnet
    azure_rm_subnet:
      resource_group: AnsibleResourceGroup
      name: AnsibleSubnet
      address_prefix: "11.0.1.0/24"
      virtual_network: AnsibleVnet

  - name: Create public IP address
    azure_rm_publicipaddress:
      resource_group: AnsibleResourceGroup
      allocation_method: Static
      name: AnsiblePublicIP
    register: output_ip_address

  - name: Create Network Security Group
    azure_rm_securitygroup:
      resource_group: AnsibleResourceGroup
      name: AnsibleNetworkSecurityGroup
      rules:
        - name: SSH
          protocol: Tcp
          destination_port_range: 22
          source_address_prefix: 請改成堡壘機IP
          access: Allow
          priority: 500
          direction: Inbound
        - name: MySQL
          protocol: Tcp
          destination_port_range: 3306
          source_address_prefix: 請改成堡壘機IP
          access: Allow
          priority: 501
          direction: Inbound
        - name: HTTP
          protocol: Tcp
          destination_port_range: 80
          access: Allow
          priority: 502
          direction: Inbound

  - name: Create virtual network inteface card
    azure_rm_networkinterface:
      resource_group: AnsibleResourceGroup
      name: AnsibleNIC
      virtual_network: AnsibleVnet
      subnet: AnsibleSubnet
      public_ip_name: AnsiblePublicIP
      security_group: AnsibleNetworkSecurityGroup

  - name: Create VM
    azure_rm_virtualmachine:
      resource_group: AnsibleResourceGroup
      name: AnsibleVM
      vm_size: Standard_A1_v2
      admin_username: 請改成你欲設置的使用者帳號
      admin_password: 請改成你欲設置的密碼
      network_interfaces: AnsibleNIC
      image:
        offer: UbuntuServer
        publisher: Canonical
        sku: '16.04-LTS'
        version: latest

  - name: Dump public IP for VM which will be created
    debug:
      msg: "The public IP is {{ output_ip_address.state.ip_address }}."


快速提一下幾個需要注意的要點。

首先,在透過 azure_rm_securitygroup 建立 Network Security Group 時,可以一併設置多個 rules。如上述範例,即直接設置了 port 22、3306、80 的 rules,其中 port 22 與 3306 還設置了必須透過固定 IP 的堡壘機才能連上。

利用 azure_rm_virtualmachine 建立 VM 時,需要指定 admin_username、是否使用 Key 或密碼登入、vm_sizeimage

上面的範例是設置 SSH 以密碼登入為主,如果想要改用 Key 登入,可以參閱 Ansible Module - azure_rm_virtualmachine 文件設置。

如果要查詢你目前選擇的 Region 有哪些 VM Size 可以使用,可以透過 az 查詢。(請將 japanwest 換成你實際選擇的 region)

az vm list-sizes -l japanwest

或者也可以搭配官網文件查詢,Linux VM 可查看此文件,Windows VM 則查看此文件
而如果要查詢可以使用哪些 Image 來建立 VM,同樣可以透過 az 查詢,但 Image 很多,因此需要加上一些篩選條件,詳細的用法請自行查看 az vm image list -h

az vm image list --location japanwest -f Ubuntu --publisher Canonical --sku 16.04

會得到類似下圖的結果。


最後執行的 Ansible Playbook

- hosts: localhost
  connection: local

  tasks:
  - name: Create resource group
    azure_rm_resourcegroup:
      name: AnsibleResourceGroup
      location: japanwest

  - name: Create virtual network
    azure_rm_virtualnetwork:
      resource_group: AnsibleResourceGroup
      name: AnsibleVnet
      address_prefixes: "11.0.0.0/16"

  - name: Add subnet
    azure_rm_subnet:
      resource_group: AnsibleResourceGroup
      name: AnsibleSubnet
      address_prefix: "11.0.1.0/24"
      virtual_network: AnsibleVnet

  - name: Create public IP address
    azure_rm_publicipaddress:
      resource_group: AnsibleResourceGroup
      allocation_method: Static
      name: AnsiblePublicIP
    register: output_ip_address

  - name: Create Network Security Group
    azure_rm_securitygroup:
      resource_group: AnsibleResourceGroup
      name: AnsibleNetworkSecurityGroup
      rules:
        - name: SSH
          protocol: Tcp
          destination_port_range: 22
          source_address_prefix: 請改成堡壘機IP
          access: Allow
          priority: 500
          direction: Inbound
        - name: MySQL
          protocol: Tcp
          destination_port_range: 3306
          source_address_prefix: 請改成堡壘機IP
          access: Allow
          priority: 501
          direction: Inbound
        - name: HTTP
          protocol: Tcp
          destination_port_range: 80
          access: Allow
          priority: 502
          direction: Inbound

  - name: Create virtual network inteface card
    azure_rm_networkinterface:
      resource_group: AnsibleResourceGroup
      name: AnsibleNIC
      virtual_network: AnsibleVnet
      subnet: AnsibleSubnet
      public_ip_name: AnsiblePublicIP
      security_group: AnsibleNetworkSecurityGroup

  - name: Create VM
    azure_rm_virtualmachine:
      resource_group: AnsibleResourceGroup
      name: AnsibleVM
      vm_size: Standard_A1_v2
      admin_username: 請改成你欲設置的使用者帳號
      admin_password: 請改成你欲設置的密碼
      network_interfaces: AnsibleNIC
      image:
        offer: UbuntuServer
        publisher: Canonical
        sku: '16.04-LTS'
        version: latest

  - name: Dump public IP for VM which will be created
    debug:
      msg: "The public IP is {{ output_ip_address.state.ip_address }}."


如果實驗完畢,想要刪除 Resource group,也同樣能透過 Playbook 達成。

- hosts: localhost
  connection: local

  tasks:
  - name: Delete Resource Group
    azure_rm_resourcegroup:
      name: AnsibleResourceGroup
      state: absent
      force: yes

Ansible Module 官方文件

參考資料


沒有留言:

張貼留言

不歡迎留言打廣告,所以有進行留言管理,敬請見諒。