azuredeploy.json 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084
  1. {
  2. "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  3. "contentVersion": "1.0.0.0",
  4. "parameters": {
  5. "location": {
  6. "type": "string",
  7. "metadata": {
  8. "description": "All resources will be deployed here. This location should be the same as the virtual network location."
  9. }
  10. },
  11. "existingVirtualNetworkName": {
  12. "type": "string",
  13. "metadata": {
  14. "description": "Domain controller and new resources should be on same virtual network."
  15. }
  16. },
  17. "existingVirtualNetworkResourceGroupName": {
  18. "type": "string",
  19. "metadata": {
  20. "description": "Provide the resource group which contains Domain controller and virtual network. This can be different from the above resource group where the template resources like VMs are deployed."
  21. }
  22. },
  23. "availabilitySetName": {
  24. "type": "string",
  25. "metadata": {
  26. "description": "Creates a new availability set if it is not present. Check docs here: https://docs.microsoft.com/en-us/azure/virtual-machines/availability-set-overview"
  27. }
  28. },
  29. "VMNamesForPrimaryAndSecondaryReplicas": {
  30. "type": "Array",
  31. "metadata": {
  32. "description": "Maximum number of replicas including primary = 9 , Example syntax: [\"vm1\",\"vm2\",\"vm3\"]. If these VMs already exist, they should have been created by this template, else failover cluster can't be created."
  33. }
  34. },
  35. "subnetNames": {
  36. "type": "Array",
  37. "metadata": {
  38. "description": "Subnet names have to be distinct from one another and subnet list length must be equal to number of Replicas. Syntax example: [\"Subnet-1\",\"Subnet-2\",\"Subnet-3\"]. Docs: https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-manage-subnet#add-a-subnet"
  39. }
  40. },
  41. "listOfFailoverClusterIps": {
  42. "type": "Array",
  43. "metadata": {
  44. "description": "Provide list of IPs for failover cluster. Example syntax: [\"10.0.1.29\",\"10.0.2.29\",\"10.0.3.29\"]. Ensure these IPs are available from the respective Subnets. These IPs aren't required when using Windows server 2019 or later."
  45. }
  46. },
  47. "listOfListenerIps": {
  48. "type": "Array",
  49. "metadata": {
  50. "description": "Provide list of IPs for listener. Example syntax: [\"10.0.1.30\",\"10.0.2.30\",\"10.0.3.30\"]. Ensure these IPs are available from the respective Subnets."
  51. }
  52. },
  53. "SizeForVirtualMachines": {
  54. "type": "string",
  55. "metadata": {
  56. "description": "Please select size from the list mentioned here: https://docs.microsoft.com/en-us/azure/templates/microsoft.compute/virtualmachines?tabs=bicep#hardwareprofile. Note that the same size will be applied to all the replicas."
  57. }
  58. },
  59. "LocalAdminUserName": {
  60. "type": "string",
  61. "metadata": {
  62. "description": "This account will be used as a local admin for virtual machines. Eg: localadmin"
  63. }
  64. },
  65. "LocalAdminPassword": {
  66. "type": "securestring"
  67. },
  68. "SQLServerImageType": {
  69. "type": "string",
  70. "allowedValues": [
  71. "SQL2012SP4-WS2012R2",
  72. "SQL2012SP4-WS2012R2-BYOL",
  73. "SQL2014SP2-WS2012R2",
  74. "SQL2014SP2-WS2012R2-BYOL",
  75. "SQL2014SP3-WS2012R2",
  76. "SQL2014SP3-WS2012R2-BYOL",
  77. "SQL2016SP1-WS2016",
  78. "SQL2016SP1-WS2016-BYOL",
  79. "SQL2016SP2-WS2012R2",
  80. "SQL2016SP2-WS2016",
  81. "SQL2016SP2-WS2016-BYOL",
  82. "SQL2016SP2-WS2019",
  83. "SQL2016SP2-WS2019-BYOL",
  84. "SQL2016SP3-WS2019",
  85. "SQL2017-WS2016",
  86. "SQL2017-WS2016-BYOL",
  87. "SQL2017-WS2019",
  88. "SQL2017-WS2019-BYOL",
  89. "SQL2019-WS2019",
  90. "SQL2019-WS2019-BYOL",
  91. "SQL2019-WS2022"
  92. ],
  93. "metadata": {
  94. "description": "To use BYOL images, you need to have an Enterprise Agreement or Software Assurance (SA) for License mobility. https://docs.microsoft.com/en-us/azure/azure-sql/virtual-machines/windows/pricing-guidance?view=azuresql#byol"
  95. }
  96. },
  97. "SQLServerSku": {
  98. "type": "string",
  99. "allowedValues": [
  100. "Enterprise",
  101. "Developer"
  102. ],
  103. "metadata": {
  104. "description": "SQL Server Gallery Image SKU"
  105. }
  106. },
  107. "SQLServerLicenseType": {
  108. "type": "string",
  109. "allowedValues": [
  110. "PAYG",
  111. "AHUB"
  112. ],
  113. "metadata": {
  114. "description": "PAYG : Pay As You Go, AHUB : Azure Hybrid Use Benefits. You should use \"AHUB\" if BYOL SQL image was used. If you have SQL Server licenses with Software Assurance(SA) or a SQL Server subscription, use Azure HybridBenefit to save."
  115. }
  116. },
  117. "SQLServiceAccountUserName": {
  118. "type": "string",
  119. "metadata": {
  120. "description": "This account is used for managing SQL servers on VMs. Eg: sqlserviceaccount"
  121. }
  122. },
  123. "SQLServiceAccountPassword": {
  124. "type": "securestring"
  125. },
  126. "DomainUserName": {
  127. "type": "string",
  128. "metadata": {
  129. "description": "This account is used to create FCI name in Active directory and join VMs to Domain. Eg: DomainAdmin"
  130. }
  131. },
  132. "DomainUserPassword": {
  133. "type": "securestring"
  134. },
  135. "domainFQDN": {
  136. "type": "string",
  137. "metadata": {
  138. "description": "Provide existing Domain FQDN"
  139. }
  140. },
  141. "failoverClusterName": {
  142. "type": "string",
  143. "maxLength": 15,
  144. "metadata": {
  145. "description": "Specify the Windows Failover Cluster Name. Maximum length is 15"
  146. }
  147. },
  148. "createNewStorageAccount": {
  149. "type": "string",
  150. "allowedValues": [
  151. "Yes",
  152. "No"
  153. ],
  154. "metadata": {
  155. "description": "Creates new storage account for cloud witness."
  156. }
  157. },
  158. "storageAccountName": {
  159. "type": "string",
  160. "minLength": 3,
  161. "maxLength": 24,
  162. "metadata": {
  163. "description": "Enter existing storage account if you have chosen false in the above option. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only."
  164. }
  165. },
  166. "AvailabilityGroup": {
  167. "type": "string",
  168. "metadata": {
  169. "description": "Ensure there is no AG with same name registered with domain controller."
  170. }
  171. },
  172. "listenerName": {
  173. "type": "string",
  174. "maxLength": 15,
  175. "metadata": {
  176. "description": "Maximum length of AG Listener is 15. Ensure a listener with same name doesn't exist with domain controller."
  177. }
  178. }
  179. },
  180. "variables": {
  181. "shortDomain": "[toUpper(split(parameters('domainFQDN'), '.')[0])]",
  182. "validParameters": "[and( equals( length(parameters('VMNamesForPrimaryAndSecondaryReplicas')), length(parameters('subnetNames'))) , equals(length(parameters('VMNamesForPrimaryAndSecondaryReplicas')), length(parameters('listOfFailoverClusterIps')) ), equals(length(parameters('VMNamesForPrimaryAndSecondaryReplicas')), length(parameters('listOfListenerIps')) ) )]",
  183. "sqlImageArray": "[split(parameters('SQLServerImageType'),'-')]",
  184. "failoverSQLServerImageType": "[concat(substring(variables('sqlImageArray')[0],0,7),'-',variables('sqlImageArray')[1])]"
  185. },
  186. "resources": [
  187. {
  188. "condition": "[variables('validParameters')]",
  189. "type": "Microsoft.Compute/availabilitySets",
  190. "apiVersion": "2020-12-01",
  191. "name": "[parameters('availabilitySetName')]",
  192. "location": "[parameters('location')]",
  193. "tags": {},
  194. "sku": {
  195. "name": "Aligned"
  196. },
  197. "properties": {
  198. "platformUpdateDomainCount": "1",
  199. "platformFaultDomainCount": "1"
  200. }
  201. },
  202. {
  203. "condition": "[variables('validParameters')]",
  204. "type": "Microsoft.Resources/deployments",
  205. "apiVersion": "2021-04-01",
  206. "name": "[concat('create-sqlvm', '-', uniqueString(concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name)), '-', copyIndex())]",
  207. "dependsOn": [
  208. "[parameters('availabilitySetName')]"
  209. ],
  210. "copy": {
  211. "name": "Create-SQLVM",
  212. "count": "[length(parameters('VMNamesForPrimaryAndSecondaryReplicas'))]",
  213. "mode": "parallel"
  214. },
  215. "properties": {
  216. "expressionEvaluationOptions": {
  217. "scope": "inner"
  218. },
  219. "mode": "Incremental",
  220. "template": {
  221. "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  222. "contentVersion": "1.0.0.0",
  223. "parameters": {
  224. "availabilitySetName": {
  225. "type": "string"
  226. },
  227. "location": {
  228. "type": "string"
  229. },
  230. "networkInterfaceName": {
  231. "type": "string"
  232. },
  233. "enableAcceleratedNetworking": {
  234. "type": "bool"
  235. },
  236. "networkSecurityGroupName": {
  237. "type": "string"
  238. },
  239. "networkSecurityGroupRules": {
  240. "type": "array"
  241. },
  242. "subnetName": {
  243. "type": "string"
  244. },
  245. "virtualNetworkId": {
  246. "type": "string"
  247. },
  248. "publicIpAddressName": {
  249. "type": "string"
  250. },
  251. "publicIpAddressType": {
  252. "type": "string"
  253. },
  254. "publicIpAddressSku": {
  255. "type": "string"
  256. },
  257. "virtualMachineName": {
  258. "type": "string"
  259. },
  260. "virtualMachineComputerName": {
  261. "type": "string"
  262. },
  263. "osDiskType": {
  264. "type": "string"
  265. },
  266. "virtualMachineSize": {
  267. "type": "string"
  268. },
  269. "LocalAdminUserName": {
  270. "type": "string"
  271. },
  272. "LocalAdminPassword": {
  273. "type": "securestring"
  274. },
  275. "patchMode": {
  276. "type": "string"
  277. },
  278. "enableHotpatching": {
  279. "type": "bool"
  280. },
  281. "sqlVirtualMachineLocation": {
  282. "type": "string"
  283. },
  284. "sqlVirtualMachineName": {
  285. "type": "string"
  286. },
  287. "sqlServerImageType": {
  288. "type": "string"
  289. },
  290. "sqlServerLicenseType": {
  291. "type": "string"
  292. },
  293. "sqlServerSku": {
  294. "type": "string"
  295. },
  296. "sqlServerImagePublisher": {
  297. "type": "string"
  298. },
  299. "existingVirtualNetworkResourceGroupName": {
  300. "type": "string"
  301. }
  302. },
  303. "variables": {
  304. "nsgId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]",
  305. "vnetId": "[parameters('virtualNetworkId')]",
  306. "subnetRef": "[concat(variables('vnetId'), '/subnets/', parameters('subnetName'))]"
  307. },
  308. "resources": [
  309. {
  310. "name": "[parameters('networkInterfaceName')]",
  311. "type": "Microsoft.Network/networkInterfaces",
  312. "apiVersion": "2021-03-01",
  313. "location": "[parameters('location')]",
  314. "dependsOn": [
  315. "[concat('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]",
  316. "[concat('Microsoft.Network/publicIpAddresses/', parameters('publicIpAddressName'))]"
  317. ],
  318. "properties": {
  319. "ipConfigurations": [
  320. {
  321. "name": "ipconfig1",
  322. "properties": {
  323. "subnet": {
  324. "id": "[variables('subnetRef')]"
  325. },
  326. "privateIPAllocationMethod": "Dynamic",
  327. "publicIpAddress": "[json('null')]"
  328. }
  329. }
  330. ],
  331. "enableAcceleratedNetworking": "[parameters('enableAcceleratedNetworking')]",
  332. "networkSecurityGroup": {
  333. "id": "[variables('nsgId')]"
  334. }
  335. }
  336. },
  337. {
  338. "name": "[parameters('networkSecurityGroupName')]",
  339. "type": "Microsoft.Network/networkSecurityGroups",
  340. "apiVersion": "2019-02-01",
  341. "location": "[parameters('location')]",
  342. "properties": {
  343. "securityRules": "[parameters('networkSecurityGroupRules')]"
  344. }
  345. },
  346. {
  347. "name": "[parameters('publicIpAddressName')]",
  348. "type": "Microsoft.Network/publicIpAddresses",
  349. "apiVersion": "2019-02-01",
  350. "location": "[parameters('location')]",
  351. "properties": {
  352. "publicIpAllocationMethod": "[parameters('publicIpAddressType')]"
  353. },
  354. "sku": {
  355. "name": "[parameters('publicIpAddressSku')]"
  356. }
  357. },
  358. {
  359. "name": "[parameters('virtualMachineName')]",
  360. "type": "Microsoft.Compute/virtualMachines",
  361. "apiVersion": "2021-07-01",
  362. "location": "[parameters('location')]",
  363. "dependsOn": [
  364. "[concat('Microsoft.Network/networkInterfaces/', parameters('networkInterfaceName'))]"
  365. ],
  366. "properties": {
  367. "hardwareProfile": {
  368. "vmSize": "[parameters('virtualMachineSize')]"
  369. },
  370. "storageProfile": {
  371. "osDisk": {
  372. "createOption": "fromImage",
  373. "managedDisk": {
  374. "storageAccountType": "[parameters('osDiskType')]"
  375. }
  376. },
  377. "imageReference": {
  378. "publisher": "[parameters('sqlServerImagePublisher')]",
  379. "offer": "[parameters('sqlServerImageType')]",
  380. "sku": "[parameters('sqlServerSku')]",
  381. "version": "latest"
  382. }
  383. },
  384. "networkProfile": {
  385. "networkInterfaces": [
  386. {
  387. "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaceName'))]"
  388. }
  389. ]
  390. },
  391. "availabilitySet": {
  392. "id": "[resourceId('Microsoft.Compute/availabilitySets', parameters('availabilitySetName'))]"
  393. },
  394. "osProfile": {
  395. "computerName": "[parameters('virtualMachineComputerName')]",
  396. "adminUsername": "[parameters('LocalAdminUserName')]",
  397. "adminPassword": "[parameters('LocalAdminPassword')]",
  398. "windowsConfiguration": {
  399. "enableAutomaticUpdates": true,
  400. "provisionVmAgent": true,
  401. "patchSettings": {
  402. "enableHotpatching": "[parameters('enableHotpatching')]",
  403. "patchMode": "[parameters('patchMode')]"
  404. }
  405. }
  406. },
  407. "diagnosticsProfile": {
  408. "bootDiagnostics": {
  409. "enabled": true
  410. }
  411. }
  412. }
  413. }
  414. ],
  415. "outputs": {
  416. "adminUsername": {
  417. "type": "string",
  418. "value": "[parameters('LocalAdminUserName')]"
  419. }
  420. }
  421. },
  422. "parameters": {
  423. "availabilitySetName": {
  424. "value": "[parameters('availabilitySetName')]"
  425. },
  426. "sqlServerSku": {
  427. "value": "[if(equals(parameters('SQLServerSku'), 'Developer'), 'sqldev', parameters('SQLServerSku'))]"
  428. },
  429. "sqlServerImagePublisher": {
  430. "value": "MicrosoftSQLServer"
  431. },
  432. "sqlServerImageType": {
  433. "value": "[parameters('SQLServerImageType')]"
  434. },
  435. "sqlServerLicenseType": {
  436. "value": "[parameters('SQLServerLicenseType')]"
  437. },
  438. "location": {
  439. "value": "[parameters('location')]"
  440. },
  441. "networkInterfaceName": {
  442. "value": "[concat(parameters('VMNamesForPrimaryAndSecondaryReplicas')[copyIndex()],'-ni')]"
  443. },
  444. "enableAcceleratedNetworking": {
  445. "value": true
  446. },
  447. "networkSecurityGroupName": {
  448. "value": "[concat(parameters('VMNamesForPrimaryAndSecondaryReplicas')[copyIndex()],'-nsg')]"
  449. },
  450. "networkSecurityGroupRules": {
  451. "value": []
  452. },
  453. "subnetName": {
  454. "value": "[parameters('subnetNames')[copyIndex()]]"
  455. },
  456. "virtualNetworkId": {
  457. "value": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('existingVirtualNetworkResourceGroupName'), '/providers/Microsoft.Network/virtualNetworks/', parameters('existingVirtualNetworkName') )]"
  458. },
  459. "publicIpAddressName": {
  460. "value": "[concat(parameters('VMNamesForPrimaryAndSecondaryReplicas')[copyIndex()],'-ip')]"
  461. },
  462. "publicIpAddressType": {
  463. "value": "Static"
  464. },
  465. "publicIpAddressSku": {
  466. "value": "Standard"
  467. },
  468. "virtualMachineName": {
  469. "value": "[parameters('VMNamesForPrimaryAndSecondaryReplicas')[copyIndex()]]"
  470. },
  471. "virtualMachineComputerName": {
  472. "value": "[parameters('VMNamesForPrimaryAndSecondaryReplicas')[copyIndex()]]"
  473. },
  474. "osDiskType": {
  475. "value": "Premium_LRS"
  476. },
  477. "virtualMachineSize": {
  478. "value": "[parameters('SizeForVirtualMachines')]"
  479. },
  480. "LocalAdminUserName": {
  481. "value": "[parameters('LocalAdminUserName')]"
  482. },
  483. "LocalAdminPassword": {
  484. "value": "[parameters('LocalAdminPassword')]"
  485. },
  486. "patchMode": {
  487. "value": "AutomaticByOS"
  488. },
  489. "enableHotpatching": {
  490. "value": false
  491. },
  492. "sqlVirtualMachineLocation": {
  493. "value": "[parameters('location')]"
  494. },
  495. "sqlVirtualMachineName": {
  496. "value": "[parameters('VMNamesForPrimaryAndSecondaryReplicas')[copyIndex()]]"
  497. },
  498. "existingVirtualNetworkResourceGroupName": {
  499. "value": "[parameters('existingVirtualNetworkResourceGroupName')]"
  500. }
  501. }
  502. }
  503. },
  504. {
  505. "condition": "[variables('validParameters')]",
  506. "type": "Microsoft.Resources/deployments",
  507. "apiVersion": "2021-04-01",
  508. "name": "Join-the-domain",
  509. "dependsOn": [
  510. "Create-SQLVM"
  511. ],
  512. "properties": {
  513. "expressionEvaluationOptions": {
  514. "scope": "inner"
  515. },
  516. "mode": "Incremental",
  517. "template": {
  518. "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  519. "contentVersion": "1.0.0.0",
  520. "parameters": {
  521. "vmArray": {
  522. "type": "Array",
  523. "metadata": {
  524. "description": "Array of virtual machines to be domain joined, if using multiple VMs. E.g. ['VM01', 'VM02', 'VM03']."
  525. }
  526. },
  527. "location": {
  528. "type": "string",
  529. "metadata": {
  530. "description": "Location name of the virtual machine"
  531. }
  532. },
  533. "domainJoinUserName": {
  534. "type": "string",
  535. "metadata": {
  536. "description": "Domain NetBiosName plus User name of a domain user with sufficient rights to perfom domain join operation. E.g. domain\\username"
  537. }
  538. },
  539. "domainJoinUserPassword": {
  540. "type": "securestring",
  541. "metadata": {
  542. "description": "Domain user password"
  543. }
  544. },
  545. "domainFQDN": {
  546. "type": "string",
  547. "metadata": {
  548. "description": "Domain FQDN where the virtual machine will be joined"
  549. }
  550. },
  551. "ouPath": {
  552. "type": "string",
  553. "defaultValue": "",
  554. "metadata": {
  555. "description": "Specifies an organizational unit (OU) for the domain account. Enter the full distinguished name of the OU in quotation marks. Example: \"OU=testOU; DC=domain; DC=Domain; DC=com\""
  556. }
  557. }
  558. },
  559. "variables": {
  560. "domainJoinOptions": 3
  561. },
  562. "resources": [
  563. {
  564. "comments": "Join domain - JsonADDomainExtension",
  565. "apiVersion": "2015-06-15",
  566. "type": "Microsoft.Compute/virtualMachines/extensions",
  567. "name": "[concat(trim(parameters('vmArray')[copyIndex()]),'/joindomain')]",
  568. "location": "[parameters('location')]",
  569. "copy": {
  570. "name": "vmDomainJoinCopy",
  571. "count": "[length(parameters('vmArray'))]"
  572. },
  573. "properties": {
  574. "publisher": "Microsoft.Compute",
  575. "type": "JsonADDomainExtension",
  576. "typeHandlerVersion": "1.3",
  577. "autoUpgradeMinorVersion": true,
  578. "settings": {
  579. "Name": "[parameters('domainFQDN')]",
  580. "User": "[parameters('domainJoinUserName')]",
  581. "Restart": "true",
  582. "Options": "[variables('domainJoinOptions')]",
  583. "OUPath": "[parameters('ouPath')]"
  584. },
  585. "protectedSettings": {
  586. "Password": "[parameters('domainJoinUserPassword')]"
  587. }
  588. }
  589. }
  590. ]
  591. },
  592. "parameters": {
  593. "location": {
  594. "value": "[parameters('location')]"
  595. },
  596. "vmArray": {
  597. "value": "[parameters('VMNamesForPrimaryAndSecondaryReplicas')]"
  598. },
  599. "domainJoinUserName": {
  600. "value": "[concat(variables('shortDomain'),'\\',parameters('DomainUserName'))]"
  601. },
  602. "domainJoinUserPassword": {
  603. "value": "[parameters('DomainUserPassword')]"
  604. },
  605. "domainFQDN": {
  606. "value": "[parameters('domainFQDN')]"
  607. }
  608. }
  609. }
  610. },
  611. {
  612. "condition": "[variables('validParameters')]",
  613. "type": "Microsoft.Resources/deployments",
  614. "apiVersion": "2021-04-01",
  615. "name": "Failover-Cluster",
  616. "dependsOn": [
  617. "[resourceId('Microsoft.Resources/deployments/', 'Join-the-domain')]"
  618. ],
  619. "properties": {
  620. "expressionEvaluationOptions": {
  621. "scope": "inner"
  622. },
  623. "mode": "Incremental",
  624. "template": {
  625. "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  626. "contentVersion": "1.0.0.0",
  627. "parameters": {
  628. "failoverClusterName": {
  629. "type": "string",
  630. "maxLength": 15,
  631. "metadata": {
  632. "description": "Specify the Windows Failover Cluster Name"
  633. }
  634. },
  635. "failoverClusterIpArray": {
  636. "type": "Array"
  637. },
  638. "existingVmArray": {
  639. "type": "Array",
  640. "metadata": {
  641. "description": "Specify array of names of SQL Server VM's to participate in the Availability Group (e.g. ['SQLVM1', 'SQLVM2'] ). OS underneath should be at least WS 2016."
  642. }
  643. },
  644. "sqlServerLicenseType": {
  645. "allowedValues": [
  646. "PAYG",
  647. "AHUB"
  648. ],
  649. "type": "string",
  650. "metadata": {
  651. "description": "Specify the SQL Server License type for all VM's."
  652. }
  653. },
  654. "existingVmResourceGroup": {
  655. "type": "string",
  656. "metadata": {
  657. "description": "Specify resourcegroup name for existing Vms."
  658. },
  659. "defaultValue": "[resourceGroup().name]"
  660. },
  661. "sqlServerImageType": {
  662. "allowedValues": [
  663. "SQL2012-WS2012R2",
  664. "SQL2014-WS2012R2",
  665. "SQL2016-WS2012R2",
  666. "SQL2016-WS2016",
  667. "SQL2016-WS2019",
  668. "SQL2017-WS2016",
  669. "SQL2017-WS2019",
  670. "SQL2019-WS2019",
  671. "SQL2019-WS2022"
  672. ],
  673. "type": "string",
  674. "metadata": {
  675. "description": "Select the version of SQL Server Image type"
  676. }
  677. },
  678. "existingFullyQualifiedDomainName": {
  679. "type": "string",
  680. "metadata": {
  681. "description": "Specify the Fully Qualified Domain Name under which the Failover Cluster will be created. The VM's should already be joined to it. (e.g. contoso.com)"
  682. }
  683. },
  684. "existingOuPath": {
  685. "type": "string",
  686. "metadata": {
  687. "description": "Specify an optional Organizational Unit (OU) on AD Domain where the CNO (Computer Object for Cluster Name) will be created (e.g. OU=testou,OU=testou2,DC=contoso,DC=com). Default is empty."
  688. },
  689. "defaultValue": ""
  690. },
  691. "existingSqlServiceAccount": {
  692. "type": "string",
  693. "metadata": {
  694. "description": "Specify the domain account under which SQL Server service will run for AG setup in UPN format (e.g. [email protected])"
  695. }
  696. },
  697. "sqlServicePassword": {
  698. "type": "securestring",
  699. "metadata": {
  700. "description": "Specify the password for Sql Server service account"
  701. }
  702. },
  703. "cloudWitnessName": {
  704. "type": "string",
  705. "metadata": {
  706. "description": "Specify the name of the storage account to be used for creating Cloud Witness for Windows server failover cluster"
  707. },
  708. "defaultValue": "[concat('clwitness', uniqueString(resourceGroup().id))]"
  709. },
  710. "_artifactsLocation": {
  711. "type": "string",
  712. "metadata": {
  713. "description": "Location of resources that the script is dependent on such as linked templates and DSC modules"
  714. },
  715. "defaultValue": "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.sqlvirtualmachine/sql-vm-ag-setup/"
  716. },
  717. "_artifactsLocationSasToken": {
  718. "type": "securestring",
  719. "metadata": {
  720. "description": "The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated."
  721. },
  722. "defaultValue": ""
  723. },
  724. "location": {
  725. "type": "string",
  726. "metadata": {
  727. "description": "Location for all resources."
  728. }
  729. },
  730. "ClusterBootstrapAccount": {
  731. "type": "string",
  732. "metadata": {
  733. "description": "Specify the account for WS failover cluster creation in UPN format (e.g. [email protected]). This account can either be a Domain Admin or at least have permissions to create Computer Objects in default or specified OU."
  734. }
  735. },
  736. "ClusterBootstrapAccountPassword": {
  737. "type": "securestring"
  738. },
  739. "createNewStorageAccount": {
  740. "type": "bool"
  741. }
  742. },
  743. "variables": {
  744. "GroupResourceId": "[resourceId('Microsoft.SqlVirtualMachine/SqlVirtualMachineGroups', parameters('failoverClusterName'))]"
  745. },
  746. "resources": [
  747. {
  748. "condition": "[parameters('createNewStorageAccount')]",
  749. "type": "Microsoft.Storage/storageAccounts",
  750. "apiVersion": "2018-07-01",
  751. "name": "[parameters('cloudWitnessName')]",
  752. "sku": {
  753. "name": "Standard_LRS"
  754. },
  755. "kind": "StorageV2",
  756. "location": "[parameters('location')]",
  757. "properties": {
  758. "accessTier": "Hot",
  759. "supportsHttpsTrafficOnly": true
  760. }
  761. },
  762. {
  763. "type": "Microsoft.SqlVirtualMachine/SqlVirtualMachineGroups",
  764. "apiVersion": "2022-02-01",
  765. "name": "[parameters('failoverClusterName')]",
  766. "location": "[parameters('location')]",
  767. "properties": {
  768. "SqlImageOffer": "[parameters('sqlServerImageType')]",
  769. "SqlImageSku": "Enterprise",
  770. "WsfcDomainProfile": {
  771. "DomainFqdn": "[parameters('existingFullyQualifiedDomainName')]",
  772. "OuPath": "[parameters('existingOuPath')]",
  773. "ClusterBootstrapAccount": "[parameters('ClusterBootstrapAccount')]",
  774. "ClusterOperatorAccount": "[parameters('ClusterBootstrapAccount')]",
  775. "SqlServiceAccount": "[parameters('existingSqlServiceAccount')]",
  776. "StorageAccountUrl": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('cloudWitnessName')), '2018-07-01').primaryEndpoints['blob']]",
  777. "StorageAccountPrimaryKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('cloudWitnessName')), '2018-07-01').keys[0].value]",
  778. "clusterSubnetType": "multisubnet"
  779. }
  780. }
  781. },
  782. {
  783. "type": "Microsoft.Resources/deployments",
  784. "apiVersion": "2019-03-01",
  785. "name": "joincluster",
  786. "dependsOn": [
  787. "[parameters('failoverClusterName')]",
  788. "[parameters('cloudWitnessName')]"
  789. ],
  790. "properties": {
  791. "expressionEvaluationOptions": {
  792. "scope": "inner"
  793. },
  794. "mode": "Incremental",
  795. "template": {
  796. "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  797. "contentVersion": "1.0.0.0",
  798. "parameters": {
  799. "location": {
  800. "type": "string"
  801. },
  802. "existingVirtualMachineNames": {
  803. "type": "array"
  804. },
  805. "sqlServerLicenseType": {
  806. "type": "string"
  807. },
  808. "existingVmResourceGroup": {
  809. "type": "string"
  810. },
  811. "groupResourceId": {
  812. "type": "string"
  813. },
  814. "sqlServicePassword": {
  815. "type": "securestring"
  816. },
  817. "failoverClusterIpArray": {
  818. "type": "Array"
  819. },
  820. "ClusterBootstrapAccountPassword": {
  821. "type": "securestring"
  822. }
  823. },
  824. "variables": {},
  825. "resources": [
  826. {
  827. "name": "[trim(parameters('existingVirtualMachineNames')[copyIndex()])]",
  828. "type": "Microsoft.SqlVirtualMachine/SqlVirtualMachines",
  829. "apiVersion": "2022-02-01",
  830. "location": "[parameters('location')]",
  831. "copy": {
  832. "name": "vmToClusterLoop",
  833. "count": "[length(parameters('existingVirtualMachineNames'))]"
  834. },
  835. "properties": {
  836. "virtualMachineResourceId": "[resourceId(parameters('existingVmResourceGroup'),'Microsoft.Compute/virtualMachines', trim(parameters('existingVirtualMachineNames')[copyIndex()]))]",
  837. "sqlServerLicenseType": "[parameters('sqlServerLicenseType')]",
  838. "SqlVirtualMachineGroupResourceId": "[parameters('groupResourceId')]",
  839. "WSFCDomainCredentials": {
  840. "ClusterBootstrapAccountPassword": "[parameters('ClusterBootstrapAccountPassword')]",
  841. "ClusterOperatorAccountPassword": "[parameters('ClusterBootstrapAccountPassword')]",
  842. "SqlServiceAccountPassword": "[parameters('sqlServicePassword')]"
  843. },
  844. "wsfcStaticIp": "[parameters('failoverClusterIpArray')[copyIndex()]]"
  845. }
  846. }
  847. ]
  848. },
  849. "parameters": {
  850. "existingVirtualMachineNames": {
  851. "value": "[parameters('existingVmArray')]"
  852. },
  853. "location": {
  854. "value": "[parameters('location')]"
  855. },
  856. "sqlServerLicenseType": {
  857. "value": "[parameters('sqlServerLicenseType')]"
  858. },
  859. "existingVmResourceGroup": {
  860. "value": "[parameters('existingVmResourceGroup')]"
  861. },
  862. "groupResourceId": {
  863. "value": "[variables('groupResourceId')]"
  864. },
  865. "sqlServicePassword": {
  866. "value": "[parameters('sqlServicePassword')]"
  867. },
  868. "failoverClusterIpArray": {
  869. "value": "[parameters('failoverClusterIpArray')]"
  870. },
  871. "ClusterBootstrapAccountPassword": {
  872. "value": "[parameters('ClusterBootstrapAccountPassword')]"
  873. }
  874. }
  875. }
  876. }
  877. ]
  878. },
  879. "parameters": {
  880. "location": {
  881. "value": "[parameters('location')]"
  882. },
  883. "failoverClusterName": {
  884. "value": "[parameters('failoverClusterName')]"
  885. },
  886. "failoverClusterIpArray": {
  887. "value": "[parameters('listOfFailoverClusterIps')]"
  888. },
  889. "existingVmArray": {
  890. "value": "[parameters('VMNamesForPrimaryAndSecondaryReplicas')]"
  891. },
  892. "sqlServerLicenseType": {
  893. "value": "[parameters('SQLServerLicenseType')]"
  894. },
  895. "sqlServerImageType": {
  896. "value": "[variables('failoverSQLServerImageType')]"
  897. },
  898. "existingFullyQualifiedDomainName": {
  899. "value": "[parameters('domainFQDN')]"
  900. },
  901. "existingSqlServiceAccount": {
  902. "value": "[concat(parameters('SQLServiceAccountUserName'),'@',parameters('domainFQDN'))]"
  903. },
  904. "sqlServicePassword": {
  905. "value": "[parameters('SQLServiceAccountPassword')]"
  906. },
  907. "ClusterBootstrapAccount": {
  908. "value": "[concat(parameters('DomainUserName'),'@',parameters('domainFQDN'))]"
  909. },
  910. "ClusterBootstrapAccountPassword": {
  911. "value": "[parameters('DomainUserPassword')]"
  912. },
  913. "cloudWitnessName": {
  914. "value": "[parameters('storageAccountName')]"
  915. },
  916. "createNewStorageAccount": {
  917. "value": "[if(equals(parameters('createNewStorageAccount'), 'Yes'), true(), false())]"
  918. },
  919. "existingVmResourceGroup": {
  920. "value": "[resourceGroup().name]"
  921. }
  922. }
  923. }
  924. },
  925. {
  926. "condition": "[variables('validParameters')]",
  927. "type": "Microsoft.Resources/deployments",
  928. "apiVersion": "2021-04-01",
  929. "name": "AG-Listener",
  930. "dependsOn": [
  931. "[resourceId('Microsoft.Resources/deployments/', 'Failover-Cluster' )]"
  932. ],
  933. "properties": {
  934. "expressionEvaluationOptions": {
  935. "scope": "inner"
  936. },
  937. "mode": "Incremental",
  938. "template": {
  939. "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  940. "contentVersion": "1.0.0.0",
  941. "parameters": {
  942. "existingFailoverClusterName": {
  943. "type": "string",
  944. "metadata": {
  945. "description": "Specify the name of the failover cluster"
  946. }
  947. },
  948. "existingVmArray": {
  949. "type": "Array",
  950. "metadata": {
  951. "description": "Specify the array of Virtual machines participating in SQL Availability Group e.g. ['VM1', 'VM2']. Maximum number is 9."
  952. }
  953. },
  954. "Listener": {
  955. "type": "string",
  956. "metadata": {
  957. "description": "Specify a name for the listener for SQL Availability Group"
  958. },
  959. "defaultValue": "aglistener"
  960. },
  961. "ListenerPort": {
  962. "type": "Int",
  963. "metadata": {
  964. "description": "Specify the port for listener"
  965. },
  966. "defaultValue": 1433
  967. },
  968. "ListenerIpArray": {
  969. "type": "Array",
  970. "metadata": {
  971. "description": "Specify the available private IP address for the listener from all the subnets."
  972. }
  973. },
  974. "existingVirtualNetworkResourceGroupName": {
  975. "defaultValue": "[resourcegroup().name]",
  976. "type": "string",
  977. "metadata": {
  978. "description": "Specify the resourcegroup for virtual network"
  979. }
  980. },
  981. "existingVnet": {
  982. "type": "string",
  983. "metadata": {
  984. "description": "Specify the virtual network for Listener IP Address"
  985. }
  986. },
  987. "existingSubnetArray": {
  988. "type": "Array",
  989. "metadata": {
  990. "description": "Provide the subnet array"
  991. }
  992. },
  993. "Location": {
  994. "type": "string",
  995. "metadata": {
  996. "description": "Location for all resources."
  997. }
  998. },
  999. "sqlAvailabilityGroup": {
  1000. "type": "string",
  1001. "defaultValue": "sqlAG"
  1002. }
  1003. },
  1004. "variables": {},
  1005. "resources": [
  1006. {
  1007. "type": "Microsoft.SqlVirtualMachine/SqlVirtualMachineGroups/availabilityGroupListeners",
  1008. "name": "[concat(parameters('existingFailoverClusterName'), '/', parameters('Listener'))]",
  1009. "apiVersion": "2022-02-01",
  1010. "location": "[parameters('Location')]",
  1011. "properties": {
  1012. "AvailabilityGroupName": "[parameters('sqlAvailabilityGroup')]",
  1013. "AvailabilityGroupConfiguration": {
  1014. "copy": [
  1015. {
  1016. "name": "Replicas",
  1017. "count": "[length(parameters('existingVmArray'))]",
  1018. "input": {
  1019. "sqlVirtualMachineInstanceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.SqlVirtualMachine/sqlVirtualMachines/', parameters('existingVmArray')[copyIndex('Replicas')] )]",
  1020. "role": "[if(equals(copyIndex('Replicas'),0), 'Primary', 'Secondary')]",
  1021. "failover": "[if(less(copyIndex('Replicas'),2), 'Automatic', 'Manual')]",
  1022. "commit": "[if(less(copyIndex('Replicas'),2), 'Synchronous_Commit', 'Asynchronous_Commit')]",
  1023. "readableSecondary": "All"
  1024. }
  1025. }
  1026. ]
  1027. },
  1028. "copy": [
  1029. {
  1030. "name": "multiSubnetIpConfigurations",
  1031. "count": "[length(parameters('existingVmArray'))]",
  1032. "input": {
  1033. "privateIpAddress": {
  1034. "ipAddress": "[parameters('ListenerIpArray')[copyIndex('multiSubnetIpConfigurations')]]",
  1035. "subnetResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('existingVirtualNetworkResourceGroupName'), '/providers/Microsoft.Network/virtualNetworks/', parameters('existingVnet'), '/subnets/', parameters('existingSubnetArray')[copyIndex('multiSubnetIpConfigurations')] )]"
  1036. },
  1037. "sqlVirtualMachineInstance": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.SqlVirtualMachine/sqlVirtualMachines/', parameters('existingVmArray')[copyIndex('multiSubnetIpConfigurations')] )]"
  1038. }
  1039. }
  1040. ],
  1041. "Port": "[parameters('ListenerPort')]"
  1042. }
  1043. }
  1044. ]
  1045. },
  1046. "parameters": {
  1047. "existingFailoverClusterName": {
  1048. "value": "[parameters('failoverClusterName')]"
  1049. },
  1050. "existingVmArray": {
  1051. "value": "[parameters('VMNamesForPrimaryAndSecondaryReplicas')]"
  1052. },
  1053. "existingVnet": {
  1054. "value": "[parameters('existingVirtualNetworkName')]"
  1055. },
  1056. "existingSubnetArray": {
  1057. "value": "[parameters('subnetNames')]"
  1058. },
  1059. "ListenerIpArray": {
  1060. "value": "[parameters('listOfListenerIps')]"
  1061. },
  1062. "Listener": {
  1063. "value": "[parameters('listenerName')]"
  1064. },
  1065. "sqlAvailabilityGroup": {
  1066. "value": "[parameters('AvailabilityGroup')]"
  1067. },
  1068. "location": {
  1069. "value": "[parameters('location')]"
  1070. },
  1071. "existingVirtualNetworkResourceGroupName": {
  1072. "value": "[parameters('existingVirtualNetworkResourceGroupName')]"
  1073. }
  1074. }
  1075. }
  1076. }
  1077. ],
  1078. "outputs": {
  1079. "errors": {
  1080. "type": "string",
  1081. "value": "[if(variables('validParameters'), '', 'Array size of subnetNames, listOfFailoverClusterIps, listOfListenerIps, VMNamesForPrimaryAndSecondaryReplicas should be equal')]"
  1082. }
  1083. }
  1084. }