{"id":78393,"date":"2018-04-21T02:21:04","date_gmt":"2018-04-21T02:21:04","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=78393"},"modified":"2021-04-27T13:55:25","modified_gmt":"2021-04-27T13:55:25","slug":"create-azure-vms-powershell-part-1","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/create-azure-vms-powershell-part-1\/","title":{"rendered":"Create Azure VMs with PowerShell Part 1"},"content":{"rendered":"<p>Virtualization has become a great boon to the IT world. Organizations can now quickly set up and tear down new servers. Azure has taken this to the next level, making it easy to spin up a new Azure VM (Virtual Machine) for your needs, then just as easily turn it off, or even delete it.<\/p>\n<p>Automation is key to the success of a good virtual machine strategy. Naturally PowerShell is the tool of choice for automating Azure tasks. In this, the first of a two-part series, you\u2019ll learn how to use PowerShell to automate the creation of virtual machines in Azure.<\/p>\n<h2>Configuring Your Computer<\/h2>\n<p>First, you\u2019ll need to make sure your computer is configured correctly. The module was developed under PowerShell 5.1, using the AzureRM module version 5.7.0. To determine your current version of PowerShell, simply run the command <strong>$PSVersionTable<\/strong>. It will return the current version on your machine, here is the output from my computer:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"653\" height=\"366\" class=\"wp-image-78394\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-170.png\" \/><\/p>\n<p>It is possible to upgrade your version of PowerShell to the latest version. See the <a href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/scripting\/install\/installing-powershell?view=powershell-7\">instructions<\/a> if you are not currently on version 5.1. It\u2019s a free install although you will need administrator rights on your computer.<\/p>\n<p>To interact with Azure, PowerShell has a module named AzureRM. It\u2019s not installed by default however, so first you need to verify if you have the AzureRM module installed, and if so what version. To do so you can use the <strong>Get-Module<\/strong> cmdlet in PowerShell.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-Module AzureRM -ListAvailable <\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"377\" height=\"222\" class=\"wp-image-78395\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-171.png\" \/><\/p>\n<p>As you can see there are multiple versions installed on this computer, by default PowerShell will take the latest version, here 5.7.0. If it doesn\u2019t return any data, you can use the PackageManagement module introduced in PowerShell 5. You can import the module manually using:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Import-Module PackageManagement<\/pre>\n<p>It\u2019s not necessary though, a feature introduced with PowerShell 4 is the autoloading of modules. The first time you call a command from it, it will import the module automatically.<\/p>\n<p>If the <strong>Get-Module<\/strong> cmdlet did not return any versions of AzureRM, you can use PackageManagement\u2019s <strong>Install-Module<\/strong> cmdlet. It will download, then install the module for you from the internet.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Install-Module AzureRM -Force -AllowClobber <\/pre>\n<p>There is an older version of the Azure module (Azure vs AzureRM) that may be installed in your system. It has similar names, so the AllowClobber switch is necessary to let them co-exist on the same computer. It\u2019s heavily suggested that you uninstall the older Azure module as Microsoft is deprecating its features in favour of AzureRM.<\/p>\n<p>If you have previously installed the AzureRM module through PackageManagement but are not on the current version (5.7.0 as this is written), you can use the following cmdlet to update:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Update-Module AzureRM -Force<\/pre>\n<p>After updating you should reboot your computer to ensure everything gets set correctly. Additionally, always test carefully after updating; in the past Microsoft has released breaking changes in the AzureRM module.<\/p>\n<p>Many of the concepts in the PSAzure module are based on my first article, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/powershell-functions-reusability-restartability-azure\/\">PowerShell Functions for Reusablity and Restartability in Azure<\/a>. I suggest you read it before proceeding with the rest of this article.<\/p>\n<h2>Installing PSAzure Module<\/h2>\n<p>Because I\u2019ve been doing so much in Azure, I\u2019ve created a module to aid in that work. The module is named PSAzure, and you can find the <a href=\"https:\/\/github.com\/arcanecode\/PowerShell\/tree\/master\/PSAzure_Module\">full module<\/a>, along with documentation and examples on GitHub.<\/p>\n<p>To get the most from this article, you will want to download the files and install the module. While the files are loaded individually in GitHub, where you can view them without the need to download and install, there is also a ZIP file with everything in it. You can just download and unzip the file to your local machine. The examples assume you have downloaded to a folder called C:\\PowerShell, although a variable will let you change this easily. Your folder structure should look like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"232\" height=\"152\" class=\"wp-image-78396\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/c-users-arcan-appdata-local-temp-snaghtmldebd6c-p.png\" alt=\"C:\\Users\\arcan\\AppData\\Local\\Temp\\SNAGHTMLdebd6c.PNG\" \/><\/p>\n<p>We need to detour here just a moment to cover security policies. PowerShell has different policies that dictate whether scripts can run or not. To see what your policy is, use the cmdlet <strong>Get-ExecutionPolicy<\/strong>.<\/p>\n<p>There are several values that may be returned. The most common setting is <em>RemoteSigned<\/em>. With this setting, scripts you develop locally are considered \u2018safe\u2019 and will run. However, if PowerShell sees you have a script downloaded from the internet, such as the ones in the PSAzure module, it would require them to be <em>signed<\/em>. To sign a script means using a security certificate which creates a cryptographic hash that is appended to a script. If any change is made to the script, the hash will no longer match the script and PowerShell will refuse to run it.<\/p>\n<p>This presents a problem for us though, as this project was intended not just as a helpful tool but a learning exercise. You must to be able to change the code as you experiment and learn. To solve this, you\u2019ll need to unlock the files. The <strong>Unblock-File<\/strong> cmdlet can achieve this, it changes the settings on the downloaded files to mark them as \u2018local\u2019 so PowerShell will now be able to execute them under the <em>RemoteSigned<\/em> setting. There is a script in the PSAzure-Deploy folder called <em>Unblock-PSAzureModule.ps1<\/em> that has instructions on how to unblock, in brief you would simply copy the code below into a new ps1 file and run it. Since it\u2019s a new file, PowerShell will understand it to be local and run it. Alternatively, you could just copy these lines, one at a time, into the command window at the bottom of the ISE and run.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  $powershellScripts = 'C:\\PowerShell'\r\n  Unblock-File -Path \"$powershellScripts\\PSAzure-Module\\PSAzure\\*.*\"\r\n  Unblock-File -Path \"$powershellScripts\\PSAzure-Module\\PSAzure-Deploy\\*.*\"\r\n  Unblock-File -Path \"$powershellScripts\\PSAzure-Module\\PSAzure-Examples\\*.*\"<\/pre>\n<p>There are a few other security settings you may encounter. <em>Unrestricted<\/em> and <em>Bypass<\/em> will execute a downloaded script but will constantly prompt you. Thus, you\u2019d still want to unblock as described above.<\/p>\n<p>The other two settings are <em>AllSigned<\/em> and <em>Restricted<\/em>. As the name implies, <em>AllSigned<\/em> requires all scripts to be signed. This setting would be more commonly found on a corporate server than on a home computer. <em>Restricted<\/em> means scripts won\u2019t run, period. To change the setting, you can use the <strong>Set-ExecutionPolicy<\/strong> cmdlet, like so.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Set-ExecutionPolicy -ExecutionPolicy RemoteSigned <\/pre>\n<p>Note that you will have to run the PowerShell ISE in administrator mode, and you must have administrator rights on your computer. Some corporations may have group policies that prevent changing this setting, if this is your situation you will need to contact your system administrator for more help.<\/p>\n<p>For more information on PowerShell\u2019s execution policies and how to change them, see the <a href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/Microsoft.PowerShell.Security\/set-executionpolicy?view=powershell-5.1\">online help<\/a> for <strong>Set-ExecutionPolicy<\/strong>. It explains the various policies and how you can view and change your policy.<\/p>\n<p>Once you have the correct version of PowerShell, installed the <em>AzureRm <\/em>module, configured security, and downloaded the module, you are ready to install. To make installation of the module quick and easy, in the <em>PSAzure-Deploy <\/em>folder is a script, <em>Install-PSAzureModule.ps1<\/em>. It will install the module for you in your <em>C:\\Users\\&lt;user-name-here&gt;\\Documents\\WindowsPowerShell\\Modules<\/em> folder. By placing them here, PowerShell will be able to find and automatically load the PSAzure module when you call any of the functions in it.<\/p>\n<p><em>Before we go on, I want to say one thing: Don\u2019t trust me. <\/em><\/p>\n<p><em>Seriously.<\/em><\/p>\n<p><em>Before executing any PowerShell code you\u2019ve downloaded from the internet, be sure to review it completely before running it. Sure, I\u2019m harmless and lovable, but the next sample code you download my have hidden surprises. Since I\u2019m only human (mostly), I can\u2019t guarantee a bug or two might have crept in that might affect you. Always take the time to review any code you download, whether the samples here or anywhere else. <\/em><\/p>\n<h2>Making Azure Login Quick and Easy<\/h2>\n<p>When developing in Azure, constantly having to log in over and over can get annoying. Azure has the ability to save your login information in a context file, then you can just import it using the <strong>Import-AzureRmContext<\/strong> function which will prompt you for a profile context file.<\/p>\n<p>To make this easier, you can use a function found in the <em>PSAzure<\/em> module, <strong>Connect-PSToAzure<\/strong>, that automates this process, but first you have to create the context file itself. How do you do it? Well a sample script supplied with the module, <em>Create-ProfileContext.ps1<\/em>, illustrates this process.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  # Path to demos - Set this to where you want to store your code\r\n  $dir = \u2019C:\\PowerShell\\PSAzure-Module\\PSAzure-Examples\u2019\r\n  Set-Location $dir\r\n  # First login manually\r\n  Connect-AzureRmAccount\r\n  # Now save your context locally (Force will overwrite if there)\r\n  $path = \"$dir\\ProfileContext.ctx\"\r\n  Save-AzureRmContext -Path $path -Force<\/pre>\n<p>The first thing I do in every script is to set my current location, i.e. where the scripts will be executing from. As stated in the previous section, the samples assume you have a folder named <em>C:\\PowerShell<\/em> and put the module in a subfolder called <em>PSAzure-Module<\/em>. You can of course alter this, just be aware that each script in the <em>PSAzure-Demo<\/em> as well as <em>PSAzure-Examples<\/em> has a directory variable at the top you\u2019ll need to update this to reflect your install location.<\/p>\n<p>With that in place, the next step is to login to Azure, if you aren\u2019t already. That\u2019s where the <strong>Connect-AzureRMAccount<\/strong> cmdlet comes in. This is part of the <em>AzureRM<\/em> module, and, if not logged in, it will bring up a web form and ask for your credentials.<\/p>\n<p>Once logged in, you then set up the location to save your credentials to. The example above uses <em>ProfileContext.ctx,<\/em> the default filename in the PSAzure module. You can of course override. Finally, the <strong>Save-AzureRmContext<\/strong> cmdlet writes your credentials out to disk. You should save the context file in the same folder with the scripts you want to execute. For this article, that would be the <em>PSAzure-Examples<\/em> folder.<\/p>\n<p>NOTE: You need to be very careful with this method. If someone were to access your machine with your file, they could use your credentials to access your Azure account. For a learner working with their own MSDN account, for example, it should work fine.<\/p>\n<p>From here on, prior to running any Azure related code, you\u2019ll just need to run the function <strong>Connect-PSToAzure<\/strong> <em>as long as your current path is the directory where the context file is saved, <\/em>and it will log you in using the context file you just created. See the help for this function for more information on valid locations to store the file and how to override both the folder and file name it can use for auto login.<\/p>\n<h2>Discovering Your VM Options<\/h2>\n<p>In order to create your VM in Azure, you must first gather information about what you want to create. There are two basic things you need to provide: what image you want and how big you want the VM to be. You can think of the image as if it were an ISO or DVD used to install an operating system and software. You provide a combination of information to Azure that it can use to retrieve the virtual ISO that you want. The second item you must specify is the virtual hardware configuration. How much RAM, disk space, etc. The script <em>Get-AzureVMOptions.ps1<\/em>, found in the examples folder of the PSAzure module, demonstrates how to retrieve this information.<\/p>\n<p>As the options vary by region, you need to begin by determining the location. For making code reusable, this is generally put in a variable.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$location = 'southcentralus'<\/pre>\n<p>You then get a list of publishers. Publishers are the various companies who put images on Azure that are the basis for virtual machines.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-AzureRmVMImagePublisher -Location $location <\/pre>\n<p>This winds up being a huge list. It\u2019s great so many companies have embraced the Azure platform! You will want to limit the list a bit to make it easier to find what you want.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  Get-AzureRmVMImagePublisher -Location $location |\r\n    Select-Object PublisherName |\r\n    Where-Object PublisherName -Like 'Microsoft*' <\/pre>\n<p>Great, a nice short list &#8212; of about 90 entries. Here are the last few items in the list:<\/p>\n<p>MicrosoftRServer MicrosoftSharePoint MicrosoftSQLServer MicrosoftVisualStudio MicrosoftWindowsDesktop MicrosoftWindowsServer MicrosoftWindowsServerHPCPack<\/p>\n<p>For this example, select SQL Server as the publisher.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$publisher = 'MicrosoftSQLServer'<\/pre>\n<p>With the publisher determined, you need to see all the various images that are available for this publisher.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-AzureRmVMImageOffer -Location $location -PublisherName $publisher<\/pre>\n<p>For brevity a few columns have been excluded from the result set recreated here.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"696\" height=\"644\" class=\"wp-image-78397\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-172.png\" \/><\/p>\n<p>This presents a list with a variety of SQL Servers, such as 2012, 2014, 2016, and so on. In addition, the offer includes the platform, such as Windows Server 2012 or 2016. For this example, pick SQL Server 2016, Service Pack 1, on a Windows Server 2016 platform.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$offer = 'SQL2016SP1-WS2016'<\/pre>\n<p>The next step is to indicate specifically which product in the SQL Server offering you want, i.e., Enterprise, Standard, or Development. To get a list, you can retrieve the SKUs available for this offer.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  Get-AzureRmVMImageSku -Location $location `\r\n                        -PublisherName $publisher `\r\n                        -Offer $offer<\/pre>\n<p>Most of these entries should seem familiar to you if you work in the SQL Server world.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"744\" height=\"219\" class=\"wp-image-78398\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-173.png\" \/><\/p>\n<p>For the purposes of this article, use the SQLDEV SKU.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$sku = 'SQLDEV'<\/pre>\n<p>You need one more piece of information to finalize the exact version of SQL Server Developer, and that is the version number.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  Get-AzureRmVMImage -Location $location `\r\n                     -PublisherName $publisher `\r\n                     -Offer $offer `\r\n                     -Sku $sku<\/pre>\n<p>This will return a list of versions (again a few columns were trimmed for space purposes).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"858\" height=\"217\" class=\"wp-image-78399\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-174.png\" \/><\/p>\n<p>For example, 13.0.500110 is one of the versions available for SQL Server 2016 Developer. You could also choose to use \u2018latest\u2019 and Azure will use the most recent version of the image. In general, using the latest option is generally the method to use, unless you have reasons for a specific version.<\/p>\n<p>At this point, you have all the information you need to get the image, in other words that \u2018virtual ISO\u2019, but not to create the VM. Now that you know the software for the VM, you must choose the virtual hardware.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-AzureRmVMSize -Location $location <\/pre>\n<p>Since the list has over 150 options, just the first few rows are shown below.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1219\" height=\"581\" class=\"wp-image-78400\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-175.png\" \/><\/p>\n<p>The cmdlet above returns a list of configuration names, and for each one the number of cores, amount of memory, and more. Look over the list to find a size that works for you. For our example, we\u2019ll use a small size.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$vmSize = 'Basic_A3' <\/pre>\n<p>With that you now have the information you need to create your VM. While you could garner this information through the Azure user website, using PowerShell ensures you get the exact strings you need to pass into the PSAzure module.<\/p>\n<h2>Laying the Base<\/h2>\n<p>The sample file <em>Create-AzureVM.ps1,<\/em> located in the <em>PSAzure-Examples<\/em> folder, contains the example code that calls the necessary functions within the <em>PSAzure<\/em> module to create a virtual machine. Recreating each line of code here would make this article far too long. I will instead call out key highlights. You should open the file in the PowerShell ISE or another editor, so you can follow along. Be aware that the script does have some extra code just for demonstration purposes. If you decide to adapt this script for creating your own VM\u2019s, you\u2019ll want to strip this out. Additionally, there are some variables to set things like resource and account names that you should customize for your environment.<\/p>\n<p>You can create the Azure objects by running the code in the remaining sections, but you will need to change some variable values. Note that also that I\u2019ll show code here that demonstrates how the functions within the module works. These code snippets will each have a comment, so you will know not to copy and run them.<\/p>\n<p>The sample starts by setting a few variables. All the objects created in the samples use <strong>AzurePSTest<\/strong> at the beginning of their name. As just mentioned, you will want to update these for your purposes. If you are a student, perhaps change the <strong>AzurePSTest<\/strong> text to <strong>YourNameHerePSTest<\/strong>, or in a corporate environment use a variation on your company name.<\/p>\n<p>After setting the variables, a new resource group is created. Except for a slight name change, the code is the same as the new resource group routine from the article mentioned above, PowerShell Functions for Reusablity and Restartability in Azure.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  $resourceGroup = 'AzurePSTestRG'\r\n  $location = 'southcentralus'\r\n  $storageAccount = 'azurepsteststorageacct'\r\n  New-PSResourceGroup -ResourceGroupName $resourceGroup -Location $location<\/pre>\n<p>All virtual machines need to be stored somewhere in Azure. The logical place is in Azure storage. Before you go creating new that new storage account, there are some rules that need to be followed with naming storage accounts. Fortunately, the PSAzure module has some functions to aid you. The first concerns naming; storage account names must be between 3 and 24 characters in length, be numbers or lowercase letters only, with no punctuation or other characters.<\/p>\n<p>You can use the <em>PSAzure<\/em> function <strong>Test-PSAzureValidStorageAccountName<\/strong> to validate that the name is correct. The function returns an object with two properties. The first is <strong>Valid<\/strong>, a Boolean that indicates true for a valid name, or false for invalid. The second is <strong>Reason<\/strong>, a text string which contains which rules the name violated.<\/p>\n<p>In the example below, there is a hard coded a valid name, so for the <em>Create-AzureVM.ps1<\/em> script this isn\u2019t really needed. However, you may wish to adapt the example script to accept parameters. Should that be the case, the ability to test for a valid name will be valuable.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  $storageAccount = 'azurepsteststorageacct'\r\n  $isValid = Test-PSAzureValidStorageAccountName `\r\n                 -StorageAccountName $storageAccount\r\n  \"Valid Storage Account = $($isValid.Valid)\"\r\n  \"Reason: $($isValid.Reason)\"\r\n   <\/pre>\n<p>Behind the scenes, the function is very straightforward. It calls no functions related to Azure from the <em>AzureRM<\/em> module. It is just a series of checks using string properties and methods such as <strong>Length<\/strong> and <strong>ToLower()<\/strong>. Even if you\u2019ve only done basic PowerShell coding, you should find the code easy to understand, so I\u2019ll leave it to you to dive in deeper.<\/p>\n<p>The next concern is storage account name availability. Storage account names must be unique across all of Azure. You read that correctly! Across all the Azure storage accounts all around the world, the name you choose must be unique. There are many suggestions for creating unique names. You could use some combination of company, department, application, and a number. Alternatively, you could include a GUID in the name (PowerShell\u2019s <strong>New-Guid<\/strong> cmdlet could help you here).<\/p>\n<p>Whatever naming convention you arrive at, you\u2019ll want to ensure the name is available. Within <em>PSAzure<\/em>, there is a function named <strong>Test-PSStorageAccountNameAvailability<\/strong> that will return a true if the name is available or false if someone else is already using the name.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  $saAvailable = Test-PSStorageAccountNameAvailability `\r\n                     -StorageAccountName $storageAccount\r\n  Write-Host \"Is the name available? $saAvailable\" <\/pre>\n<p>Within the function, the <em>AzureRM<\/em> cmdlet <strong>Get-AzureRmStorageAccountNameAvailability<\/strong> is called. The cmdlet returns an object with several properties, so the function <strong>Test-PSStorageAccountNameAvailability<\/strong> takes care of accessing the correct property and returning true if the storage account name is available, or false if someone else has taken it already. Be warned, this function can take a long time to run. It needs to check across all of Azure, so it can take several minutes to execute.<\/p>\n<p>What if your naming convention is such that you are certain the name won\u2019t be in use elsewhere? You may just wish to test to see if the storage account name already exists within the confines of your resource group. <em>PSAzure<\/em> can help here, too, with the function <strong>Test-PSStorageAccount<\/strong>.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  $saExists = Test-PSStorageAccount `\r\n                  -StorageAccountName $storageAccount `\r\n                  -ResourceGroupName $resourceGroup `\r\n                  -Location $location\r\n  Write-Host \"Does the account already exist in our environment? $saExists\"<\/pre>\n<p>This function uses the <em>AzureRM<\/em> cmdlet <strong>Get-AzureRMStorageAccount<\/strong> to return a list of the storage accounts that match the name, resource group, and location passed in as parameters. If anything is found it will return a value of true, otherwise false.<\/p>\n<p>Once you have verified you have a storage account name that is valid, not in use, and not already in your resource group, you are ready to create it. <em>PSAzure<\/em> has a function <strong>New-PSStorageAccount<\/strong> to create an account to store VM. You could of course use a storage account that already exists, but for this example you\u2019ll create a new one.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  New-PSStorageAccount -StorageAccountName $storageAccount `\r\n                       -ResourceGroupName $resourceGroup `\r\n                       -Location $location `\r\n                       -Verbose\r\n   <\/pre>\n<p>Behind the scenes, the function first checks to see if a storage account by the name passed in, in this case <strong>azurepsteststoreageacc<em>t<\/em><\/strong>, already exists. It does so by calling the <strong>Test-PSStorageAccount<\/strong> function that was just discussed. This value is returned into a variable, <strong>$saExists<\/strong>. If the storage account doesn\u2019t exist, the variable will be false. The next bit of code found inside the function checks this, and, if it is false, creates the storage account.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\"># function code, do not run\r\nif ($saExists -eq $false)\r\n{\r\n    Write-Verbose \"$fn Creating Storage Account $StorageAccountName\"\r\n    New-AzureRmStorageAccount -ResourceGroupName $ResourceGroupName `\r\n                              -Name $StorageAccountName `\r\n                              -Location $Location `\r\n                              -Type Standard_LRS\r\n}<\/pre>\n<h2>Virtual Networking<\/h2>\n<p>Creating a virtual machine is great, but it won\u2019t be of much use unless it can communicate outside of itself. That\u2019s where virtual networking comes in. To setup a virtual network, often abbreviated vnet, you need to accomplish three things. First is the creation of the virtual network itself. After the network is created, you need to define a security group for it. In essence, the security group defines a firewall. In the process of creating it, the <em>PSAzure<\/em> module automatically creates firewall rules that allow HTTP and RDP (Remote Desktop Protocol) traffic through the firewall. There are functions in <em>PSAzure<\/em> to create security groups at a lower level, allowing one to create alternate rules. This example will demonstrate the most common options.<\/p>\n<p>The final step is to create a virtual NIC, or Network Interface Card. The NIC will form the bridge between the virtual network and the virtual machine, much like a physical network card allows a physical computer to connect to a real network. First off, a few variables are assigned. These will hold names for the security group, network and subnet names. The network addresses for the main network and subnet are also placed into into variables. Finally, a name is assigned to the NIC.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  $networkSecurityGroup = 'AzurePSTestSecurityGroup'\r\n  $networkName = 'AzurePSTestNetwork'\r\n  $networkAddress = '192.168.0.0\/16'\r\n  $subnetName = 'AzurePSTestNetworkSubnet'\r\n  $subnetAddress = '192.168.1.0\/24'\r\n  $nicName = 'ArcanePSTestNIC'<\/pre>\n<p>With variables created, it\u2019s time for the first step: creating the virtual network. The <em>PSAzure<\/em> module has a function, <strong>New-PSAzureVirtualNetwork<\/strong>, to handle this.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  New-PSAzureVirtualNetwork -ResourceGroupName $resourceGroup `\r\n                            -VirtualNetworkName $networkName `\r\n                            -VirtualSubnetName $subnetName `\r\n                            -VirtualNetworkAddress $networkAddress `\r\n                            -VirtualSubnetAddress $subnetAddress `\r\n                            -Location $location `\r\n                            -Verbose<\/pre>\n<p>Like other functions, this one checks to see if the virtual network already exists. If not, the logic flows into an <strong>if<\/strong> statement to create the new virtual network.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\"># function code, do not run\r\n$vNetExists = Test-PSAzureVirtualNetwork `\r\n              -ResourceGroupName $ResourceGroupName `\r\n              -Name $VirtualNetworkName\r\nif ($vNetExists -eq $false)\r\n{\r\n  \u2026<\/pre>\n<p>Next, the subnet is created inside the function. To be more accurate, it\u2019s a subnet configuration. At this point nothing has been created on Azure. Instead a subnet configuration object has been created and stored in the <strong>$subnetConfig<\/strong> variable.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  # function code, do not run    \r\n  $subnetConfig = New-AzureRmVirtualNetworkSubnetConfig `\r\n                        -Name $VirtualSubnetName `\r\n                        -AddressPrefix $VirtualSubnetAddress<\/pre>\n<p>With this configuration now created, the virtual network can be generated on Azure. The network name and address are passed in, along with the subnet configuration object that was just created.<\/p>\n<pre class=\"theme:powershell lang:ps decode:true\"># function code, do not run \r\nNew-AzureRmVirtualNetwork -ResourceGroupName $ResourceGroupName <span style=\"display: inline !important; float: none; background-color: transparent; color: #333333; cursor: text; font-family: Georgia,'Times New Roman','Bitstream Charter',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;\">`<\/span>\r\n                          -Name $VirtualNetworkName `\r\n                          -AddressPrefix $VirtualNetworkAddress `\r\n                          -Subnet $subnetConfig `\r\n                           -Location $Location<\/pre>\n<p>The next step is to create the security group. The <em>PSAzure<\/em> module has a function to do just that.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">New-PSAzureNetworkSecurityGroup -ResourceGroupName $resourceGroup `\r\n                                -NetworkSecurityGroupName $networkSecurityGroup `\r\n                                -Location $location `\r\n                                -Verbose\r\n   <\/pre>\n<p>Within the function, the code checks to see if the security group already exists. To do so, it uses another function within the module, <strong>Test-PSAzureNetworkSecurityGroup<\/strong>. The code inside is much like the others, it checks to see if the object exists, then returns true or false.<\/p>\n<pre class=\"lang:ps theme:powershell-ise \"># function code, do not run\r\n$exists = Test-PSAzureNetworkSecurityGroup `\r\n               -ResourceGroupName $ResourceGroupName `\r\n               -NetworkSecurityGroupName $NetworkSecurityGroupName<\/pre>\n<p>If the security group is not present, the function goes into an if statement. The first thing that happens is the creation of some security rule objects.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\"># function code, do not run\r\n$rdpRule = New-PSAzureRdpSecurityRule$httpRule = New-PSAzureHttpSecurityRule<\/pre>\n<p>Note it has created the rules as objects and placed them into variables. Much like the creation of the subnet configuration, these objects will be used later. Because the RDP and HTTP rules are used so much, <em>PSAzure<\/em> has specific functions to create them. They are similar, so just review the one for generating the RDP rule.<\/p>\n<pre class=\"theme:powershell lang:ps decode:true  \"># function code, do not run\r\n$rdpRule = New-PSAzureSecurityRule -RuleName 'rdp-rule' `\r\n                                   -RuleDescription 'Allow RDP' `\r\n                                   -Priority 100 `\r\n                                   -Port 3389return $rdpRule<\/pre>\n<p>Similar, and simple. This creates a new security rule, with the appropriate settings. RDP uses port 3389; in the HTTP version of the function, port 80 is used. The new rule object is returned to be stored in the variables you saw a moment ago. With that complete, the function can now create the actual security group in Azure.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\"># function code, do not run\r\nNew-AzureRmNetworkSecurityGroup `\r\n               -ResourceGroupName $ResourceGroupName `\r\n               -Location $Location `\r\n               -Name $NetworkSecurityGroupName `\r\n               -SecurityRules $rdpRule, $httpRule<\/pre>\n<p>With the security group created, two of the three steps to create the virtual network setup for the VM are completed. The final step is to create the virtual network card. The function within the PSAzure module to do this is <strong>New-PSAzureVirtualNIC<\/strong>.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  New-PSAzureVirtualNIC -ResourceGroupName $resourceGroup `\r\n                        -NICName $nicName `\r\n                        -VirtualNetworkName $networkName `\r\n                        -NetworkSecurityGroupName $networkSecurityGroup `\r\n                        -Location $location `\r\n                        -Verbose<\/pre>\n<p>Following the pattern of the other functions, it calls a test function to see if the NIC already exists.<\/p>\n<pre class=\"lang:ps theme:powershell-ise \">  # function code, do not run   \r\n  $exists = Test-PSAzureVirtualNIC -ResourceGroupName $ResourceGroupName `\r\n                                   -NICName $NICName<\/pre>\n<p>If not, it then calls an AzureRM function to create the network interface card.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  # function code, do not run    \r\n  $result = Get-AzureRmNetworkInterface `\r\n                   -ResourceGroupName $ResourceGroupName `\r\n                   -Name $NICName<\/pre>\n<h2>Summary<\/h2>\n<p>In this article you laid the foundation for creating Azure Virtual Machines. You saw how to make the login process easier, followed by how to determine what virtual machine images are available. More importantly, the code returned the exact values that must be used in order to generate a virtual machine with that image.<\/p>\n<p>Several fundamental steps came next: creating a resource group and storage account to hold the VM; the creation of the network components; a virtual network was generated; a security group laid on it that defined firewall rules; and, finally, a virtual NIC was created, which will act as a bridge between the virtual network and the virtual machine.<\/p>\n<p>In the <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/create-azure-vms-powershell-part-2\/\">next article<\/a> you\u2019ll actually create the virtual machine. Additionally, you\u2019ll see how to automate the generation of an RDP file to make remoting into the virtual machine easy. The next article will also show the stop and start functions built into the <em>PSAzure<\/em> module, so the machine can be set to only run when it needs to, saving you and your company money. As a final step, you\u2019ll see how to remove everything that was created.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Azure virtual machines are created for many reasons, even just to have an environment to quickly test something out. In this article, Robert Cain demonstrates the first few steps in automating the process with PowerShell. He shows how to gather information needed and set up a resource group, storage and networking needed for the VM. &hellip;<\/p>\n","protected":false},"author":316962,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143513,35],"tags":[95506],"coauthors":[52865],"class_list":["post-78393","post","type-post","status-publish","format-standard","hentry","category-containers-and-virtualization","category-powershell","tag-automate"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/78393","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/316962"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=78393"}],"version-history":[{"count":18,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/78393\/revisions"}],"predecessor-version":[{"id":88828,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/78393\/revisions\/88828"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=78393"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=78393"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=78393"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=78393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}