Azure: Know your Azure VM created date using PowerShell & AzCli.

Do you want know when was your Azure VM created? Then this script will retrive date time created for the given Azure VM(s). Not sure whether there is direct way to retrieve the VM created date, but I couldn’t find any CmdLet to know the VM created date.

However, I have written a PowerShell function to know the VM created date by considering the VM OS disk creation date as VM creation date.

The function accepts the combination of Resource Group Name and VM Name as mandatory parameters or VM object(s), and you will see the output as below…

Screenshot from 2019-09-25 22-21-12

And, here is the script…

And even I have tried the same using Azure Cli as well, and it worked like this…

And you get the output like this

Screenshot from 2019-09-26 09-30-03

Thank you.

 

PowerShell – Get Windows Activation Status across the plant.

One of my friends who is working for a retail store company as a sysadmin was seeking my help in finding the windows server activation status across his organization. Of course, there are many ways to fetch the windows activation status and there are plenty of tools are available online. But I have used CIM Instance with WIM class ‘SoftwareLicensingProduct’ to fetch the activation status for the given server list, and here is the code snippet…

And you will get the status as below…

WindowsActivattionStatus

Thank you.

Windows Terminal and my customization, it’s awesome.

Ever since I started working with Windows Terminal for my PowerShell work, I feel like I live in a royal world, I just simply love it.

Windows Terminal is a terminal emulator for Windows 10 starting from version 18362.0 or higher, and it supports Windows PowerShell, PowerShell Core, Command Prompt, Windows Subsystem for Linux (WSL) & Azure Cloud Shell. As of now, it is not detecting PowerShell 7 Preview but you can add it manually. Windows Terminal enabled with the rich tabbed view and can easily switch between the consoles/terminals with pre-defined short keys.

I have customized my Windows terminal pretty much to go along with PowerShell Core as my default terminal and of course along with all other terminals as well.

Windows Terminal Installation

Windows Terminal is available via Windows Store, and you can download it by clicking here. It can only install on Windows 10 version stating from 18362.0 or higher, and still it is in the preview version and an open-source project available on GitHub.

WindowsTerminal

Now let’s see how I customized for my Windows Terminal for PowerShell Core along with all other terminals…

Windows Terminal first look right after the installation…

WindowsTerminal1

It detected PowerShell Core, Windows PowerShell, Command Prompt and Azure Cloud Shell. Since I have already installed Ubuntu, Debian & Kali-linux WSL, they are also showing up along with the other terminals.

Windows Terminal Customization

By default, it will pick up Windows PowerShell, PowerShell Core, Command Prompt and any WSL is already installed & Azure Cloud Shell, but it will not detect PowerShell 7 Preview. However, you can add it manually…

All the settings and configurations happen through JSON format associated with the windows terminal available in the AppData local folder, and you can take a look here.

Configuration 1: Add PowerShell 7 Preview

You can open the settings file by pressing ctrl+, or click on the WindowsTerminal2 button on top of the Windows Terminal and then click on Settings, and the JSON file will be opened with your default code editor.

If you take a look at the JSON file, every terminal has a separate profile with a bunch of settings to be modified as per your need, and the profile for PowerShell Core looks this…

{
"acrylicOpacity" : 0.5,
"closeOnExit" : true,
"colorScheme" : "Campbell",
"commandline" : "C:\\Program Files\\PowerShell\\6\\pwsh.exe",
"cursorColor" : "#FFCCFF",
"cursorShape" : "bar",
"fontFace" : "Consolas",
"fontSize" : 10,
"guid" : "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
"historySize" : 9001,
"icon" : "ms-appx:///ProfileIcons/{574e775e-4f2a-5b96-ac1e-a2962a402336}.png",
"name" : "PowerShell Core",
"padding" : "0, 0, 0, 0",
"snapOnInput" : true,
"startingDirectory" : "%USERPROFILE%",
"useAcrylic" : false
},

From the settings above, you can you customize the settings like Opacity, Colors, Font and pretty much all you can set as per your need.

Okay now, by using the above profile let’s add PowerShell 7 Preview to Windows Terminal, and the profile looks like this which you can add in the JSON file under profiles section…

{
"acrylicOpacity" : 0.5,
"closeOnExit" : true,
"colorScheme" : "Campbell",
"commandline" : "C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe",
"cursorColor" : "#FFCCFF",
"cursorShape" : "bar",
"fontFace" : "Consolas",
"fontSize" : 10,
"guid" : "{77e7e60a-38e6-45fb-9c5a-44510c02c4e4}",
"historySize" : 9001,
"icon" : "C:\\Program Files\\PowerShell\\7-preview\\assets\\Powershell_av_colors.ico",
"name" : "PowerShell 7 Preview",
"padding" : "0, 0, 0, 0",
"snapOnInput" : true,
"startingDirectory" : "%USERPROFILE%",
"useAcrylic" : false
},

If you notice the settings from the above, I have modified commandline, guid, icon and name

Generate the new guid using New-Guid PowerShell CmdLet.

That’s all, the settings will be applied right after the file is saved, and it doesn’t require to close and reopen the terminal to take any effect, it’s on the fly, you can see the PowerShell 7 Preview in the tab drop-down list including the PowerShell &7 icon.

WindowsTerminal3

WindowsTerminal4

Configuration 2: Setting the background image

I like the most in the Windows Terminal is the background image, and it gives me a different and pleasant feel when I am working with, now let’s see how to add the background image…

"backgroundImage" : "ms-appdata:///roaming/yourimage.jpg",
"backgroundImageOpacity" : 0.75,
"backgroundImageStrechMode" : "fill",

the above 3 keys will do the magic for you, give the path of the image, set the opacity and the strech mode. You can use image or gif as well, and the strech mode you can use uniformToFillbesides fill. My settings are as below…

"backgroundImage" : "C:\\Users\\kiran\\AppData\\Roaming\\Microsoft\\Windows\\PowerShell\\SurrealWallpaper.jpg", 
"backgroundImageOpacity" : 0.25, 
"backgroundImageStrechMode" : "fill",

And it looks this…

WindowsTerminal5

You can still play around with the GIF images, Opacity settings, and color schemas.

Configuration 3: Setup the default terminal

Since I work with PowerShell especially PowerShell 7 Preview, I want to make my PowerShell 7 preview is my default terminal when I open the Windows Terminal. Let’s see how it can be done…

Go to settings, and find the key defaultProfileunder globalssection…

“defaultProfile” : “{2d647fbe-310d-4d05-852f-8f664e6f490c}”,

Currently, PowerShell Core is my default profile, and to make PowerShell & preview as default, you need to replace the guid of PowerShell Core with guid of PowerShell 7 Preview (You can find it under PowerShell 7 Preview profile) in the defaultProfile.

WindowsTerminal6

Configuration 4: Other Settings

Under each profile, there are various settings to change the behavior of the terminal, now let’s see a few…

"acrylicOpacity" : 0.5,
"useAcrylic" : true

These settings will apply background transparency to the terminal window. Note this setting will work only on physical machines, not on VMs.

"colorScheme" : "Campbell"

Color Scheme will change the look & feel of the terminal by applying different colors, by default there are 5 color schemes are available in the settings file, and also you can add different scheme of your own choice.

"fontSize" : 14,

You can set the default font size, and you can change the font size by using ctrl + mouse wheel, like scroll up increases and scroll down decreases the font size directly from the terminal itself.

There are few other where you can give it a try.

In addition to the above customization, I tweaked my profile inside the PowerShell and now it looks like this when I open my Windows Terminal every time…

WindowsTerminal7

You can glance at my sample profile here.

Thank you.

Azure – Host a static website in a storage account using Azure PowerShell.

What is a static website?

By the definition, the static website contains web pages with fixed content and displays the same content to every user on every visit, and static websites are very basic and easy to create.

Now static websites can be built using HTML, CSS and JavaScript and hosted on Azure Storage, and support client-side script execution but not server-side, if the static website needs some data processing or manipulation on server-side you can leverage it to Azure Cognitive Services or Azure Functions.

What is the use?

Static websites are very useful when it doesn’t require high bandwidth, backend support and targeted to limited audiences and mostly for the shorter duration of time.

Some useful areas are…

  • Explain about the project and the roadmap.
    • Just for the sake of presentation in the meeting, create some html documents with the necessary content, upload them to the Azure blob storage and simply access the url from anywhere in the world.
  • Showcase about the products, events and promotions.
    • Sales and marketing teams require nice and colorful web pages to walk through the concepts, so build a website using CSS & HTML, publish it on to Azure blob storage and share the link with the intended audience.
  • Technical Documents & Manuals
    • Host some technical documentation and manuals relating to your project, and share it with the team across the globe for their perusal.

How it works?

When the static website service is enabled on a Azure storage account you need to enter the default document name; and error document name as optional and when the feature is enabled a container named $web will be created if it doesn’t already exist to upload your website files. Files in the $web container are read only, case sensitive and available to access anonymously

How to access?

Available to the public web following this pattern:
https://<ACCOUNT_NAME&gt;.<ZONE_NAME>.web.core.windows.net/<FILE_NAME>

Available through a Blob storage endpoint following this pattern:
https://<ACCOUNT_NAME&gt;.blob.core.windows.net/$web/<FILE_NAME>

It can also available with CDN and SSL support and custom domain name as well.

What is the pricing?

Azure Static website feature is free; the pricing is only for storage. But in addition to the storage costs, data egress will apply and in case Azure CDN is enabled to use a custom domain with an SSL certificate, that will also be applicable.

How to enable the Static Website feature and host a website using Azure PowerShell?

All you need is a valid Azure subscription and follow the steps (in PowerShell)…

  • Login into Azure account.
  • Select the required subscription.
  • Select/Create a Resource Group.
  • Select /Create a Storage Account (StorageV2).
  • Set the current storage account to enable the Static Website feature.
  • Enable the Static Website feature on the current storage account.
  • Upload the website files to the blob storage container ($web).
  • Verify the files uploaded successfully.
  • Retrieve the URL to access the static website.

With the glory of GitHub public repositories, I have cloned a simple website and created my profile page just like that and hosted it on my Azure Storage.

capture

PowerShell – How to use splatting to pass parameters to commands

Many a times we might have come across the situation where we need to execute the lengthy CmdLets/functions having bunch of parameters and exceeds screen width and wrapped down to the next line or need to scroll towards end of the command or need to ignore the new line using the escape character (`).

Splatting, pass the parameter values as a collection in the form of name and value pair, as a hash table or array of values. It makes the command shorter, easy to read and can be re-used. It’s a hash table/array variable though, to pass the paramater values to the command @ sysbol will be used before the variable name instead of $.

SYNTAX

$paramtable = @{
Name1 = 'Value1'
Name2 = 'Value2'
Name3 = 'Value3'
}
C:\> Sample-Command @paramtable
or
C:\> Sample-Command <optional parameters> @paramtable <optional paramaters>

To provide the named parameter values hash table can be used and to provide the positional parameters array can be used. When splatting, it is not necessary to use either hash table or an array only to pass the parameters, positional parameters and/or named parameters can also be used along with.

EXAMPLE: Splatting with hash table

Create a new file using New-Item CmdLet by passing necessary parameters…

EXAMPLE: Splatting with array

Copy a file from one location to other using Copy-Item CmdLet by passing necessary parameters…

An another example…

Just a tip #5 – Select-Object with calculated properties in PowerShell

Sometimes the value from the output might not be in the readable format, the header might be not appropriate for the value or the value is not straightforward, and sometimes there is a need of calculated field altogether…


# Get the total memory in GB from the local computer using the calculated property with Select-Object
Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -Property PSComputerName, `
@{Name = 'Memory in GB'; Expression = {[Math]::Round($_.TotalVisibleMemorySize/1MB)}}

# Get the services where the names are starting with App, and display IsRunning with Yes/No using the calculated property
$IsRunning = @{
Label = "IsRunning"
Expression = {
if($_.Status -eq 'Running') { "Yes" }
else { "No" }
}
}
Get-Service -Name App* | Select-Object -Property Name, DisplayName, $IsRunning

jat5

 

Excel reports using ImportExcel module from PowerShell Gallery.

You know why Excel is still one of the popular reporting tools because it is very easy to use, customizable as per your needs and moreover it is interactive. There are many ways that you can generate excel reports programmatically, but especially when you are using a scripting language like PowerShell there are tons of modules are already available with sophisticated features in the public repositories to reuse in your code, and they are very easy and simple to use.

Please be careful when you are installing the modules from the public repositories, because people around the world they publish their code into the repositories which work for them as per their environment and settings, but it may harmful for your environment. So please ensure the scripts work fine for you in your test environment first and then use it in your production environment.

ImportExcel PowerShell Module by Doug Finke from PowerShellGallery is a very popular and much helpful module, it works even without Excel installed on your computer, and again it’s an open source project in GitHub. This module is very rich in features and compact to use in your code, and I find this module is very useful and helpful.

Now let’s get started with ImportModule module in PowerShell Core

SincePowerShellGalleryis a default repository in PowerShell, you don’t need to set the repository again, just ensure that you havePSGalleryas a default repository…

Get-PSRepository

Now it’s time to get the module installed and imported to the session…

# Find the module
Find-Module -Name ImportModule

# Install the modules
Find-Module -Name ImportModule | Install-Module 
Install-Module -Name Install-Module

# Update the module
Update-Module -Name ImportExcel

# Verify the module is installed
Get-Module -Name ImportExcel -ListAvailable

# Import the module
Import-Module -Name ImportExcel

import-excel-3

To check the CmdLets available in the ImportExcel module, run the CmdLet below…

Get-Command -Module ImportExcel

Now let’s see how to export the data to excel and various options…

Export-Excel CmdLet will do all the magic with various parameters; to simply export the data to excel, just pipe the output to Export-Excel, this will export the data to excel, apply filters, auto size the columns and pop up the excel window, but this will not save the file to disk.

Get-Service | Select Name, DisplayName, Status | Export-Excel

import-excel-4

… just to export the data to excel and save the file to disk, use -Path flag with Export-Excel …

Get-Service | Select Name, DisplayName, Status | Export-Excel -Path C:\Test.xlsx

import-excel-5

Observe the data in the excel opened after the file was created, where the columns are compact, not readable and no filters applied. By default without any parameters Export-ExcelCmdLet will not save the file to disk, show the window, apply filters, auto size the columns, but if we use -Path and want to pop up the window, apply filters and auto size the columns we need to use the -Show, -AutoSize, -AutoFilter flags…

# To Show the window after the file save to disk
Get-Service | Select Name, DisplayName, Status | Export-Excel -Path .\Test.xlsx -Show

# To apply filters, and allow autosize the columns
Get-Service | Select Name, DisplayName, Status | Export-Excel -Path .\Test.xlsx -Show -AutoSize -AutoFilter

Now let’s see formating the text in the excel reports…

# Get the services exported to excel and highlight the services state seperately for services running and services are stopped.
$ConTxt1 = New-ConditionalText -Text 'Stopped' -ConditionalTextColor Red -BackgroundColor Yellow
$ConTxt2 = New-ConditionalText -Text 'Running' -ConditionalTextColor Yellow -BackgroundColor Green
Get-Service | Select Status, Name, DisplayName | Export-Excel -Path .\Test.xlsx -AutoSize -Show -ConditionalFormat $ConTxt1, $ConTxt2
# '-ConditionalFormat' parameter accepts arrays

import-excel-6Setting the icons to the values to represent the changes with in the given range…

# Get the processes, and represent the changes in the memory with the icons
$ConFmt = New-ConditionalFormattingIconSet -Range "C:C" -ConditionalFormat FiveIconSet -IconType Arrows
Get-Process | Select Company, Name, PM, Handles | Export-Excel -Path .\Process.xlsx -Show -AutoSize -AutoFilter -ConditionalFormat $ConFmt

# Also club it with the conditional text
$ConTxt = New-ConditionalText -Text 'Microsoft' -ConditionalTextColor Yellow -BackgroundColor Green
Get-Process | Select Company, Name, PM, Handles | Export-Excel -Path .\Process.xlsx -Show -AutoSize -AutoFilter -ConditionalFormat $ConFmt, $ConTxt

import-excel-7

Now let’s see some creating pivot tables and charts…

# Get the services and identify the number of service are running & stopped and the services count per start type
$Data = Get-Service | Select-Object Status, Name, DisplayName, StartType | Sort-Object StartType

# Parmaters in a hashtable
$Param = @{
Show = $true
AutoSize = $true
IncludePivotTable = $true
PivotRows = 'StartType' 
PivotData = 'StartType' 
PivotColumns = 'Status'
}

# Create the pivot table
$Data | Export-Excel -Path C:\GitRepo\Test.xlsx @Param

import-excel-8

# Get the services and identify the number of service are running & stopped and the services count per start type
$Data = Get-Service | Select-Object Status, Name, DisplayName, StartType | Sort-Object StartType

# Parmaters in a hashtable
$Param = @{
Show = $true
AutoSize = $true
PivotRows = 'StartType' 
PivotData = 'StartType'
IncludePivotChart = $true
ChartType = 'PieExploded3D'
}

# Create the pivot charts
$Data | Export-Excel -Path C:\GitRepo\Test.xlsx @Param

import-excel-9

There are plenty of options are available, so please explore the all the features in the ImportExcel and make the best use of this module. You can also achive the same by writting your own code, but this is very compact and easy to use.

Many thanks to Doug Finke! #ImportExcel

 

‘Clear-RecycleBin’ is not recognized as the name of a cmdlet in PowerShell Core

Recently I have upgraded to PowerShell Core and slowly switching from Windows PowerShell to PowerShell Core. I have noticed quite a few CmdLets are missing in the PowerShell Core, since it became an open source and supports on cross-platform most of the platform dependent CmdLets won’t work on the other platforms. I usually clear my temp folders and recyclebin in all my computers frequently, and noticed Clear-RecycleBin CmdLet is not a valid CmdLet in PowerShell Core…

Clear-RecycleBin : The term 'Clear-RecycleBin' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ Clear-RecycleBin -Force
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Clear-RecycleBin:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

So I have decided to create a PowerShell function to achieve almost the the same functionality of Clear-RecycleBin using .NET class, and here is the function…

Output…

empty-recyclebin

Note: I have not added the -DriveLetter flag, since I want to clear the recyclebin from all the drives, if you want clear the recyclebin from a specific drive, you need add the driveLetter argument to the Empty method in the C# code and add the -DriveLetter parameter to the PowerShell function.

Thank you.

Just a tip #4 – Download a zip file from the internet and extract using PowerShell.

The code below will download the .zip file from the internet, then extracts the files from the zip and opens the extracted folder…


$Url = 'https://download.sysinternals.com/files/BGInfo.zip'
$ZipFile = 'C:\ZipFolder\' + $(Split-Path -Path $Url -Leaf)
$Destination= 'C:\Extracted\'

Invoke-WebRequest -Uri $Url -OutFile $ZipFile

$ExtractShell = New-Object -ComObject Shell.Application
$Files = $ExtractShell.Namespace($ZipFile).Items()
$ExtractShell.NameSpace($Destination).CopyHere($Files)
Start-Process $Destination

Create new Azure VM using PowerShell (Az)

Deploying the Virtual Machines in the Azure cloud using the templates is the best way to create the VM to satisfy the attributes like quick, consistent, reusable and handles the dependency among the resources. However PowerShell has the flexibility to deploy the VMs with ease and allows the user to choose the required parameters necessary for the particular deployment alone without even touching the code. Ofcourse this approach is also quick and reusable, but the user has to ensure the consistency and dependency among the resources if required while creating the resources in the Azure cloud.

Since the new Azure PowerShell Module Az 1.0.1 is released, I have written the scripts using the Az module CmdLets. So please install Az module on Windows PowerShell or PowerShell Core, import the module and connect to Azure account using ‘Connect-AzAccount’.

Add required parameters to the script…

#requires -Modules Az
param
(
[Parameter(Mandatory=$true)]
[string] $ResourceGroupName, # Resource Group
[Parameter(Mandatory=$true)]
[string] $VMName, # VM Name
[Parameter(Mandatory=$true)]
[string] $Location, # Location
[Parameter(Mandatory=$true)]
[ValidateSet('Windows','Linux')]
[string] $OSType, # OS Type (Windows/Linux)
[Parameter(Mandatory=$true)]
[string] $VirtualNetworkName, # VNet
[Parameter(Mandatory=$true)]
[string] $SubnetName, # Subnet
[Parameter(Mandatory=$true)]
[string] $SecurityGroupName, # NSG
[Parameter(Mandatory=$false)]
[string] $VMSize, # VM Size
[Parameter(Mandatory=$false)]
[switch] $AssignPublicIP, # Assign PIP
[Parameter(Mandatory=$false)]
[pscredential]$VMCredential, # VM login credential
[Parameter(Mandatory=$false)]
[Int[]] $AllowedPorts # NSG rules
)

Ensure you are connected to Azure subscription, if the script exits then connect to Azure subscription using Connect-AzAccount CmdLet, and this is a browser-based authentication.

# Verify Login
if( -not $(Get-AzContext) ) { return }

Ensure that there is no existing vm with the same name in the resurce group. If there is a VM already exists then exit the script.

# Verify VM doesn't exist
[Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine] `
$VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName -ErrorAction SilentlyContinue
if($null -ne $VM) { return }

Create VM login credentials, if not provided along with the script…

# Create user object
if (-not $PSBoundParameters.ContainsKey('VMCredential'))
{
[pscredential] $VMCredential = Get-Credential -Message 'Please enter the vm credentials'
}

# Verify credential
if ($VMCredential.GetType().Name -ne "PSCredential") { return }

The script identifies the existing resources with the names provided, if exist then they will be used and if they don’t exist then will be created with different names.

Two things that you need to choose based on your requirements, one is the VM Size and the other is OS Image (Sku)…

# Lists all the VM Sizes available in South India region
PS C:\> Get-AzVMSize -Location southindia

To retrieve the OS Skus, I have written an another post ‘List of available Azure VM Image skus using new Azure PowerShell module Az.‘, please refer…

Now the main block starts from here…

Github Link: https://github.com/kpatnayakuni/PowerShell/blob/master/Create-AzVM.ps1


# Verify/Create a resource group
[Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup] $ResourceGroup = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue
if ($null -eq $ResourceGroup)
{
$ResourceGroup = New-AzResourceGroup -Name $ResourceGroupName -Location $Location
}

# Verify the virtual network
[Microsoft.Azure.Commands.Network.Models.PSVirtualNetwork] $VNet = Get-AzVirtualNetwork -Name $VirtualNetworkName -ResourceGroupName $ResourceGroup.ResourceGroupName -ErrorAction SilentlyContinue
if ($null -eq $VNet)
{
[Microsoft.Azure.Commands.Network.Models.PSSubnet] $SubnetConfig = New-AzVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix 192.168.1.0/24
$VNet = New-AzVirtualNetwork -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -Name $VirtualNetworkName -AddressPrefix 192.168.0.0/16 -Subnet $SubnetConfig
}
else 
{
[Microsoft.Azure.Commands.Network.Models.PSSubnet[]] $Subnets = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $VNet
$SubnetConfig = $Subnets | Where-Object -FilterScript {$_.Name -eq $SubnetName}
if ($null -eq $SubnetConfig)
{
$VNetAddressPrefixes = $VNet.AddressSpace.AddressPrefixes
$AddressPrefix = @($VNetAddressPrefixes.Split('.'))
$AddressPrefix[2] = [int]($Subnets.AddressPrefix|Measure-Object -Maximum).Maximum.ToString().Split('.')[2] + 1
$AddressPrefix = $AddressPrefix -join '.'
$VNet | Add-AzVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix $AddressPrefix | Set-AzVirtualNetwork
}
}

[Microsoft.Azure.Commands.Network.Models.PSSubnet] $Subnet = Get-AzVirtualNetworkSubnetConfig -Name $SubnetName -VirtualNetwork $VNet

# Create a public IP address and specify a DNS name
if ($PSBoundParameters.ContainsKey('AssignPublicIP'))
{
[string] $PipName = $VMName + '-pip'
[Microsoft.Azure.Commands.Network.Models.PSPublicIpAddress] $VerifyPip = Get-AzPublicIpAddress -Name $PipName -ResourceGroupName $ResourceGroup.ResourceGroupName -ErrorAction SilentlyContinue
if ($null -ne $VerifyPip) 
{ 
$PipName = $VMName + '-pip-' + $(Get-Random).ToString()
}
[Microsoft.Azure.Commands.Network.Models.PSPublicIpAddress] $PublicIP = New-AzPublicIpAddress -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -Name $PipName -AllocationMethod Static -IdleTimeoutInMinutes 4
}

# Create/Select a network security group
[Microsoft.Azure.Commands.Network.Models.PSNetworkSecurityGroup] $NSG = Get-AzNetworkSecurityGroup -Name $SecurityGroupName -ResourceGroupName $ResourceGroup.ResourceGroupName -ErrorAction SilentlyContinue
if ($null -eq $NSG)
{
# Create an inbound network security group rules
if ($PSBoundParameters.ContainsKey('AllowedPorts'))
{
[System.Array] $NsgRules = @()
[int] $Priority = 1000
foreach ($Port in $AllowedPorts)
{
[Microsoft.Azure.Commands.Network.Models.PSSecurityRule] $Rule = New-AzNetworkSecurityRuleConfig -Name "Allow_$Port" -Protocol Tcp -Direction Inbound -Priority $Priority -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange $Port -Access Allow
$Priority++
$NsgRules += $Rule
}
[Microsoft.Azure.Commands.Network.Models.PSNetworkSecurityGroup] $NSG = New-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -Name $SecurityGroupName -SecurityRules $NsgRules
}
else
{
$NSG = New-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -Name $SecurityGroupName
}
}
else 
{
# Add an inbound network security group rules, if missing any
if ($PSBoundParameters.ContainsKey('AllowedPorts'))
{
[int[]] $NSGAllowedPorts = $NSG.SecurityRules | Where-Object -FilterScript {$_.Access -eq "Allow"} | Select-Object -ExpandProperty DestinationPortRange
[int[]] $PortsToAllow = $AllowedPorts | Where-Object -FilterScript {$_ -notin $NSGAllowedPorts}
[int] $Priority = ($NSG.SecurityRules.Priority|Measure-Object -Maximum).Maximum + 100
if ($PortsToAllow.Count -gt 0)
{
foreach($Port in $PortsToAllow)
{
$NSG | Add-AzNetworkSecurityRuleConfig -Name "Allow_$Port" -Protocol Tcp -Direction Inbound -Priority $Priority -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange $Port -Access Allow | Set-AzNetworkSecurityGroup
}
}
}
}

# Create a virtual network card and associate with public IP address and NSG
[string] $NICName = "$VMName-nic"
[Microsoft.Azure.Commands.Network.Models.PSNetworkInterface] $NIC = Get-AzNetworkInterface -Name $NICName -ResourceGroupName $ResourceGroup.ResourceGroupName -ErrorAction SilentlyContinue
if ($null -ne $NIC)
{
$NICName = $VMName + "-nic-" + $(Get-Random).ToString()
}
[Microsoft.Azure.Commands.Network.Models.PSNetworkInterface] $NIC = New-AzNetworkInterface -Name $NICName -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -SubnetId $Subnet.Id -NetworkSecurityGroupId $NSG.Id 
if ($PSBoundParameters.ContainsKey('AssignPublicIP'))
{
$NIC | Set-AzNetworkInterfaceIpConfig -Name $NIC.IpConfigurations[0].Name -PublicIpAddressId $PublicIP.Id -SubnetId $Subnet.Id | Set-AzNetworkInterface | Out-Null
}

# VM Size
if($PSBoundParameters.ContainsKey('VMSize') -eq $false )
{
$VMSize = 'Standard_A1'
}

# OS Type
[hashtable] $VMSourceImage = @{PublisherName='';Offer='';Sku=''}
switch ($OSType) {
'Windows' { $VMSourceImage.PublisherName = 'MicrosoftWindowsServer'
$VMSourceImage.Offer = 'WindowsServer'
$VMSourceImage.Sku = '2016-Datacenter'
}
'Linux'{
$VMSourceImage.PublisherName = 'Canonical'
$VMSourceImage.Offer = 'UbuntuServer'
$VMSourceImage.Sku = '18.10-DAILY'
}  
}

# Create a virtual machine configuration
[Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine] $VMConfig = New-AzVMConfig -VMName $VMName -VMSize $VMSize 
if ($OSType -eq 'Windows')
{
$VMConfig | Set-AzVMOperatingSystem -Windows -ComputerName $VMName -Credential $VMCredential | Out-Null
}
else 
{
$VMConfig | Set-AzVMOperatingSystem -Linux -ComputerName $VMName -Credential $VMCredential | Out-Null
}
$VMConfig | Set-AzVMSourceImage -PublisherName $VMSourceImage.PublisherName -Offer $VMSourceImage.Offer -Skus $VMSourceImage.Sku -Version latest | Out-Null
$VMConfig | Add-AzVMNetworkInterface -Id $NIC.Id | Out-Null
$VMConfig | Set-AzVMBootDiagnostics -Disable | Out-Null

# Create a virtual machine
New-AzVM -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -VM $VMConfig

To create a Windows VM…


.\Create-AzVM.ps1 -ResourceGroupName test-rg `
-VMName testvm -Location southindia `
-OSType Windows `
-VirtualNetworkName test-vnet `
-SubnetName testnet `
-SecurityGroupName test-nsg `
-AssignPublicIP `
-AllowedPorts 3389 `
-VMCredential $cred `
-Verbose

To create a Linux VM…


.\Create-AzVM.ps1 -ResourceGroupName test-rg `
-VMName testvm -Location southindia `
-OSType Linux `
-VirtualNetworkName test-vnet `
-SubnetName testnet `
-SecurityGroupName test-nsg `
-AssignPublicIP `
-AllowedPorts 22 `
-VMCredential $cred `
-Verbose