扒一扒AZURE Resource Template
最近AZURE发布了ARM模式(Azure Resource Manager),其中包含了对于Resource Manager Template的支持。什么是Resource Template?我们先来看一下AZURE在ARM模式下的逻辑架构。
对于Day1接触共有云的小伙伴,通常会习惯通过Azure Portal门户来完成相关服务及资源的配置交付(如创建虚拟云主机,虚拟网络,MySql数据库服务等),通常我们可以把以上创建的服务及资源抽象为一套配置模板,用于描述所创建的服务及资源。由上面的图示可见,ASM模式下用户与Azure云平台交互界面的接口包括Portal,CLI,Visual Studio等,通过以上接口用户给出的服务及资源的描述,Azure平台通过资源描述中间件(Resource Provider Contract)将客户的描述转译为平台资源描述并执行服务及资源创建。Azure Portal通过交互式的GUI方式可以友好方便的帮助用户上手,但是在批量及复杂服务及资源创建时就有些捉襟见肘啦。那么在批量及复杂服务及资源创建时有没有好的解决办法呢?Infrastructure As Code,脚本部署是可行的方案之一,类似所有自动化事务的执行,我们可以将复杂的服务及资源逐一创建并通过脚本实现自动化执行。但是本文在这里不推荐大家使用上述方法,原因有二:1. 服务及资源的抽象不够,我们在定义服务及资源的时候希望通过更高级的抽象给出简单的接口,以便更便捷的定义复杂的服务及资源;2.传统的脚本语言的执行以串行顺序方式执行,对于复杂的服务及资源定义如果希望实现并行及依赖关系定义是比较复杂的。这里给大家介绍的Azure Resource Template可以很好的解决上述问题,帮助大家简单快捷的实现复杂的服务及资源交付,它通过JSON语言高级抽象描述资源定义大大降低了用户的复杂度,同时将并行任务的拆解交给Azure平台资源描述中间件来实现高效,实现Infrastructure as Code。好啦闲话已经扯了不少,我们进入主题吧。为方便大家理解,本文后面的内容按照如下顺序编排,工具方法介绍,举个栗子快速开始,总结及课题延展。
工具方法介绍:
工欲善其事必先利其器,得力顺手的工具可以让我们事半功倍。Infrastructure as Code,和其他编程语言一样,一个好的IDE可以为我们保驾护航。MSFT为Azure Resource Template提供了Visual Studio + Azure SDK的工具组合,使用户可以在VS环境下实现开发。主要功能如下:
- 格式检查。帮助用户检查JSON格式;
- 基础模板。微软已经将AZURE服务及资源的标准模板定义好,方便客户调运。另外Github上还有海量的模板示例作为参考示例(https://github.com/Azure/azure-quickstart-templates),VS已经跟Github做了打通,在创建新项目的时候可以直接引用Github的模板,并且模板可以直接通过VS调用Powershell进行执行;
- 自动补全及函数库查询。在VS中可以帮助用户做索引、函数的补全。
VS的安装与设置这里就不给大家赘述了,大家可以参加如下文章进行配置安装:
https://github.com/Microsoft/HealthClinic.biz/wiki/Deploy-and-manage-Azure-resources
https://www.azure.cn/documentation/articles/developerdifferences/#confdevcomp (中国区AZURE的连接需要修改VS的配置文件,可以参考此链接,如果使用Global Azure可以忽略)。
举个栗子快速开始
开始之前我们先来设定一个目标场景,在Azure Portal上目前我们无法创建多网卡的虚拟机,我们的目标就是通过Template部署来实现多网卡虚拟机的交付,同时为了实现模板的复用,希望用户可以自定义操作系统类型,VM的规格,以及网卡的数量。
1.打开Visual Studio,创建项目,选择Cloud -> Azure资源组
选择100-blank-template空白模板(此示例里面使用空白模板进行介绍,小伙伴们也可以在此处直接选用内置模板及Github上的模板作为基础模板进行修改)
2.开始编写模板,点击azuredeploy.json
大家可以看到基础的模板框架已经出现:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
},
"resources": [
],
"outputs": {
}
}
Azure Resource Template模板主要分为4部分:
参数(parameters)- 为定义模板的通配性,可以定义可配置变量,用户在部署模板时候可以更改变量参数实现自定义。以我们要完成的模板为例,可以通过参数来实现用户自定义VM规格、VM操作系统、网卡数量,从而模板实现通用化,便于用户交付不同要求的资源。
变量(variables)- 类似编程语言中的环境变量,方便用户对参数值的引用。如定义VM的操作系统类型,通过命名友好可读的名称,方便在资源编写时进行调用。
资源(resources)- Infrastructure as Code的主体,所有Azure的服务及资源均通过resource来定义,通过定义中不同的参数描述实现不同的Infrastructure的部署。
输出(outputs)- 模板部署完毕后可以给出输出,如环境访问方式,成功消息等。
在VS环境中,客户在编写资源的时候无需从零写起,可以通过快速添加的方式,VS会直接将添加的资源基础模板引入的项目中,我们可以直接在上面进行参数的编辑修改即可。下面以添加一个虚拟机资源为例
在选择虚拟机模板后,向导问询输入虚拟机名称,存储账户,及虚拟网络。这里是VS环境下编写模板相较其他TEXT Editor的优势之一,帮用户自动创建依赖关系。在Azure上的资源可能与其他资源存在依赖关系,如当我们想创建一个虚拟机的时候,我们需要依赖与存储账号资源,虚拟网络资源。当我们创建虚拟机资源的时候,VS会自动将依赖关系的资源模板进行引入,方便客户开发。
在向导中输入依赖资源的相关参数后,模板主体Resource部分,除去VirtualMachine部分外还出现了StorageAccount、VirtualNetwork、NetworkNic资源定义。
"resources": [
{
"name": "[variables('DemoStorageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"location": "[resourceGroup().location]",
"apiVersion": "2016-01-01",
"sku": {
"name": "[parameters('DemoStorageAccountType')]"
},
"dependsOn": [ ],
"tags": {
"displayName": "DemoStorageAccount"
},
"kind": "Storage"
},
{
"name": "demoVnet",
"type": "Microsoft.Network/virtualNetworks",
"location": "[resourceGroup().location]",
"apiVersion": "2016-03-30",
"dependsOn": [ ],
"tags": {
"displayName": "demoVnet"
},
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('demoVnetPrefix')]"
]
},
"subnets": [
{
"name": "[variables('demoVnetSubnet1Name')]",
"properties": {
"addressPrefix": "[variables('demoVnetSubnet1Prefix')]"
}
},
{
"name": "[variables('demoVnetSubnet2Name')]",
"properties": {
"addressPrefix": "[variables('demoVnetSubnet2Prefix')]"
}
}
]
}
},
{
"name": "[variables('DemoVirtualMachineNicName')]",
"type": "Microsoft.Network/networkInterfaces",
"location": "[resourceGroup().location]",
"apiVersion": "2016-03-30",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', 'demoVnet')]"
],
"tags": {
"displayName": "DemoVirtualMachineNic"
},
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[variables('DemoVirtualMachineSubnetRef')]"
}
}
}
]
}
},
{
"name": "[parameters('DemoVirtualMachineName')]",
"type": "Microsoft.Compute/virtualMachines",
"location": "[resourceGroup().location]",
"apiVersion": "2015-06-15",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('DemoStorageAccountName'))]",
"[resourceId('Microsoft.Network/networkInterfaces', variables('DemoVirtualMachineNicName'))]"
],
"tags": {
"displayName": "DemoVirtualMachine"
},
"properties": {
"hardwareProfile": {
"vmSize": "[variables('DemoVirtualMachineVmSize')]"
},
"osProfile": {
"computerName": "[parameters('DemoVirtualMachineName')]",
"adminUsername": "[parameters('DemoVirtualMachineAdminUsername')]",
"adminPassword": "[parameters('DemoVirtualMachineAdminPassword')]"
},
"storageProfile": {
"imageReference": {
"publisher": "[variables('DemoVirtualMachineImagePublisher')]",
"offer": "[variables('DemoVirtualMachineImageOffer')]",
"sku": "[parameters('DemoVirtualMachineUbuntuOSVersion')]",
"version": "latest"
},
"osDisk": {
"name": "DemoVirtualMachineOSDisk",
"vhd": {
"uri": "[concat(reference(resourceId('Microsoft.Storage/storageAccounts', variables('DemoStorageAccountName')), '2016-01-01').primaryEndpoints.blob, variables('DemoVirtualMachineStorageAccountContainerName'), '/', variables('DemoVirtualMachineOSDiskName'), '.vhd')]"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('DemoVirtualMachineNicName'))]"
}
]
}
}
}
],
在AZURE中创建一个虚拟机需要将系统盘,网卡关联到虚拟机上,网卡需要连接的虚拟网络上。通过以上的过程我们可以看到,相关的依赖关系通过VS工具已经自动帮助我们完成。依赖关系在模板中非常重要,因为依赖关系定义的资源创建的先后顺序,从而保证资源的顺利创建。在上述模板中VirtualMachine我们可以看到dependsOn部分就是来描述依赖关系的
{
"name": "[parameters('DemoVirtualMachineName')]",
"type": "Microsoft.Compute/virtualMachines",
"location": "[resourceGroup().location]",
"apiVersion": "2015-06-15",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('DemoStorageAccountName'))]", #定义虚拟机资源依赖于存储账号资源
"[resourceId('Microsoft.Network/networkInterfaces', variables('DemoVirtualMachineNicName'))]" #定义虚拟机依赖于虚拟网络资源
],
到此我们已经将一个虚拟机创建的基本模板框架搭建完毕,后面我们都基于这个框架来进行修改来达成我们之前预设的目标,通过Template部署来实现多网卡虚拟机的交付,同时为了实现模板的复用,希望用户可以自定义操作系统类型,VM的规格,以及网卡的数量。下面我们先给出最终的实现模板,然后展开介绍,后面的介绍主要侧重一些特殊语法的说明,以便大家可以自行编排自己的模板。
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"adminUsername": {
"type": "string",
"metadata": {
"description": "User name for the Virtual Machine."
}
},
"adminPassword": {
"type": "securestring",
"metadata": {
"description": "Password for the Virtual Machine."
}
},
"dnsLabelPrefix": {
"type": "string",
"metadata": {
"description": "Unique DNS Name for the Public IP used to access the Virtual Machine."
}
},
"LinuxPublisher": {
"type": "string",
"allowedValues": [ "Ubuntu", "CentOS" ]
},
"ubuntuOSVersion": {
"type": "string",
"defaultValue": "16.04.0-LTS",
"allowedValues": [
"12.04.5-LTS",
"14.04.5-LTS",
"15.10",
"16.04.0-LTS"
],
"metadata": {
"description": "The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version."
}
},
"centOSVersion": {
"type": "string",
"defaultValue": "7.3",
"allowedValues": [
"6.9",
"7.1",
"7.2",
"7.3"
],
"metadata": {
"description": "The CentOS version for the VM. This will pick a fully patched image of this given CentOS version."
}
},
"Standard_A2": {
"type": "int",
"defaultValue": 2,
"minValue": 1,
"maxValue": 2,
"allowedValues": [ 1, 2 ]
},
"Standard_D3_v2": {
"type": "int",
"defaultValue": 4,
"minValue": 1,
"maxValue": 4,
"allowedValues": [ 1, 2, 3, 4 ]
},
"vmSize": {
"type": "string",
"defaultValue": "Standard_A2",
"allowedValues": [
"Standard_A2",
"Standard_D3_v2"
]
}
},
"variables": {
"linuxtype": "[variables(concat('linux', parameters('LinuxPublisher')))]",
"linuxUbuntu": {
"imagePublisher": "Canonical",
"imageOffer": "UbuntuServer",
"OSVersion": "[parameters('ubuntuOSVersion')]",
"VMsize": "[parameters('vmSize')]",
"NicNumber": "[parameters(parameters('vmSize'))]"
},
"linuxCentOS": {
"imagePublisher": "OpenLogic",
"imageOffer": "CentOS",
"OSVersion": "[parameters('centOSVersion')]",
"VMsize": "[parameters('vmSize')]",
"NicNumber": "[parameters(parameters('vmSize'))]"
},
"storageAccountName": "[concat('demo',uniquestring(resourceGroup().id))]",
"nicName": "DemoVMNic",
"addressPrefix": "10.0.0.0/16",
"subnetName": "Subnet",
"subnetPrefix": "10.0.0.0/24",
"storageAccountType": "Standard_LRS",
"publicIPAddressName": "DemoPublicIP",
"publicIPAddressType": "Dynamic",
"vmName": "DemoLinuxVM",
"virtualNetworkName": "DemoVNET",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('storageAccountName')]",
"apiVersion": "2017-06-01",
"location": "[resourceGroup().location]",
"sku": {
"name": "[variables('storageAccountType')]"
},
"kind": "Storage",
"properties": {}
},
{
"apiVersion": "2017-04-01",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[resourceGroup().location]",
"properties": {
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsLabelPrefix')]"
}
}
},
{
"apiVersion": "2017-04-01",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"location": "[resourceGroup().location]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"copy": [
{
"name": "subnets",
"count": "[variables('linuxtype').NicNumber]",
"input": {
"name": "[concat(variables('subnetName'),copyIndex('subnets'))]",
"properties": {
"addressPrefix": "[concat('10.0.',copyIndex('subnets'),'.0/24')]"
}
}
}
]
}
},
{
"apiVersion": "2017-04-01",
"type": "Microsoft.Network/networkInterfaces",
"name": "[concat(variables('nicName'),'0')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[resourceId('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'),'0')]"
}
}
}
]
}
},
{
"apiVersion": "2017-04-01",
"condition": "[greater(variables('linuxtype').NicNumber,1)]",
"type": "Microsoft.Network/networkInterfaces",
"name": "[concat(variables('nicName'),copyIndex(1))]",
"location": "[resourceGroup().location]",
"copy": {
"name": "nicloop",
"count": "[sub(variables('linuxtype').NicNumber,1)]"
},
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[resourceId('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'),copyIndex(1))]"
}
}
}
]
}
},
{
"apiVersion": "2017-03-30",
"type": "Microsoft.Compute/virtualMachines",
"name": "[variables('vmName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[variables('linuxtype').VMsize]"
},
"osProfile": {
"computerName": "[variables('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
"storageProfile": {
"imageReference": {
"publisher": "[variables('linuxtype').imagePublisher]",
"offer": "[variables('linuxtype').imageOffer]",
"sku": "[variables('linuxtype').OSVersion]",
"version": "latest"
},
"osDisk": {
"name": "osdisk",
"vhd": {
"uri": "[concat('http://', variables('storageAccountName'), '.blob.core.windows.net/vhds/', 'osdisk', '.vhd')]"
},
"createOption": "FromImage"
}
},
"networkProfile": {
"copy": [
{
"name": "networkInterfaces",
"count": "[variables('linuxtype').NicNumber]",
"input": {
"id": "[replace(reference(concat(variables('nicName'),copyIndex('networkInterfaces'))).ipConfigurations[0].id,'/ipConfigurations/ipconfig1','')]",
"properties": {
"primary": "[less(copyIndex('networkInterfaces'),1)]"
}
}
}
]
}
}
}
],
"outputs": {
"result": {
"type": "string",
"value": "success"
}
}
}
对于操作系统类型的定义可通过定义参数完成,通过allowedValues的定义可以在最终使用模板时实现在交互页面进行参数选择以避免手工输入带来的错误。同理操作系统版本,VM规格以及网卡数量均可采用类似方法定义。
"LinuxPublisher": {
"type": "string",
"allowedValues": [ "Ubuntu", "CentOS" ]
},
变量套用式引用,在Resource模板定义中会通过调用参数或变量的方式来实现模板的通用。在此用例中目标创建的虚拟机的操作系统由于有客户变量来进行选定,所以在虚拟机资源描述部分里面设计的操作系统的相关描述都需要随之而变。因为Ubuntu和CentOS两个选项对应选项背后,相应的操作系统发行商,操作系统版本等参数都要随之改变,为了实现资源描述部分的通用性,避免反复写两次资源描述来覆盖不同操作系统场景,我们采用变量套用式引用。通过定义linuxUbuntu和linuxCentOS变量来分别描述不同操作系统下相关变量参数,同时定义linuxtype变量,其中linuxtype的值等于linux+用户输入的LinuxPublisher参数选项,客户选定LinuPublisher选项后linuxtype的最终值将等于linuxUbuntu或linuxCentOS,在后面的虚拟机资源描述的操作系统部分只需要调用linuxtype变量对象及可实现对客户选定操作系统的变量值抽取,以"publisher": "[variables('linuxtype').imagePublisher]"为例,通过对linuxtype.imagePublisher的抽取实现相应操作系统发布商信息的提取,而且整个资源描述只需要编写一次。
"linuxtype": "[variables(concat('linux', parameters('LinuxPublisher')))]",
"linuxUbuntu": {
"imagePublisher": "Canonical",
"imageOffer": "UbuntuServer",
"OSVersion": "[parameters('ubuntuOSVersion')]",
"VMsize": "[parameters('vmSize')]",
"NicNumber": "[parameters(parameters('vmSize'))]"
},
"linuxCentOS": {
"imagePublisher": "OpenLogic",
"imageOffer": "CentOS",
"OSVersion": "[parameters('centOSVersion')]",
"VMsize": "[parameters('vmSize')]",
"NicNumber": "[parameters(parameters('vmSize'))]"
},
循环描述copy可以实现多资源创建,在此例子中客户可以自定义创建网卡的数量并且每个网卡关联不同的子网,所以无法预先通过静态反复定义的方式将虚拟网络子网资源描述写死,通过copy描述配以客户输入的网卡数量NicNumber变量作为循环次数count,实现动态创建多个资源的目标。
{
"apiVersion": "2017-04-01",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"location": "[resourceGroup().location]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"copy": [
{
"name": "subnets",
"count": "[variables('linuxtype').NicNumber]",
"input": {
"name": "[concat(variables('subnetName'),copyIndex('subnets'))]",
"properties": {
"addressPrefix": "[concat('10.0.',copyIndex('subnets'),'.0/24')]"
}
}
}
]
}
},
使用条件语句实现if or not逻辑,在上述例子中虚拟机在多网卡场景下只有主网卡绑定公网IP,在网卡资源描述中,需要公网地址的描述和不需要公网地址的描述是不同的,所以这里的做法是先静态描述一块带公网地址的网卡资源(因为客户至少创建1块网卡),在将剩余的网卡资源描述引入条件语句从而实现当客户输入大于1块网卡时("condition": "[greater(variables('linuxtype').NicNumber,1)]"),剩余网卡都按照无公网地址方式进行创建。
{
"apiVersion": "2017-04-01",
"condition": "[greater(variables('linuxtype').NicNumber,1)]",
"type": "Microsoft.Network/networkInterfaces",
"name": "[concat(variables('nicName'),copyIndex(1))]",
"location": "[resourceGroup().location]",
"copy": {
"name": "nicloop",
"count": "[sub(variables('linuxtype').NicNumber,1)]"
},
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[resourceId('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'),copyIndex(1))]"
}
}
}
]
}
},
动态依赖关系的定义,通常在资源描述中通过dependsOn来表示依赖关系,如虚拟机资源需要依赖存储账户及网卡资源。但本例中网卡的数量是变量无法预知,所以在描述依赖关系的时候无法静态将所依赖的资源ID全部放上。这里采用调用reference函数实现隐性依赖关系的描述,在虚拟机资源的dependsOn描述中移除网卡的依赖描述,在虚拟机描述中的networkProfile部分中定义id时,通过reference函数抓取ID,reference函数会在模板执行过程中抓取runtime的id值进行返回,只有当相应资源创建完毕时reference才会有返回值,从而实现资源依赖关系的隐性描述。
"networkProfile": {
"copy": [
{
"name": "networkInterfaces",
"count": "[variables('linuxtype').NicNumber]",
"input": {
"id": "[replace(reference(concat(variables('nicName'),copyIndex('networkInterfaces'))).ipConfigurations[0].id,'/ipConfigurations/ipconfig1','')]",
"properties": {
"primary": "[less(copyIndex('networkInterfaces'),1)]"
}
3.部署模板
至此通过本例中几个基本目标给大家介绍了在模板中常用的一些高级功能,这些功能有助于大家描述定义复杂的服务和资源,实现Infrastructure as Code的目标。最后模板一切就绪可以来部署了,Azure支持通过在Portal,PowerShell,CLI中部署模板,其中Powershell可以直接在VS环境中调用,各种部署方法大家可以参阅如下链接:https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-deploy。本文介绍通过Portal的方式进行部署,以便帮助大家理解在模板中定义的参数Parameter部分。
打开Azure Portal,搜索Template Deployment
点击Create创建,选择Build your own template in the editor
将在VS模式下编写的模板粘贴至Portal并保存
进入参数Parameter输入页面,模板中参数Parameter部分定义的参数选项均出现在该页面下,客户可以自定义输入预期值,如操作系统类型,虚拟机大小,网卡数量等。
勾选确认条款点击Purchase,模板即开始部署。
部署开始后可以在Portal中看到部署任务的实时状态,在未完成前STATUS为Deploying状态,Outputs为空。
部署成功后,STATUS变为Succeeded,Outputs部分给出模板中Outputs部分给出的定义描述(可查看前面例子中Outputs部分的描述)。
总结及课题延展:
通过上述例子大家对Azure Resource Template应该已经有了一定的了解。有了这些了解大家可以着手开始自己的Infrastructure as Code的旅程,下面这些资源应该会帮到大家。
- Azure Resource Template使用指南,基本使用方法帮助快速上手:https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview
- Azure Resource Template 最佳实践,介绍Template定义的一些常用方法提高效率:https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-template-best-practices
- Azure Resource Template 高级函数使用指南,介绍Template内置函数帮助实现复杂逻辑:https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions?toc=%2fazure%2ftemplates%2ftoc.json&bc=%2Fazure%2Ftemplates%2Fbreadcrumb%2Ftoc.json
- Azure Resource Template 服务及资源原始模板参考,不知道每种服务及资源额如何定义描述时可作为字典参考:https://docs.microsoft.com/en-us/azure/templates/microsoft.analysisservices/servers
- Azure Resource Template Github,你想做的内容别人可能已经做了七八成,弯道超车搞起:https://github.com/Azure/azure-quickstart-templates
- Azure Resource Portal 查阅现有Resource的模板作为参考:https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-export-template
最后我们再来谈一谈延伸的话题,有不少小伙伴也许用过其他的云平台也有类似的模板工具,那么在多云场景下岂不是需要维护两套语言逻辑,这里给大家简单提一下Terraform,它是一个跨平台的模板语言工具,它可以适配如AWS,AZURE,Alicloud等云厂商提供的template接口,不仅仅如此,它还可以适配操作系统配置,应用配置等终端对象,其通过标准的模板描述语言抽象从而实现云平台无关以及更复杂的Infrastructure as Code,有兴趣的同学可以自行科学上网查询更多相关资料。好啦,准备好手头的工具开始自己的Infrastructure as Code之旅吧。