以 Ansible 建立 Azure Virtual Machines
(2021/12/25 註記:左思右想,決定棄用 Medium,未來更多文章請參閱 https://chengweichen.com)
(本文的 Canonical URL —https://chengweichen.com/2018/12/ansible-azure-virtual-machines.html)
因為一些緣故,要再次嘗試透過 Ansible 建立 Azure 的一些資源,於是重新踏入這個坑進行了一番研究。Azure 和其他雲端供應商一樣,提供的服務越來越多樣,同時也支援越來越多服務都能以 Infrastructure as Code 的方式創建與管理,不過既然是重新入坑,還是先從簡單的動作開始著手,首先嘗試以 Ansible 來建立一個 Azure Virtual machines。
為 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_size
與 image
。
上面的範例是設置 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 官方文件
- azure_rm_resourcegroup
- azure_rm_virtualnetwork
- azure_rm_subnet
- azure_rm_publicipaddress
- azure_rm_networkinterface
參考資料
- Building Infrastructure with Microsoft Azure and Ansible
- Azure 官方文件:Ansible on Azure documentation
- Azure 官方文件:Install Ansible on Azure virtual machines
- Azure 官方文件:使用 Azure CLI 來建立 Azure 服務主體
- Azure 官方文件:How to: Use the portal to create an Azure AD application and service principal that can access resources
- Azure 官方文件:Ansible Playbooks for Azure
聽說現在流行鼓勵讀者「鼓掌」,如果您喜歡本文,歡迎多多分享本文,並給予我掌聲,讓我知道本文有為這個世界貢獻了一些價值,謝謝大家!